Authentication
JWT Tokens

JWT Tokens

HiveForge uses Supabase Auth for user authentication. When a user signs in, Supabase issues a JSON Web Token (JWT) that your application sends with every API request.

How it works

  1. User signs in via email/password or OAuth provider
  2. Supabase returns an access token (JWT) and a refresh token
  3. Your app includes the access token in the Authorization header
  4. The HiveForge API verifies the token using either HS256 (JWT secret) or RS256 (JWKS)

Getting a token

Sign in with email and password

import { createClient } from '@supabase/supabase-js'
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
 
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'your-password',
})
 
// data.session.access_token contains the JWT
const token = data.session?.access_token

Using the token

Include the JWT as a Bearer token in the Authorization header:

const response = await fetch('https://api.hiveforge.dev/api/v1/organizations', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
})
 
const data = await response.json()

Token structure

The JWT payload contains:

FieldTypeDescription
substringUser ID (UUID)
emailstringUser email address
rolestringSupabase role (typically authenticated)
expnumberExpiration timestamp (Unix epoch)
audstringAudience (always authenticated)

Token verification

The HiveForge API supports two verification methods:

HS256 (default)

Uses the SUPABASE_JWT_SECRET to verify tokens. This is the default for Supabase-issued tokens.

RS256

For tokens signed with RSA keys, the API fetches public keys from the Supabase JWKS endpoint:

{SUPABASE_URL}/auth/v1/.well-known/jwks.json

Keys are cached for 1 hour and refreshed automatically.

Token refresh

Access tokens expire (default: 1 hour). Use the refresh token to get a new access token without requiring the user to sign in again.

// Supabase JS client handles refresh automatically
// If you need to manually refresh:
const { data, error } = await supabase.auth.refreshSession()
const newToken = data.session?.access_token

The Supabase JavaScript client handles token refresh automatically. You only need to manage refresh manually if you are building a custom client.

User resolution

After token verification, the API resolves the full user profile:

  1. Looks up the user profile in the profiles table using sub (user ID)
  2. Fetches the user's primary organization membership
  3. Loads the user's role and permissions within that organization
  4. Checks if the user is a platform admin

The resolved user object is available to all downstream route handlers.

Error responses

StatusDetailCause
401Invalid token: ...Token is expired, malformed, or signature is invalid
401Authentication failed: ...General authentication error
404User not foundToken is valid but no user profile or email exists

Security best practices

  • Store tokens securely (httpOnly cookies in browsers, secure storage on mobile)
  • Never expose tokens in URLs or client-side JavaScript logs
  • Use short-lived access tokens with refresh token rotation
  • Always verify the aud claim matches authenticated