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:
| 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": "user@example.com",
"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:
| 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": "user@example.com",
"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:
Response: 200 OK
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:
Request Body:
Response: 200 OK
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:
Response: 200 OK
{
"id": "01912345-6789-7abc-def0-123456789abc",
"email": "user@example.com",
"username": "inkuser",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:30:00Z",
"oauthAccounts": [
{
"provider": "google",
"providerUserId": "117382948271639",
"email": "user@gmail.com",
"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:
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": "user@example.com",
"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:
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:
Response: 200 OK
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:
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:
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