API scopes

Scopes are how OAuth 2.0 and OpenID Connect express what an access token is allowed to do. Your application asks for scopes during authentication, the user (or the consent configuration) approves them, and Kenni issues tokens that carry only those scopes.

Kenni separates scopes into two categories:

  • Identity scopes — Built-in scopes that release information about the authenticated user (or, for delegated sessions, the actor). They map to claims on the ID token and the user-info endpoint. The full list lives on Standard scopes.
  • Custom API scopes — Scopes you define for your team's own APIs. They protect your backend resources, not user data. They're prefixed with your team domain (e.g. @my-app.is/orders.read) and managed from API scopes in the developer portal.

Requesting scopes

Scopes are sent as a space-separated scope parameter on the authorization request:

scope=openid profile email @my-app.is/orders.read

The token endpoint then issues an access token that carries only the scopes the client is allowed to request and the user has consented to. Anything you didn't ask for is silently dropped.

openid is mandatory for any OpenID Connect flow — without it Kenni won't issue an ID token. offline_access is required for a refresh token to be issued.

Access token format

The access token Kenni issues comes in one of two shapes, depending on whether a custom API scope was requested:

  • JWT access token — issued when the request includes at least one custom API scope. Self-contained, signed, and verifiable by your resource server using Kenni's JWKS endpoint without an extra round-trip.
  • Opaque access token — issued when no custom API scope was requested. Not a JWT — it's a reference to a session held inside Kenni. You can still call /oidc/me (user-info) and /oidc/token/introspection with it, but it carries no claims your resource server can read directly.

If your application needs to validate access tokens at the edge of your own API, request at least one custom API scope so you get a JWT. If you only need user info, an opaque token is fine.

Custom API scopes

Custom API scopes let you split your backend into independently authorizable surfaces. A typical pattern is one scope per resource and verb (orders.read, orders.write), but the granularity is yours to decide.

Create a scope from API scopes in the portal:

  • Name — alphanumerics, underscores, and dashes. Kenni prefixes it with your team domain at runtime, so orders.read becomes @my-app.is/orders.read in the request.
  • Description — internal note shown in the portal. Not visible to end users.

A new scope is inert until at least one application is authorized to request it. Open the application's API Scopes tab and toggle the scope on. From then on, that application can include the scope on its authorization or token request.

Scopes are case-sensitive. The name you create in the portal is the exact string your code must send.

Machine-to-machine clients

M2M clients use the client_credentials grant and don't have an end user to consent to anything. The same opaque-vs-JWT rule applies: authorize at least one custom API scope so the resulting access token is a JWT your services can validate locally. An M2M client with no API scopes can still call user-info and introspection, but most M2M use cases want a verifiable token.

Renaming and deleting scopes

Renaming a scope or deleting it is immediate and breaking. Every application requesting the old name will start failing on the next token request. Coordinate with downstream consumers before changing anything in production.

Next steps