| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
ย | ย | |||
ย | ย | |||
ย | ย | |||
Full-stack MERN ๐ฅญ (MongoDB, Express, React, Node.js) app ๐ secured via Microsoft Identity ๐ (Azure AD). Features ๐ง authentication with MSAL ๐ญ (frontend) & access token ๐๏ธ validation with jwks-rsa + jsonwebtoken (backend).
| ๐งฑ Layer | ๐งฐ Stack |
|---|---|
| ๐จ Frontend | โ๏ธ React + ๐ฆ React Router + ๐ MSAL React |
| ๐ง Backend | ๐ฉ Node.js + ๐งญ Express + ๐ jwks-rsa + ๐ชช JWT |
| ๐ฅ Identity | โ๏ธ Azure AD (Microsoft Identity) |
| ๐ Flow | ๐ Authorization Code Flow w/ PKCE |
1๏ธโฃ Go to Azure Portal โ ๐ Microsoft Entra ID โ ๐ App registrations โ โ New registration 2๏ธโฃ Name: new-app 3๏ธโฃ Account types: ๐ข My org only
App ID URI: api://<CLIENT_ID>
โ Scope:
Create server/.env:
PORT=5000
CLIENT_ID=your-client-id
TENANT_ID=your-tenant-id
CLIENT_SECRET=your-client-secretrequire('dotenv').config();
const express = require('express');
const cors = require('cors');
const auth = require('./middleware/auth');
const app = express();
app.use(cors({ origin: 'http://localhost:3000' }));
app.use(express.json());
app.get('/api/protected', auth, (req, res) => {
res.json({ message: '๐ Hello, protected!', user: req.user });
});
app.listen(process.env.PORT || 5000, () =>
console.log(`๐ Server on ${process.env.PORT}`)
);const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `https://login.microsoftonline.com/${process.env.TENANT_ID}/discovery/v2.0/keys`
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
callback(err, key.getPublicKey());
});
}
module.exports = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).send('๐ซ No token');
jwt.verify(token, getKey, {
audience: process.env.CLIENT_ID,
issuer: `https://login.microsoftonline.com/${process.env.TENANT_ID}/v2.0`,
algorithms: ['RS256'],
}, (err, decoded) => {
if (err) return res.status(401).send('๐ซ Unauthorized');
req.user = decoded;
next();
});
};๐ ๏ธ Install:
npm i @azure/msal-browser @azure/msal-react react-router-dom axiosexport const msalConfig = {
auth: {
clientId: 'your-client-id',
authority: 'https://login.microsoftonline.com/your-tenant-id',
redirectUri: 'http://localhost:3000',
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false,
}
};
export const loginRequest = {
scopes: ['api://your-client-id/access_as_user']
};import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import AutoLogout from './AutoLogout';
import { PublicClientApplication } from '@azure/msal-browser';
import { MsalProvider } from '@azure/msal-react';
import { msalConfig } from './authConfig';
const msalInstance = new PublicClientApplication(msalConfig);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<MsalProvider instance={msalInstance}>
<AutoLogout>
<App />
</AutoLogout>
</MsalProvider>
);import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './Home';
import Dashboard from './Dashboard';
import Navbar from './Navbar';
import ProtectedRoute from './ProtectedRoute';
function App() {
return (
<Router>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
</Routes>
</Router>
);
}
export default App;import { Navigate } from 'react-router-dom';
import { useIsAuthenticated } from '@azure/msal-react';
export default function ProtectedRoute({ children }) {
const isAuthenticated = useIsAuthenticated();
return isAuthenticated ? children : <Navigate to="/" replace />;
}import { useMsal } from '@azure/msal-react';
import { useEffect, useRef } from 'react';
export default function AutoLogout({ children }) {
const { instance } = useMsal();
const timer = useRef();
const reset = () => {
clearTimeout(timer.current);
timer.current = setTimeout(() => {
instance.logoutRedirect();
}, 2 * 60 * 1000);
};
useEffect(() => {
window.addEventListener('mousemove', reset);
window.addEventListener('keydown', reset);
reset();
return () => {
clearTimeout(timer.current);
window.removeEventListener('mousemove', reset);
window.removeEventListener('keydown', reset);
};
}, []);
return <>{children}</>;
}import { useMsal } from '@azure/msal-react';
import { loginRequest } from './authConfig';
export default function Home() {
const { instance } = useMsal();
return (
<div style={{ padding: 20, textAlign: 'center' }}>
<h1>๐ Welcome</h1>
<button onClick={() => instance.loginRedirect(loginRequest)}>
๐ Login with Microsoft
</button>
</div>
);
}import { useEffect, useState } from 'react';
import { useMsal, useIsAuthenticated } from '@azure/msal-react';
import { loginRequest } from './authConfig';
import axiosInstance from './axiosInstance';
export default function Dashboard() {
const { instance, accounts } = useMsal();
const isAuthenticated = useIsAuthenticated();
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
if (!isAuthenticated || accounts.length === 0) return;
try {
const authResult = await instance.acquireTokenSilent({
...loginRequest,
account: accounts[0],
});
const res = await axiosInstance.get('/protected', {
headers: { Authorization: `Bearer ${authResult.accessToken}` }
});
setData(res.data);
} catch (err) {
console.error(err);
}
};
fetchData();
}, [isAuthenticated, accounts]);
return (
<div style={{ padding: 20 }}>
<h2>๐ Dashboard</h2>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'โณ Loading...'}
</div>
);
}import { Link } from 'react-router-dom';
import { useMsal } from '@azure/msal-react';
export default function Navbar() {
const { instance, accounts } = useMsal();
const loggedIn = accounts.length > 0;
return (
<nav style={{ background: '#222', padding: 10, color: '#fff' }}>
<Link to="/" style={{ color: '#fff', marginRight: 20 }}>๐ Home</Link>
{loggedIn && (
<>
<Link to="/dashboard" style={{ color: '#fff', marginRight: 20 }}>๐ Dashboard</Link>
<span>{accounts[0].username}</span>
<button onClick={() => instance.logoutRedirect()} style={{ marginLeft: 20 }}>
๐ช Logout
</button>
</>
)}
</nav>
);
}import axios from 'axios';
const axiosInstance = axios.create({
baseURL: 'http://localhost:5000/api',
withCredentials: true
});
export default axiosInstance;| โ๏ธ Scenario | ๐ฏ Result |
|---|---|
| Access /dashboard w/o login | โช๏ธ Redirect to / |
| Login โ /dashboard | โ Shows protected ๐ |
| Missing token | โ 401 Unauthorized |
| Invalid token | โ 401 Unauthorized |
| Idle > 2 mins | ๐ Auto logout |
# ๐ง Backend
cd server
npm i
node server.js
# ๐ฅ๏ธ Frontend
npm i
npm start
| Back | FazBrowse Home | New Git URL |