Login with Navy (OAuth)
Let people sign into your app with their NavyAI account and call models on their behalf — no manual API-key pasting. NavyAI is a standard OAuth 2.0 authorization-code provider with PKCE. This is ideal if you run a chat UI or tool and want your users to bring their own NavyAI plan.
How it works
- You register an OAuth app in your NavyAI dashboard and get a
client_idandclient_secret. - Your site sends the user to NavyAI's
/v1/oauth/authorizescreen. - The user signs in with Discord (if needed) and approves the requested scopes.
- NavyAI redirects back to your
redirect_uriwith a single-usecode. - Your backend exchanges that
codeat/v1/oauth/tokenfor anaccess_token(+refresh_token). - You use the access token to read the user's profile at
/v1/oauth/me, and — if you requested theinferencescope — to call/v1/chat/completionsand every other inference endpoint, billed to the user's own plan.
Registering an app
Open the Dashboard, go to Developer → OAuth Apps, and create an app. You'll set:
- Name & description — shown to users on the consent screen.
- Redirect URIs — exact-match allowlist. NavyAI only ever redirects back to a URI you registered here.
- Homepage / logo URL — optional branding for the consent screen.
The client_secret is shown once at creation. Store it server-side; if you lose it, rotate it (the old one stops working immediately). Every user can register up to 5 apps.
Scopes
Request the least you need, space-separated:
identity— read the user's NavyAI id, username, avatar and current plan.email— read the email on the user's account.inference— send API requests (chat, images, audio, embeddings) billed to the user's plan. Also makes/v1/oauth/mereturn the user's API key.
Step 1 — Send the user to the authorize screen
Redirect the browser to:
1https://api.navy/v1/oauth/authorize
2 ?client_id=navy-client-...
3 &redirect_uri=https://yourapp.com/auth/navy/callback
4 &response_type=code
5 &scope=identity%20inference
6 &state=RANDOM_ANTI_CSRF_STRING
7 &code_challenge=BASE64URL_SHA256_OF_VERIFIER
8 &code_challenge_method=S256state— an opaque random string you generate and later verify to prevent CSRF.code_challenge/code_challenge_method— PKCE. Generate a randomcode_verifier, then sendcode_challenge = BASE64URL(SHA256(code_verifier))with methodS256. Required for public clients (SPAs/mobile); recommended for everyone.
After the user approves, NavyAI redirects to:
1https://yourapp.com/auth/navy/callback?code=ONE_TIME_CODE&state=YOUR_STATEIf the user denies, you get ?error=access_denied&state=... instead.
Step 2 — Exchange the code for tokens
From your backend (so the secret stays private), POST to the token endpoint:
1curl -X POST https://api.navy/v1/oauth/token \
2 -H "Content-Type: application/json" \
3 -d '{
4 "grant_type": "authorization_code",
5 "code": "ONE_TIME_CODE",
6 "redirect_uri": "https://yourapp.com/auth/navy/callback",
7 "client_id": "navy-client-...",
8 "client_secret": "navy-secret-...",
9 "code_verifier": "THE_ORIGINAL_VERIFIER"
10 }'Response:
1{
2 "access_token": "navy-oat-...",
3 "token_type": "Bearer",
4 "expires_in": 3600,
5 "refresh_token": "navy-ort-...",
6 "scope": "identity inference"
7}Access tokens live for 1 hour. Keep the refresh token to mint new ones without sending the user through consent again.
Step 3 — Read the user's profile
1curl https://api.navy/v1/oauth/me \
2 -H "Authorization: Bearer navy-oat-..."api_key is only included when the inference scope was granted.
Step 4 — Call models on the user's behalf
The OAuth access token works directly as a Bearer credential on the inference endpoints — usage is billed to the user's own key and plan:
1curl -X POST https://api.navy/v1/chat/completions \
2 -H "Authorization: Bearer navy-oat-..." \
3 -H "Content-Type: application/json" \
4 -d '{
5 "model": "claude-sonnet-4.6",
6 "messages": [{"role": "user", "content": "Hello from my app!"}]
7 }'You can point any OpenAI-compatible SDK at https://api.navy/v1 and pass the OAuth access token as the API key. Alternatively, use the api_key returned by /v1/oauth/me if you prefer a stable credential.
Refreshing an expired access token
1curl -X POST https://api.navy/v1/oauth/token \
2 -H "Content-Type: application/json" \
3 -d '{
4 "grant_type": "refresh_token",
5 "refresh_token": "navy-ort-...",
6 "client_id": "navy-client-...",
7 "client_secret": "navy-secret-..."
8 }'Each refresh rotates both tokens: use the new access_token and refresh_token from the response and discard the old ones.
Revoking access
Either side can disconnect:
- Users revoke an app from Dashboard → Connected Apps at any time.
- Your app can revoke a token programmatically:
1curl -X POST https://api.navy/v1/oauth/revoke \
2 -H "Content-Type: application/json" \
3 -d '{ "token": "navy-oat-... or navy-ort-..." }'Revoking kills the whole grant — both the access and refresh tokens for that user+app die together, and the user must re-authorize.
Endpoint reference
GET /v1/oauth/authorize— start the flow (browser redirect).POST /v1/oauth/token— exchange a code, or refresh (grant_type:authorization_code|refresh_token). Client auth via body or HTTP Basic.GET /v1/oauth/me— userinfo; authenticated with the Bearer access token. Returns fields gated by scope.POST /v1/oauth/revoke— revoke an access or refresh token.
Security notes
- Always keep the
client_secretserver-side. For browser/mobile clients that can't hold a secret, use PKCE and omit the secret. - Always generate and verify
stateto defend against CSRF. redirect_urimust exactly match one you registered — no wildcards, no trailing-slash mismatches.- Tokens and secrets are stored hashed on NavyAI's side; treat the plaintext values you receive as sensitive.
- OAuth error responses follow the OAuth 2.0 shape:
{ "error": "...", "error_description": "..." }.