How to Use Implicit Flow in Fabrixly-IDS

Overview

The Implicit Flow was designed for browser-based single-page apps (SPA) that can't keep a client secret secure. It returns tokens directly from the authorization endpoint.

⚠️ Important: Authorization Code Flow with PKCE is now recommended instead of Implicit Flow for SPAs! But we still support it for legacy apps.

Step 1: Configure Implicit Flow Client

First, set up your OIDC client for implicit flow:

Admin Console Setup

  1. Go to Fabrixly-IDS Admin ConsoleClients
  2. Create or select client → click Edit
  3. Under OAuth/OIDC Settings:
    • Response Types: add id_token, token (or both)
    • Grant Types: add implicit
    • Token Endpoint Auth Method: select none
    • Auth Mode: choose any auth mode you like
  4. 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": "Implicit Flow SPA",
    "redirect_uris": ["http://localhost:3009/callback"],
    "response_types": ["id_token", "token"],
    "grant_types": ["implicit"],
    "token_endpoint_auth_method": "none",
    "auth_mode": "flexible"
  }'

Step 2: Example Implicit Flow SPA

Create an HTML file implicit-spa.html:

<!DOCTYPE html>
<html>
<head>
  <title>Implicit Flow Demo</title>
  <style>
    .hidden { display: none; }
  </style>
</head>
<body>
  <h1>Implicit Flow Demo</h1>
  
  <div id="login-section">
    <button id="login-btn">Login</button>
  </div>
  
  <div id="user-section" class="hidden">
    <h2>Hello, <span id="user-name"></span></h2>
    <p>Email: <span id="user-email"></span></p>
    <p>Access Token: <span id="access-token"></span></p>
    <button id="logout-btn">Logout</button>
  </div>
  
  <script>
    const ISSUER_URL = 'https://ids.fabrixly.com/oidc';
    const CLIENT_ID = 'your-implicit-client-id';
    const REDIRECT_URI = 'http://localhost:3009/callback';
    const SCOPE = 'openid profile email';
    
    // Helper functions
    function generateRandomString(length) {
      const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      let result = '';
      for (let i = 0; i < length; i++) {
        result += chars.charAt(Math.floor(Math.random() * chars.length));
      }
      return result;
    }
    
    function parseHash(hash) {
      const params = {};
      const queryString = hash.substring(1);
      const regex = /([^&=]+)=([^&]*)/g;
      let m;
      while (m = regex.exec(queryString)) {
        params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
      }
      return params;
    }
    
    // Check for tokens in URL hash when page loads
    window.addEventListener('DOMContentLoaded', async () => {
      if (window.location.hash) {
        const params = parseHash(window.location.hash);
        
        if (params.access_token || params.id_token) {
          // Store tokens
          sessionStorage.setItem('access_token', params.access_token);
          sessionStorage.setItem('id_token', params.id_token);
          
          // Clear hash from URL
          window.history.replaceState({}, document.title, window.location.pathname);
          
          // Show user info
          await showUserInfo(params.access_token);
        }
      } else if (sessionStorage.getItem('access_token')) {
        await showUserInfo(sessionStorage.getItem('access_token'));
      } else {
        // Show login button
        document.getElementById('login-section').classList.remove('hidden');
      }
    });
    
    // Login handler
    document.getElementById('login-btn').addEventListener('click', () => {
      const state = generateRandomString(32);
      const nonce = generateRandomString(32);
      sessionStorage.setItem('state', state);
      sessionStorage.setItem('nonce', nonce);
      
      const authUrl = new URL(`${ISSUER_URL}/auth`);
      authUrl.searchParams.set('client_id', CLIENT_ID);
      authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
      authUrl.searchParams.set('response_type', 'id_token token');
      authUrl.searchParams.set('scope', SCOPE);
      authUrl.searchParams.set('state', state);
      authUrl.searchParams.set('nonce', nonce);
      
      window.location.href = authUrl.toString();
    });
    
    // Logout handler
    document.getElementById('logout-btn').addEventListener('click', () => {
      sessionStorage.clear();
      document.getElementById('login-section').classList.remove('hidden');
      document.getElementById('user-section').classList.add('hidden');
    });
    
    // Fetch user info
    async function showUserInfo(accessToken) {
      try {
        const response = await fetch(`${ISSUER_URL}/me`, {
          headers: { 'Authorization': `Bearer ${accessToken}` }
        });
        const user = await response.json();
        
        document.getElementById('user-name').textContent = user.name;
        document.getElementById('user-email').textContent = user.email;
        document.getElementById('access-token').textContent = accessToken.substring(0, 20) + '...';
        
        document.getElementById('login-section').classList.add('hidden');
        document.getElementById('user-section').classList.remove('hidden');
      } catch (err) {
        console.error('Error fetching user info:', err);
      }
    }
  </script>
</body>
</html>

Step 3: Serve the SPA

Run a simple HTTP server to serve implicit-spa.html:

npx http-server -p 3009

Visit http://localhost:3009/implicit-spa.html to test!

Implicit Flow Steps

  1. SPA redirects user to Fabrixly-IDS authorization endpoint
  2. User logs in (via any auth mode)
  3. Fabrixly-IDS redirects back to SPA with id_token and/or access_token in URL hash
  4. SPA parses tokens from hash
  5. SPA stores tokens in sessionStorage or localStorage
  6. SPA uses access token to call APIs

Security Considerations

  • Use HTTPS always: Tokens are in URL, so HTTPS is critical!
  • Short-lived tokens: Implicit flow tokens have short expiry
  • Don't store in localStorage: Prefer sessionStorage to limit token lifetime
  • Validate state and nonce: Prevents CSRF and replay attacks
  • Prefer Authorization Code Flow with PKCE: For new apps, use PKCE instead of implicit!

Troubleshooting

  • Tokens not in URL hash: Check client's response_types includes id_token and/or token
  • "Invalid redirect_uri": Verify redirect URI matches exactly what's in client config
  • CORS errors: Ensure your SPA domain is allowed by Fabrixly-IDS CORS settings

Next Steps

  • Try Authorization Code Flow with PKCE instead!
  • Add token refresh using silent authentication

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