Skip to main content
Version: Next

Applications

An application is any piece of software you want O2ID to handle authentication for: a website, a mobile app, a single-page app, or a backend service. Registering your application tells O2ID where it's allowed to send users back to after login, and what it's allowed to do once it has a token. Under the hood, each application is an OAuth 2.0 client, identified by a clientId that OAuth requests reference.

note

Managing applications requires a role with the applications:read/ applications:create/applications:update/applications:delete scopes — see Managing Roles for how to create and assign roles.

Creating an application

o2idctl applications create --name "My App" --callback-url https://myapp.example.com/callback
  • --name: required, purely descriptive.

  • --callback-url: can be repeated to register more than one redirect URI (e.g. one for local development, one for production). Every redirect_uri an authorization request sends must match one of them exactly, byte for byte, port included.

    note

    A native or CLI app with no browser-reachable redirect at all should use the device authorization grant instead, which needs no --callback-url.

The response above includes a one-time clientSecret. See Confidential vs. public clients below for more details.

Confidential vs. public clients

o2idctl applications create --name "My App" --callback-url https://myapp.example.com/callback
o2idctl applications create --name "My SPA" --callback-url https://myapp.example.com/callback --public

Applications are confidential by default (one that can keep a secret — a server-side backend); --public creates one with no secret at all (a browser SPA or native/mobile app, which can't keep anything truly confidential).

O2ID always generates the client secret and returns it in plaintext exactly once, in the create response, since only its hash is ever stored afterward. Copy it somewhere safe immediately; if you lose it, the only recovery method is regenerating a new one, which invalidates the old one.

PKCE is required for every application on the authorization-code grant regardless of this choice — it isn't a public-client-only mechanism in O2ID. What confidential-vs-public actually gates is:

  • The token endpoint (/oauth2/token) always checks client_secret — a confidential client must present its real secret; a public client must send none (or empty). Getting this wrong fails with invalid_client.
  • The client credentials grant — an application requesting its own token with no user involved — requires a secret to authenticate itself outright (there's no browser, no PKCE, no human to substitute for one). A public application attempting client_credentials is rejected with unauthorized_client.

Trusted applications

By default, applications are third-party: on the authorization code flow the user is shown a consent screen to approve the requested scopes. Mark an application trusted (first-party) to skip that prompt entirely:

o2idctl applications create --name "My App" \
--callback-url https://myapp.example.com/callback --trusted

Use this only for applications you operate yourself — trusted means "I, the operator, vouch that this client won't misuse whatever scope it asks for," which is not something to extend to any application a user didn't build themselves. The seeded o2idctl CLI application is trusted so o2idctl login doesn't prompt for consent.

Whenever you want the application to collect consent from its end users, you can simply mark the application as untrusted.

o2idctl applications update <id> --untrusted

Allowed scopes

An application's tokens can never carry more than the RBAC scopes it's been explicitly granted, regardless of what its logged-in user's own role permits — the effective scope of any issued token is the intersection of the application's allowed scopes and the user's role scopes. New applications are created with no allowed scopes (deny-by-default): a user logging in through a freshly created application can't exercise any management-API permission through it until an admin grants some.

o2idctl applications create --name "My App" \
--callback-url https://myapp.example.com/callback \
--scope users:read --scope applications:read

Repeat --scope to grant more than one. Passing --scope to applications update replaces the full list — it isn't merged with the existing one, the same replace-not-merge semantics as --callback-url. Grant --scope "*" for every scope, present and future (this is what the seeded o2idctl CLI application itself is granted, so command-line management keeps working for any user regardless of which scopes get added later). See the API Reference for the scope each management endpoint requires, and Managing Roles for the role/scope model this intersects with.

For example, a role granting roles:delete is useless through an application whose allowedScopes don't include it — the token that application issues for that user will never carry roles:delete, even though the user's role does grant it. Conversely, an application allowed "*" doesn't widen anyone's access beyond their own role — it simply stops narrowing it.

These --scope flags are the same mechanism used for any other API: O2ID's own management API is itself a built-in resource server, identifier o2id-management-api, and a bare --scope (one that appears before any --api) authorizes the application against it. To let an application call one of your own APIs instead, authorize it against that resource server and its scopes — --api <resource-server-identifier> at create time, or applications authorize-resource-server afterwards:

o2idctl applications create --name "My App" \
--callback-url https://myapp.example.com/callback \
--api <resource-server-identifier> --scope payments:read --scope payments:write

--api takes the resource server's identifier (the same value used as resource=/audience=), not its internal ID. Repeat --api to authorize more than one resource server; every --scope after an --api grants that scope on it, until the next --api. A --scope before any --api is shorthand for --api o2id-management-api --scope ... — both forms produce the identical authorization.

Application settings

Beyond scopes and callback URLs, an application carries a small set of its own runtime settings. Today that's just token lifetimes, but this is where any future per-application setting (rate limits, branding, and the like) would live too.

Token lifetimes

An application can override how long its own access tokens, refresh tokens, authorization codes, sessions, and login/consent transactions last:

o2idctl applications create --name "My App" \
--callback-url https://myapp.example.com/callback \
--access-token-lifetime 5m --refresh-token-lifetime 720h

o2idctl applications update <id> --session-lifetime inherit
FlagAffects
--access-token-lifetimeAccess tokens (default: 1 hour)
--refresh-token-lifetimeRefresh tokens (default: 30 days)
--authorization-code-lifetimeAuthorization codes (default: 10 minutes)
--session-lifetimeSessions (default: 12 hours)
--login-transaction-lifetimeLogin transactions (default: 10 minutes)
--consent-transaction-lifetimeConsent transactions (default: 10 minutes)

Each accepts a Go-style duration string (30m, 2h, 720h). Pass 0 or inherit to clear a previously-set override and fall back to the built-in default above — a real 0-second lifetime is meaningless, so it's safe to reuse as the "clear this" signal.

Access tokens, refresh tokens, authorization codes, and consent transactions are always issued for a specific, already-authenticated application, so an override applies unambiguously. Sessions and login transactions are a little different: a login transaction is created by /oauth2/authorize as soon as client_id is seen, before the user has even authenticated, and a session created at login time inherits whichever login transaction (if any) the user arrived through — so if a client_id was present anywhere in that chain, its application's override applies; otherwise (a user landing directly on /login with no in-flight authorization request) only the built-in default applies.

Listing applications

o2idctl applications list

Inspecting an application

<id> here is the application's internal id — distinct from its clientId, which is what OAuth requests actually reference (client_id=...). The response omits the client secret hash; there is no way to retrieve a previously-set secret, only to rotate it (see below).

o2idctl applications get <id>

Updating an application

applications update accepts most of the same flags as create (plus --regenerate-secret, which create has no equivalent of), and follows the same optional-field, replace-not-merge convention as the rest of the API: omit a flag to leave that field unchanged, but supplying --callback-url or --scope at all replaces the entire list rather than appending to it.

o2idctl applications update <id> --name "My App (renamed)"
o2idctl applications update <id> --callback-url https://myapp.example.com/callback --callback-url https://staging.myapp.example.com/callback

Rotating a client secret

o2idctl applications update <id> --regenerate-secret

O2ID generates a new secret and returns its plaintext once, in the update response — there's no flag to set your own here either. The new secret takes effect immediately and replaces the old one outright — there's no overlap window where both work, so coordinate the rotation with whatever deploys the application's own configuration to avoid an outage. --regenerate-secret also switches a public application to confidential, since a confidential application always needs a secret. Switch the other way, from confidential to public, with --public, which clears the secret entirely. --regenerate-secret and --public are mutually exclusive.

Deleting an application

o2idctl applications delete <id>

Deletion is immediate and has no undo. It stops the application from authenticating or being issued new tokens (invalid_client on the next /oauth2/authorize or /oauth2/token call), but does not retroactively revoke access or refresh tokens already issued through it — those remain valid until they expire naturally, since token validation checks the token store, not that the originating application still exists.

See the CLI Reference for the full applications command list, and the API Reference for the underlying /applications API.