Authentication
Authentication
FerrisKey provides OpenID Connect (OIDC) based authentication, enabling secure identity management for your applications. This guide covers the available authentication flows and how to implement them.
Overview
FerrisKey implements the OpenID Connect protocol built on OAuth 2.0, providing:
- Standardized authentication following OIDC specifications
- Multiple grant types for different application scenarios
- Multi-factor authentication with TOTP support
- Realm-based isolation for multi-tenant environments
Available Grant Types
FerrisKey currently supports four OAuth 2.0 grant types to accommodate different application architectures:
Authorization Code Flow
Best for: Web applications with backend servers
The most secure flow for applications that can securely store client secrets:
- User initiates login - Redirect user to authorization endpoint
- User authentication - User authenticates with FerrisKey
- Authorization code - FerrisKey redirects back with authorization code
- Token exchange - Backend exchanges code for tokens using client secret
Client Credentials Flow
Best for: Backend services and API-to-API communication
Machine-to-machine authentication without user interaction:
- Direct token request - Application authenticates using client ID and secret
- Service tokens - Receive access tokens for API access
- No user context - Tokens represent the application itself, not a user
Password Flow (Resource Owner Password Credentials)
Best for: Trusted first-party applications only
Direct username/password authentication:
- Direct credential exchange - Send username/password directly to token endpoint
- Immediate token response - Receive tokens without redirect flow
- High trust requirement - Only use for applications you fully control
Refresh Token Flow
Best for: Token renewal for all client types
Obtain new access tokens without re-authentication:
- Long-lived refresh tokens - Securely stored tokens for token renewal
- Seamless experience - Users stay logged in without interruption
- Enhanced security - Access tokens can have short lifespans
Multi-Factor Authentication (MFA)
FerrisKey supports Time-based One-Time Passwords (TOTP) for enhanced security:
TOTP Implementation
- Compatible authenticator apps - Google Authenticator, Authy, Microsoft Authenticator
- Standard RFC 6238 - Industry-standard TOTP algorithm
- QR code enrollment - Easy setup for users
- Backup codes - Recovery options when devices are unavailable
MFA Flow Integration
MFA integrates seamlessly with the authorization code flow:
- Initial authentication - User provides username/password
- MFA challenge - System prompts for TOTP code
- TOTP verification - User enters code from authenticator app
- Complete authentication - Authorization continues upon successful verification
OIDC Endpoints
FerrisKey exposes standard OpenID Connect endpoints per realm:
Authorization Endpoint
GET /realms/{realm_name}/protocol/openid-connect/auth
Purpose: Initiate the authorization code flow
Parameters:
client_id- Your application's client identifierresponse_type- Set tocodefor authorization code flowscope- Requested scopes (e.g.,openid profile email)redirect_uri- Where to redirect after authenticationstate- CSRF protection parameter (recommended)
Example:
https://your-ferriskey.example.com/realms/my-app/protocol/openid-connect/auth
?client_id=my-web-app
&response_type=code
&scope=openid%20profile%20email
&redirect_uri=https://myapp.com/callback
&state=random-state-value
Token Endpoint
POST /realms/{realm_name}/protocol/openid-connect/token
Purpose: Exchange authorization codes for tokens or refresh existing tokens
Content-Type: application/x-www-form-urlencoded
Authorization Code Exchange
Parameters:
grant_type=authorization_codecode- Authorization code from auth endpointredirect_uri- Must match the URI from auth requestclient_id- Your application's client identifierclient_secret- Your application's client secret
Client Credentials
Parameters:
grant_type=client_credentialsclient_id- Your application's client identifierclient_secret- Your application's client secretscope- Requested scopes (optional)
Password Grant
Parameters:
grant_type=passwordusername- User's username or emailpassword- User's passwordclient_id- Your application's client identifierclient_secret- Your application's client secret (if required)scope- Requested scopes (optional)
Refresh Token
Parameters:
grant_type=refresh_tokenrefresh_token- The refresh token received previouslyclient_id- Your application's client identifierclient_secret- Your application's client secret (if required)
Implementation Examples
Web Application (Authorization Code)
Step 1: Redirect to Authorization
const authUrl = new URL('/realms/my-app/protocol/openid-connect/auth', 'https://ferriskey.example.com');
authUrl.searchParams.set('client_id', 'my-web-app');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('redirect_uri', 'https://myapp.com/callback');
authUrl.searchParams.set('state', generateRandomState());
window.location.href = authUrl.toString();
Step 2: Handle Callback and Exchange Code
// Backend endpoint handling the callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state parameter for CSRF protection
if (!verifyState(state)) {
return res.status(400).send('Invalid state parameter');
}
// Exchange code for tokens
const tokenResponse = await fetch('https://ferriskey.example.com/realms/my-app/protocol/openid-connect/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: 'https://myapp.com/callback',
client_id: 'my-web-app',
client_secret: process.env.CLIENT_SECRET
})
});
const tokens = await tokenResponse.json();
// Store tokens securely and redirect user
});
Service-to-Service (Client Credentials)
async function getServiceToken() {
const response = await fetch('https://ferriskey.example.com/realms/my-service/protocol/openid-connect/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: 'my-service',
client_secret: process.env.SERVICE_SECRET,
scope: 'api:read api:write'
})
});
const tokens = await response.json();
return tokens.access_token;
}
Token Refresh
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://ferriskey.example.com/realms/my-app/protocol/openid-connect/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'my-web-app',
client_secret: process.env.CLIENT_SECRET
})
});
const tokens = await response.json();
return tokens;
}
Token Structure
FerrisKey issues standard JWT tokens:
Access Token
Contains claims for API authorization:
sub- Subject (user ID)aud- Audience (your application)exp- Expiration timestampiat- Issued at timestampscope- Granted scopes
ID Token
Contains user identity information:
sub- Subject (user ID)name- User's full nameemail- User's email addressemail_verified- Email verification statuspreferred_username- Username
Refresh Token
Opaque token for obtaining new access tokens:
- Long-lived (configurable expiration)
- Single-use (new refresh token issued with each use)
- Securely stored and transmitted
Best Practices
Security Considerations
- Always use HTTPS in production environments
- Validate state parameters to prevent CSRF attacks
- Store client secrets securely - never expose in frontend code
- Use short-lived access tokens with longer-lived refresh tokens
- Implement proper token storage - secure HTTP-only cookies for web apps
Error Handling
Handle common error scenarios:
// Token endpoint error handling
if (!tokenResponse.ok) {
const error = await tokenResponse.json();
switch (error.error) {
case 'invalid_grant':
// Authorization code expired or invalid
redirectToLogin();
break;
case 'invalid_client':
// Client credentials are invalid
console.error('Client configuration error');
break;
case 'invalid_request':
// Missing or malformed parameters
console.error('Request error:', error.error_description);
break;
}
}
Monitoring and Logging
Track important authentication events:
- Successful authentications and token exchanges
- Failed login attempts and their reasons
- MFA enrollments and usage patterns
- Token refresh patterns and failures