Skip to main content
Version: Latest (v0.0.1)

Applications

An application is an OAuth 2.0 client — a website, native app, SPA, or backend service that redirects users through O2ID to log in, or requests its own tokens directly. Registering an application turns a piece of software into something O2ID actually recognizes: a clientId OAuth requests reference, with its own callback URLs, configurations, and the specific scopes it's allowed to issue to be included in tokens.

note

Managing applications requires a role with the applications:read/ applications:create/applications:update/applications:delete scopes — see Using o2idctl for how to log in, and 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 is required and 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. The one exception is native apps using a loopback redirect (http://127.0.0.1:<port>/... or http://[::1]:<port>/...): since the OS assigns that port at random, O2ID matches scheme, host, and path while ignoring the port, per RFC 8252 §7.3. Every other application is matched exactly, port included.

Confidential vs. public clients

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

Passing --client-secret creates a confidential client (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). --client-secret and --public are mutually exclusive.

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.

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 the same flags as create, 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> --client-secret a-new-secret

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. Switch a confidential client to public (or back) the same way, with --public or --client-secret.

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.