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

  1. Go to Fabrixly-IDS Admin ConsoleClients → select your client
  2. Under OAuth/OIDC Settings:
    • Grant Types: add refresh_token
    • Response Types: add code (for authorization code flow)
    • Scope: make sure to request offline_access in your auth requests!
  3. Save!

API Setup

Authenticating API Requests

Management API endpoints (like /api/clients, /api/users, etc.) require administrative authentication. To call them:

  1. 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

  1. Store Refresh Tokens Securely: Never store them in localStorage or frontend code!
  2. Always Use HTTPS: Never send refresh tokens over unencrypted connections!
  3. Rotate Refresh Tokens: Fabrixly-IDS does this by default!
  4. Short Expiry: Use short-lived access tokens and longer-lived refresh tokens!
  5. Revoke When Not Needed: Revoke refresh tokens when user logs out!

Troubleshooting

  • "No refresh token in response": Make sure you included offline_access in 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!

Subscribe to The Fabrixly Blog

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe