Authentication - arilonUK/iotagentmesh GitHub Wiki
The Authentication API provides secure access control for the IoT Agent Mesh platform using JWT-based authentication with row-level security (RLS) and multi-tenant organization isolation.
The authentication system provides:
- JWT-based authentication with automatic token refresh
- Row-level security (RLS) for data isolation between organizations
- Role-based access control (RBAC) within organizations
- Session management and logout functionality
- Password reset and email verification
- API key management for programmatic access
/auth/v1/
sequenceDiagram
participant Client
participant Auth API
participant Database
participant Application API
Client->>Auth API: Sign In Request
Auth API->>Database: Validate Credentials
Database->>Auth API: User Data + Organization
Auth API->>Client: JWT Token + User Profile
Client->>Application API: API Request (with JWT)
Application API->>Database: Query with RLS
Database->>Application API: Filtered Results
Application API->>Client: Response
Authenticate a user with email and password credentials.
POST /auth/v1/token?grant_type=password
Content-Type: application/json
apikey: YOUR_SUPABASE_ANON_KEY
{
"email": "[email protected]",
"password": "secure-password"
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721750400,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": "2024-01-15T10:30:00Z",
"phone": null,
"confirmed_at": "2024-01-15T10:30:00Z",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"organization_id": "org_01H8K2M3N4P5Q6R7S8T9U0V1W2",
"organization_role": "admin",
"full_name": "John Doe",
"avatar_url": "https://example.com/avatar.jpg"
},
"identities": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"identity_data": {
"email": "[email protected]",
"sub": "550e8400-e29b-41d4-a716-446655440000"
},
"provider": "email",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
}
JavaScript/TypeScript:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://your-project-id.supabase.co',
'your-anon-key'
)
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'secure-password'
})
if (error) throw error
console.log('User:', data.user)
console.log('Session:', data.session)
// The JWT token is now automatically included in all API requests
const { data: organizations } = await supabase
.from('organizations')
.select('*')
Python:
from supabase import create_client
supabase = create_client(
"https://your-project-id.supabase.co",
"your-anon-key"
)
response = supabase.auth.sign_in_with_password({
"email": "[email protected]",
"password": "secure-password"
})
user = response.user
session = response.session
cURL:
curl -X POST \
"https://your-project-id.supabase.co/auth/v1/token?grant_type=password" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "secure-password"
}'
Create a new user account.
POST /auth/v1/signup
{
"email": "[email protected]",
"password": "secure-password",
"data": {
"full_name": "Jane Smith",
"organization_name": "Tech Innovations Inc",
"phone": "+1-555-0123"
}
}
{
"access_token": null,
"token_type": "bearer",
"expires_in": null,
"expires_at": null,
"refresh_token": null,
"user": {
"id": "7f3e8b2a-1d45-4c8e-9a7b-2e5f8c9d0a1b",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": null,
"confirmation_sent_at": "2024-07-23T17:00:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"full_name": "Jane Smith",
"organization_name": "Tech Innovations Inc",
"phone": "+1-555-0123"
},
"created_at": "2024-07-23T17:00:00Z",
"updated_at": "2024-07-23T17:00:00Z"
}
}
Note: Email confirmation is required before the user can sign in.
JavaScript/TypeScript:
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'secure-password',
options: {
data: {
full_name: 'Jane Smith',
organization_name: 'Tech Innovations Inc',
phone: '+1-555-0123'
}
}
})
if (error) throw error
console.log('User created:', data.user)
console.log('Check email for confirmation link')
Refresh an expired JWT token using a refresh token.
POST /auth/v1/token?grant_type=refresh_token
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"last_sign_in_at": "2024-07-23T17:00:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.refreshSession()
if (error) throw error
console.log('Token refreshed:', data.session)
Invalidate the current session and sign out the user.
POST /auth/v1/logout
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
{
"message": "Successfully logged out"
}
JavaScript/TypeScript:
const { error } = await supabase.auth.signOut()
if (error) throw error
console.log('User signed out successfully')
Request a password reset email for a user.
POST /auth/v1/recover
{
"email": "[email protected]"
}
{
"message": "Password recovery email sent"
}
JavaScript/TypeScript:
const { error } = await supabase.auth.resetPasswordForEmail(
'[email protected]',
{
redirectTo: 'https://yourapp.com/reset-password'
}
)
if (error) throw error
console.log('Password reset email sent')
Update the user's password (requires authentication).
PUT /auth/v1/user
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
{
"password": "new-secure-password"
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"updated_at": "2024-07-23T17:15:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.updateUser({
password: 'new-secure-password'
})
if (error) throw error
console.log('Password updated successfully')
Retrieve the current user's profile information.
GET /auth/v1/user
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": "2024-01-15T10:30:00Z",
"phone": "+1-555-0123",
"phone_confirmed_at": "2024-01-15T10:35:00Z",
"confirmed_at": "2024-01-15T10:30:00Z",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"organization_id": "org_01H8K2M3N4P5Q6R7S8T9U0V1W2",
"organization_role": "admin",
"full_name": "John Doe",
"avatar_url": "https://example.com/avatar.jpg",
"timezone": "America/New_York",
"preferences": {
"email_notifications": true,
"sms_notifications": false,
"dashboard_theme": "dark"
}
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
JavaScript/TypeScript:
const { data: { user }, error } = await supabase.auth.getUser()
if (error) throw error
console.log('Current user:', user)
Update the current user's profile metadata.
PUT /auth/v1/user
{
"data": {
"full_name": "John Smith",
"avatar_url": "https://example.com/new-avatar.jpg",
"timezone": "America/Los_Angeles",
"preferences": {
"email_notifications": true,
"sms_notifications": true,
"dashboard_theme": "light"
}
}
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"user_metadata": {
"full_name": "John Smith",
"avatar_url": "https://example.com/new-avatar.jpg",
"timezone": "America/Los_Angeles",
"preferences": {
"email_notifications": true,
"sms_notifications": true,
"dashboard_theme": "light"
}
},
"updated_at": "2024-07-23T17:20:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.updateUser({
data: {
full_name: 'John Smith',
timezone: 'America/Los_Angeles',
preferences: {
email_notifications: true,
sms_notifications: true,
dashboard_theme: 'light'
}
}
})
if (error) throw error
console.log('Profile updated:', data.user)
Create a new API key for programmatic access.
POST /functions/v1/generate-api-key
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
{
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"expires_in_days": 365
}
{
"api_key": "sk_live_01H8K3L4M5N6O7P8Q9R0S1T2U3V4W5X6Y7Z8",
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"created_at": "2024-07-23T17:25:00Z",
"expires_at": "2025-07-23T17:25:00Z",
"last_used": null
}
JavaScript/TypeScript:
const response = await supabase.functions.invoke('generate-api-key', {
body: {
name: 'Production Integration',
description: 'API key for production telemetry ingestion',
permissions: ['telemetry:write', 'agents:read'],
expires_in_days: 365
}
})
console.log('API Key:', response.data.api_key)
Retrieve all API keys for the current user.
GET /functions/v1/api-keys
{
"api_keys": [
{
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"created_at": "2024-07-23T17:25:00Z",
"expires_at": "2025-07-23T17:25:00Z",
"last_used": "2024-07-23T16:45:00Z",
"usage_count": 125000,
"status": "active"
},
{
"key_id": "key_02H8K3L4M5N6O7P8Q9R0S1T2U3V5",
"name": "Development Testing",
"description": "API key for development environment",
"permissions": ["*"],
"created_at": "2024-06-15T10:00:00Z",
"expires_at": "2024-12-15T10:00:00Z",
"last_used": "2024-07-22T14:30:00Z",
"usage_count": 5420,
"status": "active"
}
]
}
Revoke an existing API key.
DELETE /functions/v1/api-keys/{key_id}
{
"message": "API key revoked successfully",
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"revoked_at": "2024-07-23T17:30:00Z"
}
Use API keys for server-to-server authentication instead of JWT tokens.
Authorization: Bearer YOUR_API_KEY
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
curl -X POST \
"https://your-project-id.supabase.co/rest/v1/telemetry" \
-H "Authorization: Bearer sk_live_01H8K3L4M5N6O7P8Q9R0S1T2U3V4W5X6Y7Z8" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_123",
"data": {
"temperature": 23.5,
"humidity": 65.2
}
}'
JavaScript/TypeScript:
// Create a Supabase client with API key authentication
const supabaseWithApiKey = createClient(
'https://your-project-id.supabase.co',
'your-anon-key',
{
global: {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
}
)
// Use the client for API requests
const { data, error } = await supabaseWithApiKey
.from('telemetry')
.insert({
agent_id: 'agent_123',
data: { temperature: 23.5, humidity: 65.2 }
})
Row Level Security automatically filters data based on the authenticated user's organization membership. This ensures complete data isolation between organizations.
-- Organizations: Users can only access their own organization
CREATE POLICY "org_access" ON organizations
FOR ALL USING (
id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
)
);
-- Agents: Organization-scoped access
CREATE POLICY "agent_access" ON agents
FOR ALL USING (
organization_id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
)
);
-- Telemetry: Access through agent ownership
CREATE POLICY "telemetry_access" ON telemetry
FOR ALL USING (
agent_id IN (
SELECT a.id
FROM agents a
JOIN users u ON a.organization_id = u.organization_id
WHERE u.id = auth.uid()
)
);
Additional access control based on user roles within organizations:
-- Admin-only access to user management
CREATE POLICY "admin_user_management" ON users
FOR ALL USING (
organization_id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
AND role = 'admin'
)
);
-- Read-only access for viewer role
CREATE POLICY "viewer_read_only" ON telemetry
FOR SELECT USING (
agent_id IN (
SELECT a.id
FROM agents a
JOIN users u ON a.organization_id = u.organization_id
WHERE u.id = auth.uid()
AND u.role IN ('admin', 'user', 'viewer')
)
);
The authentication system automatically manages sessions:
- Access tokens expire after 1 hour
- Refresh tokens are valid for 30 days
- Automatic refresh happens when access token expires
- Session persistence across browser sessions
// Listen for auth state changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') {
console.log('User signed in:', session.user)
// Redirect to dashboard or update UI
} else if (event === 'SIGNED_OUT') {
console.log('User signed out')
// Redirect to login or clear user data
} else if (event === 'TOKEN_REFRESHED') {
console.log('Token refreshed:', session)
// Token automatically refreshed
}
})
// Get current session
const { data: { session } } = await supabase.auth.getSession()
if (session) {
console.log('User is authenticated:', session.user)
} else {
console.log('User is not authenticated')
}
HTTP Code | Error Code | Description |
---|---|---|
400 | INVALID_CREDENTIALS | Invalid email or password |
400 | SIGNUP_DISABLED | User registration is disabled |
400 | WEAK_PASSWORD | Password doesn't meet requirements |
401 | UNAUTHORIZED | Missing or invalid authentication |
403 | EMAIL_NOT_CONFIRMED | Email address not confirmed |
403 | USER_DISABLED | User account has been disabled |
422 | EMAIL_ALREADY_EXISTS | Email address already registered |
429 | RATE_LIMIT_EXCEEDED | Too many authentication attempts |
Rate limits are per IP address and apply globally across all organizations.
# Authentication APIThe Authentication API provides secure access control for the IoT Agent Mesh platform using JWT-based authentication with row-level security (RLS) and multi-tenant organization isolation.
The authentication system provides:
- JWT-based authentication with automatic token refresh
- Row-level security (RLS) for data isolation between organizations
- Role-based access control (RBAC) within organizations
- Session management and logout functionality
- Password reset and email verification
- API key management for programmatic access
/auth/v1/
sequenceDiagram
participant Client
participant Auth API
participant Database
participant Application API
Client->>Auth API: Sign In Request
Auth API->>Database: Validate Credentials
Database->>Auth API: User Data + Organization
Auth API->>Client: JWT Token + User Profile
Client->>Application API: API Request (with JWT)
Application API->>Database: Query with RLS
Database->>Application API: Filtered Results
Application API->>Client: Response
Authenticate a user with email and password credentials.
POST /auth/v1/token?grant_type=password
Content-Type: application/json
apikey: YOUR_SUPABASE_ANON_KEY
{
"email": "[email protected]",
"password": "secure-password"
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721750400,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": "2024-01-15T10:30:00Z",
"phone": null,
"confirmed_at": "2024-01-15T10:30:00Z",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"organization_id": "org_01H8K2M3N4P5Q6R7S8T9U0V1W2",
"organization_role": "admin",
"full_name": "John Doe",
"avatar_url": "https://example.com/avatar.jpg"
},
"identities": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"identity_data": {
"email": "[email protected]",
"sub": "550e8400-e29b-41d4-a716-446655440000"
},
"provider": "email",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
}
JavaScript/TypeScript:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://your-project-id.supabase.co',
'your-anon-key'
)
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'secure-password'
})
if (error) throw error
console.log('User:', data.user)
console.log('Session:', data.session)
// The JWT token is now automatically included in all API requests
const { data: organizations } = await supabase
.from('organizations')
.select('*')
Python:
from supabase import create_client
supabase = create_client(
"https://your-project-id.supabase.co",
"your-anon-key"
)
response = supabase.auth.sign_in_with_password({
"email": "[email protected]",
"password": "secure-password"
})
user = response.user
session = response.session
cURL:
curl -X POST \
"https://your-project-id.supabase.co/auth/v1/token?grant_type=password" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "secure-password"
}'
Create a new user account.
POST /auth/v1/signup
{
"email": "[email protected]",
"password": "secure-password",
"data": {
"full_name": "Jane Smith",
"organization_name": "Tech Innovations Inc",
"phone": "+1-555-0123"
}
}
{
"access_token": null,
"token_type": "bearer",
"expires_in": null,
"expires_at": null,
"refresh_token": null,
"user": {
"id": "7f3e8b2a-1d45-4c8e-9a7b-2e5f8c9d0a1b",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": null,
"confirmation_sent_at": "2024-07-23T17:00:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"full_name": "Jane Smith",
"organization_name": "Tech Innovations Inc",
"phone": "+1-555-0123"
},
"created_at": "2024-07-23T17:00:00Z",
"updated_at": "2024-07-23T17:00:00Z"
}
}
Note: Email confirmation is required before the user can sign in.
JavaScript/TypeScript:
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'secure-password',
options: {
data: {
full_name: 'Jane Smith',
organization_name: 'Tech Innovations Inc',
phone: '+1-555-0123'
}
}
})
if (error) throw error
console.log('User created:', data.user)
console.log('Check email for confirmation link')
Refresh an expired JWT token using a refresh token.
POST /auth/v1/token?grant_type=refresh_token
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"last_sign_in_at": "2024-07-23T17:00:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.refreshSession()
if (error) throw error
console.log('Token refreshed:', data.session)
Invalidate the current session and sign out the user.
POST /auth/v1/logout
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
{
"message": "Successfully logged out"
}
JavaScript/TypeScript:
const { error } = await supabase.auth.signOut()
if (error) throw error
console.log('User signed out successfully')
Request a password reset email for a user.
POST /auth/v1/recover
{
"email": "[email protected]"
}
{
"message": "Password recovery email sent"
}
JavaScript/TypeScript:
const { error } = await supabase.auth.resetPasswordForEmail(
'[email protected]',
{
redirectTo: 'https://yourapp.com/reset-password'
}
)
if (error) throw error
console.log('Password reset email sent')
Update the user's password (requires authentication).
PUT /auth/v1/user
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
{
"password": "new-secure-password"
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"updated_at": "2024-07-23T17:15:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.updateUser({
password: 'new-secure-password'
})
if (error) throw error
console.log('Password updated successfully')
Retrieve the current user's profile information.
GET /auth/v1/user
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"email_confirmed_at": "2024-01-15T10:30:00Z",
"phone": "+1-555-0123",
"phone_confirmed_at": "2024-01-15T10:35:00Z",
"confirmed_at": "2024-01-15T10:30:00Z",
"last_sign_in_at": "2024-07-23T16:45:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email"]
},
"user_metadata": {
"organization_id": "org_01H8K2M3N4P5Q6R7S8T9U0V1W2",
"organization_role": "admin",
"full_name": "John Doe",
"avatar_url": "https://example.com/avatar.jpg",
"timezone": "America/New_York",
"preferences": {
"email_notifications": true,
"sms_notifications": false,
"dashboard_theme": "dark"
}
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-07-23T16:45:00Z"
}
JavaScript/TypeScript:
const { data: { user }, error } = await supabase.auth.getUser()
if (error) throw error
console.log('Current user:', user)
Update the current user's profile metadata.
PUT /auth/v1/user
{
"data": {
"full_name": "John Smith",
"avatar_url": "https://example.com/new-avatar.jpg",
"timezone": "America/Los_Angeles",
"preferences": {
"email_notifications": true,
"sms_notifications": true,
"dashboard_theme": "light"
}
}
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": 1721754000,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"user_metadata": {
"full_name": "John Smith",
"avatar_url": "https://example.com/new-avatar.jpg",
"timezone": "America/Los_Angeles",
"preferences": {
"email_notifications": true,
"sms_notifications": true,
"dashboard_theme": "light"
}
},
"updated_at": "2024-07-23T17:20:00Z"
}
}
JavaScript/TypeScript:
const { data, error } = await supabase.auth.updateUser({
data: {
full_name: 'John Smith',
timezone: 'America/Los_Angeles',
preferences: {
email_notifications: true,
sms_notifications: true,
dashboard_theme: 'light'
}
}
})
if (error) throw error
console.log('Profile updated:', data.user)
Create a new API key for programmatic access.
POST /functions/v1/generate-api-key
Authorization: Bearer YOUR_JWT_TOKEN
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
{
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"expires_in_days": 365
}
{
"api_key": "sk_live_01H8K3L4M5N6O7P8Q9R0S1T2U3V4W5X6Y7Z8",
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"created_at": "2024-07-23T17:25:00Z",
"expires_at": "2025-07-23T17:25:00Z",
"last_used": null
}
JavaScript/TypeScript:
const response = await supabase.functions.invoke('generate-api-key', {
body: {
name: 'Production Integration',
description: 'API key for production telemetry ingestion',
permissions: ['telemetry:write', 'agents:read'],
expires_in_days: 365
}
})
console.log('API Key:', response.data.api_key)
Retrieve all API keys for the current user.
GET /functions/v1/api-keys
{
"api_keys": [
{
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"name": "Production Integration",
"description": "API key for production telemetry ingestion",
"permissions": ["telemetry:write", "agents:read"],
"created_at": "2024-07-23T17:25:00Z",
"expires_at": "2025-07-23T17:25:00Z",
"last_used": "2024-07-23T16:45:00Z",
"usage_count": 125000,
"status": "active"
},
{
"key_id": "key_02H8K3L4M5N6O7P8Q9R0S1T2U3V5",
"name": "Development Testing",
"description": "API key for development environment",
"permissions": ["*"],
"created_at": "2024-06-15T10:00:00Z",
"expires_at": "2024-12-15T10:00:00Z",
"last_used": "2024-07-22T14:30:00Z",
"usage_count": 5420,
"status": "active"
}
]
}
Revoke an existing API key.
DELETE /functions/v1/api-keys/{key_id}
{
"message": "API key revoked successfully",
"key_id": "key_01H8K3L4M5N6O7P8Q9R0S1T2U3V4",
"revoked_at": "2024-07-23T17:30:00Z"
}
Use API keys for server-to-server authentication instead of JWT tokens.
Authorization: Bearer YOUR_API_KEY
apikey: YOUR_SUPABASE_ANON_KEY
Content-Type: application/json
curl -X POST \
"https://your-project-id.supabase.co/rest/v1/telemetry" \
-H "Authorization: Bearer sk_live_01H8K3L4M5N6O7P8Q9R0S1T2U3V4W5X6Y7Z8" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_123",
"data": {
"temperature": 23.5,
"humidity": 65.2
}
}'
JavaScript/TypeScript:
// Create a Supabase client with API key authentication
const supabaseWithApiKey = createClient(
'https://your-project-id.supabase.co',
'your-anon-key',
{
global: {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
}
)
// Use the client for API requests
const { data, error } = await supabaseWithApiKey
.from('telemetry')
.insert({
agent_id: 'agent_123',
data: { temperature: 23.5, humidity: 65.2 }
})
Row Level Security automatically filters data based on the authenticated user's organization membership. This ensures complete data isolation between organizations.
-- Organizations: Users can only access their own organization
CREATE POLICY "org_access" ON organizations
FOR ALL USING (
id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
)
);
-- Agents: Organization-scoped access
CREATE POLICY "agent_access" ON agents
FOR ALL USING (
organization_id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
)
);
-- Telemetry: Access through agent ownership
CREATE POLICY "telemetry_access" ON telemetry
FOR ALL USING (
agent_id IN (
SELECT a.id
FROM agents a
JOIN users u ON a.organization_id = u.organization_id
WHERE u.id = auth.uid()
)
);
Additional access control based on user roles within organizations:
-- Admin-only access to user management
CREATE POLICY "admin_user_management" ON users
FOR ALL USING (
organization_id IN (
SELECT organization_id
FROM users
WHERE id = auth.uid()
AND role = 'admin'
)
);
-- Read-only access for viewer role
CREATE POLICY "viewer_read_only" ON telemetry
FOR SELECT USING (
agent_id IN (
SELECT a.id
FROM agents a
JOIN users u ON a.organization_id = u.organization_id
WHERE u.id = auth.uid()
AND u.role IN ('admin', 'user', 'viewer')
)
);
The authentication system automatically manages sessions:
- Access tokens expire after 1 hour
- Refresh tokens are valid for 30 days
- Automatic refresh happens when access token expires
- Session persistence across browser sessions
// Listen for auth state changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') {
console.log('User signed in:', session.user)
// Redirect to dashboard or update UI
} else if (event === 'SIGNED_OUT') {
console.log('User signed out')
// Redirect to login or clear user data
} else if (event === 'TOKEN_REFRESHED') {
console.log('Token refreshed:', session)
// Token automatically refreshed
}
})
// Get current session
const { data: { session } } = await supabase.auth.getSession()
if (session) {
console.log('User is authenticated:', session.user)
} else {
console.log('User is not authenticated')
}
HTTP Code | Error Code | Description |
---|---|---|
400 | INVALID_CREDENTIALS |
Invalid email or password |
400 | SIGNUP_DISABLED |
User registration is disabled |
400 | WEAK_PASSWORD |
Password doesn't meet requirements |
401 | UNAUTHORIZED |
Missing or invalid authentication |
403 | EMAIL_NOT_CONFIRMED |
Email address not confirmed |
403 | USER_DISABLED |
User account has been disabled |
422 | EMAIL_ALREADY_EXISTS |
Email address already registered |
429 | RATE_LIMIT_EXCEEDED |
Too many authentication attempts |
{
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid email or password",
"details": {
"field": "password",
"reason": "Password does not match"
}
},
"status": 400
}
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character
- Key Rotation: Rotate API keys regularly (recommended: every 90 days)
- Principle of Least Privilege: Grant minimal required permissions
- Secure Storage: Never expose API keys in client-side code
- Monitoring: Monitor API key usage for suspicious activity
- HTTPS Only: Always use HTTPS in production
- Secure Cookies: Enable secure cookie settings
- Session Timeout: Implement appropriate session timeouts
- Device Tracking: Monitor sign-ins from new devices
- [Organizations API](API-Organizations.md) - Organization management
- [Users API](API-Users.md) - User management within organizations
- [Rate Limiting](API-Rate-Limiting.md) - API rate limiting policies
- [Error Handling](API-Error-Handling.md) - Error handling best practices
Operation | Rate Limit | Window |
---|---|---|
Sign In | 10 requests | 1 minute |
Sign Up | 5 requests | 1 minute |
Password Reset | 3 requests | 1 minute |
Token Refresh | 20 requests | 1 minute |
Profile Update | 10 requests | 1 minute |
API Key Generation | 2 requests | 1 hour |
Rate limits are per IP address and apply globally across all organizations.