Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ security:

* [Basic setup](docs/basic-setup.md)
* [Controlling token scopes](docs/controlling-token-scopes.md)
* [Authorization code grant](docs/authorization-code-grant.md)
* [Password grant handling](docs/password-grant-handling.md)

## Development
Expand Down
127 changes: 127 additions & 0 deletions docs/authorization-code-grant.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Authorization code grant

Authorization code grant has two steps

1. Getting authorization code
2. Getting token from authorization code

## Requirements

To use authorization code grant `enable_auth_code_grant` parameter inside `authorization_server` must be set to `true` (it is set to `true` by default).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[info] User Deprecated: "trikoder_oauth2.authorization_server.enable_auth_code_grant" is deprecated, use "trikoder_oauth2.authorization_server.grant_types.authorization_code.enable" instead.

This should be updated to use the new parameters.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdutrion
Thank you for noticing this, I have updated the documentation.


### Example: config

```yaml
# /packages/trikoder_oauth2.yaml

trikoder_oauth2:
authorization_server:
enable_auth_code_grant: true
```

After authorization code grant is enabled, token and authorization endpoints must be set.
It can be done by including `Resources/config/routes.xml` which will provide `/authorize` or `/token` endpoints or manually by setting

1. Controller `Trikoder\Bundle\OAuth2Bundle\Controller\AuthorizationController::indexAction` with `GET` method for authorization endpoint
2. Controller `Trikoder\Bundle\OAuth2Bundle\Controller\TokenController::indexAction` with `POST` method for token endpoint

### Example: custom setup

```yaml
# /routes/trikoder_oauth2.yaml

oauth2_authorization_code:
controller: Trikoder\Bundle\OAuth2Bundle\Controller\AuthorizationController::indexAction
path: /oauth2-authorization-code

oauth2_token:
controller: Trikoder\Bundle\OAuth2Bundle\Controller\TokenController::indexAction
path: /api/token
```

After assigning routes, listener for `trikoder.oauth2.authorization_request_resolve` must be configured.

`\Trikoder\Bundle\OAuth2Bundle\Event\AuthorizationRequestResolveEvent` (whose name is `trikoder.oauth2.authorization_request_resolve`) consist of three important methods which have to be used

1. `setUser(?UserInterface $user)` and `resolveAuthorization(bool $authorizationResolution)` when user is already logged in when accessing authorization endpoint
2. `setResponse(ResponseInterface $response)` when user needs to log in before authorization server can issue authorization code

`\Trikoder\Bundle\OAuth2Bundle\EventListener\AuthorizationRequestUserResolvingListener` with priority value `1024` calls `setUser(?UserInterface $user)` if user is logged in, so make sure your listener has lower priority than it.

### Example: services.yml and php class

```yaml
BestNamespace\OAuthLogin\Listener\AuthorizationCodeListener:
tags:
- { name: kernel.event_listener, event: 'trikoder.oauth2.authorization_request_resolve', method: onAuthorizationRequestResolve }
```

```php
<?php

declare(strict_types=1);

namespace BestNamespace\OAuthLogin\Listener;

use Nyholm\Psr7\Response;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Trikoder\Bundle\OAuth2Bundle\Event\AuthorizationRequestResolveEvent;

final class AuthorizationCodeListener
{
private $urlGenerator;
private $requestStack;

public function __construct(
UrlGeneratorInterface $urlGenerator,
RequestStack $requestStack
) {
$this->urlGenerator = $urlGenerator;
$this->requestStack = $requestStack;
}

public function onAuthorizationRequestResolve(AuthorizationRequestResolveEvent $event)
{
if (null !== $event->getUser()) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could reverse the condition (null === $event->getUser()) and just return after setting the response:

public function onAuthorizationRequestResolve(AuthorizationRequestResolveEvent $event): void
{
    if (null === $event->getUser()) {
        $event->setResponse(new Response(302, [
            'Location' => $this->urlGenerator->generate('login', [
                'returnUrl' => $this->requestStack->getMasterRequest()->getUri(),
            ]),
        ]));

        return;
    }

    $event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdutrion
I agree, simplifying if statements using early returns is (usually) good practice. I have updated the documentation with your example.

$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
} else {
$event->setResponse(
new Response(
302,
[
'Location' => $this->urlGenerator->generate(
'login',
[
'returnUrl' => $this->requestStack->getMasterRequest()->getUri(),
]
),
]
)
);
}
}
}
```

After listener is configured new client can be registered.

### Example: cli

```
bin/console trikoder:oauth2:create-client best_client not_so_secret --redirect-uri "https://www.bestclient.com/" --grant-type "authorization_code" --scope "user.view"
```

This example assumes scope `user.view` is already registered scope inside `trikoder_oauth2` configuration

### Example: config

```yaml
# /packages/trikoder_oauth2.yaml

trikoder_oauth2:
scopes:
- 'user.view'
```

After client is registered he can communicate with your server using authorization code grant.