From b2f834752634664d748d43cf22bcd7c5a85a482f Mon Sep 17 00:00:00 2001 From: flashwave Date: Wed, 26 Feb 2025 18:06:46 +0000 Subject: [PATCH] OAuth metadata endpoints. --- ...d_index_on_restricted_field_for_scopes.php | 12 ++++ src/Apps/ScopesData.php | 23 +++++++ src/OAuth2/OAuth2ApiRoutes.php | 64 ++++++++++++++++++- src/OpenID/OpenIDRoutes.php | 2 +- 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 database/2025_02_26_173829_add_index_on_restricted_field_for_scopes.php diff --git a/database/2025_02_26_173829_add_index_on_restricted_field_for_scopes.php b/database/2025_02_26_173829_add_index_on_restricted_field_for_scopes.php new file mode 100644 index 00000000..26f3c716 --- /dev/null +++ b/database/2025_02_26_173829_add_index_on_restricted_field_for_scopes.php @@ -0,0 +1,12 @@ +execute(<<cache = new DbStatementCache($dbConn); } + public function getScopes( + ?bool $restricted = null, + ?bool $deprecated = null + ): iterable { + $args = 0; + $query = <<' : '='); + } + if($deprecated !== null) + $query .= sprintf(' %s scope_deprecated %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deprecated ? 'IS NOT' : 'IS'); + + $stmt = $this->cache->get($query); + $stmt->execute(); + + return $stmt->getResultIterator(ScopeInfo::fromResult(...)); + } + public function getScopeInfo(string $value, ScopeInfoGetField $field): ScopeInfo { $stmt = $this->cache->get(sprintf( <<setHeader('Access-Control-Allow-Origin', '*'); + if($request->method === 'OPTIONS') + return 204; + + return [ + 'resource' => $this->siteInfo->url, + 'authorization_servers' => [$this->siteInfo->url], + 'scopes_supported' => [], + 'bearer_methods_supported' => ['header'], + ]; + } + + #[HttpOptions('/.well-known/oauth-authorization-server')] + #[HttpGet('/.well-known/oauth-authorization-server')] + public function getWellKnownAuthorizationServer(HttpResponseBuilder $response, HttpRequest $request): array|int { + $response->setHeader('Access-Control-Allow-Origin', '*'); + if($request->method === 'OPTIONS') + return 204; + + return [ + 'issuer' => $this->siteInfo->url, + 'authorization_endpoint' => sprintf('%s%s', $this->siteInfo->url, $this->urls->format('oauth2-authorise')), + 'token_endpoint' => sprintf('%s%s', $this->siteInfo->url, $this->urls->format('oauth2-token')), + 'jwks_uri' => sprintf('%s%s', $this->siteInfo->url, $this->urls->format('openid-jwks')), + 'protected_resources' => [$this->siteInfo->url], + 'scopes_supported' => XArray::select( + $this->appsCtx->scopes->getScopes( + restricted: false, + deprecated: false, + ), + fn($scopeInfo) => $scopeInfo->string + ), + 'response_types_supported' => ['code', 'code id_token'], + 'response_modes_supported' => ['query'], + 'grant_types_supported' => [ + 'authorization_code', + 'client_credentials', + 'refresh_token', + 'urn:ietf:params:oauth:grant-type:device_code', + ], + 'token_endpoint_auth_methods_supported' => [ + 'none', + 'client_secret_basic', + 'client_secret_post', + ], + //'revocation_endpoint' => 'TODO: implement this', + //'revocation_endpoint_auth_methods_supported' => ['client_secret_basic'], + //'introspection_endpoint ' => 'TODO: implement this', + //'introspection_endpoint_auth_methods_supported' => ['Bearer'], + 'code_challenge_methods_supported' => ['plain', 'S256'], + ]; + } + /** * @return array{ * device_code: string, diff --git a/src/OpenID/OpenIDRoutes.php b/src/OpenID/OpenIDRoutes.php index deffcb93..f082ef86 100644 --- a/src/OpenID/OpenIDRoutes.php +++ b/src/OpenID/OpenIDRoutes.php @@ -23,7 +23,7 @@ class OpenIDRoutes implements RouteHandler, UrlSource { private ProfileContext $profileCtx, private SiteInfo $siteInfo, private AuthInfo $authInfo, - private UrlRegistry $urls + private UrlRegistry $urls, ) {} #[HttpOptions('/.well-known/openid-configuration')]