Skip to content

Authentication API

All authentication endpoints live under the /auth prefix. These handle user registration, login, OAuth, session management, and profile updates.

Endpoints


POST /auth/register

Create a new user account.

Request Body:

{
  "email": "[email protected]",
  "username": "inkuser",
  "password": "securePassword123"
}
Field Type Required Description
email string Yes Valid email address
username string Yes Unique username
password string Yes Minimum 8 characters

Response: 201 Created

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "user": {
    "id": "01912345-6789-7abc-def0-123456789abc",
    "email": "[email protected]",
    "username": "inkuser",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-01-15T10:30:00Z"
  }
}

Errors:

Code Cause
400 Missing or invalid fields
409 Email or username already taken

POST /auth/login

Authenticate with email or username and password.

Request Body:

{
  "identifier": "[email protected]",
  "password": "securePassword123"
}
Field Type Required Description
identifier string Yes Email address or username
password string Yes Account password

Response: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "user": {
    "id": "01912345-6789-7abc-def0-123456789abc",
    "email": "[email protected]",
    "username": "inkuser",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-01-15T10:30:00Z"
  }
}

Errors:

Code Cause
400 Missing fields
401 Invalid credentials

POST /auth/refresh

Exchange a valid refresh token for a new token pair. The old refresh token is invalidated.

Request Body:

{
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl..."
}

Response: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "bmV3IHJlZnJlc2ggdG9r..."
}

Errors:

Code Cause
401 Refresh token is invalid, expired, or already used

Token Rotation

Refresh tokens are single-use. Each call to this endpoint invalidates the previous refresh token and returns a new pair. If a refresh token is reused, all sessions for the user may be revoked as a security measure.


POST /auth/logout

:material-lock: Requires authentication

Revoke the provided refresh token, ending the session.

Request Headers:

Authorization: Bearer {accessToken}

Request Body:

{
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl..."
}

Response: 200 OK

{
  "message": "logged out"
}

Errors:

Code Cause
401 Missing or invalid access token

GET /auth/me

:material-lock: Requires authentication

Retrieve the authenticated user's profile, including linked OAuth accounts and subscription status.

Request Headers:

Authorization: Bearer {accessToken}

Response: 200 OK

{
  "id": "01912345-6789-7abc-def0-123456789abc",
  "email": "[email protected]",
  "username": "inkuser",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-01-15T10:30:00Z",
  "oauthAccounts": [
    {
      "provider": "google",
      "providerUserId": "117382948271639",
      "email": "[email protected]",
      "linkedAt": "2026-01-20T14:00:00Z"
    }
  ],
  "subscription": {
    "status": "active",
    "plan": "pro",
    "interval": "monthly",
    "currentPeriodEnd": "2026-02-15T10:30:00Z",
    "cancelAtPeriodEnd": false
  }
}

Tip

The subscription field is null if the user has never subscribed.

Errors:

Code Cause
401 Missing or invalid access token

PUT /auth/me

:material-lock: Requires authentication

Update the authenticated user's profile. All fields are optional --- only include fields you want to change.

Request Headers:

Authorization: Bearer {accessToken}

Request Body:

{
  "username": "newusername",
  "password": "newSecurePassword456",
  "currentPassword": "securePassword123"
}
Field Type Required Description
username string No New username
password string No New password (min 8 characters)
currentPassword string Conditional Required when changing password

Response: 200 OK

{
  "id": "01912345-6789-7abc-def0-123456789abc",
  "email": "[email protected]",
  "username": "newusername",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-01-16T08:00:00Z"
}

Errors:

Code Cause
400 Invalid fields or currentPassword missing when changing password
401 Missing or invalid access token
409 Username already taken

GET /auth/sessions

:material-lock: Requires authentication

List all active sessions for the authenticated user.

Request Headers:

Authorization: Bearer {accessToken}

Response: 200 OK

[
  {
    "id": "01912345-0000-7abc-def0-000000000001",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
    "ip": "192.168.1.100",
    "createdAt": "2026-01-15T10:30:00Z",
    "lastUsedAt": "2026-01-16T08:00:00Z"
  },
  {
    "id": "01912345-0000-7abc-def0-000000000002",
    "userAgent": "InkletDesktop/1.0",
    "ip": "10.0.0.50",
    "createdAt": "2026-01-14T09:00:00Z",
    "lastUsedAt": "2026-01-15T22:00:00Z"
  }
]

Errors:

Code Cause
401 Missing or invalid access token

DELETE /auth/sessions/{id}

:material-lock: Requires authentication

Revoke a specific session by its ID. This invalidates the refresh token associated with that session.

Path Parameters:

Parameter Description
id Session UUID

Request Headers:

Authorization: Bearer {accessToken}

Response: 200 OK

{
  "message": "session revoked"
}

Errors:

Code Cause
401 Missing or invalid access token
404 Session not found or does not belong to user

OAuth Endpoints

Inklet supports Sign in with Google and Sign in with Apple. The OAuth flow uses server-side redirects.

GET /auth/oauth/google

Initiate Google OAuth sign-in. The client query parameter determines which redirect URI to use.

Query Parameters:

Parameter Values Description
client web, desktop, ios Client platform initiating the flow

Example:

GET /auth/oauth/google?client=web

Response: 302 Found --- redirects to Google's OAuth consent screen.


GET /auth/oauth/google/callback

Handles the OAuth callback from Google. This endpoint is not called directly by clients --- Google redirects here after the user authorizes.

Behavior:

  • Creates a new account if the Google email is not registered
  • Links the Google account if the email matches an existing user
  • Returns tokens via redirect (with tokens as query parameters or fragment, depending on client)

GET /auth/oauth/apple

Initiate Apple OAuth sign-in.

Query Parameters:

Parameter Values Description
client web, desktop, ios Client platform initiating the flow

Example:

GET /auth/oauth/apple?client=web

Response: 302 Found --- redirects to Apple's OAuth consent screen.


POST /auth/oauth/apple/callback

Handles the OAuth callback from Apple. Apple uses form_post response mode, so this endpoint receives a POST with form-encoded data rather than query parameters.

Apple OAuth Differences

Unlike Google, Apple sends the callback as a POST request with application/x-www-form-urlencoded body. Apple also only provides the user's name on the first authorization --- subsequent sign-ins only include the email.

Behavior:

  • Creates a new account if the Apple email is not registered
  • Links the Apple account if the email matches an existing user
  • Returns tokens via redirect