ory / hydra

The most scalable and customizable OpenID Certified™ OpenID Connect and OAuth Provider on the market. Become an OpenID Connect and OAuth2 Provider over night. Broad support for related RFCs. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
https://www.ory.sh/?utm_source=github&utm_medium=banner&utm_campaign=hydra
Apache License 2.0
15.62k stars 1.5k forks source link

client_credentials grant type accepts audience at creation, but doesn't return them in access token #3441

Open hpylypets-ua opened 1 year ago

hpylypets-ua commented 1 year ago

Preflight checklist

Describe the bug

No audience is returned for client_credentials grant flow even when the audience parameter is explicitly set. This issue results in an invalid JWT token when validating it because of an empty aud claim.

How to set up an audience for the client? Docs doesn't help - https://www.ory.sh/docs/hydra/guides/audiences, same for tutorial - https://www.ory.sh/docs/hydra/5min-tutorial

Reproducing the bug

This is easily reproducible:

  1. Start Ory Hydra with minimal setup - docker run --name hydra -e DSN=memory -e SECRETS_SYSTEM=abcdefghjklimnop -e STRATEGIES_ACCESS_TOKEN=jwt -p 4444:4444 -p 4445:4445 oryd/hydra:v2.0.3 serve all --dev
  2. Create client_credentials client with specified audience. docker exec hydra hydra create client --endpoint http://127.0.0.1:4445/ --format json --grant-type client_credentials --audience account.
  3. Send a request without the audience
    curl --location 'http://localhost:4444//oauth2/token' \
    --header 'Authorization: Basic M2M0OGI5Y2QtNzEyNS00ZDFkLThkZDMtMGJiYTIzNGQ4NGNiOmo2LjBET2h4U0stVnpHaUhnUHVoLnppNEJQ' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'grant_type=client_credentials'
  4. We can verify that the audience is validated but not returned in the response by specifying the incorrect value. Adding --data-urlencode 'audience=account' results in the same response as without the audience.
  5. Response example
    {
    "access_token": "***",
    "expires_in": 3599,
    "scope": "",
    "token_type": "bearer"
    }

Relevant log output

{"audience":["account"],"client_id":"3c48b9cd-7125-4d1d-8dd3-0bba234d84cb","client_name":"","client_secret":"j6.0DOhxSK-VzGiHgPuh.zi4BP","client_secret_expires_at":0,"client_uri":"","created_at":"2023-02-15T01:55:55Z","grant_types":["client_credentials"],"jwks":{},"logo_uri":"","metadata":{},"owner":"","policy_uri":"","registration_access_token":"ory_at_v9rdUliw9PqR3X7saMk-GdMqUA-MH-HPfwyxg1ARDdQ.R6sB7EKSB0I5BoNftG6pGlqP7k2XWtxB3i5vrbXKOK4","registration_client_uri":"http://localhost:4444/oauth2/register/3c48b9cd-7125-4d1d-8dd3-0bba234d84cb","request_object_signing_alg":"RS256","response_types":["code"],"scope":"offline_access offline openid","subject_type":"public","token_endpoint_auth_method":"client_secret_basic","tos_uri":"","updated_at":"2023-02-15T01:55:55.234026Z","userinfo_signed_response_alg":"none"}

Relevant configuration

No response

Version

2.0.3(latest)

On which operating system are you observing this issue?

Linux

In which environment are you deploying?

Docker

Additional Context

I'm unable to use Ory Hydra as a token provider for Kafka Oauthbearer OIDC because of empty audience array, which is unexpected for JwtConsumer.

Keycloak works fine since it returns audience.

Caused by: org.jose4j.jwt.consumer.InvalidJwtException: JWT (claims->{"aud":[],"client_id":"a61f7d4d-e365-4134-8b5c-2053213b4cd8","exp":1676427193,"ext":{},"iat":1676423593,"iss":"http://localhost:4444/","jti":"a98d16a5-6bfd-4498-b3d9-ef6d961615f9","nbf":1676423593,"scp":[],"sub":"a61f7d4d-e365-4134-8b5c-2053213b4cd8"}) rejected due to invalid claims or other invalid content. Additional details: [[8] Audience (aud) claim [] present in the JWT but no expected audience value(s) were provided to the JWT Consumer. Expected one of [] as an aud value.]
    at org.jose4j.jwt.consumer.JwtConsumer.validate(JwtConsumer.java:459)
aeneasr commented 1 year ago

The OAuth2 spec does not specify how the audience would be used and validated for client_credentials flows, which is why we do not support it currently. I think auth0 supports an audience claim, but I don't think they validate it?

Pilipets commented 1 year ago

Screenshot from 2023-03-09 00-30-01 Screenshot from 2023-03-09 00-19-40

I will check later if the audience is validated, but it's included in JWT tokens - here are decoded tokens from Azure AD and Keycloak.

hpylypets-ua commented 1 year ago

Sorry for the delay in response.

I think auth0 supports an audience claim, but I don't think they validate it?

I checked for Keycloak: there are multiple ways to use audience there. One is a hardcoded value without a validation that can be customized, and another one, dynamic setting/verifying the audience claim based on the provided scope. So answering to your question, they have an option for validating an audience.

  1. https://www.keycloak.org/docs/latest/server_admin/#_audience_hardcoded For example, Client -> Client Details -> Client Scope -> choose dedicated scope -> add mapper -> by configuration -> audience/audience resolve. Then added audience claims can be included in the token.

  2. https://www.keycloak.org/docs/latest/server_admin/#setup-4 "verify-token-audience" : true,

When setting up audience checking:

Ensure that services are configured to check audience on the access token sent to them by adding the flag verify-token-audience in the adapter configuration. See Adapter configuration for details.

Ensure that access tokens issued by Keycloak contain all necessary audiences. Audiences can be added using the client roles as described in the next section or hardcoded. See Hardcoded audience.

I don't have a ready to test auth0 setup to verify how it works there, but they also have examples with audience and client_credentials requests - https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens#example-post-to-token-url

The OAuth2 spec does not specify how the audience would be used and validated for client_credentials flows, which is why we do not support it currently.

But as I showed in my first message, specifying an invalid audience leads to an error. The only problem is that aud claim is empty in the JWT token obtained for the client_credentials grant type in case of Ory Hydra.

I think it makes sense for Ory Hydra to include an audience claim in the JWT token if specified at client creation to mimic the hardcoded behaviour from the Keycloak. What do you think, @aeneasr?

droger88 commented 1 year ago

i am also curious how we should validate the token without the aud is returned from introspection? using scope instead? if using scope, does hydra support registering resource with custom scope?

In our case, we are trying to use hydra as the authorization server for secure our APIs. currently we are using the context path as the scope, so that nginx will intercept the request and use issuer url+scope to do the authorization before sending request to the upstream. I am hoping to use aud for this validation, and use scope for granting permission for things like read or write

aeneasr commented 1 year ago

I believe this to now be possible using actions / webhooks: https://www.ory.sh/docs/hydra/guides/claims-at-refresh