Skip to main content
Fluxer supports OAuth2 for third-party application authorization, allowing users to grant limited access to their accounts without sharing credentials.

OAuth2 Scopes

Scopes define what resources an application can access:
export type OAuth2Scope = 
  | 'identify'     // Basic user information
  | 'email'        // User email address
  | 'guilds'       // User's guilds
  | 'connections'  // User's connections
  | 'bot'          // Add bot to guilds
  | 'admin';       // Administrative access

Scope Descriptions

Basic user informationAccess to:
  • User ID
  • Username
  • Avatar
  • Discriminator
  • Public flags
{
  "id": "123456789",
  "username": "user",
  "avatar": "a_1234567890abcdef",
  "discriminator": "0001",
  "public_flags": 0
}
User email addressIncludes:
  • Email address
  • Verification status
{
  "email": "[email protected]",
  "verified": true
}
Requires identify scope
User’s guildsAccess to:
  • Guild list
  • Guild names
  • Guild icons
  • User’s permissions
[
  {
    "id": "987654321",
    "name": "My Server",
    "icon": "1234567890abcdef",
    "owner": false,
    "permissions": "2147483647"
  }
]
User’s connected accountsAccess to:
  • Connected services (GitHub, Twitter, etc.)
  • Connection metadata
[
  {
    "type": "github",
    "id": "12345",
    "name": "username",
    "verified": true
  }
]
Add bot to guildsAllows:
  • Adding the bot to guilds the user has MANAGE_GUILD permission in
  • Joining guilds via invite links
This scope is only valid for bot applications
Administrative accessFull access to:
  • All user data
  • Administrative endpoints
  • Sensitive operations
This scope should only be granted to fully trusted applications

Authorization Flow

1. Authorization URL

Redirect users to the authorization endpoint:
import { authorizeUrl } from '@fluxer/oauth2';

const authUrl = authorizeUrl({
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
  redirectUri: 'https://yourapp.com/callback',
  authorizeEndpoint: 'https://fluxer.app/oauth2/authorize',
  tokenEndpoint: 'https://fluxer.app/api/oauth2/token',
  scope: 'identify email guilds'
}, state);

// Redirect user to authUrl
window.location.href = authUrl;
Authorization URL Format:
https://fluxer.app/oauth2/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://yourapp.com/callback&
  scope=identify+email+guilds&
  state=random_state_string

2. Authorization Grant

User approves the authorization request. Fluxer redirects to your redirect_uri:
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=random_state_string

3. Token Exchange

Exchange the authorization code for an access token:
const response = await fetch('https://fluxer.app/api/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: 'AUTHORIZATION_CODE',
    redirect_uri: 'https://yourapp.com/callback',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET'
  })
});

const data = await response.json();
Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "scope": "identify email guilds"
}

4. Accessing Resources

Use the access token to make API requests:
const response = await fetch('https://fluxer.app/api/users/@me', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

const user = await response.json();

Token Refresh

Access tokens expire after 7 days. Use the refresh token to obtain a new access token:
const response = await fetch('https://fluxer.app/api/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    grant_type: 'refresh_token',
    refresh_token: 'YOUR_REFRESH_TOKEN',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET'
  })
});

const data = await response.json();
Response:
{
  "access_token": "new_access_token",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": "new_refresh_token",
  "scope": "identify email guilds"
}

State Parameter

The state parameter prevents CSRF attacks:
import { generateState } from '@fluxer/oauth2';

// Generate random state
const state = generateState();
// Store state in session
session.oauthState = state;

// Include in authorization URL
const authUrl = authorizeUrl(config, state);

// Verify state on callback
if (req.query.state !== session.oauthState) {
  throw new Error('Invalid state parameter');
}

Error Handling

Authorization Errors

Fluxer redirects to your redirect_uri with error parameters:
https://yourapp.com/callback?
  error=access_denied&
  error_description=The+user+denied+access
Error Codes:
CodeDescription
invalid_requestMissing or invalid parameters
unauthorized_clientClient not authorized for this grant type
access_deniedUser denied the authorization request
unsupported_response_typeAuthorization server doesn’t support this response type
invalid_scopeRequested scope is invalid or unknown
server_errorInternal server error
temporarily_unavailableAuthorization server is temporarily unavailable

Token Errors

{
  "error": "invalid_grant",
  "error_description": "Invalid authorization code"
}
HTTP Status: 400 Bad Request

Scope Validation

Validate that your token has required scopes:
function hasScope(token: string, requiredScope: string): boolean {
  const decoded = jwt.decode(token);
  const scopes = decoded.scope.split(' ');
  return scopes.includes(requiredScope);
}

if (!hasScope(accessToken, 'email')) {
  throw new Error('Missing email scope');
}

Rate Limits

OAuth2 endpoints have specific rate limits:
EndpointLimitWindow
/oauth2/authorize1010 seconds
/oauth2/token1010 seconds
/oauth2/revoke1010 seconds
See Rate Limiting for details.

Bot Applications

Bot Scope

Bot applications require the bot scope:
https://fluxer.app/oauth2/authorize?
  response_type=code&
  client_id=BOT_CLIENT_ID&
  scope=bot&
  permissions=8&
  guild_id=123456789
Parameters:
scope
string
required
Must include bot
permissions
string
Bitwise permission value to request (optional)
guild_id
string
Pre-select a guild for the user (optional)

Bot Token Format

Bot tokens use a different format:
Bot YOUR_BOT_TOKEN
Use this in the Authorization header:
const response = await fetch('https://fluxer.app/api/guilds/123456789', {
  headers: {
    'Authorization': `Bot ${botToken}`
  }
});

Security Best Practices

Never Expose Client Secret

Keep your client secret secure. Never include it in client-side code or version control.

Validate State Parameter

Always validate the state parameter to prevent CSRF attacks.

Use HTTPS

Only use OAuth2 over HTTPS. Never send credentials over unencrypted connections.

Minimal Scopes

Request only the scopes your application actually needs.

Token Storage

Store tokens securely. Use encryption for sensitive storage (databases, cookies).

Token Rotation

Rotate refresh tokens regularly and invalidate old tokens when new ones are issued.

OAuth2 Errors

Missing Scope Error

import { MissingOAuthScopeError } from '@fluxer/errors';

try {
  // Attempt operation requiring 'email' scope
} catch (error) {
  if (error instanceof MissingOAuthScopeError) {
    console.log('Missing required OAuth2 scope');
  }
}
API Response:
{
  "code": "MISSING_OAUTH_SCOPE",
  "message": "Missing required OAuth2 scope"
}
HTTP Status: 403 Forbidden

Client Credentials Flow

For server-to-server authentication:
const response = await fetch('https://fluxer.app/api/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    grant_type: 'client_credentials',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    scope: 'bot'
  })
});
Client credentials flow is typically used for bot applications making API requests without user context.

Build docs developers (and LLMs) love