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/appsfor 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, nonpx justauthcommand and nocdn.justauth.orgscript. 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
Open https://justauth.org, select Sign in, and sign in with GitHub.
In the dashboard, open Apps and create an app. Enter a name and your redirect URIs, for example
http://localhost:*andhttps://myapp.com/auth/callback.Copy the client ID (starts with
ja_) and the client secret (starts withjas_). The secret is shown only once.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=RANDOMJustAuth redirects back to your
redirect_uriwith?code=...&state=.... Check thatstatematches what you sent.On your server, exchange the code for an access token:
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_...{ "access_token": "...", "token_type": "Bearer", "expires_in": 3600, "scope": "profile email" }Read the user's profile:
curl https://justauth.org/oauth/userinfo -H "Authorization: Bearer ACCESS_TOKEN"{ "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
- Select Sign in in the top bar of the homepage.
- GitHub asks you to authorize JustAuth. Select Authorize.
- 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
httpis allowed only onlocalhost,127.0.0.1and[::1]. Everything else must behttps. - A wildcard subdomain needs at least two labels after it:
https://*.myapp.comis valid,https://*.comis 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_uriwithcodeandstate. The code is single-use and expires after 10 minutes. - If the person selects Deny, the redirect carries
error=access_denied. Other errors areunsupported_response_type,invalid_scopeandinvalid_request. - If
client_idis unknown orredirect_uridoesn'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.
- Create a key on the API keys page of the dashboard. Keys start with
jak_and are shown once. - Send the key as a Bearer token. Send request bodies as JSON with
Content-Type: application/json.
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:
{
"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 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.