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
- User signs in via email/password or OAuth provider
- Supabase returns an access token (JWT) and a refresh token
- Your app includes the access token in the
Authorizationheader - 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_tokenUsing 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:
| Field | Type | Description |
|---|---|---|
sub | string | User ID (UUID) |
email | string | User email address |
role | string | Supabase role (typically authenticated) |
exp | number | Expiration timestamp (Unix epoch) |
aud | string | Audience (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.jsonKeys 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_tokenThe 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:
- Looks up the user profile in the
profilestable usingsub(user ID) - Fetches the user's primary organization membership
- Loads the user's role and permissions within that organization
- Checks if the user is a platform admin
The resolved user object is available to all downstream route handlers.
Error responses
| Status | Detail | Cause |
|---|---|---|
| 401 | Invalid token: ... | Token is expired, malformed, or signature is invalid |
| 401 | Authentication failed: ... | General authentication error |
| 404 | User not found | Token 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
audclaim matchesauthenticated