From 4576a66c1699ef8acf7f28fb2f167cbaf49b7ccd Mon Sep 17 00:00:00 2001 From: Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com> Date: Mon, 22 Apr 2024 21:54:41 +0200 Subject: [PATCH] chore: oauth2 session authentication hook --- .../security/oauth2/installation/index.md | 28 +++++++++++++++ resources/css/docs-hljs.css | 1 + ...h2UserSessionAuthenticatedInterceptor.php} | 22 ++++++++---- src/OAuth2UserSessionAuthenticated.php | 36 +++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) rename src/{HttpResponder/OAuth2/PostSessionAuthentication.php => HttpInterceptor/OAuth2UserSessionAuthenticatedInterceptor.php} (69%) create mode 100644 src/OAuth2UserSessionAuthenticated.php diff --git a/docs/pages/docs/features/security/oauth2/installation/index.md b/docs/pages/docs/features/security/oauth2/installation/index.md index 7b072a25..5ad28745 100644 --- a/docs/pages/docs/features/security/oauth2/installation/index.md +++ b/docs/pages/docs/features/security/oauth2/installation/index.md @@ -103,3 +103,31 @@ $ php ./bin/resonance.php generate:defuse-key > oauth2/defuse.key ``` Then, change the CHMOD permissions for that key to `0600`. + +## Post Session Authentication Hook + +If you are using {{tutorials/session-based-authentication/index}} you need +to return `OAuth2UserSessionAuthenticated` instance +from your authentication {{docs/features/http/responders}} (see also: +{{docs/features/http/interceptors}}). + +It allows OAuth2 to know that the user is authenticated and that it should +check if user is in the middle of OAuth2 flow. + +```php +<?php + +use Distantmagic\Resonance\OAuth2UserSessionAuthenticated; + +final readonly class LoginValidation extends HttpController +{ + public function createResponse(): HttpInterceptableInterface + { + // ... + // perform session authentication somehow + // ... + + return new OAuth2UserSessionAuthenticated(); + } +} +``` diff --git a/resources/css/docs-hljs.css b/resources/css/docs-hljs.css index f9fafeb9..ff3a8f72 100644 --- a/resources/css/docs-hljs.css +++ b/resources/css/docs-hljs.css @@ -85,6 +85,7 @@ code[class] { .fenced-code { background-color: var(--color-block-background); box-shadow: 8px 8px #00000033; + margin: 20px 0; @media screen and (min-width: 1024px) { position: relative; diff --git a/src/HttpResponder/OAuth2/PostSessionAuthentication.php b/src/HttpInterceptor/OAuth2UserSessionAuthenticatedInterceptor.php similarity index 69% rename from src/HttpResponder/OAuth2/PostSessionAuthentication.php rename to src/HttpInterceptor/OAuth2UserSessionAuthenticatedInterceptor.php index 5579045b..8db35697 100644 --- a/src/HttpResponder/OAuth2/PostSessionAuthentication.php +++ b/src/HttpInterceptor/OAuth2UserSessionAuthenticatedInterceptor.php @@ -2,25 +2,32 @@ declare(strict_types=1); -namespace Distantmagic\Resonance\HttpResponder\OAuth2; +namespace Distantmagic\Resonance\HttpInterceptor; use Distantmagic\Resonance\Attribute\GrantsFeature; +use Distantmagic\Resonance\Attribute\Intercepts; use Distantmagic\Resonance\Attribute\Singleton; use Distantmagic\Resonance\Feature; use Distantmagic\Resonance\HttpInterceptableInterface; -use Distantmagic\Resonance\HttpResponder; +use Distantmagic\Resonance\HttpInterceptor; use Distantmagic\Resonance\HttpResponderInterface; use Distantmagic\Resonance\OAuth2AuthorizationCodeFlowControllerInterface; use Distantmagic\Resonance\OAuth2AuthorizationRequestSessionStore; use Distantmagic\Resonance\OAuth2AuthorizedUser; +use Distantmagic\Resonance\OAuth2UserSessionAuthenticated; use Distantmagic\Resonance\SessionAuthentication; +use Distantmagic\Resonance\SingletonCollection; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use RuntimeException; +/** + * @template-extends HttpInterceptor<OAuth2UserSessionAuthenticated> + */ #[GrantsFeature(Feature::OAuth2)] -#[Singleton] -final readonly class PostSessionAuthentication extends HttpResponder +#[Intercepts(OAuth2UserSessionAuthenticated::class)] +#[Singleton(collection: SingletonCollection::HttpInterceptor)] +final readonly class OAuth2UserSessionAuthenticatedInterceptor extends HttpInterceptor { public function __construct( private OAuth2AuthorizationCodeFlowControllerInterface $authorizationCodeFlowController, @@ -28,8 +35,11 @@ final readonly class PostSessionAuthentication extends HttpResponder private SessionAuthentication $sessionAuthentication, ) {} - public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface|HttpResponderInterface|ResponseInterface - { + public function intercept( + ServerRequestInterface $request, + ResponseInterface $response, + object $intercepted, + ): HttpInterceptableInterface|HttpResponderInterface|ResponseInterface { if (!$this->authorizationRequestSessionStore->has($request)) { return $this->authorizationCodeFlowController->redirectToAuthenticatedPage($request, $response); } diff --git a/src/OAuth2UserSessionAuthenticated.php b/src/OAuth2UserSessionAuthenticated.php new file mode 100644 index 00000000..998d33b8 --- /dev/null +++ b/src/OAuth2UserSessionAuthenticated.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +final readonly class OAuth2UserSessionAuthenticated implements HttpInterceptableInterface +{ + private SwooleContextRequestResponseReader $swooleContextRequestResponseReader; + + /** + * @psalm-taint-source file $templatePath + */ + public function __construct( + ?ServerRequestInterface $request = null, + ?ResponseInterface $response = null, + ) { + $this->swooleContextRequestResponseReader = new SwooleContextRequestResponseReader( + request: $request, + response: $response, + ); + } + + public function getResponse(): ResponseInterface + { + return $this->swooleContextRequestResponseReader->getResponse(); + } + + public function getServerRequest(): ServerRequestInterface + { + return $this->swooleContextRequestResponseReader->getServerRequest(); + } +} -- GitLab