Skip to content

OAuth API Reference

Complete reference for Flowsta OAuth 2.0 endpoints.

Base URL

https://auth-api.flowsta.com

All endpoints use this base URL.

Authentication

OAuth endpoints use different authentication methods:

EndpointAuthentication Method
/oauth/authorizeNone (public)
/oauth/tokenPKCE (client_id + code_verifier)
/oauth/userinfoBearer token (Authorization header)
/oauth/revokeClient ID header (X-Client-Id)

No Client Secret Required

Flowsta uses OAuth 2.0 with PKCE, which means you don't need a client secret. PKCE provides security for browser and mobile apps without exposing secrets in frontend code.


Authorization Endpoint

GET /oauth/authorize

Initiates the OAuth flow by redirecting the user to the Flowsta login page.

Parameters

ParameterTypeRequiredDescription
response_typestringMust be code
client_idstringYour application's client ID
redirect_uristringOne of your configured redirect URIs
scopestringSpace-separated scopes (see Available Scopes)
statestring⚠️CSRF protection token (highly recommended)
code_challengestring⚠️PKCE code challenge (highly recommended)
code_challenge_methodstring⚠️Must be S256 (if using PKCE)

Example Request

GET https://auth-api.flowsta.com/oauth/authorize?
  response_type=code&
  client_id=abc123...&
  redirect_uri=https://yourapp.com/auth/callback&
  scope=profile%20email&
  state=random_state_string&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256

Response

Redirect to login page:

If the user is not logged in:

https://login.flowsta.com/login?
  client_id=abc123...&
  redirect_uri=https://yourapp.com/auth/callback&
  scope=profile%20email&
  state=random_state_string&
  code_challenge=E9Melhoa...&
  code_challenge_method=S256

Redirect to callback:

After successful authentication and consent:

https://yourapp.com/auth/callback?
  code=def456...&
  state=random_state_string

Error Responses

If an error occurs, the user is redirected to redirect_uri with error parameters:

https://yourapp.com/auth/callback?
  error=invalid_request&
  error_description=Missing+required+parameter+client_id&
  state=random_state_string

Error Codes:

ErrorDescription
invalid_requestMissing or invalid required parameters
unauthorized_clientClient ID not found or app disabled
access_deniedUser denied authorization
unsupported_response_typeresponse_type is not code
invalid_scopeOne or more requested scopes are invalid
server_errorInternal server error

Token Endpoint

POST /oauth/token

Exchange authorization code for access token and refresh token.

Request Headers

http
Content-Type: application/json

Request Body

Grant Type: authorization_code

FieldTypeRequiredDescription
grant_typestringMust be authorization_code
codestringAuthorization code from callback
redirect_uristringMust match the original redirect URI
client_idstringYour application's client ID
code_verifierstringPKCE code verifier

PKCE Security

PKCE replaces client secrets for all clients. The code_verifier proves you initiated the authorization request without needing to store secrets.

Grant Type: refresh_token

FieldTypeRequiredDescription
grant_typestringMust be refresh_token
refresh_tokenstringThe refresh token
client_idstringYour application's client ID

Example Request (Authorization Code)

http
POST /oauth/token HTTP/1.1
Host: auth-api.flowsta.com
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "def456...",
  "redirect_uri": "https://yourapp.com/auth/callback",
  "client_id": "flowsta_app_abc123...",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}

Example Request (Refresh Token)

http
POST /oauth/token HTTP/1.1
Host: auth-api.flowsta.com
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "ghi012...",
  "client_id": "flowsta_app_abc123..."
}

Success Response

Status: 200 OK

json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "ghi012...",
  "scope": "profile email"
}
FieldTypeDescription
access_tokenstringJWT access token (valid for 1 hour)
token_typestringAlways Bearer
expires_innumberSeconds until token expires (3600 = 1 hour)
refresh_tokenstringRefresh token (valid for 30 days)
scopestringGranted scopes (may differ from requested)

Error Response

Status: 400 Bad Request

json
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired or is invalid"
}

Error Codes:

ErrorDescription
invalid_requestMissing required parameters
invalid_clientInvalid client credentials
invalid_grantInvalid or expired authorization code
unauthorized_clientClient not authorized for this grant type
unsupported_grant_typeGrant type not supported

User Info Endpoint

GET /oauth/userinfo

Retrieve user profile information using an access token.

Request Headers

http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Example Request

http
GET /oauth/userinfo HTTP/1.1
Host: auth-api.flowsta.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Success Response

Status: 200 OK

With openid display_name scopes only:

json
{
  "sub": "550e8400-e29b-41d4-a716-446655440000",
  "name": "John Doe"
}

With all scopes (and user has granted permission):

json
{
  "sub": "550e8400-e29b-41d4-a716-446655440000",
  "name": "John Doe",
  "preferred_username": "johndoe",
  "did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "agent_pub_key": "uhCAk7JpEWfkiV_RdAFfCnRZcJ9PwJR4yTLN-E3EcVU7KYCnRRZc",
  "profile_picture": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0...",
  "has_custom_picture": true,
  "email": "john@example.com",
  "email_verified": true
}

Email Requires Permission

The email field is only included if:

  1. The access token has email scope
  2. The user has granted email permission for your app

If permission is not granted, email and emailVerified will not be present.

FieldTypeScopeDescription
substring (UUID)openidUnique user identifier (always included)
namestringdisplay_nameUser's display name
preferred_usernamestringusernameUsername (if set by user)
didstringdidW3C Decentralized Identifier
agent_pub_keystringpublic_keyHolochain agent public key
profile_picturestringprofile_pictureProfile picture (base64 data URI or URL)
has_custom_picturebooleanprofile_pictureWhether user uploaded a custom picture
emailstringemailEmail address (if permitted)
email_verifiedbooleanemailEmail verification status

Error Response

Status: 401 Unauthorized

json
{
  "error": "invalid_token",
  "error_description": "Access token is invalid or expired"
}

Error Codes:

ErrorDescription
invalid_tokenToken is invalid, expired, or revoked
insufficient_scopeToken doesn't have required scopes

Token Revocation Endpoint

POST /oauth/revoke

Revoke a refresh token to logout the user.

Request Headers

http
Content-Type: application/json
X-Client-Id: abc123...

Request Body

FieldTypeRequiredDescription
tokenstringThe refresh token to revoke
token_type_hintstringMust be refresh_token

Example Request

http
POST /oauth/revoke HTTP/1.1
Host: auth-api.flowsta.com
Content-Type: application/json
X-Client-Id: abc123...

{
  "token": "ghi012...",
  "token_type_hint": "refresh_token"
}

Success Response

Status: 200 OK

json
{
  "message": "Token revoked successfully"
}

Error Response

Status: 400 Bad Request

json
{
  "error": "invalid_request",
  "error_description": "Token parameter is required"
}

PKCE (Proof Key for Code Exchange)

What is PKCE?

PKCE enhances OAuth security by preventing authorization code interception attacks. It's especially important for public clients (SPAs, mobile apps).

How PKCE Works

  1. Generate code verifier (random 43-128 character string)
  2. Generate code challenge (SHA256 hash of verifier, base64url encoded)
  3. Send code challenge with authorization request
  4. Send code verifier with token exchange request
  5. Server verifies that challenge matches verifier

Implementation

javascript
import crypto from 'crypto';

// 1. Generate code verifier
function generateCodeVerifier() {
  return crypto.randomBytes(32).toString('base64url');
}

// 2. Generate code challenge
function generateCodeChallenge(verifier) {
  return crypto
    .createHash('sha256')
    .update(verifier)
    .digest('base64url');
}

// Usage
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);

// Store verifier in session for later use
sessionStorage.setItem('code_verifier', codeVerifier);

// Use challenge in authorization request
const authUrl = `https://auth-api.flowsta.com/oauth/authorize?
  response_type=code&
  client_id=${clientId}&
  redirect_uri=${redirectUri}&
  scope=openid%20display_name%20email%20profile_picture&
  code_challenge=${codeChallenge}&
  code_challenge_method=S256`;

Button Widget Handles PKCE

The @flowsta/login-button package automatically handles PKCE for you. You don't need to implement it manually.


Rate Limiting

OAuth endpoints are rate-limited to prevent abuse:

EndpointRate Limit
/oauth/authorize100 requests per minute per IP
/oauth/token50 requests per minute per client
/oauth/userinfo500 requests per minute per token
/oauth/revoke50 requests per minute per client

Rate Limit Headers:

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1699564800

429 Too Many Requests Response:

json
{
  "error": "rate_limit_exceeded",
  "error_description": "Too many requests. Please try again later.",
  "retry_after": 60
}

Token Lifetimes

Token TypeLifetimeRenewable?
Authorization Code10 minutes❌ No
Access Token1 hour❌ No (use refresh token)
Refresh Token30 days✅ Yes (sliding window)

Refresh Token Sliding Window

When you use a refresh token to get a new access token, the refresh token's expiration extends by 30 days from the time of use.

Example:

  • Day 1: Issue refresh token (expires Day 31)
  • Day 20: Use refresh token → expires Day 50
  • Day 45: Use refresh token → expires Day 75

Scopes

Available Scopes

Flowsta uses granular scopes so users only consent to the specific data your app needs:

ScopeData IncludedDescription
openidsubUser ID (UUID) - auto-included
display_namenameUser's display name
usernamepreferred_usernameUser's @username
emailemail, email_verifiedEmail address
profile_pictureprofile_picture, has_custom_pictureProfile picture
diddidW3C Decentralized Identifier
public_keyagent_pub_keyHolochain agent public key

Request Only What You Need

Instead of requesting all scopes, choose only the ones your app actually needs. This builds user trust and simplifies the consent screen.

Requesting Multiple Scopes

Separate scopes with spaces:

scope=openid display_name email profile_picture

URL encoded:

scope=openid%20display_name%20email%20profile_picture

Scope Downgrading

If a user hasn't granted permission for a requested scope, the token will be issued with reduced scopes.

Request: scope=openid display_name email
User hasn't granted email permission
Token issued with: scope=openid display_name

Always check the scope field in the token response to see what was actually granted.


Security Best Practices

1. Always Use PKCE

javascript
// ✅ Good: With PKCE
const authUrl = buildAuthUrl({
  code_challenge: codeChallenge,
  code_challenge_method: 'S256'
});

// ❌ Bad: Without PKCE
const authUrl = buildAuthUrl({});

2. Always Use State Parameter

javascript
// ✅ Good: With state for CSRF protection
const state = crypto.randomBytes(16).toString('hex');
sessionStorage.setItem('oauth_state', state);

// ❌ Bad: Without state
const authUrl = buildAuthUrl({ /* no state */ });

3. Verify State in Callback

javascript
// ✅ Good: Verify state
const callbackState = new URLSearchParams(window.location.search).get('state');
const storedState = sessionStorage.getItem('oauth_state');

if (callbackState !== storedState) {
  throw new Error('Possible CSRF attack');
}

4. Use HTTPS

All redirect URIs must use HTTPS in production:

javascript
// ✅ Good
redirect_uri: 'https://yourapp.com/callback'

// ❌ Bad (except localhost)
redirect_uri: 'http://yourapp.com/callback'

Testing

Test Credentials

Use these test values during development:

javascript
// Development
const clientId = 'test_client_id';
const redirectUri = 'http://localhost:3000/callback';

Test Users

Create test accounts at dev.flowsta.com for testing.

Test OAuth Flow Button

In your app dashboard, use the "Test OAuth Flow" button to verify your configuration.


Need Help?

Documentation licensed under CC BY-SA 4.0.