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.
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). Everyredirect_urian authorization request sends must match one of them exactly, byte for byte, port included.noteA 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 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.
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
| 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 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.