← Back to list

Micro-Frontends Part 3: Implementing Okta Authentication Across Next.js Micro-Frontends

In Part 2, I walked through a hands-on setup for micro-frontends using Next.js + Webpack Module Federation, with a shell application…

Goutam Singha · 2026-04-28 18:24 · 0 claps · 3.3 min read paywalled
#micro-frontends #nextjs #authentication #okta #software-architecture
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🏛️ · Architecture

Micro-Frontends Part 3: Implementing Okta Authentication Across Next.js Micro-Frontends

OKTA LOGIN IN MICROFRONTENDS

OKTA LOGIN IN MICROFRONTENDS

In Part 2, I walked through a hands-on setup for micro-frontends using Next.js + Webpack Module Federation, with a shell application composing remote applications at runtime.

A common follow-up question was:

How do you implement authentication across multiple micro-frontends without every app building its own login flow?

The short answer:

Authenticate once in the shell, share user context to remotes, and enforce authorization at both shell and domain level.

In this article, I’ll walk through an end-to-end approach using Okta + Next.js + Module Federation.

Architecture Goal

We want this flow:

User authenticates with Okta
↓
Shell establishes shared user session
↓
Customer, Order, Cart and Admin apps consume shared auth context
↓
Access is controlled by roles and claims
↓
Domain APIs enforce fine-grained permissions

Single sign-on. No duplicate login. No multiple sessions.

Why Authentication Should Live in the Shell

A common anti-pattern is letting each micro-frontend manage login.

That creates:

  • Multiple auth implementations
  • Session inconsistencies
  • Token duplication
  • Access-control drift

Instead:

Shell owns:
- Customer sign-in
- Shared session for Order, Cart and Admin apps
- Role-based access to each domain
- Protection of private routes
- Authentication context shared to all remotes

Remotes trust the shell.

Step 1: Configure Okta Application

Create an OIDC app in Okta.

Example settings:

Application Type: Single Page App
Grant Type: Authorization Code + PKCE
Redirect URI:
http://localhost:3000/login/callback
Logout URI:
http://localhost:3000

Capture:

Client ID
Issuer URL

Example:

NEXT_PUBLIC_OKTA_ISSUER=https://dev-xxxx.okta.com/oauth2/default
NEXT_PUBLIC_OKTA_CLIENT_ID=xxxx

Step 2: Install Dependencies

npm install @okta/okta-auth-js @okta/okta-react

Step 3: Create Shared Auth Package

Instead of putting auth logic inside one app, create a shared package.

packages/auth-sdk

AuthContext

import React,
{
 createContext,
 useContext
} from 'react';
const AuthContext =
 createContext(null);
export function AuthProvider({
 user,
 token,
 children
}) {
 return (
  <AuthContext.Provider
   value={{ user, token }}
  >
   {children}
  </AuthContext.Provider>
 );
}
export function useAuth(){
 const ctx = useContext(AuthContext);
 if(!ctx){
  throw new Error(
   'useAuth must be inside AuthProvider'
  );
 }
 return ctx;
}

This will be shared across shell and remotes.

Step 4: Initialize Okta in Shell

import { OktaAuth }
from '@okta/okta-auth-js';
export const oktaAuth =
 new OktaAuth({
  issuer:
   process.env.NEXT_PUBLIC_OKTA_ISSUER,
  clientId:
   process.env.NEXT_PUBLIC_OKTA_CLIENT_ID,
  redirectUri:
   window.location.origin +
   '/login/callback'
 });

Step 5: Wrap Shell with Security Provider

import {
 Security
} from '@okta/okta-react';
import {
 AuthProvider
} from '@org/auth-sdk';
<Security oktaAuth={oktaAuth}>
 <AuthProvider
   user={user}
   token={accessToken}
 >
   <RemoteApps />
 </AuthProvider>
</Security>

Now every remote can consume the same authenticated user.

Step 6: Share Auth Package as Singleton

Critical for Module Federation.

shared: {
 '@org/auth-sdk': {
   singleton:true
 },
 react:{ singleton:true },
 'react-dom':{
   singleton:true
 }
}

If not shared as singleton, remotes may get a different context instance.

That breaks authentication.

Step 7: Consume User Context in Remote MFE

import {
 useAuth
} from '@org/auth-sdk';
function ComplianceApp(){
 const {
  user,
  token
 } = useAuth();
 return (
   <div>
    Welcome {user.name}
   </div>
 );
}

Remote did not implement login.

It consumed shared session.

Step 8: Protect Routes in Shell

Example Next.js middleware:

import { NextResponse }
from 'next/server';
export function middleware(req){
 const token =
  req.cookies.get('token');
 if(!token){
  return NextResponse.redirect(
   new URL('/login', req.url)
  );
 }
 return NextResponse.next();
}

Private routes stay protected.

Step 9: Add Role-Based Access

Example claims:

{
 "roles":["customer"],
 "apps":["customer","cart","orders"]
}

Hide unauthorized domains.

if(
 !apps.includes('order')
){
 return <AccessDenied />
}

You can also filter sidebar entries.

Step 10: Use Token in Domain APIs

Remotes use shared token.

fetch('/api/order',{
 headers:{
  Authorization:
   `Bearer ${token}`
 }
})

Backend still validates scopes.

Never trust frontend-only checks.

End-to-End Flow

User clicks Login
↓
Redirect to Okta
↓
Okta authenticates
↓
Shell receives access token
↓
Shell creates AuthProvider
↓
Remote consumes useAuth()
↓
Remote calls APIs with token

Single login. Shared access.

What About Different Permissions Per Domain?

Use layered authorization.

Level 1: Shell decides if user can access the micro-frontend.

Level 2: Domain backend enforces fine-grained permissions.

Example:

Customer can access Cart
Admin can access Admin Console

Handled in domain service.

Common Pitfalls

1. Each MFE implementing its own login

Avoid it.

Centralize authentication.

2. Not sharing AuthContext as singleton

Can break context.

3. Frontend-only authorization

Backend must validate claims.

4. Storing tokens separately in every MFE

Keep trust centralized.

Architecture Summary

                Okta
                  |
                Shell
     (login, claims, route protection)
                  |
   --------------------------------------
   |                |                   |
Product MFE      Cart MFE           Order MFE
All consume shared AuthContext

Final Takeaway

For authentication in micro-frontends, I would keep three rules:

  1. Authenticate once in the shell.
  2. Share user context to remotes through a shared auth package.
  3. Enforce authorization in both shell and domain backends.

That keeps authentication centralized, scalable, and secure.

In the next part, I’ll cover how to handle shared state and cross-micro-frontend communication without introducing tight coupling.

MicroFrontends #NextJS #Okta #Authentication #ModuleFederation #SingleSignOn #SSO #ReactJS #SoftwareArchitecture #FrontendArchitecture #WebSecurity #OAuth2 #OIDC #RoleBasedAccessControl #RBAC #DistributedSystems #EnterpriseArchitecture


메타데이터
post_id
be4e72639a2f
slug
micro-frontends-part-3-implementing-okta-authentication-across-next-js-micro-frontends-be4e72639a2f
url
https://medium.com/@GoutamSingha/micro-frontends-part-3-implementing-okta-authentication-across-next-js-micro-frontends-be4e72639a2f
canonical_url
https://medium.com/@GoutamSingha/micro-frontends-part-3-implementing-okta-authentication-across-next-js-micro-frontends-be4e72639a2f
author_url
https://medium.com/@GoutamSingha
status
ok
fetched_at
2026-06-20 20:29:01