# Authorize endpoint

> GET /api/oauth/authorize — begin the authorization-code flow. Parameters, the iss response parameter (RFC 9207), prompt and max_age, error redirects, and the open-redirect guard.

The authorization endpoint begins the sign-in flow. It is a **browser navigation**, not an API call — open it in the user's browser (native apps: the **system browser** per [RFC 8252](https://www.rfc-editor.org/rfc/rfc8252), never a WebView). EntryBit runs login and consent, then 302-redirects back to your app with a one-time code.

```http
GET /api/oauth/authorize
```

## Parameters

Passed as query parameters.

| Parameter | Required | Notes |
|---|---|---|
| `response_type` | ✓ | `code` only. |
| `client_id` | ✓ | Your registered client. |
| `redirect_uri` | ✓ | **Byte-exact** match against the client's registered allowlist. |
| `code_challenge` | ✓ | `BASE64URL(SHA-256(code_verifier))` — PKCE, always. |
| `code_challenge_method` | ✓ | `S256`. `plain` is rejected. |
| `scope` |  | Space-delimited subset of the client's allowed scopes. Default: `openid profile email`. See [Scopes](/docs/oauth/scopes/). |
| `state` | recommended | Opaque CSRF value, echoed back verbatim. |
| `nonce` | recommended | Binds the `id_token` to this request. Required if you validate id_tokens client-side. |
| `prompt` |  | Space-set of `none`, `login`, `consent`. |
| `max_age` |  | Maximum seconds since the user last authenticated. |

## Building the request

```
GET /api/oauth/authorize
      ?response_type=code
      &client_id=eb_9f1c2ab34cd56ef7
      &redirect_uri=entrybitresident://oauth/callback
      &scope=openid%20profile%20email%20offline_access%20passes:read
      &state=<random>
      &code_challenge=<base64url(sha256(verifier))>
      &code_challenge_method=S256
      &nonce=<random>
```

Most OAuth libraries build this for you — see the [React Native quickstart](/docs/get-started/quickstart-react-native/). The `state` and PKCE values are typically generated and verified by the library.

## `prompt` and `max_age`

`prompt` controls the interaction:

- `none` — never show UI. If login or consent would be required, the request fails fast with `login_required` or `consent_required` (redirected as an error). `none` must stand alone — it cannot be combined with other values.
- `login` — force a fresh sign-in even if a session exists. (It is stripped from the resumed request internally so it cannot loop.)
- `consent` — always show the consent screen, even if consent was previously remembered.

`max_age` requests a maximum authentication age in seconds: if the session's `auth_time` is older, the user must re-authenticate. Use `prompt=login` or a small `max_age` before a sensitive screen when you need a *fresh* login — the resulting `id_token` carries an updated `auth_time` and `amr`. See [ID token & UserInfo](/docs/oauth/id-token-userinfo/).

## Success response

On success EntryBit issues a 302 redirect to your `redirect_uri`:

```http
HTTP/1.1 302 Found
Location: entrybitresident://oauth/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=<same>&iss=https://entrybit.net
```

The `code` is **single-use**, has a **60-second TTL**, and is bound to your `client_id`, `redirect_uri`, and PKCE challenge. Exchange it immediately at the [token endpoint](/docs/oauth/token/).

### The `iss` parameter (RFC 9207)

Every authorization response — **success and error** — carries `iss=https://entrybit.net` ([RFC 9207](https://www.rfc-editor.org/rfc/rfc9207), authorization-server mix-up mitigation). If your library validates it, it must equal the issuer; otherwise it is safe to ignore.

## Error responses

How an error is returned depends on **when** it is detected:

- **Invalid `client_id` or `redirect_uri`** — a flat, **non-redirecting `400`**. The pair is validated *before* any redirect happens, so a bad request can never bounce the browser to an attacker-controlled URL. This is the **open-redirect and enumeration guard**.
- **Any protocol error after that point** — redirected back to your `redirect_uri` with `error`, `error_description`, `state`, and `iss`:

```http
HTTP/1.1 302 Found
Location: entrybitresident://oauth/callback?error=access_denied&error_description=...&state=<same>&iss=https://entrybit.net
```

Error descriptions are deliberately generic — **branch on the `error` code, never the prose**. The codes you may see here include `access_denied` (user declined), `invalid_scope`, `invalid_request`, `login_required`, `consent_required`, and `interaction_required`. Full catalog: [Errors](/docs/api-reference/errors/).

## What happens mid-flow

If the user is not signed in, EntryBit sends the browser to its own login page (`/auth?return_to=<this request>`) and resumes the authorization once login, 2FA, or passkey completes. Consent is remembered per user, client, and scope: a repeat or narrower request stays silent; a superset re-prompts. None of this changes your app's code — it targets this endpoint either way.

Next: exchange the code at the [Token endpoint](/docs/oauth/token/).