How to Use Authorization Code Flow with Fabrixly-IDS
Overview
The Authorization Code Flow is the most secure and recommended flow for web applications with a backend. It exchanges an authorization code for tokens, keeping the client secret secure.
Prerequisites
- Fabrixly-IDS instance running
- OIDC client registered in Fabrixly-IDS
- Node.js (for example code)
Step 1: Register Your OIDC Client
First, create a client in Fabrixly-IDS via the Admin Console:
- Go to Clients > Create Client
- Save the client and note down the
client_idandclient_secret
Fill in these details:
{
"client_name": "My Web App",
"redirect_uris": ["http://localhost:3001/callback"],
"response_types": ["code"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic",
"auth_mode": "flexible",
"enable_mobile_login": false,
"otp_delivery_method": "email",
"requires_consent": false
}
Step 2: Configure Your App
Install dependencies:
npm install openid-client express express-session
Create app.js:
const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');
const app = express();
const PORT = 3001;
app.use(session({
secret: 'your-session-secret-keep-it-safe',
resave: false,
saveUninitialized: true
}));
// Step 3: Discover the Fabrixly-IDS Issuer
async function initClient() {
const issuer = await Issuer.discover('https://ids.fabrixly.com/oidc');
return new issuer.Client({
client_id: 'your-client-id',
client_secret: 'your-client-secret',
redirect_uris: ['http://localhost:3001/callback'],
response_types: ['code']
});
}
let client;
initClient().then(c => client = c);
// Step 4: Redirect to Fabrixly-IDS for Login
app.get('/login', async (req, res) => {
const codeVerifier = generators.codeVerifier();
const codeChallenge = generators.codeChallenge(codeVerifier);
// Store verifier in session
req.session.codeVerifier = codeVerifier;
req.session.state = generators.state();
req.session.nonce = generators.nonce();
const authUrl = client.authorizationUrl({
scope: 'openid profile email offline_access',
code_challenge: codeChallenge,
code_challenge_method: 'S256',
state: req.session.state,
nonce: req.session.nonce
});
res.redirect(authUrl);
});
// Step 5: Handle Callback and Exchange Code for Tokens
app.get('/callback', async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback(
'http://localhost:3001/callback',
params,
{
code_verifier: req.session.codeVerifier,
state: req.session.state,
nonce: req.session.nonce
}
);
// Store tokens in session
req.session.tokenSet = tokenSet;
// Get user info
const userInfo = await client.userinfo(tokenSet.access_token);
req.session.user = userInfo;
res.redirect('/dashboard');
});
// Step 6: Protected Route Using Access Token
app.get('/dashboard', (req, res) => {
if (!req.session.user) return res.redirect('/login');
res.send(`
<h1>Welcome, ${req.session.user.name}</h1>
<p>Email: ${req.session.user.email}</p>
<a href="/logout">Logout</a>
`);
});
// Step 7: Logout
app.get('/logout', async (req, res) => {
if (req.session.tokenSet) {
try {
// Revoke tokens
if (req.session.tokenSet.access_token) {
await client.revoke(req.session.tokenSet.access_token);
}
if (req.session.tokenSet.refresh_token) {
await client.revoke(req.session.tokenSet.refresh_token);
}
} catch (err) {
console.error('Error revoking tokens:', err);
}
}
req.session.destroy();
res.redirect('/');
});
app.listen(PORT, () => {
console.log(`App running on http://localhost:${PORT}`);
});
Step 8: Run the App
node app.js
Visit http://localhost:3001/login to test!
Using PKCE for Public Clients
For mobile or single-page apps (SPA), use PKCE (Proof Key for Code Exchange) which doesn't require a client secret:
// Configure client without client_secret
const client = new issuer.Client({
client_id: 'your-public-client-id',
redirect_uris: ['http://localhost:3001/callback'],
response_types: ['code'],
token_endpoint_auth_method: 'none'
});
// Use PKCE code verifier and challenge
// Same as Step 4
Token Refresh
Use refresh tokens to get new access tokens without user interaction:
// In your app, check if token is expired
if (req.session.tokenSet.expired()) {
const newTokenSet = await client.refresh(req.session.tokenSet.refresh_token);
req.session.tokenSet = newTokenSet;
}
API Reference
- Discovery URL:
https://ids.fabrixly.com/oidc/.well-known/openid-configuration - Authorization Endpoint:
/oidc/auth - Token Endpoint:
/oidc/token - UserInfo Endpoint:
/oidc/me - Token Introspection:
/oidc/token/introspection - Token Revocation:
/oidc/token/revocation - End Session:
/oidc/session/end
Best Practices
- Always use HTTPS in production
- Store refresh tokens securely
- Rotate refresh tokens (enabled by default in Fabrixly-IDS)
- Validate
stateandnonceto prevent CSRF - Use short-lived access tokens