Resource Indicator

Introduction

the original mechanism on OAuth 2 framework for expressing authorizations is the scopes presented on the token by the subject to the token holder. That allowed many enterprises and adopters to properly secured their environments when there is no ambiguity of the resource that the token is about.

Today with the micro service architecture and data partition due to data residency requirements or data sharding, the resource that the grant is about needs to be increasingly precise identified on the request, token and introspection.

There are 2 specifications addressing this: the Resource Identifier (RFC 8707) and the Rich Authorization Requests (that is still in draft state). This note is about the Resource Identifier specification and the support that Authlete provides.

The resource identifier specification extends the authorization requests by introducing the “resource” parameter. This parameter can be included on URI, Request Objects or the body of the push or token requests.

Basic flow

The authorization requests, pushed or not and wrapped or not on a Request Object can single or multi valued.

The request below is a sample of an authorization request with 2 resources listed to be included on the authorization using the parameter resource . The response body include the resources attribute for further evaluation of the AS and authorization of the final user.

curl --request POST 'https://api.authlete.com/api/auth/authorization' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXXXXXXE=' \
--data '{
  "parameters":
    "client_id=4015029369818770&
     scope=openid&
     response_type=code&
     redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
     code_challenge=OJeFyctrNbrZKVR60DQhAMqN6X_q00x7koQWAMZivnI&
     code_challenge_method=S256&
     resource=https%3A%2F%2Fapi.example.com%2Fapp%2F&
     resource=https%3A%2F%2Fcontacts.example.com%2F" }'
{
    "type": "authorizationResponse",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 974410533989) for the authorization request from the client (ID = 4015029369818770). [response_type=code, openid=true]",
    "action": "INTERACTION",
    ...
    "resources": [
        "https://api.example.com/app/",
        "https://contacts.example.com/"
    ],
    ...
    "ticket": "GKXW-NWoXHEH8Xh2lOVXbImFKZF3NY-yfpxMdqvwsms"
}

When token request with the authorization code is done by the AS to Authlete the response will echo the resources that are granted to the token holder.

curl --request POST 'https://api.authlete.com/api/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXXXXXXVlE=' \
--data '{
   "parameters": "grant_type=authorization_code&
                  code=R9eLSo9quzYjjO8zdlqFMAMdqkBz0vthmZWUDRpQw2s&
                  redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
                  code_verifier=_OtZIlJuuFwniie_nb6A172G2576YD_NwppB-I2ezfY",
   "clientId" : "4015029369818770"
   }'
{
    "type": "tokenResponse",
    "resultCode": "A050001",
    "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
    "accessToken": "FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A",
    "accessTokenResources": [
        "https://api.example.com/app/",
        "https://contacts.example.com/"
    ],
    ...
    "grantType": "AUTHORIZATION_CODE",
    "idToken": "eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIwNDAxOTg3NTE1NCIsIm5hbWUiOiJEci4gQmV2ZXJseSBQYWNvY2hhIiwiZW1haWwiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJzdWIiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiYXVkIjpbIjQwMTUwMjkzNjk4MTg3NzAiXSwiZXhwIjoyNTM0MDIxMjgwMDAsImlhdCI6MTYyODI3ODE2MH0.MGIEUwzFOiySL1klpCrGgcP7u70J1lGuxM154WV8l6HuGRlDbul8RsjjqkM-cZrX-wohWnXr4keqoQ8uJAEJwIuSL3Szt8KNpFD6fDwNfYC6SL_cTO1va3sgLSmDH7xnvaLfDMEedidw5-DPCYXg9r6jBAkixTkeSPbjZXgMkk44a6kq17TA0nRO2HZ_KNB-ewhjTpQhwrTxOoPtH-ThwOqUSg7DQfSndlcm-yBWE0ZveGj3m5MbwQhzqt6qokQN8bgbAsPMVDF34gM613v_kEWCiiI3iWzIDjGfJ0nzPZTjm7q8MDGRaSKIEXI98XDhyv-Ah4dQiIMYLuP48tz-xg",
    "responseContent": "{\"access_token\":\"FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A\",\"scope\":\"openid\",\"id_token\":\"eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIwNDAxOTg3NTE1NCIsIm5hbWUiOiJEci4gQmV2ZXJseSBQYWNvY2hhIiwiZW1haWwiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJzdWIiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiYXVkIjpbIjQwMTUwMjkzNjk4MTg3NzAiXSwiZXhwIjoyNTM0MDIxMjgwMDAsImlhdCI6MTYyODI3ODE2MH0.MGIEUwzFOiySL1klpCrGgcP7u70J1lGuxM154WV8l6HuGRlDbul8RsjjqkM-cZrX-wohWnXr4keqoQ8uJAEJwIuSL3Szt8KNpFD6fDwNfYC6SL_cTO1va3sgLSmDH7xnvaLfDMEedidw5-DPCYXg9r6jBAkixTkeSPbjZXgMkk44a6kq17TA0nRO2HZ_KNB-ewhjTpQhwrTxOoPtH-ThwOqUSg7DQfSndlcm-yBWE0ZveGj3m5MbwQhzqt6qokQN8bgbAsPMVDF34gM613v_kEWCiiI3iWzIDjGfJ0nzPZTjm7q8MDGRaSKIEXI98XDhyv-Ah4dQiIMYLuP48tz-xg\",\"token_type\":\"Bearer\",\"expires_in\":86400}"
}

Note that the list resource list are encoded into the access token and are not included on id token, as the intent of the id token is not to be user as Bearer token when accessing the APIs.

When the access token is presented to a resource server (RS), the RS can use the introspection endpoint to query the resources list and therefore fulfill or not the request.

curl --request POST 'https://api.authlete.com/api/auth/introspection/standard' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXXXXXXXXXXX=' \
--data '{ "parameters": "token=FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A"
}'
{
    "type": "standardIntrospectionResponse",
    "resultCode": "A145001",
    "resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
    "action": "OK",
    "responseContent":
        "{\"sub\":\"Berniece15@hotmail.com\",
          \"aud\":[\"https://api.example.com/app/\",
                   \"https://contacts.example.com/\"],
          \"scope\":\"openid\",
          \"iss\":\"https://authlete.com\",
          \"active\":true,
          \"token_type\":\"Bearer\",
          \"exp\":1628364560,
          \"client_id\":\"4015029369818770\"}"
}

Narrowing the resource during token request

The Resources Identicator specification creates is the option for the client narrowing the list of resources that it is targeting. Let’s say the client requested the authorization to resources “https://api.example.com/customers/", “https://api.example.com/orders/" and “https://api.example.com/inventory/" the while the original authorization retain all those resource, the client can request an access token restrict to “https://api.example.com/orders/" to run a specific use case and later request an access token to a different resource list.

The sequence below of requests shows an api client creating an access token for 2 resources, the second request creates an access token specific to the “https://contacts.example.com/” and later the same client changes the token to “https://api.example.com/app/"

curl --request POST 'https://api.authlete.com/api/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXX=' \
--data '{
   "parameters": "grant_type=authorization_code&
                  code=HEEutGTtpXDVD_3FcRATvshyqMgTsfhdcBFjy2TE3ro&
                  redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
                  code_verifier=XTzm4vHHPQXWUI45M1wJMiIO3oF5sZz6OhKWEvFdsG0",
   "clientId" : "4015046106493768"
}'
{
    "type": "tokenResponse",
    "resultCode": "A050001",
    "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
    "accessToken": "4Ntb16h21_nFrc637_vXF-3HfR1g8or3646o4cAVyNE",
    "accessTokenResources": [
        "https://api.example.com/app/",
        "https://contacts.example.com/"
    ],
    ...
    "subject": "Bonita89@yahoo.com"
}

Invoking the token endpoint again, the api client  can narrow

curl --location --request POST 'https://api.authlete.com/api/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXX=' \
--data-raw '{
   "parameters": "grant_type=refresh_token&
                  refresh_token=JWmEeN5BtBTL0um-pB9diYlJP5LRr7YREAJ8XrqY8CQ&
                  redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
                  resource=https%3A%2F%2Fcontacts.example.com%2F",
   "clientId" : "4015046106493768"
}'
{
    "type": "tokenResponse",
    "resultCode": "A053001",
    "resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
    "accessToken": "5XvZzUjduXQInKi5ywyj7hP0dScDmfhMmp_yl-fSKDI",

    "accessTokenResources": [
        "https://contacts.example.com/"
    ],
    "resources": [
        "https://contacts.example.com/"
    ],
    "subject": "Bonita89@yahoo.com"
}

and later

curl --location --request POST 'https://api.authlete.com/api/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXX=' \
--data-raw '{
   "parameters": "grant_type=refresh_token&
                  refresh_token=JWmEeN5BtBTL0um-pB9diYlJP5LRr7YREAJ8XrqY8CQ&
                  redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
                  resource=https%3A%2F%2Fapi.example.com%2Fapp%2F",
   "clientId" : "4015046106493768"
}'
{
    "type": "tokenResponse",
    "resultCode": "A053001",
    "resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
    "accessToken": "W_g41P7zgGMW8TtYdb32sg9pBD7IthLQKFTP7hrB7ZY",
    "accessTokenResources": [
        "https://api.example.com/app/"
    ],
    "resources": [
        "https://api.example.com/app/"
    ],
    ...
    "subject": "Bonita89@yahoo.com"
}

If you are planning to use this feature, check if the single access token per subject is enable . if that is the case, the access token will be invalidated when the refresh token is used. In your example, when the second request is done, the access token from the first became invalid. When the 3rd call is done, the access token of the 2nd call is invalidated.