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.
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 checksclient_secret— a confidential client must present its real secret; a public client must send none (or empty). Getting this wrong fails withinvalid_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_credentialsis rejected withunauthorized_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
| Flag | Affects |
|---|---|
--access-token-lifetime | Access tokens (default: 1 hour) |
--refresh-token-lifetime | Refresh tokens (default: 30 days) |
--authorization-code-lifetime | Authorization codes (default: 10 minutes) |
--session-lifetime | Sessions (default: 12 hours) |
--login-transaction-lifetime | Login transactions (default: 10 minutes) |
--consent-transaction-lifetime | Consent 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.