Session Tokens
Session tokens are JWT (JSON Web Token) tokens issued by the Reeflow API that enable secure data queries for end users in embedded analytics contexts.
These tokens provide a secure way to authenticate users when they interact with embedded Reeflow components like dashboards and tables.
What are session tokens?
Section titled “What are session tokens?”Session tokens serve as temporary authentication credentials that:
- Enable data access: Allow embedded Reeflow components to query data on behalf of end users
- Provide security: Use JWT standard with cryptographic signatures for tamper-proof authentication
- Support multi-tenancy: Include organization context for proper data isolation
- Have limited lifetime: Expire automatically to minimize security risks (default: 1 hour, max: 24 hours)
When to use session tokens
Section titled “When to use session tokens”Session tokens should be created by your backend server and provided to Reeflow Elements when:
- Embedding analytics: Users interact with Reeflow dashboards, tables, or charts in your application
- Server-to-server queries: Your backend needs to query data on behalf of specific users
- Temporary access: You need time-limited access tokens for enhanced security
Important: Always create session tokens on your backend server, never in client-side code, to keep your API credentials secure.
Creating session tokens
Section titled “Creating session tokens”To create a session token, make a POST request to /sessions with user information.
Request format
Section titled “Request format”interface CreateSessionRequest { user: { id: string; // Required: User ID (max 64 chars) }; organization?: { id: string; // Optional: Organization ID (max 64 chars) }; expiration?: string; // Optional: ISO8601 date (default: 1 hour, max: 24 hours) not_before?: string; // Optional: ISO8601 date (token not valid before this time)}from typing import Optionalfrom datetime import datetime
class CreateSessionRequest: def __init__( self, user_id: str, organization_id: Optional[str] = None, expiration: Optional[datetime] = None, not_before: Optional[datetime] = None ): self.user = {"id": user_id} if organization_id: self.organization = {"id": organization_id} if expiration: self.expiration = expiration.isoformat() if not_before: self.not_before = not_before.isoformat()Code examples
Section titled “Code examples”Here’s how to create session tokens using the authenticated API:
import { callApi } from './hmac-client';
/** * Creates a session token for a user to access embedded analytics. * Should be called from your backend server, not client-side code. * @param userId - The ID of the user for whom to create the session * @param organizationId - Optional organization ID for multi-tenant isolation * @param expirationHours - Hours until token expires (default: 1, max: 24) * @returns Promise resolving to the session token response */async function createSessionToken( userId: string, organizationId?: string, expirationHours: number = 1,) { // Calculate expiration time const expiration = new Date(); expiration.setHours(expiration.getHours() + expirationHours);
const requestBody = { user: { id: userId }, ...(organizationId && { organization: { id: organizationId } }), expiration: expiration.toISOString(), };
const response = await callApi({ baseUrl: process.env.REEFLOW_API_BASE || 'http://localhost:3001', pathWithQuery: '/sessions', method: 'POST', apiKeyId: process.env.API_KEY_ID!, apiKeySecret: process.env.API_KEY_SECRET!, body: requestBody, });
return response.token;}
// Example usageconst sessionToken = await createSessionToken( 'user_12345', 'org_67890', 2, // 2 hours);console.log('Session token:', sessionToken);import osimport jsonimport requestsfrom datetime import datetime, timedeltafrom urllib.parse import urljoinfrom hmac_client import build_canonical_string, sign
def create_session_token( user_id: str, organization_id: str = None, expiration_hours: int = 1) -> str: """ Creates a session token for a user to access embedded analytics. Should be called from your backend server, not client-side code.
Args: user_id: The ID of the user for whom to create the session organization_id: Optional organization ID for multi-tenant isolation expiration_hours: Hours until token expires (default: 1, max: 24)
Returns: The JWT session token string """ # Calculate expiration time expiration = datetime.utcnow() + timedelta(hours=expiration_hours)
# Build request body request_body = { 'user': {'id': user_id}, 'expiration': expiration.isoformat() + 'Z' }
if organization_id: request_body['organization'] = {'id': organization_id}
# Prepare API request method = 'POST' path_with_query = '/sessions' timestamp = str(int(time.time())) content_type = 'application/json' raw_body = json.dumps(request_body, separators=(',', ':'))
# Generate HMAC signature canonical = build_canonical_string(method, path_with_query, timestamp, content_type, raw_body) signature = sign(os.environ['API_KEY_SECRET'], canonical)
# Make authenticated request base_url = os.environ.get('REEFLOW_API_BASE', 'http://localhost:3001') url = urljoin(base_url, path_with_query)
response = requests.post( url, headers={ 'X-API-Key': os.environ['API_KEY_ID'], 'X-API-Timestamp': timestamp, 'X-API-Signature': signature, 'Content-Type': content_type, }, data=raw_body )
response.raise_for_status() return response.json()['token']
# Example usagesession_token = create_session_token( user_id='user_12345', organization_id='org_67890', expiration_hours=2 # 2 hours)print(f'Session token: {session_token}')Response format
Section titled “Response format”The API returns a JSON object containing the JWT token:
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}JWT token structure
Section titled “JWT token structure”The issued JWT contains these standard and custom claims:
| Claim | Description | Source |
|---|---|---|
sub | Subject (user ID) | user.id from request |
iss | Issuer | JWT_ISSUER environment variable |
aud | Audience | JWT_AUDIENCE environment variable |
exp | Expiration time | Calculated from expiration or default (1 hour) |
iat | Issued at | Current timestamp |
jti | JWT ID | Unique session token ID with sess_ prefix |
organization_id | Organization ID (optional) | organization.id from request |
nbf | Not before (optional) | not_before from request |
Using session tokens
Section titled “Using session tokens”Once created, session tokens can be used to:
- Initialize Reeflow Elements: Pass the token to embedded dashboards, tables, and charts
- Make data queries: Use the token for authenticated
/queriesAPI requests - Access user-scoped data: Query data with proper user and organization context
Token security
Section titled “Token security”Session tokens contain sensitive authentication information and must be handled carefully to maintain security:
- Create tokens server-side only: Never generate session tokens in client-side code (browser JavaScript, mobile apps). Your API credentials must remain on your secure backend server. Client applications should request tokens from your backend endpoint, which then calls the Reeflow API.
- Use HTTPS for all communication: Session tokens should only be transmitted over encrypted HTTPS connections. This prevents token interception during transmission between your backend, client applications, and Reeflow services.
- Implement short expiration times: Use the shortest practical token lifetime for your use case. While tokens can be valid for up to 24 hours, shorter durations reduce the risk if a token is compromised.
- Generate unique tokens per user session: Each user session should receive its own token. Don’t share tokens between users or reuse tokens across different sessions, as this can lead to security vulnerabilities and audit trail confusion.
- Store tokens securely in client applications: In web applications, avoid storing tokens in
localStoragedue to XSS risks. Use secure,httpOnlycookies or in-memory storage. For mobile apps, use secure keychain/keystore APIs. Never log tokens or include them in error messages.
Error handling
Section titled “Error handling”Common error responses:
| Status | Error Code | Description |
|---|---|---|
| 400 | INVALID_EXPIRATION | Expiration time exceeds 24 hours |
| 400 | INVALID_NOT_BEFORE | Not before time is in the past |
| 400 | VALIDATION_ERROR | Invalid request format or missing required fields |
| 401 | UNAUTHORIZED | Invalid or missing API key/authentication |
| 500 | JWT_CONFIGURATION_ERROR | Server JWT configuration issue |
Example error response:
{ "code": "INVALID_EXPIRATION", "message": "Expiration cannot be more than 24 hours from now"}