3using System.Threading.Tasks;
86 if (ClientInfo is
null)
89 Dictionary<string, object> ResponseObj = this.RegistrationResponse(Request, Response, ClientInfo);
91 Response.StatusCode = 200;
92 Response.StatusMessage =
"OK";
94 await Response.
Return(ResponseObj);
97 internal Dictionary<string, object> RegistrationResponse(
HttpRequest Request,
100 Dictionary<string, object> ResponseObj =
new Dictionary<string, object>
102 [
"client_id"] = ClientInfo.
ClientId!,
110 ResponseObj[
"grant_types"] = ClientInfo.
GrantTypes;
116 ResponseObj[
"contacts"] = ClientInfo.
Contacts;
121 if (!
string.IsNullOrEmpty(ClientInfo.
ClientName))
122 ResponseObj[
"client_name"] = ClientInfo.
ClientName;
124 if (!
string.IsNullOrEmpty(ClientInfo.
SoftwareId))
125 ResponseObj[
"software_id"] = ClientInfo.
SoftwareId;
130 if (!
string.IsNullOrEmpty(ClientInfo.
ClientUri))
131 ResponseObj[
"client_uri"] = ClientInfo.
ClientUri;
133 if (!
string.IsNullOrEmpty(ClientInfo.
LogoUri))
134 ResponseObj[
"logo_uri"] = ClientInfo.
LogoUri;
136 if (!
string.IsNullOrEmpty(ClientInfo.
TosUri))
137 ResponseObj[
"tos_uri"] = ClientInfo.
TosUri;
139 if (!
string.IsNullOrEmpty(ClientInfo.
PolicyUri))
140 ResponseObj[
"policy_uri"] = ClientInfo.
PolicyUri;
142 if (!
string.IsNullOrEmpty(ClientInfo.
JwksUri))
143 ResponseObj[
"jwks_uri"] = ClientInfo.
JwksUri;
145 if (!(ClientInfo.
Jwks is
null))
146 ResponseObj[
"jwks"] = ClientInfo.
Jwks;
148 if ((ClientInfo.
Scopes?.Length ?? 0) > 0)
149 ResponseObj[
"scope"] =
string.Join(
' ', ClientInfo.
Scopes);
153 ResponseObj[
"client_secret_expires_at"] = ClientInfo.ClientSecretExpiresAt.HasValue ?
157 ResponseObj[
"registration_access_token"] = ClientInfo.
AccessToken!;
158 ResponseObj[
"registration_client_uri"] = Request.
Header.
GetURL(
false,
false);
160 Response.
SetHeader(
"Cache-Control",
"max-age=0, no-cache, no-store");
161 Response.
SetHeader(
"Pragma",
"no-cache");
176 await
ServiceUnavailable(Response,
"server_error",
"Registration resource not available.");
181 if (ClientInfo is
null)
186 Request, Response,
true);
191 OAuthRegistrationResource.RegistrationRequest RegistrationRequest = Parsed.Request;
193 if (RegistrationRequest.ClientId != ClientInfo.
ClientId)
195 await
BadRequest(Response,
"invalid_request",
"Invalid registration or access token.");
199 IRegistration? Registration = await Parsed.DynamicUserSource.UpdateUser(
200 ClientInfo.
ClientId!, RegistrationRequest);
204 await
Forbidden(Response,
"access_denied",
205 "Not permitted to update client.");
209 bool UpdateRedirectUris = !AreEqual(ClientInfo.
RedirectUris, RegistrationRequest.RedirectUris);
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;
232 if (UpdateRedirectUris)
237 RegistrationRequest.RedirectUris);
240 Dictionary<string, object> ResponseObj = this.RegistrationResponse(Request,
241 Response, ClientInfo);
243 Response.StatusCode = 200;
244 Response.StatusMessage =
"OK";
246 await Response.
Return(ResponseObj);
249 private static bool AreEqual(
string[]? A1,
string[]? A2)
251 if (A1 is
null ^ A2 is
null)
257 int i, c = A1.Length;
261 HashSet<string> Set1 =
new HashSet<string>(A1);
262 HashSet<string> Set2 =
new HashSet<string>(A2);
264 if (Set1.Count != Set2.Count)
267 for (i = 0; i < c; i++)
269 if (!Set1.Contains(A2[i]))
276 private async Task<OAuthClientInformation?> GetClientInformation(
HttpRequest Request,
279 string ClientId = Request.
SubPath;
280 if (
string.IsNullOrEmpty(ClientId))
282 await
BadRequest(Response,
"invalid_request",
"Invalid client URI.");
287 if (
string.IsNullOrEmpty(Authorization) || !Authorization.StartsWith(
"Bearer "))
289 await
Unauthorized(Response,
"access_denied",
"Missing or invalid registration token.",
290 Array.Empty<
string>());
294 Authorization = Authorization[7..].Trim();
301 if (Result is
null || Result.
AccessToken != Authorization)
303 await
Unauthorized(Response,
"access_denied",
"Missing or invalid registration token.",
304 Array.Empty<
string>());
313 "Unable to retrieve client information.");
327 if (ClientInfo is
null)
332 await
ServiceUnavailable(Response,
"server_error",
"Client registration service not available.");
339 await
Forbidden(Response,
"access_denied",
340 "Not permitted to delete client.");
348 Response.StatusCode = 204;
349 Response.StatusMessage =
"No Content";
351 Response.
SetHeader(
"Cache-Control",
"max-age=0, no-cache, no-store");
352 Response.
SetHeader(
"Pragma",
"no-cache");
Helps with common JSON-related tasks.
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
Represents an HTTP request.
HttpRequestHeader Header
Request header.
string RemoteEndPoint
Remote end-point.
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
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 a redirect URI using by an OAuth client.
Manages the OAuth 2 environment.
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...
bool AllowsGET
If the GET method is allowed.
async Task PUT(HttpRequest Request, HttpResponse Response)
Executes the PUT method on the resource.
bool AllowsDELETE
If the DELETE method is allowed.
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.
bool AllowsPUT
If the PUT method is allowed.
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...
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
This filter selects objects that have a named field equal to a given value.
Base class for all filter classes.
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.
string ClientId
OAuth 2.0 client identifier string.