Skip to main content

SSO and service accounts (priompt-auth)

A static tokens file works until an enterprise asks how do people log in with SSO, what happens when an employee leaves, and how do we rotate secrets? priompt-auth answers all three. It exchanges a credential your company already trusts for a short-lived signed JWT that the Priompt server verifies offline.

  • Tokens last minutes, not months. A leaked credential expires on its own.
  • Offboarding happens in the IdP. Remove someone from the Okta group and their next token request fails.
  • No availability coupling. If priompt-auth is down, already-issued tokens keep verifying until they expire.
  • Stateless. No sessions and no database. It holds one signing key and the hashes of service-account secrets.

Set it up

go build -o priompt-auth . # in the auth repo; Go 1.25+

./priompt-auth init # one-time: Ed25519 signing key -> auth.key

./priompt-auth gen-secret ci-bot acme rw
# client_secret (give to the client, shown once): <secret>
# clients.txt line (safe to commit, it's a hash): ci-bot sha256:… acme rw

./priompt-auth serve -clients-file clients.txt # :8444
priompt serve -auth-jwks-url http://localhost:8444/jwks # server now trusts it

Service accounts (agents, CI)

These are OAuth2 client credentials:

curl -s -X POST localhost:8444/token \
-d grant_type=client_credentials -d client_id=ci-bot -d client_secret=<secret>
# {"access_token":"eyJ…","token_type":"Bearer","expires_in":900}

PRIOMPT_TOKEN=eyJ… priompt list -prefix priompt://acme/ -addr localhost:8443
# clients.txt: client_id sha256:<hex> [org] [rw]
ci-bot sha256:d430dc… acme rw
dashboard sha256:9f11ab… acme

To revoke a client, delete its line and restart.

SSO for humans (OIDC)

Priompt doesn't host a login page. Your IdP already has one, and priompt-auth only verifies its output (an RS256 ID token) against the IdP's published keys.

./priompt-auth serve -clients-file clients.txt \
-oidc-issuer https://your-org.okta.com -oidc-audience priompt \
-groups-file groups.txt
# groups.txt: the first matching line wins; no org = admin (all orgs)
prompt-admins rw # platform team
acme-authors acme rw # acme's prompt writers
acme-readers acme # read-only consumers
curl -s -X POST localhost:8444/token -d grant_type=oidc -d id_token=<IdP ID token>

priompt-auth checks the signature (against the issuer's JWKS), issuer, expiry, and audience, then maps the groups claim through groups.txt. A user in no mapped group gets 403 {"error":"access_denied"}.

Tested end to end against Keycloak 26. If you use Keycloak, note that users created through the admin API need an email and first/last name set, or direct grants fail with Account is not fully set up.

What a token contains

The token is a compact EdDSA (Ed25519) JWT. The claims contract lives once, in priomptproto/claims:

ClaimExampleMeaning
subci-bot, [email protected]who it was issued to (recorded in audit logs and commit authorship)
orgacme ("" = admin)org scope
rwtruemay publish, branch, merge, or roll back
exp / iatunix secondsdefault TTL 15 minutes (-ttl)
isspriompt-authissuer

The server re-fetches /jwks (rate-limited) when it sees an unknown kid, so key rotation needs no server restart. To rotate, run init -force and restart priompt-auth. Outstanding tokens die within one TTL.

Static tokens keep working alongside JWTs, so you can migrate gradually. Flags are in the priompt-auth reference.