DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Angular vs. React: Which To Choose for Front-End in 2024
  • Secure Your API With JWT: Kong OpenID Connect
  • The Role of JavaScript Frameworks in Modern Web Development
  • Security Best Practices for ReactJS in Web App Development

Trending

  • Persistent Memory for AI Agents Using LangChain's Deep Agents
  • 5 Common Security Pitfalls in Serverless Architectures
  • GenAI Implementation Isn't Magic — It’s a Lifecycle
  • Prompt Injection Is Real, So I Built a Python Firewall for LLM Pipelines
  1. DZone
  2. Coding
  3. JavaScript
  4. Integrating OpenID Connect (OIDC) Authentication in Angular and React

Integrating OpenID Connect (OIDC) Authentication in Angular and React

This article shows how to integrate OIDC using Authorization Code Flow with PKCE — the recommended approach for SPAs — in Angular and React.

By 
Renjith Kathalikkattil Ravindran user avatar
Renjith Kathalikkattil Ravindran
·
Apr. 06, 26 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
3.1K Views

Join the DZone community and get the full member experience.

Join For Free

OpenID Connect (OIDC) is an identity layer on top of OAuth 2.0. If you’ve used “Sign in with Google/Microsoft/Okta/Auth0”, you’ve already used OIDC. In modern single-page apps (SPAs), the best practice is:

  • Authorization Code Flow + PKCE
  • Store tokens in memory (avoid localStorage when possible)
  • Use the provider’s well-known discovery document
  • Protect routes and attach access tokens to API calls

This guide shows an end-to-end setup for both Angular and React.

Why Authorization Code Flow + PKCE?

For SPAs, Authorization Code Flow with PKCE is the most secure and widely recommended option because:

  • No client secrets are exposed in the browser
  • Protects against authorization code interception
  • Works with modern identity providers (Okta, Auth0, Azure AD, Keycloak, Google)
  • Aligns with OAuth 2.1 and security best practices

Prerequisites from Your Identity Provider

Before integrating OIDC, configure an SPA client in your Identity Provider (IdP) and collect:

  • Issuer / Authority URL
  • Client ID
  • Redirect URI
  • Angular: http://localhost:4200/auth/callback
  • React: http://localhost:3000/auth/callback
  • Post-logout Redirect URI
  • Scopes: openid profile email (+ API scopes if required)

Ensure the client:

  • Uses PKCE
  • Is marked as a public / SPA client
  • Does not use a client secret

Part 1: OpenID Connect in Angular

Recommended Library

angular-oauth2-oidc
A mature and production-tested OIDC library for Angular.

1) Install Dependencies

TypeScript
 
 
npm i angular-oauth2-oidc


2. Configure OIDC Settings

Create auth.config.ts:

TypeScript
 
import { AuthConfig } from 'angular-oauth2-oidc';

export const authConfig: AuthConfig = {
  issuer: 'https://YOUR_ISSUER',                 // e.g. https://idp.example.com/realms/myrealm
  clientId: 'YOUR_CLIENT_ID',
  redirectUri: window.location.origin + '/auth/callback',
  postLogoutRedirectUri: window.location.origin + '/',
  responseType: 'code',
  scope: 'openid profile email',
  strictDiscoveryDocumentValidation: false,      // set true if issuer matches exactly and uses https in prod
  showDebugInformation: true,                    // disable in production
  requireHttps: false,                           // only for local dev on http
};


3. Create an Authentication Service

Now create auth.service.ts:

TypeScript
 
import { Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { authConfig } from './auth.config';

@Injectable({ providedIn: 'root' })
export class AuthService {
  constructor(private oauthService: OAuthService) {}

  async init(): Promise<void> {
    this.oauthService.configure(authConfig);
    this.oauthService.setupAutomaticSilentRefresh();

    // Loads discovery document and tries login via redirect callback
    await this.oauthService.loadDiscoveryDocumentAndTryLogin();
  }

  login(): void {
    this.oauthService.initCodeFlow();
  }

  logout(): void {
    this.oauthService.logOut();
  }

  get isLoggedIn(): boolean {
    return this.oauthService.hasValidAccessToken();
  }

  get accessToken(): string {
    return this.oauthService.getAccessToken();
  }

  get idTokenClaims(): object | null {
    return this.oauthService.getIdentityClaims() || null;
  }
}


4) Initialize Auth on App Startup

In app.module.ts:

TypeScript
 
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { OAuthModule } from 'angular-oauth2-oidc';
import { AppComponent } from './app.component';
import { AuthService } from './auth/auth.service';

export function initAuth(auth: AuthService) {
  return () => auth.init();
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    OAuthModule.forRoot({
      resourceServer: {
        allowedUrls: ['http://localhost:8080/api'], // your backend API
        sendAccessToken: true,
      },
    }),
  ],
  providers: [
    { provide: APP_INITIALIZER, useFactory: initAuth, deps: [AuthService], multi: true },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}


5) Add a Callback Route

Create a route that your redirect URI points to:

TypeScript
 
// app-routing.module.ts
const routes: Routes = [
  { path: 'auth/callback', component: EmptyCallbackComponent },
  // ...
];


EmptyCallbackComponent can simply show a spinner.

6) Protect Routes with a Guard

TypeScript
 
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private auth: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (!this.auth.isLoggedIn) {
      this.auth.login();
      return false;
    }
    return true;
  }
}


Use it:

TypeScript
 
{ path: 'dashboard', canActivate: [AuthGuard], component: DashboardComponent }


Part 2: OpenID Connect in React

Recommended Library

react-oidc-context
Built on oidc-client-ts, lightweight and idiomatic for React.

1) Install Dependencies

TypeScript
 
npm i react-oidc-context


2) Wrap Your App with AuthProvider

TypeScript
 
// main.tsx or index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { AuthProvider } from "react-oidc-context";
import App from "./App";

const oidcConfig = {
  authority: "https://YOUR_ISSUER",
  client_id: "YOUR_CLIENT_ID",
  redirect_uri: window.location.origin + "/auth/callback",
  post_logout_redirect_uri: window.location.origin + "/",
  response_type: "code",
  scope: "openid profile email",
};

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <AuthProvider {...oidcConfig}>
      <App />
    </AuthProvider>
  </React.StrictMode>
);


3) Add a Callback Page

TypeScript
 
// AuthCallback.tsx
import { useEffect } from "react";
import { useAuth } from "react-oidc-context";

export default function AuthCallback() {
  const auth = useAuth();

  useEffect(() => {
    // react-oidc-context automatically processes the callback on this route
  }, []);

  if (auth.isLoading) return <div>Signing you in…</div>;
  if (auth.error) return <div>Error: {auth.error.message}</div>;

  // Once user is loaded, route away (your router logic here)
  return <div>Signed in. You can close this page.</div>;
}


4) Create a Simple Route Guard Pattern

TypeScript
 
import { useAuth } from "react-oidc-context";

export function RequireAuth({ children }: { children: React.ReactNode }) {
  const auth = useAuth();

  if (auth.isLoading) return <div>Loading…</div>;
  if (!auth.isAuthenticated) {
    auth.signinRedirect();
    return null;
  }
  return <>{children}</>;
}


Usage:

XML
 
<RequireAuth>
  <Dashboard />
</RequireAuth>


5) Call APIs with the Access Token

TypeScript
 
import { useAuth } from "react-oidc-context";

export function useApi() {
  const auth = useAuth();

  return async (url: string) => {
    const token = auth.user?.access_token;
    const res = await fetch(url, {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    });
    return res.json();
  };
}


Token Storage: What to Do (And What to Avoid)

For SPAs, storing tokens in localStorage is convenient but increases risk if an XSS bug exists.

Preferred options:

  • In-memory storage (default in many libs)
  • Use httpOnly secure cookies (requires a backend “BFF” pattern)

If your app is high-security (healthcare/finance), strongly consider the BFF pattern.

Common Pitfalls (And Quick Fixes)

“Invalid redirect_uri”

  The redirect URI must match exactly what you configured in the IdP.

CORS issues calling your API

Your API must allow your SPA origin and accept Authorization header.

Logout doesn’t fully log out

Some IdPs require an id_token_hint or specific end-session endpoint (the OIDC library usually handles this if discovery is correct).

Silent refresh fails

Check allowed iframe origins / third-party cookies, and consider refresh-token rotation or short sessions.

Production Checklist

  • Use https in production (no exceptions)
  • Turn off debug logging
  • Validate issuer and discovery doc
  • Use least-privilege scopes
  • Protect routes + secure APIs with JWT validation
  • Consider BFF for sensitive apps
OpenID AngularJS authentication React (JavaScript library)

Opinions expressed by DZone contributors are their own.

Related

  • Angular vs. React: Which To Choose for Front-End in 2024
  • Secure Your API With JWT: Kong OpenID Connect
  • The Role of JavaScript Frameworks in Modern Web Development
  • Security Best Practices for ReactJS in Web App Development

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook