Today, the only standardized way for a client to specify an audience is to represent those audiences as scopes (e.g., a https://api.iyasec.io/api scope). Then, this value would either be defined as an audience in the access token or just tracked as a scope. This is vague and confuses the concepts of scope and audience as defined earlier.
It is very important to remember that since the OAuth 2 and OIDC specs do not define a required mechanism of allowing a client to specify what the access token audience should be or how it is represented, a proprietary mechanism must be developed by each IdP vendor. Whatever mechanism is chosen, it will likely match one of the approaches described in this section. You can imagine an extension to the OAuth 2 and OIDC specs some day that formalizes all of this.
Using information from the above, we can request access tokens using one of the approaches below. All of this should be done within an authentication library (used by the client, our SPA or native mobile app) that the application developer doesn’t need to worry about, other than a small amount of integration logic.
Option #1: Resource Parameter Given to OAuth 2 Token Endpoint
The call to the authorization endpoint looks similar to:
GET /oidc/authorize?
response_type=code
&Scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj...rtereyt
&redirect_uri=https%3A%2F%2Fapi.iyasec.io%2Fcb HTTP/1.1
The call to the token endpoint will look similar to:
POST /oidc/token HTTP/1.1
Host: api.iyasec.io
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https%3A%2F%2Fapp1.iyasec.io%2Fcb&
client_id=s6BhdRkqt3&
resource=https://api.iyasec.io&
Scope=openid%20api.iyasec.io/read%20api.iyasec.io/write
Option #2: Resource Parameter with Multiple Audience Values
The calls for this option are essentially the same as that of Option #1. The only difference is that the resource parameter must support multiple audience values. We could use a similar pattern used with the scope parameter: a space-separated list of values. So the token endpoint call would look similar to:
POST /oidc/token HTTP/1.1
Host: api.iyasec.io
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https%3A%2F%2Fcapi.iyasec.io%2Fcb&
client_id=s6BhdRkqt3&
resource=https://api.iyasec.io/api1%20https://api.iyasec.io/api2&
Scope=openid%20api.iyasec.io/read%20api.iyasec.io/write
Option #3: Use The id_token_hint Parameter with the OAuth 2 Authorization Endpoint
For Option #3, the initial end-user authentication would occur more or less the same as with Option #1. The resource parameter to the token endpoint would probably not be needed.
A new OIDC authorization code flow should be initiated in the background for each new access token that is needed. These subsequent OIDC flows can be initiated with:
GET /oidc/authorize?
response_type=code
&Scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fapp1.iyasec.io%2Fcb
&prompt=none
&id_token_hint=lskjdflksjdfdslk... HTTP/1.1
The id_token_hint parameter contains the ID token from the original user authentication. If the IdP doesn’t support this parameter, a security-session tracking cookie could also be used to authenticate the user. However, this is not a mechanism that is defined by the OAuth 2 or OIDC specs.
If a session timeout or other issue caused an error to be returned, the authentication library would have to handle the error and initiate an authorization code flow that the end user can use to re-enter credentials.
Again, it must be pointed out that what I’m presenting in this section is attempting to fill a gap in the original specs and is most certainly non-standard at this point. However, one can imagine that in the future, the specs could be extended to address this specific use case, but that new mechanism probably won’t exactly match the syntax I’ve laid out here.
The options above will result in an access token (JWT format) being returned that looks similar to the following:
{
"iss": "https://idp.iyasec.io",
"aud": "https://api1.iyasec.io",
"Scope": "openid api1.iyasec.io/read api1.iyasec.io/write",
"sub": "6dfc53a9-9d8f-4668-a310-861ef662d256","Scope": "openid api1.iyasec.io/read api1.iyasec.io/write",
"iat": 1484016426,
"nbf": 1484016426,
"exp": 1484020326,
"claim1": "value1",
.
.
.
}
Or
{
"iss": "https://idp.iyasec.io",
"Aud": [ "https://api1.iyasec.io",
“https://api2.iyasec.io” ],
"Scope": "openid api1.iyasec.io/read api1.iyasec.io/write",
"sub": "6dfc53a9-9d8f-4668-a310-861ef662d256","Scope": "openid api1.iyasec.io/read api1.iyasec.io/write",
"iat": 1484016426,
"nbf": 1484016426,
"exp": 1484020326,
"claim1": "value1",
.
.
.
}