From 0bbba2e411deec4e3c90ebe9422499fbea947f3a Mon Sep 17 00:00:00 2001 From: matiasperrone-exo Date: Fri, 24 Apr 2026 14:27:33 +0000 Subject: [PATCH 1/4] feat: Add OpenAPI documentation on controller OAuth2ProviderController web routes --- .../OAuth2/OAuth2ProviderController.php | 233 +++++++++++++++++- .../OAuth2ProviderControllerSchemas.php | 172 +++++++++++++ .../OAuth2AuthorizationRequestSchema.php | 29 +++ .../OAuth2EndSessionRequestSchema.php | 22 ++ .../OAuth2TokenIntrospectionRequestSchema.php | 21 ++ .../Requests/OAuth2TokenRequestSchema.php | 32 +++ .../OAuth2TokenRevocationRequestSchema.php | 22 ++ ...OAuth2ProviderControllerSecuritySchema.php | 21 ++ 8 files changed, 551 insertions(+), 1 deletion(-) create mode 100644 app/Swagger/OAuth2ProviderControllerSchemas.php create mode 100644 app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php create mode 100644 app/Swagger/Requests/OAuth2EndSessionRequestSchema.php create mode 100644 app/Swagger/Requests/OAuth2TokenIntrospectionRequestSchema.php create mode 100644 app/Swagger/Requests/OAuth2TokenRequestSchema.php create mode 100644 app/Swagger/Requests/OAuth2TokenRevocationRequestSchema.php create mode 100644 app/Swagger/Security/OAuth2ProviderControllerSecuritySchema.php diff --git a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php index 285fc005..b7edbdbb 100644 --- a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php +++ b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php @@ -27,6 +27,8 @@ use OAuth2\Requests\OAuth2TokenRevocationRequest; use OAuth2\Responses\OAuth2Response; use OAuth2\Strategies\OAuth2ResponseStrategyFactoryMethod; +use OpenApi\Attributes as OA; +use Symfony\Component\HttpFoundation\Response as HttpResponse; use Utils\Http\HttpContentType; use Utils\Services\IAuthService; use Illuminate\Support\Facades\Log; @@ -76,6 +78,117 @@ public function __construct * use of the "POST" method as well. * @return mixed */ + #[OA\Get( + path: '/oauth2/auth', + operationId: 'oauth2AuthorizeGet', + summary: 'OAuth2 Authorization Endpoint (GET)', + description: 'Initiates an OAuth2 authorization flow. Supports Authorization Code, Implicit, Hybrid, and OpenID Connect flows. Per RFC 6749 §3.1, GET is required.', + tags: ['OAuth2 / OpenID Connect'], + parameters: [ + new OA\Parameter(name: 'response_type', in: 'query', required: true, description: 'OAuth2 response type', schema: new OA\Schema(type: 'string', enum: ['code', 'token', 'id_token', 'code token', 'code id_token', 'token id_token', 'code token id_token', 'otp', 'none'])), + new OA\Parameter(name: 'client_id', in: 'query', required: true, description: 'OAuth2 client identifier', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'redirect_uri', in: 'query', required: true, description: 'Redirect URI (must match a registered URI)', schema: new OA\Schema(type: 'string', format: 'uri')), + new OA\Parameter(name: 'scope', in: 'query', required: false, description: 'Space-delimited scopes (include "openid" for OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'state', in: 'query', required: false, description: 'Opaque state parameter returned in the redirect', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'nonce', in: 'query', required: false, description: 'Nonce for ID token replay protection (OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'response_mode', in: 'query', required: false, description: 'Response mode override', schema: new OA\Schema(type: 'string', enum: ['query', 'fragment', 'form_post', 'direct'])), + new OA\Parameter(name: 'prompt', in: 'query', required: false, description: 'Space-delimited user interaction prompts (OIDC)', schema: new OA\Schema(type: 'string', enum: ['none', 'login', 'consent', 'select_account'])), + new OA\Parameter(name: 'login_hint', in: 'query', required: false, description: 'Hint about login identifier (OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'display', in: 'query', required: false, description: 'UI display preference (OIDC)', schema: new OA\Schema(type: 'string', enum: ['page', 'popup', 'touch', 'wap', 'native'])), + new OA\Parameter(name: 'max_age', in: 'query', required: false, description: 'Maximum authentication age in seconds (OIDC)', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'acr_values', in: 'query', required: false, description: 'Authentication context class reference values (OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'code_challenge', in: 'query', required: false, description: 'PKCE code challenge', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'code_challenge_method', in: 'query', required: false, description: 'PKCE challenge method', schema: new OA\Schema(type: 'string', enum: ['plain', 'S256'])), + new OA\Parameter(name: 'id_token_hint', in: 'query', required: false, description: 'Previously issued ID token hint (OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'approval_prompt', in: 'query', required: false, description: 'Consent handling', schema: new OA\Schema(type: 'string', enum: ['auto', 'force'])), + new OA\Parameter(name: 'access_type', in: 'query', required: false, description: 'Token refresh behavior', schema: new OA\Schema(type: 'string', enum: ['online', 'offline'])), + ], + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to client redirect_uri with authorization code, tokens, or error'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Bad Request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] + #[OA\Post( + path: '/oauth2/auth', + operationId: 'oauth2AuthorizePost', + summary: 'OAuth2 Authorization Endpoint (POST)', + description: 'Initiates an OAuth2 authorization flow via POST. Same parameters as GET but sent as form data.', + tags: ['OAuth2 / OpenID Connect'], + requestBody: new OA\RequestBody( + description: 'Authorization request parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2AuthorizationRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to client redirect_uri with authorization code, tokens, or error'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Bad Request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] + #[OA\Put( + path: '/oauth2/auth', + operationId: 'oauth2AuthorizePut', + summary: 'OAuth2 Authorization Endpoint (PUT)', + description: 'Initiates an OAuth2 authorization flow via PUT. Same parameters as GET but sent as form data.', + tags: ['OAuth2 / OpenID Connect'], + requestBody: new OA\RequestBody( + description: 'Authorization request parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2AuthorizationRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to client redirect_uri with authorization code, tokens, or error'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Bad Request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] + #[OA\Patch( + path: '/oauth2/auth', + operationId: 'oauth2AuthorizePatch', + summary: 'OAuth2 Authorization Endpoint (PATCH)', + description: 'Initiates an OAuth2 authorization flow via PATCH. Same parameters as GET but sent as form data.', + tags: ['OAuth2 / OpenID Connect'], + requestBody: new OA\RequestBody( + description: 'Authorization request parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2AuthorizationRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to client redirect_uri with authorization code, tokens, or error'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Bad Request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] + #[OA\Delete( + path: '/oauth2/auth', + operationId: 'oauth2AuthorizeDelete', + summary: 'OAuth2 Authorization Endpoint (DELETE)', + description: 'Initiates an OAuth2 authorization flow via DELETE. Same parameters as GET but sent as form data.', + tags: ['OAuth2 / OpenID Connect'], + requestBody: new OA\RequestBody( + description: 'Authorization request parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2AuthorizationRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to client redirect_uri with authorization code, tokens, or error'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Bad Request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] public function auth() { try { @@ -136,6 +249,26 @@ public function auth() * Token HTTP Endpoint * @return mixed */ + #[OA\Post( + path: '/oauth2/token', + operationId: 'oauth2Token', + summary: 'OAuth2 Token Endpoint', + description: 'Issues access tokens. Supports authorization_code, client_credentials, password, refresh_token, and passwordless grant types.', + tags: ['OAuth2 / OpenID Connect'], + security: [['OAuth2ProviderSecurity' => []]], + requestBody: new OA\RequestBody( + description: 'Token request parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2TokenRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Successful token response', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2TokenResponse')), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Token error', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] public function token() { @@ -166,6 +299,26 @@ public function token() * Revoke Token HTTP Endpoint * @return mixed */ + #[OA\Post( + path: '/oauth2/token/revoke', + operationId: 'oauth2RevokeToken', + summary: 'OAuth2 Token Revocation Endpoint', + description: 'Revokes an access token or refresh token per RFC 7009. The endpoint is idempotent — revoking a non-existent token returns success.', + tags: ['OAuth2 / OpenID Connect'], + security: [['OAuth2ProviderSecurity' => []]], + requestBody: new OA\RequestBody( + description: 'Token revocation parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2TokenRevocationRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Token successfully revoked'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Revocation error', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] public function revoke() { $response = $this->oauth2_protocol->revoke @@ -196,6 +349,26 @@ public function revoke() * Introspection Token HTTP Endpoint * @return mixed */ + #[OA\Post( + path: '/oauth2/token/introspection', + operationId: 'oauth2IntrospectToken', + summary: 'OAuth2 Token Introspection Endpoint', + description: 'Validates and returns metadata about an access token per RFC 7662. Returns detailed information about the token including associated user data.', + tags: ['OAuth2 / OpenID Connect'], + security: [['OAuth2ProviderSecurity' => []]], + requestBody: new OA\RequestBody( + description: 'Token introspection parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2TokenIntrospectionRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Token introspection response', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2IntrospectionResponse')), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Introspection error', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] public function introspection() { @@ -226,6 +399,16 @@ public function introspection() * OP's JSON Web Key Set [JWK] document. * @return string */ + #[OA\Get( + path: '/oauth2/certs', + operationId: 'oauth2GetCerts', + summary: 'JSON Web Key Set (JWKS) Endpoint', + description: 'Returns the OpenID Provider JSON Web Key Set document containing public keys used for signing tokens (RFC 7517).', + tags: ['OAuth2 / OpenID Connect'], + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'JWKS document', content: new OA\JsonContent(ref: '#/components/schemas/JWKSResponse')), + ] + )] public function certs() { @@ -236,6 +419,16 @@ public function certs() return $response; } + #[OA\Get( + path: '/.well-known/openid-configuration', + operationId: 'oauth2Discovery', + summary: 'OpenID Connect Discovery Endpoint', + description: 'Returns the OpenID Provider Configuration document per OpenID Connect Discovery 1.0. Also available at /oauth2/.well-known/openid-configuration.', + tags: ['OAuth2 / OpenID Connect'], + responses: [ + new OA\Response(response: HttpResponse::HTTP_OK, description: 'OpenID Connect Discovery document', content: new OA\JsonContent(ref: '#/components/schemas/OpenIDDiscoveryResponse')), + ] + )] public function discovery() { @@ -258,6 +451,44 @@ public function checkSessionIFrame() /** * @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout */ + #[OA\Get( + path: '/oauth2/end-session', + operationId: 'oauth2EndSessionGet', + summary: 'OpenID Connect End Session Endpoint (GET)', + description: 'Initiates RP-Initiated Logout per OpenID Connect Session Management 1.0. Terminates the user session and optionally redirects to the post-logout URI.', + tags: ['OAuth2 / OpenID Connect'], + parameters: [ + new OA\Parameter(name: 'client_id', in: 'query', required: true, description: 'OAuth2 client identifier', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'id_token_hint', in: 'query', required: false, description: 'Previously issued ID token', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'post_logout_redirect_uri', in: 'query', required: false, description: 'URI to redirect after logout (must be registered)', schema: new OA\Schema(type: 'string', format: 'uri')), + new OA\Parameter(name: 'state', in: 'query', required: false, description: 'Opaque state parameter returned in the redirect', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to post_logout_redirect_uri'), + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Session ended page (HTML)'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Invalid logout request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] + #[OA\Post( + path: '/oauth2/end-session', + operationId: 'oauth2EndSessionPost', + summary: 'OpenID Connect End Session Endpoint (POST)', + description: 'Initiates RP-Initiated Logout via POST. Same parameters as GET but sent as form data.', + tags: ['OAuth2 / OpenID Connect'], + requestBody: new OA\RequestBody( + description: 'End session parameters', + required: true, + content: new OA\MediaType( + mediaType: 'application/x-www-form-urlencoded', + schema: new OA\Schema(ref: '#/components/schemas/OAuth2EndSessionRequest') + ) + ), + responses: [ + new OA\Response(response: HttpResponse::HTTP_FOUND, description: 'Redirect to post_logout_redirect_uri'), + new OA\Response(response: HttpResponse::HTTP_OK, description: 'Session ended page (HTML)'), + new OA\Response(response: HttpResponse::HTTP_BAD_REQUEST, description: 'Invalid logout request', content: new OA\JsonContent(ref: '#/components/schemas/OAuth2ErrorResponse')), + ] + )] public function endSession() { $request = new OAuth2LogoutRequest @@ -285,4 +516,4 @@ public function endSession() return View::make('oauth2.session.session-ended'); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/Swagger/OAuth2ProviderControllerSchemas.php b/app/Swagger/OAuth2ProviderControllerSchemas.php new file mode 100644 index 00000000..1b95a530 --- /dev/null +++ b/app/Swagger/OAuth2ProviderControllerSchemas.php @@ -0,0 +1,172 @@ + Date: Fri, 24 Apr 2026 21:18:10 +0000 Subject: [PATCH 2/4] chore: Add PR's requested changes on Security concerns --- app/Http/Controllers/OAuth2/OAuth2ProviderController.php | 4 ++-- .../Security/OAuth2ProviderControllerSecuritySchema.php | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php index b7edbdbb..5b6ebf07 100644 --- a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php +++ b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php @@ -305,7 +305,7 @@ public function token() summary: 'OAuth2 Token Revocation Endpoint', description: 'Revokes an access token or refresh token per RFC 7009. The endpoint is idempotent — revoking a non-existent token returns success.', tags: ['OAuth2 / OpenID Connect'], - security: [['OAuth2ProviderSecurity' => []]], + security: [['OAuth2ProviderClientBasic' => []], ['OAuth2ProviderSecurity' => []]], requestBody: new OA\RequestBody( description: 'Token revocation parameters', required: true, @@ -355,7 +355,7 @@ public function revoke() summary: 'OAuth2 Token Introspection Endpoint', description: 'Validates and returns metadata about an access token per RFC 7662. Returns detailed information about the token including associated user data.', tags: ['OAuth2 / OpenID Connect'], - security: [['OAuth2ProviderSecurity' => []]], + security: [['OAuth2ProviderClientBasic' => []], ['OAuth2ProviderSecurity' => []]], requestBody: new OA\RequestBody( description: 'Token introspection parameters', required: true, diff --git a/app/Swagger/Security/OAuth2ProviderControllerSecuritySchema.php b/app/Swagger/Security/OAuth2ProviderControllerSecuritySchema.php index 29e05df3..292e9cab 100644 --- a/app/Swagger/Security/OAuth2ProviderControllerSecuritySchema.php +++ b/app/Swagger/Security/OAuth2ProviderControllerSecuritySchema.php @@ -16,6 +16,12 @@ ), ] )] +#[OA\SecurityScheme( + securityScheme: 'OAuth2ProviderClientBasic', + type: 'http', + scheme: 'basic', + description: 'HTTP Basic authentication with OAuth2 client_id:client_secret (RFC 6749 §2.3.1).' +)] class OAuth2ProviderControllerSecuritySchema { } \ No newline at end of file From f5d04d07801ad2cb24aa515f4695b11905d28ccc Mon Sep 17 00:00:00 2001 From: matiasperrone-exo Date: Thu, 30 Apr 2026 15:29:44 +0000 Subject: [PATCH 3/4] chore: Add PR's requested changes --- .../OAuth2/OAuth2ProviderController.php | 34 ++++++++----------- .../OAuth2AuthorizationRequestSchema.php | 15 ++++---- .../Requests/OAuth2TokenRequestSchema.php | 4 +-- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php index 5b6ebf07..e2545f6b 100644 --- a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php +++ b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php @@ -1,4 +1,5 @@ -oauth2_protocol = $oauth2_protocol; $this->auth_service = $auth_service; $this->client_repository = $client_repository; @@ -85,23 +85,18 @@ public function __construct description: 'Initiates an OAuth2 authorization flow. Supports Authorization Code, Implicit, Hybrid, and OpenID Connect flows. Per RFC 6749 §3.1, GET is required.', tags: ['OAuth2 / OpenID Connect'], parameters: [ - new OA\Parameter(name: 'response_type', in: 'query', required: true, description: 'OAuth2 response type', schema: new OA\Schema(type: 'string', enum: ['code', 'token', 'id_token', 'code token', 'code id_token', 'token id_token', 'code token id_token', 'otp', 'none'])), + new OA\Parameter(name: 'response_type', in: 'query', required: true, description: 'The OAuth 2.0 specification allows for registration of space-separated response_type parameter values. If a Response Type contains one of more space characters (%20), it is compared as a space-delimited list of values in which the order of values does not matter. Possible values are: code, token, id_token, otp, none. The "none" value cannot be used with any other response type value.', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'client_id', in: 'query', required: true, description: 'OAuth2 client identifier', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'redirect_uri', in: 'query', required: true, description: 'Redirect URI (must match a registered URI)', schema: new OA\Schema(type: 'string', format: 'uri')), - new OA\Parameter(name: 'scope', in: 'query', required: false, description: 'Space-delimited scopes (include "openid" for OIDC)', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'state', in: 'query', required: false, description: 'Opaque state parameter returned in the redirect', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'nonce', in: 'query', required: false, description: 'Nonce for ID token replay protection (OIDC)', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'response_mode', in: 'query', required: false, description: 'Response mode override', schema: new OA\Schema(type: 'string', enum: ['query', 'fragment', 'form_post', 'direct'])), - new OA\Parameter(name: 'prompt', in: 'query', required: false, description: 'Space-delimited user interaction prompts (OIDC)', schema: new OA\Schema(type: 'string', enum: ['none', 'login', 'consent', 'select_account'])), - new OA\Parameter(name: 'login_hint', in: 'query', required: false, description: 'Hint about login identifier (OIDC)', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'display', in: 'query', required: false, description: 'UI display preference (OIDC)', schema: new OA\Schema(type: 'string', enum: ['page', 'popup', 'touch', 'wap', 'native'])), - new OA\Parameter(name: 'max_age', in: 'query', required: false, description: 'Maximum authentication age in seconds (OIDC)', schema: new OA\Schema(type: 'integer')), - new OA\Parameter(name: 'acr_values', in: 'query', required: false, description: 'Authentication context class reference values (OIDC)', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'redirect_uri', in: 'query', required: true, description: 'Redirect URI', schema: new OA\Schema(type: 'string', format: 'uri')), + new OA\Parameter(name: 'scope', in: 'query', required: false, description: 'Space-delimited scopes', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'state', in: 'query', required: false, description: 'Opaque state parameter', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'approval_prompt', in: 'query', required: false, description: 'Indicates whether the user should be re-prompted for consent. The default is auto, so a given user should only see the consent page for a given set of scopes the first time through the sequence. If the value is force, then the user sees a consent page even if they previously gave consent to your application for a given set of scopes.', enum: ['auto', 'force'], schema: new OA\Schema(type: 'string', enum: ['auto', 'force'])), + new OA\Parameter(name: 'access_type', in: 'query', required: false, description: 'Indicates whether your application needs to access an API when the user is not present at the browser. This parameter defaults to online. If your application needs to refresh access tokens when the user is not present at the browser, then use offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user.', schema: new OA\Schema(type: 'string', enum: ['online', 'offline'])), + new OA\Parameter(name: 'response_mode', in: 'query', required: false, description: 'OPTIONAL. Informs the Authorization Server of the mechanism to be used for returning Authorization Response parameters from the Authorization Endpoint. This use of this parameter is NOT RECOMMENDED with a value that specifies the same Response Mode as the default Response Mode for the Response Type used.\nThe default Response Mode for the OAuth 2.0 code Response Type is the query encoding. For purposes of this specification, the default Response Mode for the OAuth 2.0 token Response Type is the fragment encoding.', schema: new OA\Schema(type: 'string', enum: ['query', 'fragment', 'form_post', 'direct'])), new OA\Parameter(name: 'code_challenge', in: 'query', required: false, description: 'PKCE code challenge', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'code_challenge_method', in: 'query', required: false, description: 'PKCE challenge method', schema: new OA\Schema(type: 'string', enum: ['plain', 'S256'])), - new OA\Parameter(name: 'id_token_hint', in: 'query', required: false, description: 'Previously issued ID token hint (OIDC)', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'approval_prompt', in: 'query', required: false, description: 'Consent handling', schema: new OA\Schema(type: 'string', enum: ['auto', 'force'])), - new OA\Parameter(name: 'access_type', in: 'query', required: false, description: 'Token refresh behavior', schema: new OA\Schema(type: 'string', enum: ['online', 'offline'])), + new OA\Parameter(name: 'code_challenge_method', in: 'query', required: false, description: 'Optional. PKCE challenge method', schema: new OA\Schema(type: 'string', enum: ['plain', 'S256'])), + new OA\Parameter(name: 'display', in: 'query', required: false, description: 'UI display preference (OIDC)', schema: new OA\Schema(type: 'string', enum: ['page', 'popup', 'touch', 'wap', 'native'])), + new OA\Parameter(name: 'tenant', in: 'query', required: false, description: 'Tenant identifier', schema: new OA\Schema(type: 'string')), ], responses: [ new OA\Response(response: HttpResponse::HTTP_OK, description: 'Authorization request processed (response in body), depends on "response_mode" param'), @@ -255,7 +250,6 @@ public function auth() summary: 'OAuth2 Token Endpoint', description: 'Issues access tokens. Supports authorization_code, client_credentials, password, refresh_token, and passwordless grant types.', tags: ['OAuth2 / OpenID Connect'], - security: [['OAuth2ProviderSecurity' => []]], requestBody: new OA\RequestBody( description: 'Token request parameters', required: true, @@ -423,7 +417,7 @@ public function certs() path: '/.well-known/openid-configuration', operationId: 'oauth2Discovery', summary: 'OpenID Connect Discovery Endpoint', - description: 'Returns the OpenID Provider Configuration document per OpenID Connect Discovery 1.0. Also available at /oauth2/.well-known/openid-configuration.', + description: 'Returns the OpenID Provider Configuration document per OpenID Connect Discovery 1.0.', tags: ['OAuth2 / OpenID Connect'], responses: [ new OA\Response(response: HttpResponse::HTTP_OK, description: 'OpenID Connect Discovery document', content: new OA\JsonContent(ref: '#/components/schemas/OpenIDDiscoveryResponse')), diff --git a/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php b/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php index edf9eeab..fdc780d5 100644 --- a/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php +++ b/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php @@ -11,19 +11,20 @@ type: 'object', required: ['response_type', 'client_id', 'redirect_uri'], properties: [ - new OA\Property(property: 'response_type', type: 'string', description: 'OAuth2 response type', enum: ['code', 'token', 'id_token', 'code token', 'code id_token', 'token id_token', 'code token id_token', 'otp', 'none']), + new OA\Property(property: 'response_type', type: 'string', description: 'The OAuth 2.0 specification allows for registration of space-separated response_type parameter values. If a Response Type contains one of more space characters (%20), it is compared as a space-delimited list of values in which the order of values does not matter. Possible values are: code, token, id_token, otp, none. The "none" value cannot be used with any other response type value.'), new OA\Property(property: 'client_id', type: 'string', description: 'OAuth2 client identifier'), new OA\Property(property: 'redirect_uri', type: 'string', format: 'uri', description: 'Redirect URI'), new OA\Property(property: 'scope', type: 'string', description: 'Space-delimited scopes'), new OA\Property(property: 'state', type: 'string', description: 'Opaque state parameter'), - new OA\Property(property: 'nonce', type: 'string', description: 'Nonce for ID token replay protection'), - new OA\Property(property: 'response_mode', type: 'string', description: 'Response mode override', enum: ['query', 'fragment', 'form_post', 'direct']), - new OA\Property(property: 'prompt', type: 'string', description: 'User interaction prompts'), - new OA\Property(property: 'login_hint', type: 'string', description: 'Login identifier hint'), + new OA\Property(property: 'approval_prompt', type: 'string', description: 'Indicates whether the user should be re-prompted for consent. The default is auto, so a given user should only see the consent page for a given set of scopes the first time through the sequence. If the value is force, then the user sees a consent page even if they previously gave consent to your application for a given set of scopes.', enum: ['auto', 'force']), + new OA\Property(property: 'access_type', type: 'string', description: 'Indicates whether your application needs to access an API when the user is not present at the browser. This parameter defaults to online. If your application needs to refresh access tokens when the user is not present at the browser, then use offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user.'), + new OA\Property(property: 'response_mode', type: 'string', description: 'OPTIONAL. Informs the Authorization Server of the mechanism to be used for returning Authorization Response parameters from the Authorization Endpoint. This use of this parameter is NOT RECOMMENDED with a value that specifies the same Response Mode as the default Response Mode for the Response Type used.\nThe default Response Mode for the OAuth 2.0 code Response Type is the query encoding. For purposes of this specification, the default Response Mode for the OAuth 2.0 token Response Type is the fragment encoding.', enum: ['query', 'fragment', 'form_post', 'direct']), new OA\Property(property: 'code_challenge', type: 'string', description: 'PKCE code challenge'), - new OA\Property(property: 'code_challenge_method', type: 'string', description: 'PKCE challenge method', enum: ['plain', 'S256']), + new OA\Property(property: 'code_challenge_method', type: 'string', description: 'Optional. PKCE challenge method', enum: ['plain', 'S256']), + new OA\Property(property: 'display', type: 'string', description: 'UI display preference (OIDC)', enum: ['page', 'popup', 'touch', 'wap', 'native']), + new OA\Property(property: 'tenant', type: 'string', description: 'Tenant identifier'), ] )] class OAuth2AuthorizationRequestSchema { -} +} \ No newline at end of file diff --git a/app/Swagger/Requests/OAuth2TokenRequestSchema.php b/app/Swagger/Requests/OAuth2TokenRequestSchema.php index 5fe9f548..9e4dbefe 100644 --- a/app/Swagger/Requests/OAuth2TokenRequestSchema.php +++ b/app/Swagger/Requests/OAuth2TokenRequestSchema.php @@ -21,7 +21,7 @@ new OA\Property(property: 'username', type: 'string', description: 'Username (password grant)'), new OA\Property(property: 'password', type: 'string', description: 'Password (password grant)'), new OA\Property(property: 'audience', type: 'string', description: 'Target audience (client_credentials grant)'), - new OA\Property(property: 'connection', type: 'string', description: 'Connection type (passwordless grant)', enum: ['sms', 'email', 'inline']), + new OA\Property(property: 'connection', type: 'string', description: 'Connection type (passwordless grant)', enum: ['sms', 'email']), new OA\Property(property: 'send', type: 'string', description: 'Delivery method (passwordless grant)', enum: ['code', 'link']), new OA\Property(property: 'email', type: 'string', description: 'Email address (passwordless grant)'), new OA\Property(property: 'phone_number', type: 'string', description: 'Phone number (passwordless grant)'), @@ -29,4 +29,4 @@ )] class OAuth2TokenRequestSchema { -} +} \ No newline at end of file From 3147c4cb964d9cdbbf155b3d1be53f847494f53d Mon Sep 17 00:00:00 2001 From: matiasperrone-exo Date: Thu, 30 Apr 2026 20:35:43 +0000 Subject: [PATCH 4/4] chore: Add PR's requested changes --- app/Http/Controllers/OAuth2/OAuth2ProviderController.php | 3 ++- app/Swagger/OAuth2ProviderControllerSchemas.php | 4 ++++ app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php index e2545f6b..8151231c 100644 --- a/app/Http/Controllers/OAuth2/OAuth2ProviderController.php +++ b/app/Http/Controllers/OAuth2/OAuth2ProviderController.php @@ -90,7 +90,7 @@ public function __construct new OA\Parameter(name: 'redirect_uri', in: 'query', required: true, description: 'Redirect URI', schema: new OA\Schema(type: 'string', format: 'uri')), new OA\Parameter(name: 'scope', in: 'query', required: false, description: 'Space-delimited scopes', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'state', in: 'query', required: false, description: 'Opaque state parameter', schema: new OA\Schema(type: 'string')), - new OA\Parameter(name: 'approval_prompt', in: 'query', required: false, description: 'Indicates whether the user should be re-prompted for consent. The default is auto, so a given user should only see the consent page for a given set of scopes the first time through the sequence. If the value is force, then the user sees a consent page even if they previously gave consent to your application for a given set of scopes.', enum: ['auto', 'force'], schema: new OA\Schema(type: 'string', enum: ['auto', 'force'])), + new OA\Parameter(name: 'approval_prompt', in: 'query', required: false, description: 'Indicates whether the user should be re-prompted for consent. The default is auto, so a given user should only see the consent page for a given set of scopes the first time through the sequence. If the value is force, then the user sees a consent page even if they previously gave consent to your application for a given set of scopes.', schema: new OA\Schema(type: 'string', enum: ['auto', 'force'])), new OA\Parameter(name: 'access_type', in: 'query', required: false, description: 'Indicates whether your application needs to access an API when the user is not present at the browser. This parameter defaults to online. If your application needs to refresh access tokens when the user is not present at the browser, then use offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user.', schema: new OA\Schema(type: 'string', enum: ['online', 'offline'])), new OA\Parameter(name: 'response_mode', in: 'query', required: false, description: 'OPTIONAL. Informs the Authorization Server of the mechanism to be used for returning Authorization Response parameters from the Authorization Endpoint. This use of this parameter is NOT RECOMMENDED with a value that specifies the same Response Mode as the default Response Mode for the Response Type used.\nThe default Response Mode for the OAuth 2.0 code Response Type is the query encoding. For purposes of this specification, the default Response Mode for the OAuth 2.0 token Response Type is the fragment encoding.', schema: new OA\Schema(type: 'string', enum: ['query', 'fragment', 'form_post', 'direct'])), new OA\Parameter(name: 'code_challenge', in: 'query', required: false, description: 'PKCE code challenge', schema: new OA\Schema(type: 'string')), @@ -250,6 +250,7 @@ public function auth() summary: 'OAuth2 Token Endpoint', description: 'Issues access tokens. Supports authorization_code, client_credentials, password, refresh_token, and passwordless grant types.', tags: ['OAuth2 / OpenID Connect'], + security: [['OAuth2ProviderClientBasic' => []], ['OAuth2ProviderSecurity' => []]], requestBody: new OA\RequestBody( description: 'Token request parameters', required: true, diff --git a/app/Swagger/OAuth2ProviderControllerSchemas.php b/app/Swagger/OAuth2ProviderControllerSchemas.php index 1b95a530..a964aa56 100644 --- a/app/Swagger/OAuth2ProviderControllerSchemas.php +++ b/app/Swagger/OAuth2ProviderControllerSchemas.php @@ -9,6 +9,7 @@ title: 'OAuth2 Token Response', description: 'Successful token response per RFC 6749 §5.1', type: 'object', + required: ['access_token', 'token_type'], properties: [ new OA\Property(property: 'access_token', type: 'string', description: 'The access token issued by the authorization server'), new OA\Property(property: 'token_type', type: 'string', description: 'The type of the token (typically Bearer)', example: 'Bearer'), @@ -42,6 +43,7 @@ class OAuth2ErrorResponseSchema title: 'OAuth2 Token Introspection Response', description: 'Token introspection response per RFC 7662', type: 'object', + required: ['active'], properties: [ new OA\Property(property: 'active', type: 'boolean', description: 'Whether the token is active'), new OA\Property(property: 'access_token', type: 'string', description: 'The access token value'), @@ -87,6 +89,7 @@ class OAuth2IntrospectionResponseSchema title: 'JSON Web Key Set', description: 'JWK Set document per RFC 7517', type: 'object', + required: ['keys'], properties: [ new OA\Property( property: 'keys', @@ -115,6 +118,7 @@ class JWKSResponseSchema title: 'OpenID Connect Discovery Document', description: 'OpenID Provider Configuration per OpenID Connect Discovery 1.0', type: 'object', + required: ['issuer', 'authorization_endpoint', 'token_endpoint', 'jwks_uri', 'response_types_supported', 'subject_types_supported', 'id_token_signing_alg_values_supported'], properties: [ new OA\Property(property: 'issuer', type: 'string', format: 'uri', description: 'Issuer identifier URL'), new OA\Property(property: 'authorization_endpoint', type: 'string', format: 'uri', description: 'Authorization endpoint URL'), diff --git a/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php b/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php index fdc780d5..dcc25649 100644 --- a/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php +++ b/app/Swagger/Requests/OAuth2AuthorizationRequestSchema.php @@ -17,7 +17,7 @@ new OA\Property(property: 'scope', type: 'string', description: 'Space-delimited scopes'), new OA\Property(property: 'state', type: 'string', description: 'Opaque state parameter'), new OA\Property(property: 'approval_prompt', type: 'string', description: 'Indicates whether the user should be re-prompted for consent. The default is auto, so a given user should only see the consent page for a given set of scopes the first time through the sequence. If the value is force, then the user sees a consent page even if they previously gave consent to your application for a given set of scopes.', enum: ['auto', 'force']), - new OA\Property(property: 'access_type', type: 'string', description: 'Indicates whether your application needs to access an API when the user is not present at the browser. This parameter defaults to online. If your application needs to refresh access tokens when the user is not present at the browser, then use offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user.'), + new OA\Property(property: 'access_type', type: 'string', description: 'Indicates whether your application needs to access an API when the user is not present at the browser. This parameter defaults to online. If your application needs to refresh access tokens when the user is not present at the browser, then use offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user.', enum: ['online', 'offline']), new OA\Property(property: 'response_mode', type: 'string', description: 'OPTIONAL. Informs the Authorization Server of the mechanism to be used for returning Authorization Response parameters from the Authorization Endpoint. This use of this parameter is NOT RECOMMENDED with a value that specifies the same Response Mode as the default Response Mode for the Response Type used.\nThe default Response Mode for the OAuth 2.0 code Response Type is the query encoding. For purposes of this specification, the default Response Mode for the OAuth 2.0 token Response Type is the fragment encoding.', enum: ['query', 'fragment', 'form_post', 'direct']), new OA\Property(property: 'code_challenge', type: 'string', description: 'PKCE code challenge'), new OA\Property(property: 'code_challenge_method', type: 'string', description: 'Optional. PKCE challenge method', enum: ['plain', 'S256']),