Skip to main content

Security best practices

1. Always use HTTPS​

All communication with Klubero SSO must use HTTPS. HTTP requests will be rejected.

✓ https://your-sso-domain.com/connect/authorize
✗ http://your-sso-domain.com/connect/authorize

2. Implement PKCE for public clients​

Single Page Applications and mobile apps must use PKCE:

// Generate a code verifier and challenge
const verifier = generateCodeVerifier(); // Random string, 43-128 characters
const challenge = await sha256(verifier); // SHA256 hash

// Include in the authorization request
const authUrl = `https://your-sso-domain.com/connect/authorize?
code_challenge=${challenge}&
code_challenge_method=S256`;

// Include the verifier in the token request
const tokenResponse = await fetch('/connect/token', {
body: `code_verifier=${verifier}&...`
});

3. Validate the state parameter​

Always generate and validate the state parameter to prevent CSRF attacks:

// Before redirecting
const state = generateRandomString(32);
sessionStorage.setItem('oauth_state', state);

// After the callback
const returnedState = new URLSearchParams(location.search).get('state');
const savedState = sessionStorage.getItem('oauth_state');

if (returnedState !== savedState) {
throw new Error('State mismatch - possible CSRF attack');
}

4. Store tokens securely​

Client typeStorage recommendation
Server-side applicationServer-side session or an encrypted cookie
SPAMemory (not localStorage), or an httpOnly cookie via a BFF
Mobile appSecure keychain/keystore
Desktop applicationOS credential manager
Never store tokens in
  • localStorage (vulnerable to XSS)
  • Plain cookies (vulnerable to CSRF)
  • URL parameters
  • Browser history

5. Never expose the client secret​

The client secret must never be exposed in:

  • Frontend/client-side code
  • Version control (use environment variables)
  • Logs
  • Error messages
  • URLs

6. Validate tokens​

Always validate tokens before trusting them:

// 1. Verify the signature using JWKS
// 2. Check that the issuer (iss) matches your SSO server
// 3. Check that the audience (aud) matches your client_id
// 4. Check that the expiration (exp) is in the future
// 5. Check that the nonce matches (if you use one)

7. Handle token expiration​

function isTokenExpired(token, bufferSeconds = 60) {
const payload = JSON.parse(atob(token.split('.')[1]));
const expiresAt = payload.exp * 1000;
return Date.now() >= expiresAt - (bufferSeconds * 1000);
}

// Refresh proactively before expiration
if (isTokenExpired(accessToken, 300)) { // 5-min buffer
accessToken = await refreshToken();
}

8. Use minimal scopes​

Request only the scopes your application actually needs:

# Good - minimal scopes
scope=openid profile email

# Avoid - requesting everything
scope=openid profile email phone address offline_access

9. Implement proper sign-out​

When signing out:

  1. Revoke the refresh token (on the server)
  2. Clear all stored tokens
  3. Redirect to the SSO logout endpoint
  4. Clear the application session

10. Monitor security events​

Watch for these events, which may indicate security issues:

  • Multiple failed sign-in attempts
  • Token refresh from a new IP/device
  • Unusual scope requests
  • Sessions from an unexpected location