azure-auth
from jezweb/claude-skills
Skills for Claude Code CLI such as full stack dev Cloudflare, React, Tailwind v4, and AI integrations.
213 stars24 forksUpdated Jan 26, 2026
npx skills add https://github.com/jezweb/claude-skills --skill azure-authSKILL.md
Azure Auth - Microsoft Entra ID for React + Cloudflare Workers
Package Versions: @azure/msal-react@5.0.2, @azure/msal-browser@5.0.2, jose@6.1.3 Breaking Changes: MSAL v4→v5 migration (January 2026), Azure AD B2C sunset (May 2025 - new signups blocked, existing until 2030), ADAL retirement (Sept 2025 - complete) Last Updated: 2026-01-21
Architecture Overview
┌─────────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ React SPA │────▶│ Microsoft Entra ID │────▶│ Cloudflare Worker │
│ @azure/msal-react │ │ (login.microsoft) │ │ jose JWT validation│
└─────────────────────┘ └──────────────────────┘ └─────────────────────┘
│ │
│ Authorization Code + PKCE │
│ (access_token, id_token) │
└──────────────────────────────────────────────────────────┘
Bearer token in Authorization header
Key Constraint: MSAL.js does NOT work in Cloudflare Workers (relies on browser/Node.js APIs). Use jose library for backend token validation.
Quick Start
1. Install Dependencies
# Frontend (React SPA)
npm install @azure/msal-react @azure/msal-browser
# Backend (Cloudflare Workers)
npm install jose
2. Azure Portal Setup
- Go to Microsoft Entra ID → App registrations → New registration
- Set Redirect URI to
http://localhost:5173(SPA type) - Note the Application (client) ID and Directory (tenant) ID
- Under Authentication:
- Enable Access tokens and ID tokens
- Add production redirect URI
- Under API permissions:
- Add
User.Read(Microsoft Graph) - Grant admin consent if required
- Add
Frontend: MSAL React Setup
Configuration (src/auth/msal-config.ts)
import { Configuration, LogLevel } from "@azure/msal-browser";
export const msalConfig: Configuration = {
auth: {
clientId: import.meta.env.VITE_AZURE_CLIENT_ID,
authority: `https://login.microsoftonline.com/${import.meta.env.VITE_AZURE_TENANT_ID}`,
redirectUri: window.location.origin,
postLogoutRedirectUri: window.location.origin,
navigateToLoginRequestUrl: true,
},
cache: {
cacheLocation: "localStorage", // or "sessionStorage"
storeAuthStateInCookie: true, // Required for Safari/Edge issues
},
system: {
loggerOptions: {
logLevel: LogLevel.Warning,
loggerCallback: (level, message) => {
if (level === LogLevel.Error) console.error(message);
},
},
},
};
// Scopes for token requests
export const loginRequest = {
scopes: ["User.Read", "openid", "profile", "email"],
};
// Scopes for API calls (add your API scope here)
export const apiRequest = {
scopes: [`api://${import.meta.env.VITE_AZURE_CLIENT_ID}/access_as_user`],
};
MsalProvider Setup (src/main.tsx)
import React from "react";
import ReactDOM from "react-dom/client";
import { PublicClientApplication, EventType } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
import { msalConfig } from "./auth/msal-config";
import App from "./App";
// CRITICAL: Initialize MSAL outside component tree to prevent re-instantiation
const msalInstance = new PublicClientApplication(msalConfig);
// Handle redirect promise on page load
msalInstance.initialize().then(() => {
// Set active account after redirect
// IMPORTANT: Use getAllAccounts() (returns array), NOT getActiveAccount() (returns single account or null)
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
msalInstance.setActiveAccount(accounts[0]);
}
// Listen for sign-in events
msalInstance.addEventCallback((event) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
const account = (event.payload as { account: any }).account;
msalInstance.setActiveAccount(account);
}
});
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
</React.StrictMode>
);
});
Protected Route Component
import { useMsal, useIsAuthenticated } from "@azure/msal-react";
import { InteractionStatus } from "@azure/msal-browser";
import { loginRequest } from "./msal-config";
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { instance, inProgress } = useMsal();
const isAuthenticated = useIsAuthenticated();
// Wait for MSAL to finish any in-progress operations
if (inProgress !== InteractionStatus.None) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
// Trigger login redirect
instance.loginRedirect(loginRequest);
return <div>Redirecting to login...</div>;
}
return <>{children}</>;
}
Acquiring Tokens for API Calls
...
Repository
jezweb/claude-skillsParent repository
Repository Stats
Stars213
Forks24
LicenseMIT License