Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
OAuthManagementResource.cs
1using System;
3using System.Threading.Tasks;
4using Waher.Content;
9
11{
18 {
22 public const string DefaultResourcePath = "/oauth/registration";
23
30 {
31 }
32
39 string ResourceName)
41 {
43 }
44
48 public override bool HandlesSubPaths => true;
49
53 public bool AllowsGET => true;
54
58 public bool AllowsPUT => true;
59
63 public bool AllowsDELETE => true;
64
73 {
74 return null;
75 }
76
83 public async Task GET(HttpRequest Request, HttpResponse Response)
84 {
85 OAuthClientInformation? ClientInfo = await this.GetClientInformation(Request, Response);
86 if (ClientInfo is null)
87 return;
88
89 Dictionary<string, object> ResponseObj = this.RegistrationResponse(Request, Response, ClientInfo);
90
91 Response.StatusCode = 200;
92 Response.StatusMessage = "OK";
93
94 await Response.Return(ResponseObj);
95 }
96
97 internal Dictionary<string, object> RegistrationResponse(HttpRequest Request,
98 HttpResponse Response, OAuthClientInformation ClientInfo)
99 {
100 Dictionary<string, object> ResponseObj = new Dictionary<string, object>
101 {
102 ["client_id"] = ClientInfo.ClientId!,
103 ["client_id_issued_at"] = (long)ClientInfo.Created.Subtract(JSON.UnixEpoch).TotalSeconds
104 };
105
106 if (!(ClientInfo.RedirectUris is null))
107 ResponseObj["redirect_uris"] = ClientInfo.RedirectUris;
108
109 if (!(ClientInfo.GrantTypes is null))
110 ResponseObj["grant_types"] = ClientInfo.GrantTypes;
111
112 if (!(ClientInfo.ResponseTypes is null))
113 ResponseObj["response_types"] = ClientInfo.ResponseTypes;
114
115 if (!(ClientInfo.Contacts is null))
116 ResponseObj["contacts"] = ClientInfo.Contacts;
117
118 if (!string.IsNullOrEmpty(ClientInfo.TokenEndpointAuthMethod))
119 ResponseObj["token_endpoint_auth_method"] = ClientInfo.TokenEndpointAuthMethod;
120
121 if (!string.IsNullOrEmpty(ClientInfo.ClientName))
122 ResponseObj["client_name"] = ClientInfo.ClientName;
123
124 if (!string.IsNullOrEmpty(ClientInfo.SoftwareId))
125 ResponseObj["software_id"] = ClientInfo.SoftwareId;
126
127 if (!string.IsNullOrEmpty(ClientInfo.SoftwareVersion))
128 ResponseObj["software_version"] = ClientInfo.SoftwareVersion;
129
130 if (!string.IsNullOrEmpty(ClientInfo.ClientUri))
131 ResponseObj["client_uri"] = ClientInfo.ClientUri;
132
133 if (!string.IsNullOrEmpty(ClientInfo.LogoUri))
134 ResponseObj["logo_uri"] = ClientInfo.LogoUri;
135
136 if (!string.IsNullOrEmpty(ClientInfo.TosUri))
137 ResponseObj["tos_uri"] = ClientInfo.TosUri;
138
139 if (!string.IsNullOrEmpty(ClientInfo.PolicyUri))
140 ResponseObj["policy_uri"] = ClientInfo.PolicyUri;
141
142 if (!string.IsNullOrEmpty(ClientInfo.JwksUri))
143 ResponseObj["jwks_uri"] = ClientInfo.JwksUri;
144
145 if (!(ClientInfo.Jwks is null))
146 ResponseObj["jwks"] = ClientInfo.Jwks;
147
148 if ((ClientInfo.Scopes?.Length ?? 0) > 0)
149 ResponseObj["scope"] = string.Join(' ', ClientInfo.Scopes);
150
151 if (ClientInfo.ClientSecretExpiresAt.HasValue)
152 {
153 ResponseObj["client_secret_expires_at"] = ClientInfo.ClientSecretExpiresAt.HasValue ?
154 (long)ClientInfo.ClientSecretExpiresAt.Value.Subtract(JSON.UnixEpoch).TotalSeconds : 0L;
155 }
156
157 ResponseObj["registration_access_token"] = ClientInfo.AccessToken!;
158 ResponseObj["registration_client_uri"] = Request.Header.GetURL(false, false);
159
160 Response.SetHeader("Cache-Control", "max-age=0, no-cache, no-store");
161 Response.SetHeader("Pragma", "no-cache");
162
163 return ResponseObj;
164 }
165
172 public async Task PUT(HttpRequest Request, HttpResponse Response)
173 {
175 {
176 await ServiceUnavailable(Response, "server_error", "Registration resource not available.");
177 return;
178 }
179
180 OAuthClientInformation? ClientInfo = await this.GetClientInformation(Request, Response);
181 if (ClientInfo is null)
182 return;
183
185 await this.Environment.RegistrationResource.ParseRegistrationRequest(
186 Request, Response, true);
187
188 if (Parsed is null)
189 return;
190
191 OAuthRegistrationResource.RegistrationRequest RegistrationRequest = Parsed.Request;
192
193 if (RegistrationRequest.ClientId != ClientInfo.ClientId)
194 {
195 await BadRequest(Response, "invalid_request", "Invalid registration or access token.");
196 return;
197 }
198
199 IRegistration? Registration = await Parsed.DynamicUserSource.UpdateUser(
200 ClientInfo.ClientId!, RegistrationRequest);
201
202 if (Registration is null || ClientInfo.ClientId != Registration.ClientId)
203 {
204 await Forbidden(Response, "access_denied",
205 "Not permitted to update client.");
206 return;
207 }
208
209 bool UpdateRedirectUris = !AreEqual(ClientInfo.RedirectUris, RegistrationRequest.RedirectUris);
210
211 ClientInfo.Updated = DateTime.UtcNow;
212 ClientInfo.RemoteEndPoint = RegistrationRequest.RemoteEndPoint;
213 ClientInfo.RedirectUris = RegistrationRequest.RedirectUris;
214 ClientInfo.GrantTypes = RegistrationRequest.GrantTypes;
215 ClientInfo.ResponseTypes = RegistrationRequest.ResponseTypes;
216 ClientInfo.TokenEndpointAuthMethod = RegistrationRequest.TokenEndpointAuthMethod;
217 ClientInfo.ClientName = RegistrationRequest.ClientName;
218 ClientInfo.SoftwareId = RegistrationRequest.SoftwareId;
219 ClientInfo.SoftwareVersion = RegistrationRequest.SoftwareVersion;
220 ClientInfo.ClientUri = RegistrationRequest.ClientUri?.ToString();
221 ClientInfo.LogoUri = RegistrationRequest.LogoUri?.ToString();
222 ClientInfo.TosUri = RegistrationRequest.TosUri?.ToString();
223 ClientInfo.PolicyUri = RegistrationRequest.PolicyUri?.ToString();
224 ClientInfo.JwksUri = RegistrationRequest.JwksUri?.ToString();
225 ClientInfo.Scopes = RegistrationRequest.Scopes;
226 ClientInfo.Contacts = RegistrationRequest.Contacts;
227 ClientInfo.Jwks = RegistrationRequest.Jwks;
228 ClientInfo.MetaData = RegistrationRequest.MetaData;
229
230 await Database.Update(ClientInfo);
231
232 if (UpdateRedirectUris)
233 {
235 ClientInfo.ClientId));
236 await OAuthRegistrationResource.AddRedirectUrls(Registration.ClientId,
237 RegistrationRequest.RedirectUris);
238 }
239
240 Dictionary<string, object> ResponseObj = this.RegistrationResponse(Request,
241 Response, ClientInfo);
242
243 Response.StatusCode = 200;
244 Response.StatusMessage = "OK";
245
246 await Response.Return(ResponseObj);
247 }
248
249 private static bool AreEqual(string[]? A1, string[]? A2)
250 {
251 if (A1 is null ^ A2 is null)
252 return false;
253
254 if (A1 is null)
255 return true;
256
257 int i, c = A1.Length;
258 if (A2!.Length != c)
259 return false;
260
261 HashSet<string> Set1 = new HashSet<string>(A1);
262 HashSet<string> Set2 = new HashSet<string>(A2);
263
264 if (Set1.Count != Set2.Count)
265 return false;
266
267 for (i = 0; i < c; i++)
268 {
269 if (!Set1.Contains(A2[i]))
270 return false;
271 }
272
273 return true;
274 }
275
276 private async Task<OAuthClientInformation?> GetClientInformation(HttpRequest Request,
277 HttpResponse Response)
278 {
279 string ClientId = Request.SubPath;
280 if (string.IsNullOrEmpty(ClientId))
281 {
282 await BadRequest(Response, "invalid_request", "Invalid client URI.");
283 return null;
284 }
285
286 string? Authorization = Request.Header.Authorization?.Value;
287 if (string.IsNullOrEmpty(Authorization) || !Authorization.StartsWith("Bearer "))
288 {
289 await Unauthorized(Response, "access_denied", "Missing or invalid registration token.",
290 Array.Empty<string>());
291 return null;
292 }
293
294 Authorization = Authorization[7..].Trim();
295
296 try
297 {
299 ClientId[1..]);
300
301 if (Result is null || Result.AccessToken != Authorization)
302 {
303 await Unauthorized(Response, "access_denied", "Missing or invalid registration token.",
304 Array.Empty<string>());
305 return null;
306 }
307
308 return Result;
309 }
310 catch (Exception)
311 {
312 await ServiceUnavailable(Response, "server_error",
313 "Unable to retrieve client information.");
314 return null;
315 }
316 }
317
324 public async Task DELETE(HttpRequest Request, HttpResponse Response)
325 {
326 OAuthClientInformation? ClientInfo = await this.GetClientInformation(Request, Response);
327 if (ClientInfo is null)
328 return;
329
330 if (!(this.Users is IDynamicUserSource DynamicUserSource))
331 {
332 await ServiceUnavailable(Response, "server_error", "Client registration service not available.");
333 return;
334 }
335
336 if (ClientInfo.ClientId is null ||
337 !await DynamicUserSource.DeleteUser(ClientInfo.ClientId, Request.RemoteEndPoint))
338 {
339 await Forbidden(Response, "access_denied",
340 "Not permitted to delete client.");
341 return;
342 }
343
344 FilterFieldEqualTo Filter = new FilterFieldEqualTo("ClientId", ClientInfo.ClientId);
347
348 Response.StatusCode = 204;
349 Response.StatusMessage = "No Content";
350
351 Response.SetHeader("Cache-Control", "max-age=0, no-cache, no-store");
352 Response.SetHeader("Pragma", "no-cache");
353
354 await Response.SendResponse();
355 }
356 }
357}
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:20
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
HttpFieldAuthorization Authorization
Authorization HTTP Field header. (RFC 2616, §14.8)
string GetURL()
Gets an absolute URL for the request.
Represents an HTTP request.
Definition: HttpRequest.cs:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:243
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
Definition: HttpRequest.cs:194
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
Task Return(Exception ex)
Returns an error to the client.
Contains information about an OAuth client, as defined in RFC 7591.
DateTime? ClientSecretExpiresAt
When the client secret expires, if one is defined.
string? TosUri
URL string that points to a human-readable terms of service document for the client that describes a ...
DateTime Created
When the client information was created.
string? LogoUri
URL string that references a logo for the client.
string?[] GrantTypes
Array of OAuth 2.0 grant type strings that the client can use at the token endpoint.
string? SoftwareVersion
A version identifier string for the client software identified by "software_id".
string? AccessToken
Access token required to update or delete the client information object.
string? PolicyUri
URL string that points to a human-readable privacy policy document that describes how the deployment ...
string?[] Scopes
List of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use whe...
string? TokenEndpointAuthMethod
String indicator of the requested authentication method for the token endpoint.
string?[] Contacts
Array of strings representing ways to contact people responsible for this client, typically email add...
string?[] RedirectUris
Array of redirection URI strings for use in redirect-based flows such as the authorization code and i...
string? ClientName
Human-readable string name of the client to be presented to the end-user during authorization.
string? ClientUri
URL string of a web page providing information about the client.
string? SoftwareId
A unique identifier string (e.g., a Universally Unique Identifier (UUID)) assigned by the client deve...
string? JwksUri
URL string referencing the client's JSON Web Key (JWK) Set [RFC7517] document, which contains the cli...
Dictionary< string, object?>? Jwks
Client's JSON Web Key Set [RFC7517] document value, which contains the client's public keys.
string?[] ResponseTypes
Array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint.
Contains information about a redirect URI using by an OAuth client.
OAuthRegistrationResource RegistrationResource
Registered registration resource
void Register(OAuthAuthorizeResource? AuthorizeResource)
Registers an authorization resource.
bool HasRegistrationResource
If the environment has a registered registration resource
OAUTH client management resource, as defined in RFCs 7591. https://datatracker.ietf....
override? HttpAuthenticationScheme[] GetAuthenticationSchemes(HttpRequest Request)
Any authentication schemes used to authenticate users before access is granted to the corresponding r...
async Task PUT(HttpRequest Request, HttpResponse Response)
Executes the PUT method on the resource.
OAuthManagementResource(OAuth2Environment Environment, string ResourceName)
OAUTH client management resource, as defined in RFCs 7591.
async Task GET(HttpRequest Request, HttpResponse Response)
Executes the GET method on the resource.
async Task DELETE(HttpRequest Request, HttpResponse Response)
Executes the DELETE method on the resource.
override bool HandlesSubPaths
If the resource handles sub-paths.
OAuthManagementResource(OAuth2Environment Environment)
OAUTH client management resource, as defined in RFCs 7591.
const string DefaultResourcePath
OAUTH client management resource, as defined in RFCs 7591.
OAUTH dynamic registration resource, as defined in RFCs 7591 and 7592. https://datatracker....
Abstract base class for OAUTH resources.
IUserSource? Users
Data source for users, used to authenticate clients.
OAuth2Environment Environment
OAUTH2 environment, used to access clients, tokens, and other resources.
static Task ServiceUnavailable(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Service Unavailable error back to the client.
static Task BadRequest(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Bad Request error back to the client.
static Task Unauthorized(HttpResponse Response, string ErrorCode, string ErrorDescription, string[] Challenges)
Returns an Unauthorized error back to the client.
static Task Forbidden(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Forbidden error back to the client.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
Definition: Database.cs:1838
This filter selects objects that have a named field equal to a given value.
Base class for all filter classes.
Definition: Filter.cs:15
DELETE Interface for HTTP resources.
GET Interface for HTTP resources.
PUT Interface for HTTP resources.
A dynamic user source, supporting registering new users.
Dynamic client registration, as defined in RFC 7591.
Definition: IRegistration.cs:9
string ClientId
OAuth 2.0 client identifier string.