Rich Authorization Requests (RAR)

Rich Authorization Requests (RAR)

Preface

RFC 9396: OAuth 2.0 Rich Authorization Requests (RAR for short) is a proposed standard extension of OAuth 2.0. It allows clients to use JSON data structures to specify more fine-grained authorization requirements than an existing capability of “scope” parameter.

This article describes the support for RAR on Authlete and how you can use it to represent fine-grained permissions.

RAR overview

“scope” is a mechanism introduced by OAuth 2 Authorization Framework specification for representing the permissions granted to a third party by a resource owner. You can assign a specific semantic to a scope to represent a permission or an option.

For instance: OpenID Connect specification defines the “openid” scope as well as “profile” among others. While “profile” grants to 3rd party access to specific attributes of the user profile, the “openid” scope has a different semantic: it implies an option to generate an ID token and an access permission to UserInfo endpoint.

With the broad usage of OAuth 2, new use cases rise and as such new technical solutions are required. The RAR specification addresses scenarios where thrid parties are required to express an intent with more context for user approval, e.g., online payments, file sharing, health exams etc.

RAR structure

RAR defines a common JSON structure for the finer grained permissions, where each permission has a “type” attribute, and optionally “locations”, “actions”, “datatypes”, “identifier”,  “privileges”, and others defined by the parties. You can check the structure description under Request parameter “authorization_details” section.

The idea is that an authorization server (AS), or an ecosystem that the AS is in, will define different “types” identifiers and their semantics: what do they represent, their structure, their meaning to end users, and association to the other attributes. It will precisely define the permission to be requested to end user.

The “locations” attribute has the same concept of the Resource Indicators mechanism , where the location specifies a resource server URI that the permission is about. With the attribute “identifier”, you can point to the specific resource on requesting permissions. While the “locations” indicate a branch of resources, the “identifier” points to a single resource, for short.

The “actions” is a big step forward as it can represent for instance: the action on a filesystem, the action on the bank account, an action on medical device, an act on an appliance, a permission on a database, etc.

The “datatypes” and “privileges” can be used to represent specifics of the entitlements been requested.

Configuration

Authlete’s support for the RAR specification is enabled by default. But no authorization types are granted by default. They must be configured on service level and granted to individual clients.

Service level

You have to configure a list of supported authorization “types” using Service Owner Console under Authorization tab. Clients may be able to request some of types in the list, but none are granted to any client by default.

Screen_Shot_2021-08-09_at_14
List of supported authorization types by a service

Client level

On Developer Console, you can configure individual clients the set of requestable authorization types under Authorization tab.

Screen_Shot_2021-08-09_at_14
List of authorization types that a client can request

Examples

Authorization request

You can use RAR regardless of whether or not the request is pushed, or a request object mechanism is used. There are constraints in place for those mechanisms: if the RAR request is very large, it will require clients to use PAR, if you need to make tamper evident, the request object (or JAR) should be used.

For instance, if an RAR is URL-encoded and sent in an authorization request by a client, a request from an AS (that receives the authorization request) to Authlete’s /auth/authorization API would be like this:

  • Request (response omitted)
curl --request POST 'https://api.authlete.com/api/auth/authorization' \
     --header 'Content-Type: application/json' \
     --header 'Authorization: Basic OXXXXXXXXXX=' \
     --data '{
         "parameters": "client_id=4025660683512920&
            scope=openid&
            response_type=code&
            redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
            code_challenge=NcCW6zMwKWy5Mya8jopzE1SVeTBJBAHH1jU7TPpYK9A&
            code_challenge_method=S256&
            authorization\_details=%5B%7B%22type%22%3A%20%22customer\_information%22%2C%22locations%22%3A%20%5B%22https%3A%2F%2Fexample.com%2Fcustomers%22%2C%5D%2C%22actions%22%3A%5B%22read%22%2C%22write%22%5D%2C%22datatypes%22%3A%5B%22contacts%22%2C%22photos%22%5D%7D%5D
"
     }'

The same RAR content can be wrapped on a JAR as the sample below. Authlete extracts and echoes back the authorization details to the AS so they can be used for collecting end user approval.

  • Request
curl --request POST 'https://api.authlete.com/api/auth/authorization' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXX8=' \
--data-raw '{ "parameters": "client_id=4025658857453025&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.eyJpc3MiOiI0MDI1NjU4ODU3NDUzMDI1IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjQwMjU2NTg4NTc0NTMwMjUiLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiIsInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxlbmdlIjoiQW9OM29NcVFvUVF1RGhoQmc2VTF2Y0ljbXRtQ19QWm9UMGNoVEVSVktFZyIsImNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJhdXRob3JpemF0aW9uX2RldGFpbHMiOlt7InR5cGUiOiJjdXN0b21lcl9pbmZvcm1hdGlvbiIsImxvY2F0aW9ucyI6WyJodHRwczovL2V4YW1wbGUuY29tL2N1c3RvbWVycyJdLCJhY3Rpb25zIjpbInJlYWQiLCJ3cml0ZSJdLCJkYXRhdHlwZXMiOlsiY29udGFjdHMiLCJwaG90b3MiXX1dLCJpYXQiOjE2Mjg1MzE0NDQsIm5iZiI6MTYyODUzMTQ0NCwiZXhwIjoxNjI4NTMyMDQ5LCJqdGkiOiI4WjJEbGpCaUZVckpnTUt3bThiQ3EifQ.PIxVI2GFWi7B_frRfLg9r8AWEz7HGeopMeQo7MLYVEMGpOdoPkt5piBrLXI7PPI7ohrUmhxd-B4kZfm4WfkKH5qSub4A_mdd6pBpTWacBgfVQDIOvzE1yPrawCDEWQn2xgdYd1G-KM6pk8rDngOMEfaBbnoJ5C9krQtgYMHGbDIScgm8Y5AHf5aEF41FboZI67BlvbzXdxcJEPvB2zLGwV9twMrJ07OeRX0NVpIamhhEgfMQ87FyOsPVx9bqYUPN_VjwgB8lkKgrCdIkc9jPs2mQkpUbx0AIg8Pfmwyw0F5Vih7tgBbpb1LlwNgW36La3DPtTY9xSZ7SQGcyGxteIA
" }'
  • Response
{
    "type": "authorizationResponse",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 979748525706) for the authorization request from the client (ID = 4025658857453025). [response_type=code, openid=true]",
    "action": "INTERACTION",
    "authorizationDetails": {
        "elements": [
            {
                "actions": ["read", "write"],
                "dataTypes": ["contacts", "photos"],
                "locations": ["https://example.com/customers"],
                "type": "customer_information"
            }
        ]
    },
    "requestObjectPayload": "{\"iss\":\"4025658857453025\",\"aud\":\"https://authlete.com\",\"response_type\":\"code\",\"client_id\":\"4025658857453025\",\"redirect_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code_challenge\":\"AoN3oMqQoQQuDhhBg6U1vcIcmtmC_PZoT0chTERVKEg\",\"code_challenge_method\":\"S256\",\"authorization_details\":[{\"type\":\"customer_information\",\"locations\":[\"https://example.com/customers\"],\"actions\":[\"read\",\"write\"],\"datatypes\":[\"contacts\",\"photos\"]}],\"iat\":1628531444,\"nbf\":1628531444,\"exp\":1628532049,\"jti\":\"8Z2DljBiFUrJgMKwm8bCq\"}",
    "ticket": "xTZCagNjVJUltVS-WD7CKZr1fp0zeFTAAva86Rmzuow"
}

Token request

After the user has approved, an authorization code is generated and sent to the original client. The client will invoke the token endpoint authenticating itself (if required) to the AS, and Authlete’s /auth/token API behind the AS will generate an access token and optionally an ID token (if using openid).

Note that the authorization details are not encoded into the ID token.

  • Request
curl --request POST 'https://api.authlete.com/api/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXXXXXXXXXXXX8=' \
--data '{ "parameters": "grant_type=authorization_code&code=wkPCpsu-HsMDeaNFBI78LydnmW7IyadBLloa3Mn7ZzM&redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&code_verifier=ZhoMDipQfa7iMabyG-wSQ83ATy1GCVvE8Lh3SlDZdNo", "clientId" : "4025658857453025" }'
  • Response
{
    "type": "tokenResponse",
    "resultCode": "A050001",
    "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
    "accessToken": "Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc",
    "action": "OK
",
    "authorizationDetails": {
        "elements": [
            {
                "actions": [
                    "read",
                    "write"
                ],
                "dataTypes": [
                    "contacts",
                    "photos"
                ],
                "locations": [
                    "https://example.com/customers"
                ],
                "type": "customer_information"
            }
        ]
    },
    "idToken": "eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIzOTY3ODczNjY4MyIsIm5hbWUiOiJDYXNleSBQdXJkeSIsImVtYWlsIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwic3ViIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImF1ZCI6WyI0MDI1NjU4ODU3NDUzMDI1Il0sImV4cCI6MjUzNDAyMTI4MDAwLCJpYXQiOjE2Mjg1MzIzMjAsInNfaGFzaCI6ImJPaHRYOEY3M0lNalNQZVZBcXh5VFEifQ.Vrc3BcXtnAUtrnfSOPWagTWnV_SB0DL5cEp535pt33n8S4op94GSM51waTS6OcoZ-R7YKQ0l7FrMGxFl6MGFd6Wn_FjHNyu7J2TmEH9sARZTW7ZmWQo5euWkLx6NjgMAp_9LsjDXwB8Cjr3ujkVt3DdKRg6fkETwgGYYkEIrfdtRO_yJLANunHG-wm92TzOd44xXaTBF4bBinBcZFUrpr2nCPRM0rrUmBLw...
    "responseContent": "{\"access_token\":\"Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc\",\"authorization_details\":[{\"type\":\"customer_information\",\"locations\":[\"https://example.com/customers\"],\"actions\":[\"read\",\"write\"],\"datatypes\":[\"contacts\",\"photos\"]}],\"scope\":\"openid\",\"id_token\":\"eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIzOTY3ODczNjY4MyIsIm5hbWUiOiJDYXNleSBQdXJkeSIsImVtYWlsIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwic3ViIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImF1ZCI6WyI0MDI1NjU4ODU3NDUzMDI1Il0sImV4cCI6MjUzNDAyMTI4MDAwLCJpYXQiOjE2Mjg1MzIzMjAsInNfaGFzaCI6ImJPaHRYOEY3M0lNalNQZVZBcXh5VFEifQ.Vrc3BcXtnAUtrnfSOPWagTWnV_SB0DL5cEp535pt33n8S4op94GSM51waTS6OcoZ-R7YKQ0l7FrMGxFl6MGFd6Wn_FjHNyu7J2TmEH...
    ...
}

Token introspection

When a resource server receives an API request from a client with an access token, it would make a token introspection request to discover the grants behind the access token, and fulfill or vet the API request of the client.

  • Request
curl --request POST 'https://api.authlete.com/api/auth/introspection/standard' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic OXXXXXXXXXXXXXXXXXX8=' \
--data '{ "parameters": "token=Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc"}'
  • Response
{
    "type": "standardIntrospectionResponse",
    "resultCode": "A145001",
    "resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
    "action": "OK",
    "responseContent": "{
        \"sub\":\"Ciara_Sporer@gmail.com\",
        \"authorization_details\":[ {
            \"type\":\"customer_information\",
            \"locations\":[\"https://example.com/customers\"],
            \"actions\":[\"read\",\"write\"],
            \"datatypes\":[\"contacts\",\"photos\"]
        }],
        \"scope\":\"openid\",
        \"iss\":\"https://authlete.com\",
        \"active\":true,
        \"token_type\":\"Bearer\",
        \"exp\":1628618721,
        \"client_id\":\"4025658857453025\"
    }"
}