Skip to content
Connect2id

How to define and manage OAuth 2.0 scopes

In OAuth 2.0, the access a token has to a protected resource is represented by the concept of “scope”. This guide explains the best practises for defining and managing scopes with the Connect2id server.

1. Defining scopes

The OAuth 2.0 authorisation framework (RFC 6749) defines the scope syntax as a space-delimited list of opaque strings. Deployments are therefore free to define the semantics of scope values.

The scope values may represent:

  • Specific actions that a client is authorised perform on a protected resource (web API)
  • Roles or entitlements assigned to the client or user.

What are the best practises?

  • Choose appropriate granularity – Define scopes at a level of detail that reflects the actual permissions needed. They should be fine-grained enough to control access precisely, but not so granular that they become unmanageable.

  • Use namespaces per protected resource – Prefix or namespace scope values to avoid accidental clashes between different resources.

Use URIs or URNs to define scope values:

  • Natural namespacing – The URI domain, together with a path component, provides automatic namespacing for an organisation and its resources (web APIs).

  • Action indication – A path component can signify an action or a class of actions.

  • Optional parameters – Parameters for the authorisation, such as the amount in a financial transaction, can be included in the URI query string.

  • Standard URI handling – Standard URI tools can be used to construct and parse scope values.

Example scope values as URIs:

https://scopes.c2id.com/accounts/read
https://scopes.c2id.com/accounts/update
https://scopes.c2id.com/accounts/delete
https://scopes.c2id.com/accounts/transfer
https://scopes.c2id.com/sec-events/send
https://scopes.c2id.com/sec-events/listen

The above scope values, as URNs:

urn:c2id:scope:accounts:read
urn:c2id:scope:accounts:update
urn:c2id:scope:accounts:delete
urn:c2id:scope:accounts:transfer
urn:c2id:scope:sec-events:send
urn:c2id:scope:sec-events:listen

1.1 Scope shorthands for OpenID claims

OpenID Connect defines a set of standard scope values that map to claims (attributes) about the logged-in user. For example, the profile scope value indicates a request for the following OpenID claims: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale and updated_at.

The Connect2id server supports custom mappings from scope values to claims.

Example definition of a scope shorthand for organisation-related claims:

https://scopes.c2id.com/org_profile=roles,supervisor,employee_number

1.2 Scopes with parameters

The scopes can include optional parameters to express variables associated with an action.

For scope values that are URIs, parameters can be encoded in the query string:

https://scopes.c2id.com/accounts/transfer&dest=org4519

Note, when a scope with parameters is consented to, this is typically a one-time operation. Therefore, the consent should not be persisted (long_lived=false), i.e. treated as transient.

The content of Rich Authorisation Requests, or RAR (RFC 9396), a recent OAuth 2.0 extension, can be easily mapped to or from a URI-encoded scope.

For example the RAR parameter

{
   "type": "payment_initiation",
   "locations": [
      "https://example.com/payments"
   ],
   "instructedAmount": {
      "currency": "EUR",
      "amount": "123.50"
   },
   "creditorName": "Merchant123",
   "creditorAccount": {
      "iban": "DE02100100109307118603"
   },
   "remittanceInformationUnstructured": "Ref Number Merchant"
}

can be represented as the scope value

https://scopes.c2id.com/payments?
 type=payment_initiation
 &instructedAmount.currency=EUR
 &instructedAmount.amount=123.50
 &creditorName=Merchant123
 &creditorAccount.iban=DE02100100109307118603
 &remittanceInformationUnstructured=Ref%20Number%20Merchant

2. Scopes registry

A Connect2id server deployment can be supplied with a registry to manage and query the scope values supported by the resource servers (web APIs) in its security domain.

  • Establishes a central location to track all supported OAuth 2.0 and OpenID Connect scopes in the organisation.
  • Enables application teams to define the scopes that fall in their domain, using a namespace.
  • Can be used to set the advertised supported scopes in OAuth 2.0 authorisation server / OpenID provider metadata parameter, if such advertising is required.
  • Can be used when registering a client, to set the allowed and default scope values for the client.
  • Can be used to inform the logic in authorisation grant handlers of the Connect2id server.

2.1 Static file

A simple scopes registry can be established by means of a static JSON file in a central Git repo. Review and acceptance of new scope values can be done via pull requests.

2.2 Service

A dedicated scope registry service can be created, to enable online registration, query and management of the supported scopes.

If the scope values are URLs, the service may allow them to be queried, for example:

GET /accounts/read HTTP/1.1
Host: scopes.c2id.com
Authorization: Bearer ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6

Example scope details:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "value"       : "https://scopes.c2id.com/accounts/read",
  "resource"    : "https://accounts.c2id.com/v1/",
  "action"      : "read",
  "grants"      : [ "authorization_code", "client_credentials" ],
  "claims"      : [],
  "registered"  : 1592219549,
  "description" : "Read a user's account information"
}

A 404 status code would indicate an unsupported scope value. Caching directives can be used to indicate how often services that depend on the scopes registry need to re-fetch the scope information.

3. Advertising supported scopes

The scope values that an OAuth 2.0 authorisation server / OpenID provider supports can be advertised in the standard scopes_supported metadata parameter. The values are set in the op.authz.advertisedScopes configuration property.

Sample OpenID Provider metadata snippet with supported standard OpenID Connect scope values:

{
  "issuer"                 : "https://c2id.com",
  "jwks_uri"               : "https://c2id.com/jwks.json",
  "registration_endpoint"  : "https://c2id.com/clients",
  "authorization_endpoint" : "https://c2id.com/login",
  "token_endpoint"         : "https://c2id.com/token",
  "scopes_supported"       : [ "openid",
                               "profile",
                               "email",
                               "address",
                               "phone",
                               "offline_access" ],
  ...
}

Note that for privacy or other reasons the scopes_supported can be a subset the all supported scopes, or omitted entirely.

4. Assigning allowed and default scopes to a client in its metadata

When a client is registered with the Connect2id server, its scope metadata parameter can be set, as shown in this example:

{
  "redirect_uris" : [ "https://client.example.org/callback" ],
  "scope"         : "openid email profile"
}

4.1 Authorisation code, implicit grant

If the client is using an OAuth 2.0 authorisation code or implicit grant (flow), the scope metadata determines which scope values the client is allowed to request. Scope values that are not present in the client metadata, say offline_access given the above example, will be filtered out by the Connect2id server and will not appear in the consent prompt for the end-user.

If the scope client metadata is not set (omitted), the scope values in authorisation requests made by client will appear without any filtering in the consent prompt.

This behaviour for the code and implicit grants can be disabled by the op.authz.limitToRegisteredScope configuration property.

4.2 Other OAuth 2.0 grants

For clients using other OAuth 2.0 grants, such as the client credentials grant, the treatment of the scope metadata parameter is determined by its grant handler.

The grant handler plugins included in the Connect2id server bound the requested scope to those values registered in the client metadata. If the client omits the scope parameter in the token request, the requested scope will default to the values in the scope client metadata parameter. This is explained in greater detail the section below.

5. Scope handling in requests

The Connect2id server presents the scope requested by the client to the handler of the particular OAuth 2.0 grant type.

When consenting a requested scope, the handler, respectively the end-user, has choices along four dimensions:

  1. Allow / deny – Allow all, allow some, deny all scope values.
  2. Long-lived / transient consent – If the consent is flagged as long-lived (long_lived=true) it will be persisted, i.e. remembered for subsequent requests.
  3. Explicit / implicit consent – Explicit is the term for consent obtained from the end-user, implicit for consent determined by rule or policy.
  4. Grant non-requested scope values – The handler can allow scope values not explicitly requested by the client.

5.1 Authorisation code, implicit grants

For the browser (user-agent) based OAuth 2.0 grants, authorisation code and implicit, the authorisation session API presents the requested scope (after removing all values that the client is not allowed to request) in the consent prompt.

The requested scope values are segmented into two sets:

  • consented – Requested scope values for which previous end-user consent exists on record.

  • new – Requested scope values for which there is no previous end-user consent exists on record.

The end-user can thus be given a clear presentation of previous and newly requested consent, with the possibility to deselect some of the previously consented scope values.

If the op.authz.includeOtherConsentedScopeAndClaimsInPrompt configuration setting is enabled the handler will also be presented with all existing consented scope values which the Connect2id server has on record for the given client and end-user.

If the client registration has a scope metadata parameter, the values will appear under client.scope.

Snippet of an example consent prompt:

{
  "type"        : "consent",
  "sid"         : "g6f5K6Kf6EY11zC00errCf64yLtg9lLANAcnXQk2xUE",
  "display"     : "popup",
  "sub_session" : { ... },
  "client"      : { "client_id"        : "8cc2043",
                    "client_type"      : "confidential",
                    "application_type" : "web",
                    "scope"            : [ "https://scopes.c2id.com/accounts/read",
                                           "https://scopes.c2id.com/accounts/update",
                                           "https://scopes.c2id.com/accounts/delete",
                                           "https://scopes.c2id.com/accounts/transfer" ] },
  "scope"       : { "new"       : [ "https://scopes.c2id.com/accounts/read",
                                    "https://scopes.c2id.com/accounts/update" ],
                    "consented" : [ "openid" ] },
  "claims"      : { ... }
}

5.2 Other OAuth 2.0 grants

The Connect2id server handles other OAuth 2.0 grants, such as the client credentials grant, by invoking a dedicated handler SPI for the grant type.

Administrators and end-users can be provided with a UI to modify the scope of persisted consent and for any linked refresh tokens. This is done via the authorisation store API.

The persisted consent for a given client and end-user is obtained with an HTTP GET request to the authorisations resource.

GET /authz-store/rest/v2/authorizations?subject=alice&client_id=65564eb0058d HTTP/1.1
Host: c2id.com
Authorization: Bearer ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6

The Connect2id server then returns the authorisation record, or HTTP 404 if none is found.

Snippet of an example authorisation record, the currently active scope values are listed in the scp:

HTTP/1.1 200 OK
Content-Type: application/json

{
 "sub" : "alice",
 "cid" : "65564eb0058d",
 "scp" : [ "openid",
           "https://scopes.c2id.com/accounts/read",
           "https://scopes.c2id.com/accounts/update" ],
 ...
}

A PUT request can be used to modify the consented scope values.

Revoking the entire consent is done by POSTing a revocation.

POST /authz-store/rest/v2/revocation HTTP/1.1
Host: server.example.com
Authorization: Bearer ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6
Content-Type: application/x-www-form-urlencoded

subject=alice&client_id=1d6a3150fd3c