Open ThaDaVos opened 2 years ago
I've found a way and it seems to work correctly also:
First off, one has to override the AuthCodeGrant
to include the nonce
in it's payload - mine is inside an custom extension package I made for Laravel/Passport, but hope it helps:
<?php
namespace {SOME_NAMESPACE{;
use Illuminate\Support\Arr;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use Nyholm\Psr7\Response as Psr7Response;
class OpenIdAuthCodeGrant extends AuthCodeGrant
{
/**
* {@inheritdoc}
*/
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
{
/** @var RedirectResponse $response */
$response = parent::completeAuthorizationRequest($authorizationRequest);
if (request()->query->has('nonce')) {
$httpResponse = $response->generateHttpResponse(new Psr7Response());
$redirectUri = Arr::first($httpResponse->getHeader('Location'));
$parsed = parse_url($redirectUri);
parse_str($parsed['query'], $query);
$authCodePayload = json_decode($this->decrypt($query['code']), true);
$authCodePayload['nonce'] = request()->query('nonce');
$query['code'] = $this->encrypt(json_encode($authCodePayload));
$parsed['query'] = http_build_query($query);
$response->setRedirectUri($this->unparse_url($parsed));
}
return $response;
}
/**
* Inverse of parse_url
*
* @param mixed $parsed_url
* @return string
*/
private function unparse_url($parsed_url)
{
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
}
After this is done, and it's registered on the AuthorizationServer, you can use it inside the IdTokenResponse
as follows:
<?php
namespace {SOME_NAMESPACE{;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use OpenIDConnectServer\IdTokenResponse as OpenIDConnectServerIdTokenResponse;
class IdTokenResponse extends OpenIDConnectServerIdTokenResponse
{
protected function getBuilder(AccessTokenEntityInterface $accessToken, UserEntityInterface $userEntity)
{
$request = request();
$builder = parent::getBuilder($accessToken, $userEntity)
->issuedBy($request->getScheme() . '://' . $request->getHost());
$authCodePayload = json_decode($this->decrypt($request->input('code')), true);
if (isset($authCodePayload['nonce'])) {
$builder->withClaim('nonce', $authCodePayload['nonce']);
}
return $builder;
}
}
And it will be available inside the id_token
as it should
hi @ThaDaVos,
I just finished to integrate oauth2-server and openid-connect to a Symfony application, and the next step was : supporting the nonce parameter ! You code will help me a lot !
I got to implement the /ket/set GET call to validate the id_token response. Not sure this is mandatory tho.
But for sure i will try to update the doc here, as the constructor for AuthorizationServer in https://github.com/thephpleague/oauth2-server has changed...
In what way did it change? Cause I still use the same as in the docs here - are you sure you're not accidentally looking at a different version of the AuthorizationServer? Maybe in V9 it will change (which isn't out yet)
Edit: My implementation is in Laravel BTW - so hope a lot will be the same for you @thejoelinux
Docs says:
// Setup the authorization server
$server = new \League\OAuth2\Server\AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKey,
$publicKey,
$responseType
);
But it's not the $publicKey
any more, it's the encryptionKey (I use a Defuse\Crypto\Key
btw).
$encryptionKey = Key::loadFromAsciiSafeString(getenv('DEFUSE_KEY'));
$this->server = new \League\OAuth2\Server\AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKey,
$encryptionKey,
$responseType
);
Oh yeah, that's correct 😂
To the Symfony users out there : you can use the code made by @ThaDaVos with some minor adaptations;
First, you probably don't have (or want) the Illuminate\Support\Arr
, the request is not available the same way, so it's something like:
public function completeAuthorizationRequest(
AuthorizationRequest $authorizationRequest
) {
$request = Request::createFromGlobals();
/** @var RedirectResponse $response */
$response = parent::completeAuthorizationRequest($authorizationRequest);
if ($request->get('nonce') != null) {
$httpResponse = $response->generateHttpResponse(new Psr7Response());
$redirectUri = $httpResponse->getHeader('Location');
$parsed = parse_url($redirectUri[0]);
parse_str($parsed['query'], $query);
$authCodePayload = json_decode($this->decrypt($query['code']), true);
$authCodePayload['nonce'] = $request->get('nonce');
$query['code'] = $this->encrypt(json_encode($authCodePayload));
$parsed['query'] = http_build_query($query);
$response->setRedirectUri($this->unparse_url($parsed));
}
return $response;
}
Then, don't forget to register it on the AuthorizationServer
on the /authorize
end-point. (I almost lose my mind trying to register the thing on the /token
end-point).
The same same thing goes for the IdTokenResponse
: replace the $request = request();
by the $request = Request::createFromGlobals();
and give it to the AuthorizationServer
on the /token/
end-point.
May the force be with you, because that was not an easy one.
Yesterday I took a deep dive into and re-did it all - it's now spec compliant (as far as I know) as I now have support for hybrid flow too - including c_hash
- took some time to implement though
I am looking into a way of passing the
nonce
parameter through as discussed here: https://github.com/thephpleague/oauth2-server/issues/962Any idea on how we can implement this, as it's required by the spec here: https://openid.net/specs/openid-connect-core-1_0.html