Overview
OpsHub NAV uses Supabase Auth with JWT tokens for authentication. All API requests must include a valid authentication token.
Authentication Flow
Sign In
Authenticate with email/password or OAuth provider
Receive JWT
Get access token and refresh token
Include in Requests
Add token to Authorization header
Refresh Token
Use refresh token to get new access token before expiry
API Key Authentication
For server-to-server communication, use API keys:
Generate API Key
curl -X POST https://api.opshub.nomark.ai/v1/auth/api-keys \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key",
"scopes": ["portfolios:read", "portfolios:write"]
}'
Use API Key
curl https://api.opshub.nomark.ai/v1/portfolios \
-H "Authorization: Bearer YOUR_API_KEY"
Store API keys securely. Never commit them to version control or expose them in client-side code.
Email/Password Authentication
Sign Up
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 . signUp ({
email: '[email protected] ' ,
password: 'secure-password' ,
options: {
data: {
full_name: 'John Doe' ,
organization: 'ACME Investments'
}
}
});
Sign In
const { data , error } = await supabase . auth . signInWithPassword ({
email: '[email protected] ' ,
password: 'secure-password'
});
// Access token available at
const accessToken = data . session ?. access_token ;
const refreshToken = data . session ?. refresh_token ;
Refresh Token
Access tokens expire after 1 hour. Use refresh tokens to get new access tokens:
const { data , error } = await supabase . auth . refreshSession ({
refresh_token: 'YOUR_REFRESH_TOKEN'
});
const newAccessToken = data . session ?. access_token ;
OAuth Authentication
Authenticate with OAuth providers:
Supported Providers
Google
Microsoft Azure AD
GitHub
GitLab
OAuth Flow
// Redirect to OAuth provider
const { data , error } = await supabase . auth . signInWithOAuth ({
provider: 'google' ,
options: {
redirectTo: 'https://opshub.nomark.ai/auth/callback' ,
scopes: 'email profile'
}
});
// Handle callback
const { data : { session }, error : callbackError } = await supabase . auth . getSession ();
Service Role Authentication
For administrative operations and server-to-server communication:
The service role key bypasses Row Level Security (RLS). Use only in trusted server environments.
import { createClient } from '@supabase/supabase-js' ;
const supabase = createClient (
process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
process . env . SUPABASE_SERVICE_ROLE_KEY ! , // Service role key
{
auth: {
autoRefreshToken: false ,
persistSession: false
}
}
);
// Bypass RLS - use with caution
const { data , error } = await supabase
. from ( 'investment.portfolios' )
. select ( '*' );
Row Level Security (RLS)
OpsHub uses PostgreSQL Row Level Security to enforce access control:
Role-Based Access
Users are automatically assigned roles:
ADMIN - Full system access
FUND_MANAGER - Manage funds and strategies
PORTFOLIO_MANAGER - Manage portfolios
OPERATIONS_LEAD - Operational activities
COMPLIANCE_OFFICER - Compliance and audits
VIEWER - Read-only access
Team-Based Access
Users can only access data for teams they belong to:
-- Example RLS policy
CREATE POLICY portfolio_team_access ON investment . portfolios
FOR SELECT
USING (
team_id IN (
SELECT team_id FROM iam . team_members
WHERE user_id = auth . uid ()
)
);
Check Your Permissions
// Get current user's roles
const { data : userRoles , error } = await supabase
. rpc ( 'get_user_roles' , { user_uuid: user . id });
// Check specific permission
const { data : hasPermission , error } = await supabase
. rpc ( 'user_has_permission' , {
user_uuid: user . id ,
permission_code: 'portfolios:write'
});
Multi-Factor Authentication (MFA)
Enable MFA for enhanced security:
Enroll MFA
// Enroll TOTP MFA
const { data , error } = await supabase . auth . mfa . enroll ({
factorType: 'totp' ,
friendlyName: 'My Authenticator App'
});
// Display QR code to user
const qrCode = data ?. totp ?. qr_code ;
const secret = data ?. totp ?. secret ;
Verify MFA
// Challenge MFA
const { data : challengeData , error } = await supabase . auth . mfa . challenge ({
factorId: 'factor-id'
});
// Verify code from authenticator app
const { data : verifyData , error : verifyError } = await supabase . auth . mfa . verify ({
factorId: 'factor-id' ,
challengeId: challengeData . id ,
code: '123456'
});
Session Management
Get Current Session
const { data : { session }, error } = await supabase . auth . getSession ();
if ( session ) {
console . log ( 'User is authenticated:' , session . user );
console . log ( 'Access token:' , session . access_token );
}
Sign Out
const { error } = await supabase . auth . signOut ();
Listen to Auth Changes
supabase . auth . onAuthStateChange (( event , session ) => {
if ( event === 'SIGNED_IN' ) {
console . log ( 'User signed in:' , session ?. user );
} else if ( event === 'SIGNED_OUT' ) {
console . log ( 'User signed out' );
} else if ( event === 'TOKEN_REFRESHED' ) {
console . log ( 'Token refreshed:' , session ?. access_token );
}
});
Security Best Practices
Store Credentials Securely
Use environment variables for API keys
Never commit credentials to version control
Rotate API keys regularly
Use different keys for different environments
Always use HTTPS for API requests
Enable HSTS in your application
Validate SSL certificates
Respect API rate limits
Implement exponential backoff for retries
Cache responses when appropriate
Implement automatic token refresh
Handle 401 errors gracefully
Store refresh tokens securely
Require MFA for admin accounts
Use TOTP or WebAuthn
Provide backup codes
Troubleshooting
Common Issues
Cause: Invalid or expired access tokenSolution:
Check that you’re including the Authorization header
Verify the token hasn’t expired
Refresh the token using the refresh token
Cause: Insufficient permissionsSolution:
Check your user role and permissions
Verify you’re a member of the required team
Contact your admin to grant access
Cause: Invalid or expired refresh tokenSolution:
Sign in again to get a new session
Check refresh token is stored correctly
Verify refresh token hasn’t been revoked
Next Steps