LxBlog
/Docs

Getting Started

Authentication

OAuth 2.0 Authorization Code flow

Overview

LxBlog uses the OAuth 2.0 Authorization Code flow to authenticate third-party applications. This is the industry-standard protocol for delegated authorization — your application requests permission from a LxBlog user, and upon approval, receives tokens to make API calls on their behalf.

For single-page applications (SPAs) and mobile clients that cannot securely store a client secret, LxBlog supports the Proof Key for Code Exchange (PKCE) extension, which replaces the client secret with a dynamically generated code verifier.

Authorization flow

The OAuth 2.0 Authorization Code flow involves the following steps:

  1. Redirect to authorize — Your application redirects the user to the LxBlog authorization endpoint with the required parameters.
  2. User grants permission — The user reviews the requested scopes, selects which blog to authorize, and approves the request.
  3. Receive authorization code — LxBlog redirects back to your redirect_uri with a short-lived authorization code.
  4. Exchange code for tokens — Your server exchanges the authorization code for an access token and refresh token.
  5. Use access token — Include the access token in the Authorization header of API requests.
Authorization Code Flow
┌──────────┐                              ┌──────────┐                       ┌──────────┐
│          │  1. Redirect to /authorize   │          │                       │          │
│   Your   │ ───────────────────────────> │  LxBlog  │                       │  LxBlog  │
│   App    │                              │  Auth    │  2. User approves     │  API     │
│          │  3. Redirect with code       │  Server  │     & selects blog    │          │
│          │ <─────────────────────────── │          │                       │          │
│          │                              │          │                       │          │
│          │  4. Exchange code for tokens │          │                       │          │
│          │ ───────────────────────────> │          │                       │          │
│          │  ← Access + Refresh tokens   │          │                       │          │
│          │                              │          │                       │          │
│          │  5. API request with token   │          │                       │          │
│          │ ─────────────────────────────────────────────────────────────> │          │
│          │  ← API response              │          │                       │          │
└──────────┘                              └──────────┘                       └──────────┘

Step 1: Request authorization

Redirect the user to the LxBlog authorization endpoint with the following query parameters:

GET/api/oauth/authorize

Redirect the user to this URL to begin the authorization flow.

Query parameters

NameTypeDescription
client_id*stringYour application's client ID.
redirect_uri*stringThe URL to redirect to after authorization. Must match a registered redirect URI.
response_type*stringMust be "code" for the Authorization Code flow.
scope*stringSpace-separated list of scopes your app requires.
statestringAn opaque value used to prevent CSRF attacks. Recommended.
code_challengestringPKCE code challenge. Required when using PKCE.
code_challenge_methodstringMust be "S256" when using PKCE.
Example authorization URL
https://lxblog.app/api/oauth/authorize?
  client_id=your_client_id
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=articles:read blog:read
  &state=random_state_value

Step 4: Exchange code for tokens

POST/api/oauth/token

Exchange an authorization code for an access token and refresh token.

Request body (application/x-www-form-urlencoded)

NameTypeDescription
grant_type*stringMust be "authorization_code".
code*stringThe authorization code received from the callback.
redirect_uri*stringMust match the redirect_uri used in the authorization request.
client_id*stringYour application's client ID.
client_secretstringYour application's client secret. Not required when using PKCE.
code_verifierstringThe PKCE code verifier. Required when a code_challenge was used.
Token exchange request
curl -X POST https://lxblog.app/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTH_CODE_FROM_CALLBACK" \
  -d "redirect_uri=https://yourapp.com/callback" \
  -d "client_id=your_client_id" \
  -d "client_secret=your_client_secret"
Token response
{
  "access_token": "lxb_at_xxxxxxxxxxxxxxxxxxxx",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "lxb_rt_xxxxxxxxxxxxxxxxxxxx",
  "scope": "articles:read blog:read"
}

Scopes

Scopes define the level of access your application requests. Users see the requested scopes during the authorization step and must approve them. Request only the scopes your application needs.

Available scopes

NameTypeDescription
articles:readscopeList, get, and sync published articles.
blog:readscopeRead blog metadata such as name, description, and configuration.

Token lifecycle

LxBlog issues two types of tokens, each with a distinct purpose and expiration:

  • Access tokens (lxb_at_ prefix) — Used to authenticate API requests. Expire after 1 hour (3600 seconds).
  • Refresh tokens (lxb_rt_ prefix) — Used to obtain a new access token without re-prompting the user. Expire after 30 days.

Refresh token rotation

LxBlog enforces mandatory refresh token rotation. Each time you use a refresh token to obtain a new access token, the API returns a new refresh token and immediately invalidates the previous one. This limits the damage if a refresh token is ever compromised.

Always store the new refresh token. After each token refresh, you must persist the newly returned refresh token. If you lose it or continue using the old one, the user will need to re-authorize your application. Store refresh tokens securely — never expose them in client-side code, URLs, or logs.

POST/api/oauth/token

Refresh an expired access token using a refresh token.

Request body

NameTypeDescription
grant_type*stringMust be "refresh_token".
refresh_token*stringThe current refresh token.
client_id*stringYour application's client ID.
client_secretstringYour application's client secret. Not required for PKCE clients.
Refresh token request
curl -X POST https://lxblog.app/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=lxb_rt_current_refresh_token" \
  -d "client_id=your_client_id" \
  -d "client_secret=your_client_secret"

PKCE for public clients

If your application cannot securely store a client secret (such as a single-page application or mobile app), use the PKCE extension. Instead of a client secret, your app generates a random code_verifier and derives a code_challenge from it.

PKCE implementation
// 1. Generate a random code verifier
const codeVerifier = generateRandomString(128);

// 2. Derive the code challenge (SHA-256, base64url-encoded)
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await crypto.subtle.digest('SHA-256', data);
const codeChallenge = base64UrlEncode(digest);

// 3. Include in the authorization URL
const authUrl = new URL('https://lxblog.app/api/oauth/authorize');
authUrl.searchParams.set('client_id', 'your_client_id');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'articles:read');
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');

// 4. When exchanging the code, include the code_verifier
//    instead of client_secret

The code_challenge_method must always be S256. LxBlog does not support the plain method, as it does not provide meaningful security benefits.

Token prefixes

All LxBlog tokens use prefixes to make them easy to identify and to help prevent accidental misuse:

  • lxb_at_ — Access tokens
  • lxb_rt_ — Refresh tokens

These prefixes make it easier to identify token types in your code and allow secret scanning tools (such as GitHub's) to automatically detect and flag leaked credentials.

Error codes

The OAuth endpoints return standard error codes in the JSON response body when a request fails. The response will include an error field and an optional error_description field with additional details.

OAuth error codes

NameTypeDescription
invalid_granterrorThe authorization code has expired, has already been used, or the refresh token is invalid or expired.
invalid_clienterrorThe client_id is not recognized, or the client_secret does not match.
invalid_scopeerrorOne or more of the requested scopes are not valid or not available for this application.
access_deniederrorThe user denied the authorization request, or the application does not have permission to access the requested resource.
Example error response
{
  "error": "invalid_grant",
  "error_description": "The authorization code has expired."
}