Authentication API
Endpoints for user registration, login, OAuth, device code flow, session management, and SSO.
Register
Create a new account with email and password.
POST /v1/auth/registerBody:
{
"email": "user@example.com",
"password": "securepassword123"
}Response: 201 Created
{
"data": {
"id": "usr_abc123",
"email": "user@example.com",
"tier": "core",
"email_verified": false
}
}INFO
Password must be at least 12 characters. A verification email is sent automatically. The verification link expires after 24 hours.
Errors:
| Code | Condition |
|---|---|
VALIDATION_ERROR | Password too short or invalid email format |
CONFLICT | Email already registered |
Login
Sign in with email and password.
POST /v1/auth/loginBody:
{
"email": "user@example.com",
"password": "securepassword123"
}Response: 200 OK
{
"data": {
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "rt_a1b2c3d4e5f6...",
"expires_in": 900,
"token_type": "Bearer"
}
}Errors:
| Code | Condition |
|---|---|
UNAUTHORIZED | Invalid email or password (generic message — does not reveal which field is wrong) |
EMAIL_UNVERIFIED | Account exists but email not verified |
ACCOUNT_LOCKED | Too many failed attempts (5 in 15 minutes). Locked for 30 minutes. |
Refresh Token
Exchange a valid refresh token for a new access token and refresh token.
POST /v1/auth/refreshBody:
{
"refresh_token": "rt_a1b2c3d4e5f6..."
}Response: 200 OK
{
"data": {
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "rt_new_token...",
"expires_in": 900
}
}WARNING
The old refresh token is immediately invalidated after rotation. Using it again returns UNAUTHORIZED.
Logout
Revoke the current session.
POST /v1/auth/logout
Authorization: Bearer <jwt>Response: 204 No Content
Email Verification
Verify Email
POST /v1/auth/verify-emailBody:
{
"token": "verification_token_from_email"
}Resend Verification Email
POST /v1/auth/resend-verificationBody:
{
"email": "user@example.com"
}Password Management
Forgot Password
Send a password reset email (link expires after 1 hour).
POST /v1/auth/forgot-passwordBody:
{
"email": "user@example.com"
}Reset Password
POST /v1/auth/reset-passwordBody:
{
"token": "reset_token_from_email",
"password": "new_secure_password"
}Change Password
Requires the current password.
POST /v1/auth/change-password
Authorization: Bearer <jwt>Body:
{
"current_password": "old_password",
"new_password": "new_secure_password"
}Device Code Flow
Used by NexusIDE and NexusCore CLI to authenticate without a browser-based login form.
Step 1: Request Device Code
POST /v1/auth/device/codeBody:
{
"client_type": "ide"
}client_type can be "ide" or "cli".
Response: 200 OK
{
"data": {
"device_code": "dc_a1b2c3d4e5f6...",
"user_code": "ABCD-1234",
"verification_url": "https://nexus-suite.dev/device",
"expires_in": 900,
"interval": 5
}
}Step 2: User Authorizes in Browser
The user opens verification_url in a browser, enters the user_code, and signs in.
Step 3: Poll for Authorization
The client polls at the specified interval (minimum 5 seconds):
GET /v1/auth/device/poll/:device_codeResponse (pending):
{
"data": { "status": "pending" }
}Response (authorized):
{
"data": {
"status": "authorized",
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "rt_a1b2c3d4e5f6..."
}
}Response (expired):
{
"error": {
"code": "UNAUTHORIZED",
"message": "expired_token"
}
}Step 4: Authorize a Device Code (Browser Side)
Called by the Portal when the user approves the device code:
POST /v1/auth/device/authorize
Authorization: Bearer <jwt>Body:
{
"user_code": "ABCD-1234"
}OAuth
Google
GET /v1/auth/oauth/googleRedirects to Google's authorization endpoint with PKCE.
Google Callback
GET /v1/auth/oauth/google/callback?code=...&state=...Processes the OAuth callback. Creates or links the account and redirects to the Portal dashboard.
GitHub
GET /v1/auth/oauth/githubRedirects to GitHub's authorization endpoint.
GitHub Callback
GET /v1/auth/oauth/github/callback?code=...&state=...Link OAuth Provider
Link an additional OAuth provider to an existing account.
POST /v1/auth/link-oauth/:provider
Authorization: Bearer <jwt>provider can be google or github.
Session Management
List Active Sessions
GET /v1/auth/sessions
Authorization: Bearer <jwt>Response:
{
"data": {
"sessions": [
{
"id": "sess_abc123",
"device_type": "ide",
"device_name": "NexusIDE on MacBook Pro",
"ip_address": "203.0.113.42",
"location": "San Francisco, CA",
"last_active_at": "2024-01-15T10:30:00Z",
"created_at": "2024-01-10T08:00:00Z"
}
]
}
}Revoke a Session
DELETE /v1/auth/sessions/:id
Authorization: Bearer <jwt>Revoke All Sessions (Except Current)
DELETE /v1/auth/sessions
Authorization: Bearer <jwt>User Profile
Get Current User
GET /v1/auth/me
Authorization: Bearer <jwt>Response:
{
"data": {
"id": "usr_abc123",
"email": "user@example.com",
"display_name": "Jane Developer",
"tier": "pro",
"email_verified": true,
"trial_eligible": false,
"created_at": "2024-01-01T00:00:00Z"
}
}Update Profile
PATCH /v1/auth/me
Authorization: Bearer <jwt>Body:
{
"display_name": "Jane Developer"
}Delete Account
DELETE /v1/auth/me
Authorization: Bearer <jwt>DANGER
This permanently deletes the account, revokes all sessions, and cancels any active subscription. This action cannot be undone.
SSO (Studio Tier)
Configure SAML Identity Provider
POST /v1/auth/sso/configure
Authorization: Bearer <jwt>Requires Studio tier admin role.
Body:
{
"saml_metadata_url": "https://idp.example.com/metadata.xml"
}Initiate SSO Login
POST /v1/auth/sso/loginBody:
{
"org_slug": "my-organization"
}JWKS Endpoint
GET /v1/auth/.well-known/jwks.jsonReturns the public keys used to verify JWT signatures (RS256). Clients should cache this response with a 1-hour TTL.
Response:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "key-1",
"n": "...",
"e": "AQAB"
}
]
}JWT Payload Structure
{
"sub": "usr_abc123",
"email": "user@example.com",
"tier": "pro",
"org_id": null,
"role": null,
"iat": 1700000000,
"exp": 1700000900,
"iss": "https://api.nexus-suite.dev",
"aud": "nexus-suite"
}| Claim | Description |
|---|---|
sub | User ID |
email | User's email address |
tier | Subscription tier (core, pro, studio) |
org_id | Organization ID (Studio tier, null otherwise) |
role | Organization role (admin, member, or null) |
iat | Issued at timestamp |
exp | Expiration timestamp (15 minutes after issuance) |
iss | Issuer (https://api.nexus-suite.dev) |
aud | Audience (nexus-suite) |