-
Notifications
You must be signed in to change notification settings - Fork 453
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add passport credentials and authenticator
- Loading branch information
Showing
7 changed files
with
694 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
Security/Authentication/Authenticator/OAuthAuthenticator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the FOSOAuthServerBundle package. | ||
* | ||
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace FOS\OAuthServerBundle\Security\Authentication\Authenticator; | ||
|
||
use FOS\OAuthServerBundle\Security\Authentication\Passport\OAuthCredentials; | ||
use FOS\OAuthServerBundle\Security\Authentication\Token\OAuthToken; | ||
use OAuth2\OAuth2; | ||
use OAuth2\OAuth2AuthenticateException; | ||
use OAuth2\OAuth2ServerException; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | ||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | ||
use Symfony\Component\Security\Core\Exception\AccountStatusException; | ||
use Symfony\Component\Security\Core\Exception\AuthenticationException; | ||
use Symfony\Component\Security\Core\User\UserCheckerInterface; | ||
use Symfony\Component\Security\Core\User\UserProviderInterface; | ||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; | ||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; | ||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport; | ||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; | ||
use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; | ||
|
||
/** | ||
* OAuthAuthenticator class. | ||
* | ||
* @author Israel J. Carberry <[email protected]> | ||
*/ | ||
class OAuthAuthenticator implements AuthenticatorInterface | ||
{ | ||
/** | ||
* @var OAuth2 | ||
*/ | ||
protected $serverService; | ||
|
||
/** | ||
* @var TokenStorageInterface | ||
*/ | ||
private $tokenStorage; | ||
|
||
/** | ||
* @var UserCheckerInterface | ||
*/ | ||
protected $userChecker; | ||
|
||
/** | ||
* @var UserProviderInterface | ||
*/ | ||
protected $userProvider; | ||
|
||
public function __construct( | ||
OAuth2 $serverService, | ||
TokenStorageInterface $tokenStorage, | ||
UserCheckerInterface $userChecker, | ||
UserProviderInterface $userProvider | ||
) { | ||
$this->serverService = $serverService; | ||
$this->tokenStorage = $tokenStorage; | ||
$this->userChecker = $userChecker; | ||
$this->userProvider = $userProvider; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function authenticate(Request $request): UserPassportInterface | ||
{ | ||
try { | ||
$token = $this->tokenStorage->getToken(); | ||
$tokenString = $token->getToken(); | ||
|
||
$accessToken = $this->serverService->verifyAccessToken($tokenString); | ||
|
||
/** @var \Symfony\Component\Security\Core\User\UserInterface **/ | ||
$user = $accessToken->getUser(); | ||
|
||
if (null === $user) { | ||
throw new AuthenticationException('OAuth2 authentication failed'); | ||
} | ||
|
||
// check the user | ||
try { | ||
$this->userChecker->checkPreAuth($user); | ||
} catch (AccountStatusException $e) { | ||
throw new OAuth2AuthenticateException( | ||
Response::HTTP_UNAUTHORIZED, | ||
OAuth2::TOKEN_TYPE_BEARER, | ||
$this->serverService->getVariable(OAuth2::CONFIG_WWW_REALM), | ||
'access_denied', | ||
$e->getMessage() | ||
); | ||
} | ||
|
||
return new Passport( | ||
new UserBadge($user->getUsername()), | ||
new OAuthCredentials($tokenString, $accessToken->getScope()) | ||
); | ||
} catch (OAuth2ServerException $e) { | ||
throw new AuthenticationException('OAuth2 authentication failed', 0, $e); | ||
} | ||
|
||
// this should never be reached | ||
throw new AuthenticationException('OAuth2 authentication failed'); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface | ||
{ | ||
try { | ||
// expect the badges in the passport from authenticate method above | ||
if (!$passport->hasBadge(OAuthCredentials::class) | ||
|| !$passport->hasBadge(UserBadge::class) | ||
) { | ||
throw new OAuth2AuthenticateException( | ||
Response::HTTP_UNAUTHORIZED, | ||
OAuth2::TOKEN_TYPE_BEARER, | ||
$this->serverService->getVariable(OAuth2::CONFIG_WWW_REALM), | ||
'access_denied', | ||
'Unexpected credentials type.' | ||
); | ||
} | ||
|
||
// get the passport badges | ||
$credentials = $passport->getBadge(OAuthCredentials::class); | ||
$user = $this->userProvider->loadUserByUsername( | ||
$passport->getBadge(UserBadge::class)->getUserIdentifier() | ||
); | ||
|
||
// check the user | ||
try { | ||
$this->userChecker->checkPostAuth($user); | ||
} catch (AccountStatusException $e) { | ||
throw new OAuth2AuthenticateException( | ||
Response::HTTP_UNAUTHORIZED, | ||
OAuth2::TOKEN_TYPE_BEARER, | ||
$this->serverService->getVariable(OAuth2::CONFIG_WWW_REALM), | ||
'access_denied', | ||
$e->getMessage() | ||
); | ||
} | ||
} catch (OAuth2ServerException $e) { | ||
throw new AuthenticationException('OAuth2 authentication failed', 0, $e); | ||
} | ||
|
||
$token = new OAuthToken($credentials->getRoles($user)); | ||
$token->setAuthenticated(true); | ||
$token->setToken($credentials->getTokenString()); | ||
$token->setUser($user); | ||
|
||
$credentials->markResolved(); | ||
|
||
return $token; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response | ||
{ | ||
return null; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response | ||
{ | ||
$responseException = new OAuth2AuthenticateException( | ||
Response::HTTP_UNAUTHORIZED, | ||
OAuth2::TOKEN_TYPE_BEARER, | ||
$this->serverService->getVariable(OAuth2::CONFIG_WWW_REALM), | ||
'access_denied', | ||
$exception->getMessage() | ||
); | ||
|
||
return $responseException->getHttpResponse(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function supports(Request $request): ?bool | ||
{ | ||
return $this->tokenStorage->getToken() instanceof OAuthToken; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the FOSOAuthServerBundle package. | ||
* | ||
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace FOS\OAuthServerBundle\Security\Authentication\Passport; | ||
|
||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface; | ||
use Symfony\Component\Security\Core\User\UserInterface; | ||
|
||
/** | ||
* Implements credentials checking for an OAuth token. | ||
* | ||
* @author Israel J. Carberry <[email protected]> | ||
* | ||
* @final | ||
*/ | ||
class OAuthCredentials implements CredentialsInterface | ||
{ | ||
/** | ||
* @var bool | ||
*/ | ||
private $resolved = false; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $scope; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $tokenString; | ||
|
||
public function __construct(string $tokenString, string $scope) | ||
{ | ||
$this->tokenString = $tokenString; | ||
$this->scope = $scope; | ||
} | ||
|
||
public function getRoles(UserInterface $user): array | ||
{ | ||
$roles = $user->getRoles(); | ||
|
||
if (empty($this->scope)) { | ||
return $roles; | ||
} | ||
|
||
foreach (explode(' ', $this->scope) as $role) { | ||
$roles[] = 'ROLE_'.mb_strtoupper($role); | ||
} | ||
|
||
return array_unique($roles, SORT_REGULAR); | ||
} | ||
|
||
public function getTokenString(): ?string | ||
{ | ||
return $this->tokenString; | ||
} | ||
|
||
public function markResolved(): void | ||
{ | ||
$this->resolved = true; | ||
$this->scope = null; | ||
$this->tokenString = null; | ||
} | ||
|
||
public function isResolved(): bool | ||
{ | ||
return $this->resolved; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.