oauth2 and jwt in different domains - oauth-2.0

is this a good practice? (service 1 and 2 are in different domains)
I let the user authenticate with oauth2 providers like google or github
the user (authenticated) want to use a service; he contact the api gateway
the api gateway create and sign a JWT with his private key
the api gateway forward the user request with the JWT
one of the service verify the JWT using the public key (take it from the secrets manager) and perform the original user request

For different domains/resource servers, one could also use the "aud" claim in JWT for verifying the intended recipient of the token as a part of additional validation. If your auth server is adding aud claim while issuing the token then the resource server can verifying the same by extracting the aud claim and matching against the domain name.
"aud" (Audience) Claim
The "aud" (audience) claim identifies the recipients that the JWT is
intended for. Each principal intended to process the JWT MUST identify
itself with a value in the audience claim. If the principal processing
the claim does not identify itself with a value in the "aud" claim
when this claim is present, then the JWT MUST be rejected. In the
general case, the "aud" value is an array of case- sensitive strings,
each containing a StringOrURI value. In the special case when the JWT
has one audience, the "aud" value MAY be a single case-sensitive
string containing a StringOrURI value. The interpretation of audience
values is generally application specific.

Related

Access tokens and id tokens

I am pretty new with these protocols, and I am having some trouble understanding something.
I am currently working on an application which API and Frontend is mine, I use azure identity platform to receive the tokens on the clientside and send the token to the server that validates the token using passport-azure-ad bearerStrategy. (I have my app registration for that purposes ofcourse).
The thing that I don't get, is that I missed correctly used the tokens I received from azure in my client and sent the ID Token to my API, it verifes it as a valid one and user is authenticated to perform the request sent.
But, as I read here https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens, and in any other article about oAuth2 and openID, ID tokens are for UX stuff and client, while I should have used the access token in my request to my API.
But howcome the ID Token is also verified in my API? It makes no sense for me, or am I missing something?
And if so, is there any vurlnabilty in using Id Token as I did?
Thank you!
APIs should first validate the JWT access token, to check these fields have allowed values. An ID token will then fail the audience check.
JWT signature
Not expired / valid at this time
Issuer (a Microsoft ID)
Audience (eg api.mycompany.com)
Access tokens have scopes, whereas ID tokens do not. Every API endpoint should validate the received scope, eg to ensure that it has received the right type of token. This will also ensure that the API does not accept ID tokens.
So although some API tech stacks accept ID tokens, making the standard checks will ensure the right behavior. And the real API authorization is then done using claims, to apply your business rules.

Openid Connect standard way for exchanging ID Tokens

Is there any stadated way for replacing ID tokens of 2 authorization servers ?
For example, i have a service that you can login with an external ID Token but my service is also an authorization server and therefor i want to allow users exchange external ID Tokens with my service ID token.
OAuth2 specification defines following grant types:
authorization code
implicit grant
resource owner password credentials
client credentials
But it also supports defining additional grant types where required:
New authorization grant types can be defined by assigning them a
unique absolute URI for use with the "grant_type" parameter.
Per specification, new endpoints, parameters, response types and error codes may be defined to support the grant type extension.
When implemented, a client can make a request providing new grant type:
POST /token
grant_type=id_token&id_token=external-token&...
which would validate incoming token and issue your service's token.
Here are some links from oauth servers supporting custom grant types for inspiration:
https://oauthlib.readthedocs.io/en/latest/oauth2/grants/custom_grant.html
https://is.docs.wso2.com/en/latest/learn/writing-a-custom-oauth-2.0-grant-type/
https://identityserver.github.io/Documentation/docsv2/advanced/customGrantTypes.html

The 'sub' claim value is different between Access and Id tokens

I am using the Resource Owner Password grant flow and requesting id token as well (the scope includes openid). I am sending the following to the endpoint:
client_id
client_secret
grant_type=password
username
password
scope
In the response I get the access token and the id token. The value for the sub claim is different between the two tokens. Why is this the case?
Update
It seems that the user id is actually an oid claim. This is described in Azure AD ID token reference.
Text describing the oid claim:
The immutable identifier for an object in the Microsoft identity system, in this case, a user account. This ID uniquely identifies the user across applications - two different applications signing in the same user will receive the same value in the oid claim. The Microsoft Graph will return this ID as the id property for a given user account. Because the oid allows multiple apps to correlate users, the profile scope is required in order to receive this claim. Note that if a single user exists in multiple tenants, the user will contain a different object ID in each tenant - they are considered different accounts, even though the user logs into each account with the same credentials.
Text describing the sub claim:
The principal about which the token asserts information, such as the user of an app. This value is immutable and cannot be reassigned or reused. The subject is a pairwise identifier - it is unique to a particular application ID. Therefore, if a single user signs into two different apps using two different client IDs, those apps will receive two different values for the subject claim. This may or may not be desired depending on your architecture and privacy requirements.
However, I am still not clear why the sub claim is different between the access and id tokens.
The subject (sub) claim is unique for the user and the service for which the token is intended (identified by the audience (aud) claim).
Usually, the ID Token and Access Token audiences will be different: the ID Token audience is the client app where the user is signing in, and the Access Token audience is the resource server the client app will attempt to access (on behalf of the signed-in user).
The Resource Owner Password Credentials grant type is not supposed to be used for user authentication as per the OpenID specification, only flow with that interact with the user through the authorization endpoint are allowed to do so (i.e. Implicit, Authorization Code, Hybrid and None grant type flows at the time of writing).
If you receive an ID token using the ROPC flow, then the Identity Provider proposes specific means that are out of scope of the OpenID Connect spec and may have specific and non-standard features that should be documented.
In any case, on Client side you should only rely on the sub claim in the ID Token. The Client is not supposed to parse the access token as it is only meant to be used by the resource server.
The sub claim depends on the IdP policy and the client configuration. The sub claim may be unique to the application (or application group). Please refer to Subject Identifier Types section of the specification.

Client ID or Multiple Audiences In JSON Web Token

I am implementing OAuth 2.0 with JWT in my application and am having trouble deciding what to set as my aud claim as. A user will "login" to my client via my authentication server to gain access to my API (resource) server. I want my tokens to only be valid for use with a specific client and specific API.
When logging in from my client, I include it't client_id in the request, but in most implementations I've found, the aud is set to that client_id. I'm leaning towards including a customer audience_id field in my login request and then setting the aud in the token to an array of the client_id and the audience_id, but that feels like it just means that that token is valid for both those audiences, which makes me think I should just add a custom claim called client to specifically state that this token was created for a specific client.
I have not come across any implementations online that include both a client_id and audience_id(s) in an OAuth login request, nor do I see a reserved claim for client in the spec.
Am I missing something here?
What is best practice for specifically stating a different client_id and audience_id in a JWT?
The audience of the JWT is the Resource Server as that is where the token will be processed, i.e. verified, inspected and acted upon. From RFC 7519, https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3:
The "aud" (audience) claim identifies the recipients that the JWT is
intended for. Each principal intended to process the JWT MUST
identify itself with a value in the audience claim.
[...]
The interpretation of audience values is generally application specific.
[...]
So best practice is that aud should identify the Resource Server.
The Client is only the presenter of the token and it is best practice (i.e. in OpenID Connect and some emerging OAuth 2.0 exension drafts) to use the azp (Authorized Presenter) for that claim. From http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken :
azp
OPTIONAL. Authorized party - the party to which the ID Token was
issued. If present, it MUST contain the OAuth 2.0 Client ID of this
party. This Claim is only needed when the ID Token has a single
audience value and that audience is different than the authorized
party.
[...]
So best practice is that azp identifies the Client.

JWT (Json Web Token) Audience "aud" versus Client_Id - What's the difference?

I'm working on implementing OAuth 2.0 JWT access_token in my authentication server. But, I'm not clear on what the differences are between the JWT aud claim and the client_id HTTP header value. Are they the same? If not, can you explain the difference between the two?
My suspicion is that aud should refer to the resource server(s), and the client_id should refer to one of the client applications recognized by the authentication server (i.e. web app, or iOS app).
In my current case, my resource server is also my web app client.
As it turns out, my suspicions were right. The audience aud claim in a JWT is meant to refer to the Resource Servers that should accept the token.
As this post simply puts it:
The audience of a token is the intended recipient of the token.
The audience value is a string -- typically, the base address of the
resource being accessed, such as https://contoso.com.
The client_id in OAuth refers to the client application that will be requesting resources from the Resource Server.
The Client app (e.g. your iOS app) will request a JWT from your Authentication Server. In doing so, it passes it's client_id and client_secret along with any user credentials that may be required. The Authorization Server validates the client using the client_id and client_secret and returns a JWT.
The JWT will contain an aud claim that specifies which Resource Servers the JWT is valid for. If the aud contains www.myfunwebapp.com, but the client app tries to use the JWT on www.supersecretwebapp.com, then access will be denied because that Resource Server will see that the JWT was not meant for it.
The JWT aud (Audience) Claim
According to RFC 7519:
The "aud" (audience) claim identifies the recipients that the JWT is
intended for. Each principal intended to process the JWT MUST
identify itself with a value in the audience claim. If the principal
processing the claim does not identify itself with a value in the
"aud" claim when this claim is present, then the JWT MUST be
rejected. In the general case, the "aud" value is an array of case-
sensitive strings, each containing a StringOrURI value. In the
special case when the JWT has one audience, the "aud" value MAY be a
single case-sensitive string containing a StringOrURI value. The
interpretation of audience values is generally application specific.
Use of this claim is OPTIONAL.
The Audience (aud) claim as defined by the spec is generic, and is application specific. The intended use is to identify intended recipients of the token. What a recipient means is application specific. An audience value is either a list of strings, or it can be a single string if there is only one aud claim. The creator of the token does not enforce that aud is validated correctly, the responsibility is the recipient's to determine whether the token should be used.
Whatever the value is, when a recipient is validating the JWT and it wishes to validate that the token was intended to be used for its purposes, it MUST determine what value in aud identifies itself, and the token should only validate if the recipient's declared ID is present in the aud claim. It does not matter if this is a URL or some other application specific string. For example, if my system decides to identify itself in aud with the string: api3.app.com, then it should only accept the JWT if the aud claim contains api3.app.com in its list of audience values.
Of course, recipients may choose to disregard aud, so this is only useful if a recipient would like positive validation that the token was created for it specifically.
My interpretation based on the specification is that the aud claim is useful to create purpose-built JWTs that are only valid for certain purposes. For one system, this may mean you would like a token to be valid for some features but not for others. You could issue tokens that are restricted to only a certain "audience", while still using the same keys and validation algorithm.
Since in the typical case a JWT is generated by a trusted service, and used by other trusted systems (systems which do not want to use invalid tokens), these systems simply need to coordinate the values they will be using.
Of course, aud is completely optional and can be ignored if your use case doesn't warrant it. If you don't want to restrict tokens to being used by specific audiences, or none of your systems actually will validate the aud token, then it is useless.
Example: Access vs. Refresh Tokens
One contrived (yet simple) example I can think of is perhaps we want to use JWTs for access and refresh tokens without having to implement separate encryption keys and algorithms, but simply want to ensure that access tokens will not validate as refresh tokens, or vice-versa.
By using aud, we can specify a claim of refresh for refresh tokens and a claim of access for access tokens upon creating these tokens. When a request is made to get a new access token from a refresh token, we need to validate that the refresh token was a genuine refresh token. The aud validation as described above will tell us whether the token was actually a valid refresh token by looking specifically for a claim of refresh in aud.
OAuth Client ID vs. JWT aud Claim
The OAuth Client ID is completely unrelated, and has no direct correlation to JWT aud claims. From the perspective of OAuth, the tokens are opaque objects.
The application which accepts these tokens is responsible for parsing and validating the meaning of these tokens. I don't see much value in specifying OAuth Client ID within a JWT aud claim.
If you came here searching OpenID Connect (OIDC): OAuth 2.0 != OIDC
I recognize that this is tagged for oauth 2.0 and NOT OIDC, however there is frequently a conflation between the 2 standards since both standards can use JWTs and the aud claim. And one (OIDC) is basically an extension of the other (OAUTH 2.0). (I stumbled across this question looking for OIDC myself.)
OAuth 2.0 Access Tokens##
For OAuth 2.0 Access tokens, existing answers pretty well cover it. Additionally here is one relevant section from OAuth 2.0 Framework (RFC 6749)
For public clients using implicit flows, this specification does not
provide any method for the client to determine what client an access
token was issued to.
...
Authenticating resource owners to clients is out of scope for this
specification. Any specification that uses the authorization process
as a form of delegated end-user authentication to the client (e.g.,
third-party sign-in service) MUST NOT use the implicit flow without
additional security mechanisms that would enable the client to
determine if the access token was issued for its use (e.g., audience-
restricting the access token).
OIDC ID Tokens##
OIDC has ID Tokens in addition to Access tokens. The OIDC spec is explicit on the use of the aud claim in ID Tokens. (openid-connect-core-1.0)
aud
REQUIRED. Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case sensitive string.
furthermore OIDC specifies the azp claim that is used in conjunction with aud when aud has more than one value.
azp
OPTIONAL. Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party. It MAY be included even when the authorized party is the same as the sole audience. The azp value is a case sensitive string containing a StringOrURI value.
Though this is old, I think question is valid even today
My suspicion is that aud should refer to the resource server(s), and
the client_id should refer to one of the client applications
recognized by the authentication server
Yes, aud should refer to token consuming party. And client_id refers to token obtaining party.
In my current case, my resource server is also my web app client.
In the OP's scenario, web app and resource server both belongs to same party. So this means client and audience to be same. But there can be situations where this is not the case.
Think about a SPA which consume an OAuth protected resource. In this scenario SPA is the client. Protected resource is the audience of access token.
This second scenario is interesting. RFC8707 "Resource Indicators for OAuth 2.0" explains where you can define the intended audience in your authorization request using resource parameter. So the resulting token will restricted to the specified audience. Also, Azure OIDC use a similar approach where it allows resource registration and allow auth request to contain resource parameter to define access token intended audience. Such mechanisms allow OAuth adpotations to have a separation between client and token consuming (audience) party.

Resources