Skip to content

[Security] Access Tokens #16819

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

Merged
merged 3 commits into from
Nov 26, 2022
Merged

[Security] Access Tokens #16819

merged 3 commits into from
Nov 26, 2022

Conversation

Spomky
Copy link
Contributor

@Spomky Spomky commented May 22, 2022

Documentation page related to the PR symfony/symfony#46428

@carsonbot
Copy link
Collaborator

Hey!

Oh no, it looks like you have made this PR towards a branch that is not maintained anymore. :/
Could you update the PR base branch to target one of these branches instead? 4.4, 5.4, 6.0, 6.1.

Cheers!

Carsonbot

@Spomky Spomky marked this pull request as draft May 22, 2022 16:25
@wouterj wouterj added the Waiting Code Merge Docs for features pending to be merged label May 23, 2022
@carsonbot carsonbot added this to the next milestone May 23, 2022
@Spomky Spomky force-pushed the features/access-token branch from 2eaa41a to 7d32ca7 Compare June 18, 2022 15:46
Copy link
Contributor

@94noni 94noni left a comment

Choose a reason for hiding this comment

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

some early reviews passing by :)

chalasr added a commit to symfony/symfony that referenced this pull request Aug 10, 2022
This PR was merged into the 6.2 branch.

Discussion
----------

[Security] Access Token Authenticator

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | yes
| New feature?  | yes<!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no
| Tickets       | Fix #45844
| License       | MIT
| Doc PR        | symfony/symfony-docs#16819

Hi,

This PR aims at fixing #45844.
It adds a new authenticator that is able to fetch a token in the request header and retrieve the associated user identifier.

The authenticator delegates the token loading to a handler. This handler could manage opaque tokens (random strings stored in a database) or self-contained tokens such as JWT, Paseto, SAML...

* [x] [RFC6750, section 2](https://datatracker.ietf.org/doc/html/rfc6750#section-2): Authenticated Requests
    * [x] Token in the request header ([section 2.1](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1))
    * [x]  Token in the query string ([section 2.2](https://datatracker.ietf.org/doc/html/rfc6750#section-2.2))
    * [x]  Token in the request body ([section 2.3](https://datatracker.ietf.org/doc/html/rfc6750#section-2.3))
* [x] [RFC6750, section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3): The WWW-Authenticate Response Header Field
    * [x] [RFC6750, section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1): Error Codes
* [x] Documentation: see symfony/symfony-docs#16819
* [x] Tests

# Firewall Configuration

This PR adds a new authenticator that covers the RFC6750: `access_token`.
Also, it adds the possibility to extract the token from anywhere in the request.

## Basic Configuration

```yaml
security:
    firewalls:
        main:
            pattern: ^/
            access_token:
                token_handler: access_token.access_token_handler
```

## Complete Configuration

```yaml
security:
    firewalls:
        main:
            pattern: ^/
            access_token:
                user_provider: 'dedicate_user_provider_for_this_firewall'
                success_handler: 'custom_success_handler'
                failure_handler: 'custom_failure_handler'
                token_handler: access_token.access_token_handler
                token_extractors:
                    - 'security.access_token_extractor.query_string'
                    - 'security.access_token_extractor.request_body'
                    - 'security.access_token_extractor.header'
                    - 'custom_access_token_extractor'
```

# Token Handler

This authenticator relies on a Token Handler. Its responsability is to
* load the token
* check the token (revocation, expiration time, digital signature...)
* return the user ID associated to it

Tokens could be of any kind: opaque strings or self-contained tokens such as JWT, Paseto, SAML2...

## Example: from a repository

```php
<?php

namespace App\Security;

use App\Repository\AccessTokenRepository;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\AccessTokenHandler as AccessTokenHandlerAliasInterface;

class AccessTokenHandler implements AccessTokenHandlerAliasInterface
{
    public function __construct(private readonly AccessTokenRepository $repository)
    {
    }

    public function getUserIdentifierFrom(string $token): string
    {
        $accessToken = $this->repository->findOneByValue($token);
        if ($accessToken === null || !$accessToken->isValid()) {
            throw new BadCredentialsException('Invalid credentials.');
        }

        return $accessToken->getUserId();
    }
}
```

## Example: from a JWT

```php
<?php

namespace App\Security;

use App\Security\JWTLoader;
use App\Security\JWTValidator;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\AccessTokenHandler as AccessTokenHandlerAliasInterface;

class AccessTokenHandler implements AccessTokenHandlerAliasInterface
{
    public function __construct(
        private readonly JWTLoader $loader,
        private readonly JWTValidator $validator
    )
    {
    }

    public function getUserIdentifierFrom(string $token): string
    {
        try {
            $token = $this->loader->loadJWT($token);
            $this->validator->validate($token);

            return $token->getClaim('sub');
        } catch (\Throwable $e) {
            throw new BadCredentialsException('Invalid credentials.', $e->getCode, $e);
        }
    }
}
```

Commits
-------

e5873e8 [Security] Access Token Authenticator
@Spomky Spomky force-pushed the features/access-token branch 4 times, most recently from 3ab4092 to 5decd7a Compare August 11, 2022 11:44
@Spomky Spomky marked this pull request as ready for review August 11, 2022 12:06
@carsonbot carsonbot modified the milestones: next, 6.2 Aug 11, 2022
@javiereguiluz javiereguiluz added Security and removed Waiting Code Merge Docs for features pending to be merged labels Aug 11, 2022
@carsonbot carsonbot changed the title Access Tokens [Security] Access Tokens Aug 11, 2022
@wouterj
Copy link
Member

wouterj commented Nov 5, 2022

Hi @Spomky! Thanks a lot for this nice piece of documentation.

I've taken the freedom to slightly move some sections around and complete this PR (don't be shocked by the diff, it's mostly fixing line length and moving text around). Imho, this is now ready to merge!

Status: reviewed

@wouterj wouterj force-pushed the features/access-token branch from 232cfed to f3f47ad Compare November 26, 2022 10:37
@wouterj wouterj merged commit 4588822 into symfony:6.2 Nov 26, 2022
@wouterj
Copy link
Member

wouterj commented Nov 26, 2022

Thank you for writing these docs, @Spomky!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants