Skip to content

Latest commit

 

History

History
201 lines (158 loc) · 12.9 KB

commands.md

File metadata and controls

201 lines (158 loc) · 12.9 KB

Commands

The plugin registers six commands, all run with bin/cake (inside DDEV: ddev exec bin/cake ...). The commands use the same configuration and storage as the web endpoints.

Command Purpose
oauth client add Register a client by hand
oauth client list List the clients
oauth client revoke Revoke a client and its tokens
oauth token revoke Revoke the tokens of a person or a client
oauth prune Delete expired data
oauth check Check the setup

oauth client add

bin/cake oauth client add NAME [--redirect-uri URI]... [--confidential] [--grant GRANT]...
    [--scope SCOPE]... [--introspection] [--native]

Registers a client (type Registered). NAME is the display name: up to 255 characters, with at least one letter, number, punctuation or symbol, and without control characters, bidirectional controls or other invisible characters (the core's name rules; joiners inside Persian or Indic words and emoji sequences are fine). A name that breaks them prints the core's error and exits with a non-zero code. Private-use redirect URIs (com.example.app:/oauth/callback) are accepted here whatever registration.allowPrivateSchemes says.

Option Meaning
--redirect-uri A redirect URI. Repeat for more
--confidential Give the client a secret (HTTP Basic authentication at the token endpoint)
--grant authorization_code, refresh_token or client_credentials. Repeat for more. Default: the first two
--scope A scope the client may ever get. Repeat for more. Default: any scope the resources declare
--introspection Allow the client to call /introspect. Confidential clients only; a trusted resource server privilege
--native The client is a native application (loopback or private-scheme redirect URIs)

Examples:

# A web application with a secret
bin/cake oauth client add "Reporting dashboard" --confidential \
    --redirect-uri https://dashboard.example.com/oauth/callback

# A service acting for itself (also needs userProvider.forClient, see users.md)
bin/cake oauth client add "Nightly import" --confidential --grant client_credentials --scope content:read

Output:

client_id: 0b6f0a3c...
client_secret: (the secret, shown once)
Store the secret now; it is not shown again.

The last line is a warning and goes to standard error, so bin/cake oauth client add ... > client.txt keeps only the id and the secret in the file.

The secret appears only for confidential clients, once. A refused input (for example a public client with client credentials) prints the error and exits with a non-zero code. The service identity for the client credentials grant is described in users.

oauth client list

bin/cake oauth client list

Prints a table of every client: client id, name, hosts, type, authentication method, created, last used and revoked (- when empty). The name is what the client declared (a self-registered client chooses it, so the column is headed "Name (declared)"); the hosts are the web hosts of its redirect URIs, plus "An application on this computer" when one of them is a private-use scheme. Clients created from metadata documents (MetadataDocument) and by dynamic registration (Dynamic) are listed next to the ones you added.

Client id   Name (declared)       Hosts                Type         Authentication       Created           Last used         Revoked
0b6f0a3c... Reporting dashboard   reports.example.com  registered   client_secret_basic  2026-10-16 12:00  2026-10-16 12:30  -

(The real output is drawn as a table with borders.)

oauth client revoke

bin/cake oauth client revoke CLIENT_ID

Marks the client revoked, forever, and revokes every token it holds:

Revoked the client and 3 token(s).

An unknown id prints No client with that id. and exits with a non-zero code.

oauth token revoke

bin/cake oauth token revoke --subject ID [--consents]
bin/cake oauth token revoke --client CLIENT_ID
Option Meaning
--subject The person's id (the primary key of the users table). Revokes their access, refresh and personal tokens, and deletes the authorization codes they have not exchanged yet
--consents With --subject: also delete the person's remembered consents, so no client they approved before can skip the consent page
--client The client id. Revokes every token of that client and deletes its codes not exchanged yet (the client itself stays valid; use oauth client revoke to end it)

Pending codes are deleted before the tokens are revoked, so a code issued a moment before cannot open a new connection afterwards; an exchange already in flight either fails or has its tokens revoked. Exchanged codes are kept, so a replayed code still revokes what it issued.

Revoked 4 token(s).
Deleted 2 consent(s).

The Deleted ... consent(s) line appears with --consents. Pass either --subject (optionally with --consents) or --client; any other combination prints Pass either --subject (optionally with --consents) or --client. and fails.

To cut a person off completely, use --subject ID --consents.

oauth prune

bin/cake oauth prune

Deletes what is no longer needed: expired authorization codes, expired and revoked tokens (kept for retentionDays first) and clients nobody needs any more. Revoked clients stay as records, because revocation is permanent. It prints:

Deleted 12 codes, 340 tokens and 1 clients.

Run it daily:

# crontab -e (as the user that runs the application)
17 3 * * * cd /var/www/app && bin/cake oauth prune >> logs/oauth_prune.log 2>&1

Pruning does not weaken the family lifetime cap: it is stored with every token (family_started_at). Pruning is safe while the application serves requests: each unused client is claimed with one conditional UPDATE inside its own transaction (after locking its row on MySQL, MariaDB and PostgreSQL) and deleted only when the claim succeeds, so a client that a request used, or gave a new token, after oauth prune picked it is kept with its token. More in the core's operations.

oauth check

bin/cake oauth check

Runs the checks below and prints one line each, [PASS], [WARN] or [FAIL]. The exit code is non-zero when any check fails, so it is suitable for a deployment pipeline.

[WARN] Secret: derived from Security.salt; set OAuthServer.secret to rotate it without changing the salt
[PASS] Configuration: 1 resource(s), endpoints under https://example.com/oauth
[PASS] Issuer: https://example.com
[PASS] Tables: all four tables exist
[PASS] Users: Users with finder "active"
[PASS] Users integration: cakedc/users is not loaded
[PASS] Login URL: /users/login
[PASS] Routes: /oauth/token reaches the plugin
[PASS] Middleware: RoutingMiddleware runs before the CSRF and authentication middleware
[PASS] CSRF: the token, register, revoke and introspect endpoints are skipped
[PASS] Authenticators: Token comes before Session, Form
[PASS] RBAC: cakedc/users is not loaded
[PASS] Rate limiting: cache "default"
[PASS] Client metadata documents: enabled
[PASS] Logging: the "oauth" log engine is configured
Check Fails or warns when
Secret OAuthServer.secret is shorter than 32 characters (fail), or is not set and Security.salt is missing or short (fail); a derived secret only warns
Configuration Builds the server the way the endpoints do (no request, no I/O) and fails when that is refused: an unknown key or a bad type in any section (the plugin's users, consent, tokenPaths and so on included, and a setting 1.0.0 removed, such as services or users.paths, with its replacement), an undeclared resource, a tokenPrefix that is not 1 to 16 lowercase letters or digits, a missing rate limit cache. Whatever fails here makes the endpoints answer 503
Issuer The issuer is invalid (fail). Warns for plain http, for an issuer with a path (the RFC 8414 document must be served at the host root), and when the issuer path differs from a configured App.base. When App.base is false (Cake detects the directory per request) the CLI cannot compare them and says so instead of guessing
Tables Inspects the connection of the plugin's tables (not necessarily default; the message names it). Fails when a table is missing, a column of the current migrations is missing (such as oauth_tokens.rotation_seal, added by 20261107120000_AddOAuthTokensRotationSeal.php) or a unique key is missing (client_id_hash, code_hash, token_hash, and subject, client and resource of a consent): migrate, see installation. On MySQL and MariaDB it also fails when a column is not utf8mb4_bin. Warns when the index on oauth_authorization_codes (subject) is missing (run bin/cake migrations migrate -p CakeDC/OAuthServer, or copy 20261017120000_AddOAuthCodesSubjectIndex.php among your application's migrations): revoking a person or a connection then scans every code. Each message names both ways to migrate
Users The users table has no configured finder. A custom provider is reported as custom user provider
Users integration Only with CakeDC/Users: OAuthServer.users is invalid, or the integration's resource is at the host root without paths in OAuthServer.tokenPaths (fail: the authenticator refuses every token); otherwise prints what the integration's Token authenticator validates and where. Warns when the one-time login authenticator (Auth.Authenticators.OneTimeToken) is not the plugin's guarded one, so it would read the token sent to the revoke and introspect endpoints (cakedc-users)
Login URL Prints the resolved login URL; fails when it cannot be built. Warns when a local string URL matches no route or controller (another site's URL is not checked)
Routes <prefix>/token does not reach the plugin's controller: no route, or a route of your application connected before the plugin's takes it (fail)
Middleware Your middleware queue, plus what your plugins add, has CsrfProtectionMiddleware (or the session one) or AuthenticationMiddleware before RoutingMiddleware: there is no matched route yet, so no request on a resource's routes is skipped by csrfSkip() and the Token authenticator refuses every token. Fails when a Token authenticator of this plugin is loaded (its tokens cannot work, or every POST with one gets 403), warns otherwise. Also warns when there is no RoutingMiddleware
CSRF The CsrfProtectionMiddleware or SessionCsrfProtectionMiddleware of your application does not skip the token endpoint (fail), or also skips a session route: it probes <prefix>/authorize, <prefix>/connections, / and your login (fail; a callback like fn () => true). Warns when there is no CSRF middleware in the queue (the consent and connections pages rely on it) or when it cannot inspect the middleware
Authenticators Builds your authentication service with probe requests at your bearer paths. Fails when a session, cookie (remember-me) or form authenticator comes before the Token authenticator: OAuthServer::csrfSkip() skips CSRF for a request with a bearer token of this server without looking it up, and only the Token authenticator first keeps a session cookie sent with a made up token from being used. Also fails when the service cannot be built, or when a Token authenticator would refuse every token: an undeclared resource, a resource at the host root without token paths, an invalid OAuthServer.tokenPaths, or the paths or header option that 1.0.0 removed. Warns when there is no Token authenticator (ignore that when your resources live in another application). With cakedc/users it reads Auth.Authenticators
RBAC Only with CakeDC/Users: RBAC denies the OAuth endpoints (fail). See cakedc-users
Rate limiting rateLimit.cache is null (warn: the token endpoints are not throttled), the named cache configuration does not exist (fail), or it uses the Array or Null engine, which keeps nothing from one request to the next (warn: nothing is throttled)
Client metadata documents Metadata documents are enabled but ext-curl is missing (fail), or rateLimit.cache is null or an Array or Null engine, so there is no fetch budget and no negative cache (warn)
Logging No log engine receives the oauth scope (warn)

No configuration breaks bin/cake: with an invalid one the routes are still connected (see configuration), commands and migrations run, and oauth check reports the problem on its Configuration line (or the line of the check that hit it). The web behaves the same way: the application keeps working and only the OAuth endpoints answer 503. So a test of your application does not fail on a bad OAuthServer setting unless it calls an OAuth endpoint; run oauth check in your pipeline.