# JustAuth docs

JustAuth (https://justauth.org) is an OAuth 2.0 provider. You sign in with GitHub, register your app, and your users can then "Sign in with JustAuth" using the standard authorization code flow. This page explains everything the site does today, for people and for software agents alike.

The same content is available as plain Markdown at [/llms.txt](/llms.txt).

## Status

What works today:

- Sign in to justauth.org with GitHub.
- Register apps (OAuth clients) with wildcard redirect URIs.
- The OAuth 2.0 authorization code flow, with optional PKCE: `/oauth/authorize`, `/oauth/token`, `/oauth/userinfo`.
- Personal API keys and a Management API at `/api/v1/apps` for managing apps from code.
- A dashboard that lists the people who have signed in to your apps.

Not available yet:

- Official SDK packages. There is no `@justauth/*` package on npm, no `npx justauth` command and no `cdn.justauth.org` script. Do not try to install them. Use any standard OAuth 2.0 client library with the endpoints below.
- Passkey and email-link sign-in to JustAuth. Today, everyone signs in to JustAuth with GitHub.
- Refresh tokens, ID tokens (JWTs), OpenID discovery documents and webhooks.
- Billing. The plans at https://justauth.org/#pricing (Free, Indie, Pro and Scale, priced by monthly active users) are published, but nobody is charged during early access.

## Quickstart

1. Open https://justauth.org, select **Sign in**, and sign in with GitHub.
2. In the dashboard, open **Apps** and create an app. Enter a name and your redirect URIs, for example `http://localhost:*` and `https://myapp.com/auth/callback`.
3. Copy the **client ID** (starts with `ja_`) and the **client secret** (starts with `jas_`). The secret is shown only once.
4. Send your users to the authorize endpoint:

   ```
   https://justauth.org/oauth/authorize?response_type=code&client_id=ja_...&redirect_uri=https://myapp.com/auth/callback&scope=profile%20email&state=RANDOM
   ```

5. JustAuth redirects back to your `redirect_uri` with `?code=...&state=...`. Check that `state` matches what you sent.
6. On your server, exchange the code for an access token:

   ```bash
   curl https://justauth.org/oauth/token \
     -d grant_type=authorization_code \
     -d code=CODE_FROM_STEP_5 \
     -d redirect_uri=https://myapp.com/auth/callback \
     -d client_id=ja_... \
     -d client_secret=jas_...
   ```

   ```json
   { "access_token": "...", "token_type": "Bearer", "expires_in": 3600, "scope": "profile email" }
   ```

7. Read the user's profile:

   ```bash
   curl https://justauth.org/oauth/userinfo -H "Authorization: Bearer ACCESS_TOKEN"
   ```

   ```json
   {
     "sub": "42",
     "preferred_username": "octocat",
     "name": "The Octocat",
     "picture": "https://...",
     "email": "octocat@github.com"
   }
   ```

Use `sub` as the stable user ID in your app.

## Sign in to justauth.org

1. Select **Sign in** in the top bar of the homepage.
2. GitHub asks you to authorize JustAuth. Select **Authorize**.
3. You land on `/dashboard`. If you started from a dashboard page or an app's sign-in, you return there instead.

If sign-in fails (for example, you cancel on GitHub), you return to the homepage with `?auth_error=github` in the address.

To sign out, select **Sign out** at the bottom of the dashboard sidebar, or next to your username on the homepage. This deletes your JustAuth session. It does not revoke JustAuth's authorization on GitHub. To do that, go to GitHub **Settings → Applications → Authorized GitHub Apps**.

## Dashboard

The dashboard requires you to be signed in. It has these sections:

- **Overview** (`/dashboard`): counts of your apps, users and API keys, and the five most recent sign-ins to your apps.
- **Users** (`/dashboard/users`): you, plus everyone who has approved one of your apps, most recent login first. Search matches login, name and email. You can link to a search directly: `/dashboard/users?q=octocat`.
- **Apps** (`/dashboard/apps`): create, edit and delete apps, and rotate client secrets.
- **API keys** (`/dashboard/api-keys`): create and revoke keys for the Management API.

## Apps and redirect URIs

An app is an OAuth client. Each app has a name (up to 100 characters), a client ID (`ja_...`), a client secret (`jas_...`) and 1 to 20 redirect URI patterns.

JustAuth stores only a hash of the client secret, so it is shown once, when you create the app or rotate the secret. Rotating a secret makes the old one stop working immediately and revokes the app's access tokens.

Redirect URI patterns may use wildcards:

| Pattern                           | Matches                                    |
| --------------------------------- | ------------------------------------------ |
| `https://myapp.com/auth/callback` | Exactly that path                          |
| `https://myapp.com`               | Any path on `https://myapp.com`            |
| `https://myapp.com/auth/*`        | Any path starting with `/auth/`            |
| `https://*.myapp.com`             | Any subdomain, such as preview deployments |
| `http://localhost:*`              | Any port on localhost                      |

Rules:

- Plain `http` is allowed only on `localhost`, `127.0.0.1` and `[::1]`. Everything else must be `https`.
- A wildcard subdomain needs at least two labels after it: `https://*.myapp.com` is valid, `https://*.com` is not.
- Query strings are allowed in the requested `redirect_uri`, but fragments (`#...`) and credentials are not.

## OAuth reference

### `GET /oauth/authorize`

| Parameter               | Required    | Notes                                                                          |
| ----------------------- | ----------- | ------------------------------------------------------------------------------ |
| `response_type`         | Yes         | Must be `code`.                                                                |
| `client_id`             | Yes         | Your app's `ja_...` ID.                                                        |
| `redirect_uri`          | Yes         | Must match one of the app's patterns.                                          |
| `scope`                 | No          | Space-separated. `openid`, `profile`, `email`. Defaults to `profile`.          |
| `state`                 | Recommended | Returned unchanged. Use it to prevent CSRF.                                    |
| `code_challenge`        | No          | PKCE. Recommended for browser and mobile apps.                                 |
| `code_challenge_method` | No          | `S256` (recommended) or `plain`. Defaults to `plain` when a challenge is sent. |

What happens:

- If the person is not signed in to JustAuth, they sign in with GitHub first, then continue.
- The first time, they see a consent screen listing the requested scopes. JustAuth remembers their approval, so later requests for the same scopes skip the screen.
- On success, JustAuth redirects to `redirect_uri` with `code` and `state`. The code is single-use and expires after 10 minutes.
- If the person selects **Deny**, the redirect carries `error=access_denied`. Other errors are `unsupported_response_type`, `invalid_scope` and `invalid_request`.
- If `client_id` is unknown or `redirect_uri` doesn't match, JustAuth shows an error page instead of redirecting.

### `POST /oauth/token`

Send form-encoded parameters (`application/x-www-form-urlencoded`):

| Parameter                    | Notes                                                    |
| ---------------------------- | -------------------------------------------------------- |
| `grant_type`                 | Must be `authorization_code`.                            |
| `code`                       | The code from the redirect.                              |
| `redirect_uri`               | Exactly the same value you sent to `/oauth/authorize`.   |
| `client_id`, `client_secret` | In the body, or as an HTTP Basic `Authorization` header. |
| `code_verifier`              | Required if you sent a `code_challenge`.                 |

The response contains `access_token`, `token_type` (`Bearer`), `expires_in` (3600 seconds) and `scope`. Access tokens last one hour. There are no refresh tokens yet, so send the person through `/oauth/authorize` again when a token expires. Because they already approved your app, this needs no clicks.

Errors return HTTP 400 or 401 with `{ "error": "..." }`: `unsupported_grant_type`, `invalid_client` or `invalid_grant`.

### `GET /oauth/userinfo`

Send `Authorization: Bearer ACCESS_TOKEN`. The fields depend on the granted scopes:

| Field                | Scope                                              |
| -------------------- | -------------------------------------------------- |
| `sub`                | Always. Stable JustAuth user ID.                   |
| `preferred_username` | `profile`. GitHub login.                           |
| `name`               | `profile`. GitHub name, or login if there is none. |
| `picture`            | `profile`. Avatar URL.                             |
| `email`              | `email`. Public GitHub email, or `null`.           |

An invalid or expired token returns HTTP 401 with `{ "error": "invalid_token" }`.

## Management API

Manage your apps from code.

1. Create a key on the **API keys** page of the dashboard. Keys start with `jak_` and are shown once.
2. Send the key as a Bearer token. Send request bodies as JSON with `Content-Type: application/json`.

```bash
curl https://justauth.org/api/v1/apps \
  -H "Authorization: Bearer $JUSTAUTH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "My App", "redirect_uris": ["https://myapp.com", "http://localhost:*"]}'
```

| Method | Path                             | What it does                                                                                     |
| ------ | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| GET    | `/api/v1/apps`                   | List your apps: `{ "apps": [...] }`                                                              |
| POST   | `/api/v1/apps`                   | Create an app. Body: `name`, `redirect_uris`. Returns HTTP 201 with `client_secret`, shown once. |
| GET    | `/api/v1/apps/:client_id`        | Get one app.                                                                                     |
| PATCH  | `/api/v1/apps/:client_id`        | Change `name` and/or `redirect_uris`.                                                            |
| DELETE | `/api/v1/apps/:client_id`        | Delete an app. Returns HTTP 204.                                                                 |
| POST   | `/api/v1/apps/:client_id/secret` | Rotate the secret. Returns the new `client_secret` and revokes the app's access tokens.          |

An app object looks like this:

```json
{
  "client_id": "ja_...",
  "name": "My App",
  "redirect_uris": ["https://myapp.com"],
  "created_at": "2026-10-27T00:00:00.000Z"
}
```

Errors return `{ "error": "...", "message": "..." }`:

| Status | `error`           | Meaning                                                                   |
| ------ | ----------------- | ------------------------------------------------------------------------- |
| 400    | `invalid_request` | The body isn't a JSON object, or `Content-Type` isn't `application/json`. |
| 401    | `unauthorized`    | The API key is missing or invalid.                                        |
| 404    | `not_found`       | No app with that `client_id` belongs to you.                              |
| 422    | `invalid_app`     | The name or a redirect URI is invalid. `message` says which.              |

The API accepts only API keys. It ignores browser cookies.

## What we store

- **Your GitHub profile**: user ID, login, name, public email and avatar URL, plus when you first and last signed in. Each sign-in updates these.
- **Apps**: name, client ID, redirect URIs and a hash of the client secret.
- **Consents**: which apps each person has approved, and for which scopes.
- **Authorization codes, access tokens and API keys**: hashes only. The plaintext goes only to you or your app.

JustAuth does not store your GitHub access token. It uses the token once, to read your profile during sign-in, and then discards it.

Your browser keeps one session cookie, `ja_session`. It is signed, HTTP-only and expires after 30 days. Short-lived cookies protect sign-in against forged requests.

## All routes

| Method | Path                        | What it does                                                                           |
| ------ | --------------------------- | -------------------------------------------------------------------------------------- |
| GET    | `/`                         | Homepage                                                                               |
| GET    | `/docs`                     | This page                                                                              |
| GET    | `/llms.txt`                 | This page as plain Markdown                                                            |
| GET    | `/api/auth/github`          | Starts GitHub sign-in to justauth.org. Optional `?next=/path` to return to afterwards. |
| GET    | `/api/auth/callback/github` | GitHub returns here.                                                                   |
| POST   | `/api/auth/signout`         | Signs out and redirects to `/`.                                                        |
| GET    | `/oauth/authorize`          | OAuth authorization endpoint                                                           |
| POST   | `/oauth/token`              | OAuth token endpoint                                                                   |
| GET    | `/oauth/userinfo`           | OAuth user info endpoint                                                               |
| *      | `/api/v1/apps...`           | Management API                                                                         |
| GET    | `/dashboard...`             | Dashboard. Requires sign-in.                                                           |

## For agents and LLMs

- Read [/llms.txt](/llms.txt) for this page as Markdown.
- To add "Sign in with JustAuth" to an app, use a standard OAuth 2.0 authorization code client with the endpoints in the OAuth reference. Do not generate code that imports `@justauth/*` packages: they are not published.
- Signing in to justauth.org requires a person to approve access on github.com in a browser. An agent cannot do this alone, so it cannot create the first app or API key by itself.
- Once a person gives you an API key (`jak_...`), you can create and manage apps through the Management API without a browser.
- Client secrets and API keys are shown only once. Store them as secrets, never in source code.
