Skip to main content

Overview

Grain Analytics supports three authentication strategies to fit different security needs. Choosing the right one depends on where your app runs and who can access it.
Starting out? Use NONE for development and demos. Switch to SERVER_SIDE or JWT for production.

Strategy Comparison

StrategyUse CaseSecurityComplexity
NONEPublic apps, demosLowSimple
SERVER_SIDEBackend/Node.jsHighMedium
JWTClient apps with authHighMedium

NONE Authentication

No authentication required. Events and configs are accessed without credentials.
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'NONE' // or omit (default)
});
When to use:
  • Public websites or landing pages
  • Development and testing
  • Apps without user authentication
Security note: Anyone with your tenant ID (the alias from your dashboard) can send events or read configurations. Don’t use for sensitive data.

SERVER_SIDE Authentication

Uses a secret key to authenticate requests. Best for backend applications where the secret can be kept secure.
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'SERVER_SIDE',
  secretKey: 'your-secret-key'
});
How it works: The SDK sends requests with an Authorization: Chase {SECRET} header. The server validates the secret before processing.

Getting Your Secret Key

  1. Go to grainql.com/dashboard
  2. Navigate to your tenant settings
  3. Generate or copy your secret key
  4. Store it securely (environment variable recommended)
// ✅ Good: Secret from environment
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'SERVER_SIDE',
  secretKey: process.env.GRAIN_SECRET_KEY
});

// ❌ Bad: Secret in client code
const grain = createGrainAnalytics({
  secretKey: 'sk_live_abc123...' // Never expose in client bundles!
});
When to use:
  • Node.js backends
  • Server-side API routes
  • Serverless functions
  • Any environment where secrets can be kept secure
Never include your secret key in client-side code. It will be visible in your bundle and anyone can use it.

JWT Authentication

Uses JSON Web Tokens for authentication. Perfect for client-side apps that already have user authentication.
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'JWT',
  authProvider: {
    async getToken() {
      // Return a valid JWT from your auth system
      return await getAccessToken();
    }
  }
});
How it works: Before each API request, the SDK calls getToken() to get a fresh JWT. The token is sent in the Authorization: Bearer {TOKEN} header.

Setting Up JWT Auth

Step 1: Configure JWT in Grain Dashboard Go to your tenant settings at grainql.com/dashboard and configure:
  • JWT issuer (e.g., your Auth0 domain)
  • JWT audience
  • Public key or JWKS endpoint
Step 2: Implement Auth Provider The auth provider is an object with a getToken method:
const authProvider = {
  async getToken() {
    // Get token from your auth system
    // This could be Auth0, next-auth, Firebase, etc.
    return await yourAuthSystem.getAccessToken();
  }
};
Step 3: Initialize SDK
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'JWT',
  authProvider
});

Common Auth Integrations

import { useAuth0 } from '@auth0/auth0-react';

function App() {
  const { getAccessTokenSilently } = useAuth0();

  const grain = createGrainAnalytics({
    tenantId: 'your-tenant-id',
    authStrategy: 'JWT',
    authProvider: {
      getToken: () => getAccessTokenSilently()
    }
  });

  // Use grain...
}

JWT User ID Requirements

When using JWT authentication, the user ID in events must match the JWT’s sub (subject) claim:
// JWT payload: { sub: "user_123", ... }

grain.setUserId('user_123'); // ✅ Matches JWT subject
grain.track('button_clicked', { button: 'submit' });

grain.setUserId('user_456'); // ❌ Doesn't match - will be rejected
Why this matters: This prevents users from impersonating others. The server validates that the user ID in events matches the authenticated user.

Choosing a Strategy

Use this decision tree:
  1. Is this a public website? → Use NONE
  2. Does your app have user authentication?
    • Yes, client-side → Use JWT
    • Yes, server-side → Use SERVER_SIDE
  3. Is this backend code? → Use SERVER_SIDE
  4. Still unsure? → Start with NONE, upgrade later

Switching Strategies

You can change authentication strategies without code changes in most cases:
// Development
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: process.env.NODE_ENV === 'production' ? 'JWT' : 'NONE',
  authProvider: process.env.NODE_ENV === 'production' ? authProvider : undefined
});

Query API Authentication

When using the Query API to programmatically access your analytics data, you’ll need to create a separate API key with Query API permissions:
  1. Go to Dashboard → Settings → Authentication
  2. Click Generate New Secret
  3. Enter a descriptive name (e.g., “Query API Key”)
  4. Check the “Query API” permission
  5. Copy the generated secret
Use this API key in the X-API-Key header when making Query API requests:
curl -X POST https://api.grainql.com/v1/api/query/your-tenant-id \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_QUERY_API_KEY" \
  -d '{"event": "page_viewed"}'
Independent Authentication: Query API authentication is completely separate from SDK authentication strategies. You can use Query API keys regardless of whether your SDK uses NONE, SERVER_SIDE, or JWT authentication. The authentication strategies above are only for data ingestion (tracking events).
Query API keys are different from SDK authentication. Use Query API keys only for server-side applications that need to access analytics data programmatically.

Dashboard Session Management

When using the Grain dashboard, your session is managed via Auth0 with automatic token refresh:
  • Session Duration: Access tokens are cached for ~4 minutes and automatically refreshed
  • Token Expiry: If your session expires (e.g., after prolonged inactivity), you’ll see a friendly error message with a “Reload page” action
  • Long-lived Tabs: Tabs open for extended periods will automatically redirect to login when the session expires
Session Expired? Simply reload the page or click the “Reload” button in the error notification. Your work is preserved and you’ll be redirected back after re-authentication.

Next Steps