How to Use Refresh Tokens in Fabrixly-IDS
Overview
Refresh tokens let users stay logged in without re‑entering credentials! When an access token expires, use a refresh token to get a new access token (and a new refresh token)!
Step 1: Enable Refresh Tokens for Your Client
First, make sure your client is configured for refresh tokens:
Admin Console Setup
- Go to Fabrixly-IDS Admin Console → Clients → select your client
- Under OAuth/OIDC Settings:
- Grant Types: add
refresh_token - Response Types: add
code(for authorization code flow) - Scope: make sure to request
offline_accessin your auth requests!
- Grant Types: add
- Save!
API Setup
Authenticating API Requests
Management API endpoints (like /api/clients, /api/users, etc.) require administrative authentication. To call them:
- Include the Token in the Authorization Header: Add
-H "Authorization: Bearer <your-admin-jwt-token>"to your API calls.
Obtain an Admin JWT Token:
curl -X POST https://ids.fabrixly.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "your-admin-password"
}'
Note: This returns a JSON response containing a token.
curl -X POST https://ids.fabrixly.com/api/clients \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-admin-jwt-token>" \
-d '{
"client_name": "Refresh Token App",
"redirect_uris": ["http://localhost:3011/callback"],
"response_types": ["code"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic",
"auth_mode": "flexible"
}'
Step 2: Get Refresh Token
To get a refresh token, include offline_access in your scope when requesting authorization:
const authUrl = client.authorizationUrl({
scope: 'openid profile email offline_access', // <-- offline_access is key!
state: 'your-state',
nonce: 'your-nonce'
});
The token response will include refresh_token:
{
"access_token": "ey...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "some-refresh-token-string",
"id_token": "ey...",
"scope": "openid profile email offline_access"
}
Step 3: Use Refresh Token to Get New Access Token
When your access token expires, send the refresh token to the token endpoint:
Example: Using curl
curl -X POST https://ids.fabrixly.com/oidc/token \
-u "your-client-id:your-client-secret" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token&refresh_token=your-refresh-token"
Response includes new access token and new refresh token!
{
"access_token": "new-access-token...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "new-refresh-token..." // <-- always replace with new one!
}
Example: Using openid-client
Create refresh-token-app.js:
const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');
const app = express();
const PORT = 3011;
app.use(session({
secret: 'refresh-tokens-are-convenient',
resave: false,
saveUninitialized: true
}));
async function initClient() {
const issuer = await Issuer.discover('https://ids.fabrixly.com/oidc');
return new issuer.Client({
client_id: 'your-refresh-client-id',
client_secret: 'your-refresh-client-secret',
redirect_uris: ['http://localhost:3011/callback']
});
}
let client;
initClient().then(c => client = c);
// Middleware to auto-refresh expired tokens
app.use(async (req, res, next) => {
if (req.session.tokenSet && req.session.tokenSet.expired()) {
try {
console.log('Refreshing token...');
const newTokenSet = await client.refresh(req.session.tokenSet.refresh_token);
req.session.tokenSet = newTokenSet;
} catch (err) {
console.error('Error refreshing token:', err);
req.session.destroy();
}
}
next();
});
app.get('/', (req, res) => {
if (req.session.user) {
res.redirect('/dashboard');
} else {
res.send('<a href="/login">Login</a>');
}
});
app.get('/login', (req, res) => {
req.session.state = generators.state();
req.session.nonce = generators.nonce();
const authUrl = client.authorizationUrl({
scope: 'openid profile email offline_access', // Include offline_access!
state: req.session.state,
nonce: req.session.nonce
});
res.redirect(authUrl);
});
app.get('/callback', async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback(
'http://localhost:3011/callback',
params,
{ state: req.session.state, nonce: req.session.nonce }
);
req.session.tokenSet = tokenSet;
req.session.user = await client.userinfo(tokenSet.access_token);
res.redirect('/dashboard');
});
app.get('/dashboard', (req, res) => {
if (!req.session.user) return res.redirect('/');
const token = req.session.tokenSet;
res.send(`
<h1>Refresh Token Example</h1>
<p>Hello, ${req.session.user.email}!</p>
<p>Access token expires in: ${token.expires_in} seconds</p>
<p>Token expired? ${token.expired() ? 'Yes' : 'No'}</p>
<a href="/">Home</a>
`);
});
app.listen(PORT, () => console.log(`Refresh token app running on http://localhost:${PORT}`));
Refresh Token Rotation
Fabrixly-IDS automatically rotates refresh tokens! Every time you use a refresh token, you get a new one—always use the new refresh token for next time!
Refresh Token Expiry
Refresh tokens expire after a longer period of time! Configure via .env file:
REFRESH_TOKEN_EXPIRY_DAYS=7 # Refresh tokens expire after 7 days
Revoke Refresh Tokens
To revoke a refresh token (log user out of all devices), call the revocation endpoint:
curl -X POST https://ids.fabrixly.com/oidc/token/revocation \
-u "your-client-id:your-client-secret" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=your-refresh-token&token_type_hint=refresh_token"
Security Best Practices
- Store Refresh Tokens Securely: Never store them in localStorage or frontend code!
- Always Use HTTPS: Never send refresh tokens over unencrypted connections!
- Rotate Refresh Tokens: Fabrixly-IDS does this by default!
- Short Expiry: Use short-lived access tokens and longer-lived refresh tokens!
- Revoke When Not Needed: Revoke refresh tokens when user logs out!
Troubleshooting
- "No refresh token in response": Make sure you included
offline_accessin your scope! - "Invalid refresh token": Make sure you're using the latest refresh token (they rotate)!
- "Refresh token expired": User needs to re-authenticate!
Next Steps
- Implement token revocation on logout!
- Set appropriate expiry times for tokens!