# Authentication

> OAuth 2.0 signs in users; API keys authenticate servers. Which credential to use, what each looks like, and the one rule that keeps your secret key safe.

EntryBit has two ways to prove who is calling — and they are not interchangeable. Pick by asking **who is acting**: a *person* using your app, or a *server* acting as the whole organization.

- **A signed-in user** → **OAuth 2.0 / OpenID Connect**. Your app never sees the password; it receives a short-lived access token on the user's behalf and calls [`/api/v1/*`](/docs/api-reference/passes/).
- **A backend or SDK** → an **organization API key**. A long-lived secret that acts as the organization and calls [`/api/v1/org/*`](/docs/api-reference/organization/).

## The two credentials at a glance

| | OAuth client | API key |
|---|---|---|
| Acts as | A **signed-in user** | The **organization** |
| Looks like | `eb_9f1c2ab34cd56ef7` (`client_id`) | `eb_sk_…` (secret) |
| Secret? | **No** — the `client_id` is public | **Yes** — a 256-bit secret |
| Proof of identity | The user's login + **PKCE** | The key itself |
| Where it lives | Shipped in the app / SPA | On your server, in a vault |
| Created in | **Settings → Team → OAuth apps** | **Settings → API keys** |
| Token you send | `Authorization: Bearer <access_token>` (a JWT) | `Authorization: Bearer eb_sk_…` |
| Calls | `/api/oauth/*`, `/api/v1/*` | `/api/v1/org/*` |
| Scopes | User-consent scopes (`passes:read`, …) | `org:*` scopes |

## The credential shapes

Both identifiers start with `eb_`, and the difference is the one thing worth memorising:

- **`eb_9f1c2ab34cd56ef7`** — an OAuth **`client_id`**. It has no `sk`. It is **public**: it ships inside your mobile app or SPA, and that is fine because a `client_id` alone can do nothing. The security comes from the user logging in plus **PKCE** (a per-request secret the app proves it holds).
- **`eb_sk_…`** — an organization **API key**. The `sk` means *secret key*. It is 256 bits of CSPRNG output, it acts as the whole organization, and it is shown to you exactly **once**.

### The golden rule

> **Never ship an `eb_sk_…` key inside an app, APK, SPA bundle, or any client anyone can download.** A secret key can be extracted from a shipped binary in minutes, and it grants organization-wide access. Client apps use an OAuth **`client_id`** (public, PKCE-protected); secret keys live only on servers you control. If a secret key was ever committed, embedded, or shared, **[revoke it](/docs/api-keys/create-and-manage/)** and switch the app to a `client_id`.

## Decision table

| You are building… | Use | Start here |
|---|---|---|
| A mobile app that signs users in | OAuth 2.0 (public client + PKCE) | [Quickstart: Sign in with EntryBit](/docs/get-started/quickstart-react-native/) |
| A single-page web app (SPA) | OAuth 2.0 (public client + PKCE) | [OAuth 2.0 & OpenID Connect](/docs/oauth/overview/) |
| A web app with a backend that can keep a secret | OAuth 2.0 (confidential client) | [Register an app](/docs/oauth/register-an-app/) |
| A server integration that acts for the whole org | API key | [API keys](/docs/api-keys/overview/) |
| A cron job / data sync / internal tool | API key | [API keys](/docs/api-keys/overview/) |
| A resource server that only verifies tokens | Discovery + JWKS | [Discovery & JWKS](/docs/oauth/discovery-jwks/) |

## What each model gets you

**OAuth 2.0 / OpenID Connect** — the authorization-code flow with **PKCE (S256), required for every client**. The user signs in through the system browser (never a WebView), your app exchanges the resulting code for tokens, and you receive:

- an **access token** — an RS256 JWT, ~15 minutes, sent as `Authorization: Bearer …`;
- a **refresh token** — opaque, 60-day, **rotating** (when you request `offline_access`);
- an **id_token** — who signed in and *how* (when you request `openid`).

See [OAuth 2.0 & OpenID Connect](/docs/oauth/overview/) for the full flow, and [Scopes](/docs/oauth/scopes/) for what you can ask for.

**Organization API keys** — a single secret that authenticates every request. An org admin mints it in **Settings → API keys** with least-privilege `org:*` scopes, an optional expiry, and an optional source-IP allowlist. Send it on every call and you act as the organization. See [API keys](/docs/api-keys/overview/).

## Next steps

- New to the platform? Read the [Introduction](/docs/get-started/introduction/).
- Signing users in? [Quickstart: Sign in with EntryBit](/docs/get-started/quickstart-react-native/).
- Building server-side? [API keys](/docs/api-keys/overview/) then the [Organization API](/docs/api-reference/organization/).