Skip to content

New access_token Authenticator Section #399

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all 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
96 changes: 93 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,99 @@ them into your system. In that case, instead of putting all of
the logic in `connectCheckAction()` as shown above, you'll leave that
blank and create an authenticator which will hold similar logic.

Now you can use the new Symfony Authenticator system (available **since Symfony 5.2**,
don't use it before this version) to login in your app. For legacy Symfony versions,
use [Guard Authenticator](#authenticating-with-guard) below.
Now you can use the `access_token` authenticator available **since Symfony 6.2**.

* If you use symfony ≥5.2 or <6.2, use [OAuth2Authenticator](#authenticating-with-oauth2authenticator) below.
* If you use symfony <5.2, use [Guard Authenticator](#authenticating-with-guard) below.

### Step 1) Using the `access_token` Authenticator

```php
namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface; // your user entity
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use SensitiveParameter;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;

final class AccessTokenHandler implements AccessTokenHandlerInterface
{
private $client;

public function __construct(
ClientRegistry $clientRegistry,
private readonly EntityManagerInterface $entityManager,
) {
$this->client = $clientRegistry->getClient('facebook_main');
}

public function getUserBadgeFrom(#[SensitiveParameter] string $accessToken): UserBadge
{
/** @var FacebookUser $facebookUser */
$facebookUser = $this->client->fetchUserFromToken($accessToken);

$email = $facebookUser->getEmail();

// 1) have they logged in with Facebook before? Easy!
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy([
'facebookId' => $facebookUser->getId(),
]);

if ($existingUser) {
return new UserBadge($existingUser->getIdentifier(), fn () => $existingUser);
}

// 2) do we have a matching user by email?
$user = $this->entityManager->getRepository(User::class)->findOneBy([
'email' => $email,
]);

// 3) Maybe you just want to "register" them by creating
// a User object
if ($user === null) {
// Register the user here or throw an exception depending on your security policy
throw new BadCredentialsException('Invalid credentials.');
}
$user->setFacebookId($facebookUser->getId());
$this->entityManager->persist($user);
$this->entityManager->flush();

return new UserBadge($user->getIdentifier(), fn () => $user);
}
}
```

### Step 2) Configuring the Security

Next, enable the new authenticator manager and then register your authenticator
in `security.yaml` under the `custom_authenticators` section:

```diff
# app/config/packages/security.yaml
security:
# ...
+ enable_authenticator_manager: true
Copy link

Choose a reason for hiding this comment

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

This line triggers the following error in Symfony 7.0

Unrecognized option "enable_authenticator_manager" under "security". Available options are "access_control", "access_decision_manager", "access_denied_url", "erase_credentials", "firewalls", "hide_user_not_found", "password_hashers", "providers", "role_hierarchy", "session_fixation_strategy".

Copy link
Member

@bocharsky-bw bocharsky-bw Feb 5, 2024

Choose a reason for hiding this comment

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

Thank you for testing this! That enable_authenticator_manager is not needed for Symfony 7, it was opt-in feature in previous versions but default in Sf 7, you can ignore it.

Probably makes sense to add a comment that this needed only for legacy Symfony <7 projects.

Other than that looks good in this new example to you?


firewalls:
# ...
main:
# ...
+ access_token:
+ token_handler: App\Security\AccessTokenHandler
```

> **CAUTION** You *can* also inject the individual client (e.g. `FacebookClient`)
into your authenticator instead of the `ClientRegistry`. However, this may cause
circular reference issues and degrades performance (because authenticators are instantiated
on every request, even though you *rarely* need the `FacebookClient` to be created).
The `ClientRegistry` lazily creates the client objects.



## Authenticating with OAuth2Authenticator

### Step 1) Using the new OAuth2Authenticator Class

Expand Down