Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add documentation for authorization code grant #177

Open
wants to merge 4 commits into
base: v3.x
Choose a base branch
from
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:
v-m-i marked this conversation as resolved.
Show resolved Hide resolved
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:
v-m-i marked this conversation as resolved.
Show resolved Hide resolved
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:
v-m-i marked this conversation as resolved.
Show resolved Hide resolved
scopes:
- 'user.view'
```

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