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

Support for nonce parameter #47

Open
ThaDaVos opened this issue Aug 26, 2022 · 7 comments
Open

Support for nonce parameter #47

ThaDaVos opened this issue Aug 26, 2022 · 7 comments

Comments

@ThaDaVos
Copy link

I am looking into a way of passing the nonce parameter through as discussed here:
thephpleague/oauth2-server#962

Any 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

@ThaDaVos
Copy link
Author

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

@thejoelinux
Copy link

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...

@ThaDaVos
Copy link
Author

ThaDaVos commented Aug 26, 2022

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

@thejoelinux
Copy link

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
);

@ThaDaVos
Copy link
Author

Oh yeah, that's correct 😂

@thejoelinux
Copy link

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.

@ThaDaVos
Copy link
Author

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

moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jan 19, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jan 19, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Apr 24, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to ronvanderheijden/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to moufmouf/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
moufmouf added a commit to ronvanderheijden/openid-connect that referenced this issue Jun 17, 2024
According to the OpenID connect spec:

> nonce
> String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.

Right now, if a client passes a "nounce", we don't give it back and
the client fails. This is happening to me right now with the client
from Matrix Synapse.

Here, I'm creating a new service (`CurrentRequestService`).
With this new service, I can get the current PSR-7 request.

I extend the AuthCodeGrant and inject this service into the extended class.
With this, I can:

- read the "nonce" from the request
- encode the "nonce" in the "code"

Then, in the `IdTokenResponse`, I read the "code" (if it is present),
extract the "nounce" and inject it in the ID token as a new claim.

The whole process is inspired by this comment: steverhoades/oauth2-openid-connect-server#47 (comment)

With those changes, nounce is correctly handled and I've successfully
tested a connection with the OpenID client from Matrix Synapse.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants