Sign-in discovery
Generated from core/api/router.go. Edit the router, not this file.
| Method | Path | Who can call it |
|---|---|---|
POST | /api/v1/auth/callback | Public — no credential |
GET | /api/v1/auth/config | Public — no credential |
POST | /api/v1/auth/login | Public — no credential |
POST /api/v1/auth/callback
| Who can call it | Public — no credential |
| Handler | sessionHandler.Callback |
The core redeems the authorization code, the browser does not. A single-page application that redeems it itself receives a refresh token and has nowhere safe to keep it — every storage a page can reach is readable by script on that origin. Doing the exchange here means the browser gets a session cookie and never handles a token at all.
Request body
CallbackInput is what the browser brings back from the identity provider.
| Field | Type | Description | |
|---|---|---|---|
code | string | required | |
code_verifier | string | required | |
redirect_uri | string | required | |
nonce | string | required | Nonce is the value the browser put in the authorization request. It is sent back so the core can check the ID token carries the same one — the browser cannot be trusted to check it itself, because the whole point of moving this here is that the browser is the untrusted half. |
Responses
| Status | Body |
|---|---|
200 | A MeResponse object |
400 | { "error": … } · this sign-in response was incomplete; begin again from the sign-in page |
401 | { "error": … } · the identity provider refused this sign-in; begin again from the sign-in page · the identity provider's response could not be verified, so it was refused |
403 | { "error": … } · this account is suspended in CertPilot; the identity provider still accepts it |
404 | { "error": … } · this instance is not configured for single sign-on; sign in with an account instead |
500 | { "error": … } · sign-in could not be completed |
502 | { "error": … } · CertPilot could not reach the identity provider to complete this sign-in |
Example request
curl -X POST 'https://certpilot.example.com/api/v1/auth/callback' \
-H 'Content-Type: application/json' \
-d '{
"code": "<code>",
"code_verifier": "<code_verifier>",
"redirect_uri": "<redirect_uri>",
"nonce": "<nonce>"
}'GET /api/v1/auth/config
| Who can call it | Public — no credential |
| Handler | sessionHandler.Config |
Public, and necessarily so: this is what a browser reads before it holds any credential. It carries the issuer and client id, which are public by construction in authorization code with PKCE — the user's own browser sends both to the provider in a URL they can read.
Responses
| Status | Body |
|---|---|
200 | A AuthConfigResponse object |
Example request
curl -X GET 'https://certpilot.example.com/api/v1/auth/config'POST /api/v1/auth/login
| Who can call it | Public — no credential |
| Handler | sessionHandler.Login |
Sign-in itself cannot require being signed in. It is rate-limited by the per-account lockout in the store rather than by middleware, because the core runs as several replicas and an in-process counter would reset with every request that landed on a different one.
Request body
LoginInput is an email and a password. Nothing else — no "remember me", because a longer session on a console that can export private keys is not a convenience worth offering.
| Field | Type | Description | |
|---|---|---|---|
email | string | required | an email address |
password | string | required |
Responses
| Status | Body |
|---|---|
200 | A MeResponse object |
400 | { "error": … } · an email address and a password are required · that password is too long to be one of ours |
500 | { "error": … } · sign-in could not be completed; the API log has the reason · sign-in could not be completed |
Example request
curl -X POST 'https://certpilot.example.com/api/v1/auth/login' \
-H 'Content-Type: application/json' \
-d '{
"email": "<email>",
"password": "<password>"
}'