Skip to main content

priompt-auth CLI

priompt-auth is the token issuer. It exchanges a service-account secret or an OIDC ID token from your identity provider for a short-lived JWT, which the Priompt server verifies offline against the issuer's /jwks endpoint.

priompt-auth <init|gen-secret|serve> [flags]

For the setup walkthrough, see Set up SSO and service accounts.

Commands​

CommandDescription
initGenerate the Ed25519 signing key.
gen-secretMint a service-account secret and its clients.txt line.
serveRun the HTTP token issuer.

Exit codes​

  • 0: Success.
  • 1: The command failed or is unknown. The reason is on stderr, for example unknown command "bogus" (want init, gen-secret, or serve).
  • 2: A flag that isn't defined.

init​

Generates an Ed25519 key pair and writes the private key as PEM. It refuses to overwrite an existing key, because every token signed with the old key stops verifying.

priompt-auth init [-key-file auth.key] [-force]
FlagEnv varDefaultDescription
-key-fileauth.keyWhere to write the private key.
-forcefalseOverwrite an existing key file.

Example

priompt-auth init
Output
wrote auth.key (kid 05ccf04b)
next: priompt-auth gen-secret <client-id> [org] [rw], then priompt-auth serve
point the server at it: priompt serve -auth-jwks-url http://<this-host>:8444/jwks

The kid is derived from the public key and identifies it in the JWKS. A second run prints auth.key exists; pass -force to overwrite (old tokens will stop verifying) and exits 1.

To rotate the key, run init -force and restart serve. Tokens signed with the old key stop working, so the longest outage for a client is one token lifetime.

gen-secret​

Prints a new client secret and the line to add to the clients file. The secret is shown once and never stored; the clients file holds only its SHA-256 hash.

priompt-auth gen-secret <client-id> [org] [rw]

[org] scopes the issued tokens to one org (none means admin), and rw grants write access.

Example

priompt-auth gen-secret ci-bot acme rw
Output
client_secret (give to the client, shown once):
<secret>
clients.txt line (safe to commit — it is a hash):
ci-bot sha256:65b2e1519e6121fd95ff92690ab4fedf53e3536d75ff9a64b8727efea067b99f acme rw

<secret> is 43 URL-safe base64 characters.

serve​

Runs the issuer over plain HTTP. Put it behind a TLS-terminating proxy if clients reach it over a network.

priompt-auth serve [-clients-file <file>] [-oidc-issuer <url> -groups-file <file>] [flags]
FlagEnv varDefaultDescription
-addr:8444Listen address.
-key-fileauth.keyPrivate key from init.
-clients-fileService accounts, one client_id sha256:<hex> [org] [rw] line each.
-oidc-issuerIssuer URL of your identity provider. Turns on grant_type=oidc. Requires -groups-file.
-oidc-audienceRequired aud claim on ID tokens. Empty skips the check.
-groups-fileIdentity-provider groups mapped to grants, one group [org] [rw] line each. The first matching line wins; no org means admin.
-ttl15m0sLifetime of issued tokens.

priompt-auth reads no environment variables.

At startup, serve fetches <issuer>/.well-known/openid-configuration to find the identity provider's keys, and exits if it can't. It also exits with nothing to issue: pass -clients-file and/or -oidc-issuer when neither source is set, and with -oidc-issuer requires -groups-file (or every login would be denied).

Output
priompt-auth on :8444 (kid 05ccf04b, ttl 15m0s, clients 2, oidc false)

Clients and groups files​

clients.txt
ci-bot sha256:<hex> acme rw
dashboard sha256:<hex> acme
groups.txt
prompt-admins rw
acme-authors acme rw
acme-readers acme

Blank lines and lines that start with # are ignored. From priompt-auth v0.7.1, a # after the first field also starts a comment, and a line with two org words is rejected at startup. In v0.7.0, a later # isn't a comment, so keep comments on their own lines. The files are read at startup; to revoke a client, delete its line and restart.

Endpoints​

EndpointDescription
POST /tokenExchange a credential for a JWT.
GET /jwksPublic signing key, for priompt serve -auth-jwks-url.
GET /healthzLiveness check. Returns ok.

POST /token​

Takes a form-encoded body with one of two grants:

  • grant_type=client_credentials with client_id and client_secret: a service account from the clients file.
  • grant_type=oidc with id_token: an RS256 ID token from the identity provider. priompt-auth checks its signature against the provider's keys, its iss, its exp, and its aud if -oidc-audience is set. Then it maps the token's groups claim through the groups file.
curl -s -X POST http://localhost:8444/token \
-d grant_type=client_credentials -d client_id=ci-bot -d client_secret=<secret>
Output
{"access_token":"<jwt>","expires_in":900,"token_type":"Bearer"}

Send the access_token as authorization: Bearer <jwt>, or set it as PRIOMPT_TOKEN for the CLIs.

Errors use OAuth 2.0 error bodies:

StatusBodyCause
400{"error":"invalid_request"}The body couldn't be parsed as a form.
400{"error":"unsupported_grant_type"}Unknown grant_type, or oidc when -oidc-issuer isn't set.
401{"error":"invalid_client"}Unknown client_id, or the secret doesn't match.
401{"error":"invalid_grant"}The ID token failed a check: signature, issuer, expiry, or audience.
403{"error":"access_denied"}The ID token is valid, but none of its groups is in the groups file.
405Method Not AllowedA method other than POST.

GET /jwks​

curl -s http://localhost:8444/jwks
Output
{"keys":[{"kty":"OKP","crv":"Ed25519","kid":"05ccf04b","x":"Gz_-6F9yFBIV1J9ekPkQptAxnIORb63uozI9kEnOVQc"}]}

The server fetches this document on the first JWT it sees, and again (at most every 30 seconds) when a token names a kid it doesn't know. A key rotation needs no server restart.

GET /healthz​

curl -s http://localhost:8444/healthz
# => ok

Token claims​

An issued token is a compact JWT signed with EdDSA (Ed25519). The header is {"alg":"EdDSA","kid":"<kid>","typ":"JWT"}. The claims are defined once, in github.com/priompt/proto/claims, which both priompt-auth and the server import:

ClaimExampleDescription
subci-bot, [email protected]The client ID, or the ID token's sub. Recorded as the commit author and in the audit log.
orgacmeOrg scope. Empty means admin, all orgs.
rwtrueWhether the token may publish, branch, merge, and roll back.
iat1792283867Issue time, in Unix seconds.
exp1792284767Expiry, in Unix seconds: iat plus -ttl. Required.
isspriompt-authIssuer. Always priompt-auth.

Decoded claims of the token above:

{"sub":"ci-bot","org":"acme","rw":true,"exp":1792284767,"iat":1792283867,"iss":"priompt-auth"}

The server accepts a token when its signature verifies against a key from /jwks, its alg is EdDSA, and exp is in the future. Any failure is Unauthenticated with invalid or missing token. See Errors.

Audit log​

serve writes one JSON line to stderr for each token it issues and each request it denies. Each line also has a time field.

{"level":"INFO","msg":"issued","sub":"ci-bot","org":"acme","rw":true,"ttl":"15m0s"}
{"level":"WARN","msg":"denied","grant":"client_credentials","client_id":"ci-bot"}

Denied OIDC requests carry "grant":"oidc" and a reason, such as no group mapping.