# Sign in with codai (/docs/auth)

auth.codai.ro is a standard OpenID Connect provider. Use it when your app needs to act on behalf of a codai user; use a plain API key when it does not.

`https://auth.codai.ro` is codai's identity provider — a standards-compliant **OpenID Connect** server with discovery, PKCE, refresh tokens, RP-initiated and back-channel logout. Every codai account signs in through it (password, passkey, magic link or Google), and every first-party surface — hub, pay, docs, the desktop and phone apps — is a relying party of it.

For you as an integrator it does one thing the rest of OIDC does not: after a user signs in and consents, **`GET /connect/key` mints a gateway API key bound to that user and that consent**, so your app can call `https://ai.codai.ro` *as them* — billed to their plan, revocable by them, without ever seeing their password or their other keys.

## Do you need it? [#do-you-need-it]

| You are building… | Use |
| --- | --- |
| A server, script, bot or CI job that calls codai **as you** | A plain API key from [hub.codai.ro](https://hub.codai.ro) → Keys. No OIDC. See the [gateway quickstart](../gateway/index.mdx). |
| An app where **each of your users** brings their own codai account and pays for their own usage | **Connect with codai** — the [connect flow](./connect-flow.mdx). One redirect, one token exchange, one `GET /connect/key`. |
| A web app that just needs to know *who* the codai user is (SSO), no inference | Standard OIDC code flow; request `openid email profile` and read the ID token. |
| An MCP client that should reach codai tools and memory | OIDC with the `mcp` scope against resource `https://ai.codai.ro/mcp`; clients may self-register via Dynamic Client Registration. |
| A CLI without a browser on the same machine | The device-authorization grant is implemented but only provisioned for first-party clients today — ask. |

## Discovery [#discovery]

```bash
curl -s https://auth.codai.ro/.well-known/openid-configuration | jq '{issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri, end_session_endpoint, grant_types_supported, code_challenge_methods_supported, token_endpoint_auth_methods_supported, scopes_supported}'
```

| Endpoint | Path |
| --- | --- |
| Authorization | `GET /auth` |
| Token | `POST /token` — throttled at 60 requests/min per IP (`429 { "error": "rate_limited" }`) |
| UserInfo | `GET /me` |
| JWKS | `GET /jwks` — every non-revoked RS256 key; the newest signs |
| Introspection / revocation | `POST /token/introspection`, `POST /token/revocation` |
| End session | `GET /session/end` |
| Dynamic registration | `POST /reg` (public clients only) |
| Device authorization | `POST /device/auth` |
| **Mint a gateway key** | `GET /connect/key` — codai-specific, see the [connect flow](./connect-flow.mdx) |

Facts worth knowing before you write code:

- **PKCE (`S256`) is required for every client**, public or confidential. There is no implicit or hybrid flow; `response_type` is always `code`.
- Client authentication methods: `none` (public), `client_secret_basic`, `client_secret_post`, `private_key_jwt`. `client_secret_jwt` is not offered.
- Grant types: `authorization_code`, `refresh_token`, `urn:ietf:params:oauth:grant-type:device_code`.
- Token lifetimes: access token 1 h, ID token 1 h, authorization code 10 min, refresh token 30 days rotating (≤ 1 year total), consent grant 90 days, IdP browser session 14 days.
- Refresh tokens are issued only when the client has the `refresh_token` grant **and** the request asked for `offline_access`. Public clients always rotate on use.
- Access tokens for the default resource (`https://ai.codai.ro/v1`) are opaque — validate them with introspection or just use them at `/connect/key`. Tokens for `https://ai.codai.ro/mcp` are RS256 JWTs the gateway verifies locally.
- Step-up: `acr_values` accepts `urn:codai:acr:pwd`, `urn:codai:acr:passkey`, `urn:codai:acr:mfa`; the IdP re-prompts if the session's method is weaker or older than 10 minutes.

## Registering a client [#registering-a-client]

There is no self-serve client registration for the connect flow yet — clients are provisioned by the codai team. Send us the client name, the exact redirect URIs (no wildcards; `http://` only on loopback), whether the client is public (`none`) or confidential (`client_secret_basic`, `client_secret_post` or `private_key_jwt`), and the scopes you need. You get a `client_id` and, for confidential clients, a secret shown once.

MCP clients are the exception: `POST /reg` accepts RFC 7591 registrations without an initial access token, creating a public client (`dcr_…`) with 1 – 5 `https://` / loopback / custom-scheme redirect URIs and the `authorization_code` grant.

## Logout [#logout]

- **RP-initiated**: send the browser to `GET /session/end?id_token_hint=<id_token>&post_logout_redirect_uri=<registered>&state=<yours>`. The redirect URI must be registered **exactly** (no query string — carry your local target in `state`). Without `id_token_hint` the IdP shows a confirmation page first.
- **Back-channel**: if your client registered a `backchannel_logout_uri`, the IdP `POST`s a `logout_token` (form-encoded, matched on `sid`) when the user's session ends anywhere. Answer `200` or `204`.

<Callout type="info">
Revoking the consent — the user disconnecting your app, or an admin revoking the grant — also revokes any gateway key minted for it. Your stored `codai_…` key will start returning `401 invalid_api_key`; treat that as "ask the user to connect again".
</Callout>
