Skip to main content

API-Documentation: OAuth 2.0

info

All standard OAuth APIs use sake_case for both request and response parameters to comply with the OAuth 2.0 specifications. The introspect API is specific to Liquid which alone uses camelCase.

tip

For web applications (React, Vue, Next.js, Node.js), you can use the official liquid-js-sdk package to call all Liquid APIs with zero boilerplate and full TypeScript support.

Official OAuth 2.0 Specifications & Core Concepts​

If you are new to OAuth 2.0, Liquid adheres strictly to official IETF standards and RFC specifications. Below are key references and guides to help you understand the core concepts:


Client Types: Confidential vs Public Clients​

Liquid supports two types of OAuth 2.0 clients:

  1. Confidential Clients (isPublic: false): Web applications running on backend servers (Node.js, Go, Python, etc.) capable of keeping a client_secret hidden from users.
  2. Public Clients (isPublic: true): Single Page Applications (React, Vue, Angular) or Native/Mobile apps running on user devices where secrets cannot be stored securely.
    • Authentication: Do not use a client_secret. Must use PKCE (RFC 7636) during authorization and token redemption.

Setting Up PKCE for Public Clients (SPAs / Mobile Apps)​

PKCE protects public clients against authorization code interception attacks.

PKCE Flow Summary​

  1. Generate Ephemeral Key Pair: Generate a cryptographically random code_verifier string (43-128 chars) and its SHA-256 hash (code_challenge).
  2. Authorize Request: Pass code_challenge and code_challenge_method=S256 to /oauth/authorize.
  3. Token Request: Redeem the authorization code at /oauth/token by passing code_verifier (without a client_secret).

Example: Generating PKCE in JavaScript (Web Crypto API)​

// 1. Generate code_verifier
function generateCodeVerifier(): string {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return btoa(String.fromCharCode.apply(null, Array.from(array)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}

// 2. Derive code_challenge (SHA-256)
async function generateCodeChallenge(verifier: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await window.crypto.subtle.digest("SHA-256", data);
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}

// Usage before redirecting to /oauth/authorize:
const verifier = generateCodeVerifier();
sessionStorage.setItem("pkce_code_verifier", verifier);
const challenge = await generateCodeChallenge(verifier);

// Redirect to Liquid authorize endpoint:
const authUrl = `/oauth/authorize?response_type=code` +
`&client_id=your_spa_client_id` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&code_challenge=${encodeURIComponent(challenge)}` +
`&code_challenge_method=S256`;

Client Authentication​

Access Token​


Exchange your client ID and secret for an access token.

Before You Start​

You can adjust the validity of the access token by changing the option oauth.access-token-lifetime to the intended number of seconds. By default, the validity is 1 hour.

URL​

POST /oauth/token

Request Body (Form Data)​

ParameterTypeDescriptionRequired / Optional
grant_typestringSet to "client_credentials"Required
client_idstringYour client ID.Required
client_secretstringYour client secret.Required
scopestringComma separated list of scopes.Required

Request Sample (JSON)​

{
"grant_type": "client_credentials",
"client_id": "application_client",
"client_secret": "super-secure-client-secret",
"scope": "client:profile:read"
}

Response Parameters​

ParameterTypeDescription
access_tokenstringThe access token.
token_typestringThe token type. Usually "Bearer".
expires_innumberToken expiry in seconds.

Response Sample​

{
"access_token": "cfb1983f82d187ac26598dca27aa3078236e645ccb18d77bdcba100d62a99231",
"token_type": "Bearer",
"expires_in": 3600
}

Introspect [Non-Standard OAuth]​


Verify delegated tokens.

Before You Start​

Any other microservice in your application apart from Liquid that need to provide an authentication to the frontend will use this API. For instance, let's say a chat service in your system needs to verify the user is authenticated to access your application. You will call this API with the Liquid access token and it would return the user information after verifying the authenticity of the token. The API also returns the scopes allowed for the token. Using all this information, you can choose to allow or disallow access to your chat API.

For microservices running on node, use the Liquid Node Authenticator. This will already use introspect API under the hood and makes your integration much easier.

For more information, see Understanding Access Control and Integrating with Other Microservices

URL​

POST /oauth/token/introspect

Request Body (Form Data)​

ParameterTypeDescriptionRequired / Optional
tokenstringToken of the user to be authenticated. (Acquired using authorization code grant)Required

Request Sample (JSON)​

{
"token": "7eb2b49801f4ff3bbce93531dcf559c75504013cc0d7c2d03c49a529dedf4d72"
}

Response Data Parameters​

ParameterTypeDescription
tokenInfoobjectInformation about the token.

Response Sample​

{
"ok": 1,
"data": {
"tokenInfo": {
"accessToken": "7eb2b49801f4ff3bbce93531dcf559c75504013cc0d7c2d03c49a529dedf4d72",
"accessTokenExpiresAt": "2023-09-26T18:54:14.821Z",
"scope": "delegated:all,admin:all",
"user": {
"_id": "507f1f77bcf86cd799439011",
"username": "john_doe",
"firstName": "John",
"lastName": "Doe",
"preferredLanguage": "en",
"role": "user",
"verified": true,
"followingCount": 1,
"followerCount": 0,
"isPrivate": false,
"email": "johndoe@example.com",
"emailVerified": true,
"isSubscribed": false,
"credits": 0,
"isActive": true,
"isBanned": false,
"isRestricted": false,
"deleted": false,
"createdAt": "2023-06-21T13:20:00.112Z",
"updatedAt": "2023-09-04T14:52:15.442Z",
"customLink": "https://custom.link",
"profilePicturePath": null
}
}
}
}

Delegated Authentication​

Get Authorization Code​


Get an authorization code after login.

Before You Start​

  • If you're using Liquid's built in login and signup UI, you will never need to access this API. Simply redirect to /login with the required params and you'll be redirected back to your application with the authorization code and state if everything goes well.
  • You can adjust the validity of the authorization code by changing the option oauth.authorization-code-lifetime to the intended number of seconds. By default, the validity is 5 minutes.
  • PKCE: For public clients (isPublic: true) or when oauth.authorization.require-pkce option is true, code_challenge and code_challenge_method are required.

URL​

POST /oauth/token

Request Body (Form Data)​

ParameterTypeDescriptionRequired / Optional
response_typestringSet to codeRequired
client_idstringYour client ID.Required
redirect_uristringAn authorized redirect URI.Required
statestringApplication state.Required
scopestringComma separated list of scopes.Required
code_challengestringPKCE code challenge (SHA-256 hash of code_verifier).Required for Public Clients / PKCE
code_challenge_methodstringPKCE code challenge method (S256 or plain).Required for Public Clients / PKCE

Request Sample (JSON)​

{
"response_type": "code",
"client_id": "application_client",
"code": "the_code",
"redirect_uri": "https://my-redirect.uri/path",
"state": "xxx-yyy-zzz",
"scope": "delegated:all",
"code_challenge": "E9Melhvh10-5gq_7BBNW6g3Jy3G2k_85n6_g1j45",
"code_challenge_method": "S256"
}

Response​

A redirect to your callback URL with code and state as URL parameters.

Access Token (From Authorization Code)​


Exchange authorization code for an access token.

Before You Start​

  • You can adjust the validity of the access token by changing the option oauth.access-token-lifetime to the intended number of seconds. By default, the validity is 1 hour.
  • You can adjust the validity of the refresh token by changing the option oauth.refresh-token-lifetime to the intended number of seconds. By default, the validity is 15 days.
  • Client Authentication: Confidential clients must present client_secret. Public clients using PKCE must present code_verifier.

URL​

POST /oauth/token

Request Body (Form Data)​

ParameterTypeDescriptionRequired / Optional
grant_typestringSet to authorization_codeRequired
codestringYour authorization codeRequired
client_idstringYour client ID.Required
client_secretstringYour client secret.Required for Confidential Clients
code_verifierstringPKCE code verifier string generated during authorize.Required for Public Clients / PKCE
redirect_uristringAn authorized redirect URI.Required
scopestringComma separated list of scopes.Required

Request Sample (JSON)​

{
"grant_type": "authorization_code",
"client_id": "application_client",
"client_secret": "super-secure-client-secret",
"code": "the_code",
"redirect_uri": "https://my-redirect.uri/path",
"scope": "delegated:all"
}

Response Parameters​

ParameterTypeDescription
access_tokenstringThe access token.
token_typestringThe token type. Usually "Bearer".
expires_innumberToken expiry in seconds.
refresh_tokenstringThe refresh token.

Response Sample​

{
"access_token": "7eb2b49801f4ff3bbce93531dcf559c75504013cc0d7c2d03c49a529dedf4d72",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "95068fcd6ed814ebd14981903c280dce0ff86bc794c1121cc1b10be4fd588c7a"
}

Access Token (From Refresh Token)​


Exchange a refresh token to get an access token.

URL​

POST /oauth/token

Request Body (Form Data)​

ParameterTypeDescriptionRequired / Optional
grant_typestringSet to refresh_token.Required
refresh_tokenstringThe refresh token.Required
client_idstringYour client ID.Required

Request Sample (JSON)​

{
"grant_type": "refresh_token",
"client_id": "application_client",
"refresh_token": "7eb2b49801f4ff3bbce93531dcf559c75504013cc0d7c2d03c49a529dedf4d72"
}

Response Parameters​

ParameterTypeDescription
access_tokenstringThe access token.
token_typestringThe token type. Usually "Bearer".
expires_innumberToken expiry in seconds.
refresh_tokenstringThe refresh token.

Response Sample​

{
"access_token": "7eb2b49801f4ff3bbce93531dcf559c75504013cc0d7c2d03c49a529dedf4d72",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "95068fcd6ed814ebd14981903c280dce0ff86bc794c1121cc1b10be4fd588c7a"
}