Test SSO Implementation
Verify your Single Sign-On configuration is working correctly with comprehensive testing procedures.
Overview
Testing your SSO implementation ensures users can authenticate seamlessly and securely. This page provides testing procedures for both OIDC and SAML integrations.
Pre-Testing Checklist
Before testing, verify your configuration:
OIDC Configuration
- Client ID and secret are correct
- Redirect URIs match exactly
- Scopes are properly configured
- Grant types are enabled
- PKCE is enabled for public clients
SAML Configuration
- Identity Provider metadata is configured
- Certificate is valid and not expired
- Attribute mapping is correct
- Audience/Entity ID matches
- Callback URLs are configured
Testing Tools
1. Authsec Dashboard Test Tool
Use the built-in test tool in your Authsec dashboard:
- Navigate to Applications → Your App
- Click Test tab
- Select SSO Test
- Follow the guided testing flow
2. Browser Developer Tools
Enable detailed logging to debug issues:
// Enable debug mode in SDK
const authsec = new AuthsecClient({
domain: 'your-domain.authsec.com',
clientId: 'your-client-id',
debug: true // Enable detailed logging
});
3. Network Traffic Inspection
Monitor network requests during authentication:
- Open browser Developer Tools
- Go to Network tab
- Clear existing requests
- Initiate SSO login
- Inspect requests and responses
OIDC Testing
Test Authorization Code Flow
// Test OIDC login flow
async function testOIDCLogin() {
console.log('Starting OIDC test...');
try {
// Step 1: Build authorization URL
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
scope: 'openid profile email',
state: 'test-state-123',
nonce: 'test-nonce-456'
});
console.log('✓ Authorization URL built successfully');
console.log('Auth URL:', authUrl);
// Manual step: User will be redirected to this URL
console.log('👤 Please visit the authorization URL to continue...');
} catch (error) {
console.error('✗ Authorization URL build failed:', error);
}
}
// Test token exchange (call this in callback handler)
async function testTokenExchange(authCode) {
try {
// Step 2: Exchange authorization code for tokens
const tokens = await authsec.exchangeCodeForTokens({
code: authCode,
redirect_uri: 'https://yourapp.com/callback'
});
console.log('✓ Token exchange successful');
console.log('Access Token:', tokens.access_token ? '✓ Present' : '✗ Missing');
console.log('ID Token:', tokens.id_token ? '✓ Present' : '✗ Missing');
console.log('Refresh Token:', tokens.refresh_token ? '✓ Present' : '✗ Missing');
// Step 3: Validate ID token
const idToken = await authsec.verifyIdToken(tokens.id_token);
console.log('✓ ID Token validation successful');
console.log('User ID:', idToken.sub);
console.log('Email:', idToken.email);
return tokens;
} catch (error) {
console.error('✗ Token exchange failed:', error);
throw error;
}
}
Test Silent Authentication
// Test silent authentication for SPAs
async function testSilentAuth() {
try {
console.log('Testing silent authentication...');
const user = await authsec.getTokenSilently({
audience: 'https://api.yourapp.com',
scope: 'openid profile'
});
console.log('✓ Silent authentication successful');
console.log('User:', user);
return user;
} catch (error) {
if (error.error === 'login_required') {
console.log('ℹ Silent auth failed - login required (expected for first test)');
} else {
console.error('✗ Silent authentication failed:', error);
}
}
}
SAML Testing
Test SAML Authentication Flow
// Test SAML login initiation
function testSAMLLogin() {
console.log('Starting SAML test...');
try {
// Build SAML authentication URL
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
scope: 'openid profile email',
connection: 'samlp-enterprise' // Your SAML connection name
});
console.log('✓ SAML authorization URL built successfully');
console.log('Auth URL:', authUrl);
// The URL should redirect to your SAML IdP
console.log('👤 Please visit the URL to test SAML authentication...');
} catch (error) {
console.error('✗ SAML URL build failed:', error);
}
}
Validate SAML Response
// Test SAML response handling
async function testSAMLResponse(authCode) {
try {
console.log('Testing SAML response handling...');
// Exchange code for tokens (same as OIDC)
const tokens = await authsec.exchangeCodeForTokens({
code: authCode,
redirect_uri: 'https://yourapp.com/callback'
});
console.log('✓ SAML token exchange successful');
// Get user information with SAML attributes
const user = await authsec.getUserInfo(tokens.access_token);
console.log('✓ User information retrieved');
console.log('User ID:', user.sub);
console.log('Email:', user.email);
console.log('Name:', user.name);
console.log('Groups:', user.groups);
console.log('Custom Attributes:', user);
// Validate expected SAML attributes are present
validateSAMLAttributes(user);
return user;
} catch (error) {
console.error('✗ SAML response test failed:', error);
throw error;
}
}
function validateSAMLAttributes(user) {
const requiredAttributes = ['sub', 'email', 'name'];
const missingAttributes = requiredAttributes.filter(attr => !user[attr]);
if (missingAttributes.length > 0) {
console.warn('⚠ Missing required attributes:', missingAttributes);
} else {
console.log('✓ All required attributes present');
}
// Check custom attributes
if (user.groups) {
console.log('✓ Groups attribute mapped correctly');
} else {
console.warn('⚠ Groups attribute not found - check attribute mapping');
}
}
Automated Testing
Test Suite Example
// Comprehensive SSO test suite
describe('SSO Integration Tests', () => {
let authsec;
beforeAll(() => {
authsec = new AuthsecClient({
domain: process.env.AUTHSEC_DOMAIN,
clientId: process.env.AUTHSEC_CLIENT_ID,
clientSecret: process.env.AUTHSEC_CLIENT_SECRET
});
});
describe('OIDC Tests', () => {
test('should build valid authorization URL', () => {
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
scope: 'openid profile',
state: 'test-state'
});
expect(authUrl).toContain('response_type=code');
expect(authUrl).toContain('scope=openid%20profile');
expect(authUrl).toContain('state=test-state');
});
test('should validate ID tokens', async () => {
const mockIdToken = 'eyJ...'; // Mock JWT token
const result = await authsec.verifyIdToken(mockIdToken);
expect(result).toHaveProperty('sub');
expect(result).toHaveProperty('aud');
expect(result).toHaveProperty('exp');
});
});
describe('SAML Tests', () => {
test('should handle SAML authentication', async () => {
// Mock SAML response test
const mockCode = 'saml-auth-code';
const tokens = await authsec.exchangeCodeForTokens({
code: mockCode,
redirect_uri: 'https://yourapp.com/callback'
});
expect(tokens).toHaveProperty('access_token');
expect(tokens).toHaveProperty('token_type', 'Bearer');
});
});
});
Load Testing
// Test SSO under load
async function loadTestSSO(concurrentUsers = 10) {
console.log(`Starting load test with ${concurrentUsers} concurrent users...`);
const promises = Array.from({ length: concurrentUsers }, async (_, i) => {
try {
const startTime = Date.now();
// Simulate user authentication
const token = await authsec.getClientCredentialsToken({
audience: 'https://api.yourapp.com'
});
const endTime = Date.now();
const duration = endTime - startTime;
console.log(`User ${i + 1}: Authentication completed in ${duration}ms`);
return { success: true, duration, user: i + 1 };
} catch (error) {
console.error(`User ${i + 1}: Authentication failed:`, error.message);
return { success: false, error: error.message, user: i + 1 };
}
});
const results = await Promise.all(promises);
// Analyze results
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log('\n--- Load Test Results ---');
console.log(`Total Users: ${concurrentUsers}`);
console.log(`Successful: ${successful.length}`);
console.log(`Failed: ${failed.length}`);
console.log(`Success Rate: ${(successful.length / concurrentUsers * 100).toFixed(2)}%`);
if (successful.length > 0) {
const avgDuration = successful.reduce((sum, r) => sum + r.duration, 0) / successful.length;
console.log(`Average Duration: ${avgDuration.toFixed(2)}ms`);
}
}
Error Testing
Test Error Scenarios
// Test common error scenarios
async function testErrorScenarios() {
console.log('Testing error scenarios...');
// Test invalid client ID
try {
const invalidClient = new AuthsecClient({
domain: 'your-domain.authsec.com',
clientId: 'invalid-client-id',
clientSecret: 'invalid-secret'
});
await invalidClient.getClientCredentialsToken();
console.error('✗ Should have failed with invalid credentials');
} catch (error) {
console.log('✓ Invalid credentials properly rejected:', error.error);
}
// Test invalid redirect URI
try {
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
redirect_uri: 'https://invalid-domain.com/callback'
});
// This would fail during actual redirect
console.log('⚠ Invalid redirect URI - would fail during callback');
} catch (error) {
console.log('✓ Invalid redirect URI rejected:', error.message);
}
// Test expired token
try {
const expiredToken = 'eyJ...'; // Mock expired token
await authsec.getUserInfo(expiredToken);
console.error('✗ Should have failed with expired token');
} catch (error) {
console.log('✓ Expired token properly rejected:', error.error);
}
}
Production Readiness Checklist
Security Checks
- HTTPS is enforced for all endpoints
- Certificates are valid and not expiring soon
- State parameter is used and validated (OIDC)
- Nonce parameter is used and validated (OIDC)
- PKCE is enabled for public clients
- SAML assertions are signed and validated
- Token lifetimes are appropriate
- Refresh token rotation is enabled
Performance Checks
- Authentication completes within acceptable time limits
- Silent authentication works correctly
- Token refresh works smoothly
- No memory leaks in long-running sessions
- Concurrent login handling works correctly
User Experience Checks
- Login flow is intuitive
- Error messages are user-friendly
- Logout works correctly
- Session timeout handling is graceful
- Mobile compatibility is verified
Monitoring Setup
- Authentication success/failure rates are tracked
- Performance metrics are monitored
- Error logs are captured and alerting is configured
- User experience metrics are measured
Troubleshooting Common Issues
OIDC Issues
- Invalid Redirect URI: Verify callback URLs match exactly
- Scope Not Granted: Check application scope configuration
- Token Validation Failed: Verify audience and issuer claims
- Silent Auth Failed: Check session state and iframe configuration
SAML Issues
- Certificate Validation Failed: Check certificate expiry and format
- Attribute Mapping Issues: Verify attribute names and namespaces
- Clock Skew: Synchronize server clocks
- Signature Verification Failed: Check signing certificates
General Issues
- Network Timeouts: Check network connectivity and firewall rules
- CORS Errors: Configure allowed origins properly
- Rate Limiting: Monitor API usage and implement backoff
- Session Issues: Verify session storage and configuration
Remember to test in a development environment first, then staging, before deploying to production.