Skip to content
2 changes: 1 addition & 1 deletion samples/msal-browser-samples/ExpressSample/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CLIENT_ID=ENTER_CLIENT_ID_HERE
AUTHORITY=https://login.microsoftonline.com/ENTER_TENANT_ID_HERE
AUTHORITY=https://login.microsoftonline.com/common
REDIRECT_URI=http://localhost:3000
POST_LOGOUT_REDIRECT_URI=http://localhost:3000
CACHE_LOCATION=localStorage
1 change: 1 addition & 0 deletions samples/msal-browser-samples/ExpressSample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ npm run build:package
- Once authenticated, navigate to different pages to see how authentication state is preserved.
- **Use the account switcher** by clicking the user account dropdown and selecting "Switch Account" to see the account picker modal.
- The Profile page will automatically fetch and display your user information from MS Graph.
- Navigating to the `http://localhost:3000/playground` route will take you to the MSAL.js playground where you can experiment with different configurations and requests

## Learn more

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ body {
}

.container {
max-width: 1200px;
width: 100%;
margin: 0 auto;
padding: 0 20px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,97 +9,92 @@ import { msalInstance, handleProtectedRouteAuth, signOutRedirect } from './auth.
import { updateNavigation, updateUI } from './ui.js';
import { waitForElement, showSuccess } from './utils.js';
import { loadProfileData } from "./graph.js";
import { initializePlayground } from "./playground.js";

// Navigation function - SPA style routing
export function navigate(path) {
// Update URL without page reload
history.pushState(null, '', path);

// Load content via AJAX
loadPageContentSPA(path);
}

// Handle routing (for initial page load and browser back/forward)
export async function handleRouting() {
const path = window.location.pathname;

// Refresh authentication state before checking routes
updateUI(msalInstance.getActiveAccount());

// Check if route requires authentication
if (path === '/profile' && !msalInstance.getActiveAccount()) {
const authSuccess = await handleProtectedRouteAuth(path);
if (!authSuccess) {
return; // Authentication in progress or failed
}
}

// Update navigation active states and UI
updateNavigation();
updateUI(msalInstance.getActiveAccount());

// If on profile page and authenticated, load profile data
if (path === '/profile' && msalInstance.getActiveAccount()) {
try {
await loadProfileData();
} catch (error) {
console.error('Error loading profile data:', error);
}
}

// Handle page-specific logic for all routes
await handlePageSpecificLogic(path);
}

// Load page content via AJAX for SPA navigation
async function loadPageContentSPA(path) {
const spaContent = document.getElementById('spa-content');
const spaLoading = document.getElementById('spa-loading');

if (!spaContent) {
// Fallback to full page navigation if SPA container not found
window.location.href = path;
return;
}

try {
// Show loading indicator
if (spaLoading) {
spaLoading.style.display = 'block';
}

// Fetch content from server
const response = await fetch(getContentAPIPath(path), {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
}
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}

const html = await response.text();

// Update content area
spaContent.innerHTML = html;

// Update page title
updatePageTitle(path);

// Update navigation
updateNavigation();

// IMPORTANT: Update UI after content change to handle auth-required elements
updateUI(msalInstance.getActiveAccount());

// Handle specific page logic
await handlePageSpecificLogic(path);

} catch (error) {
console.error('Error loading SPA content:', error);

// Fallback to full page navigation on error
window.location.href = path;

} finally {
// Hide loading indicator
if (spaLoading) {
Expand All @@ -113,9 +108,10 @@ function getContentAPIPath(path) {
const pathMap = {
'/': '/api/content/home',
'/profile': '/api/content/profile',
'/logout': '/api/content/logout'
'/logout': '/api/content/logout',
'/playground': '/api/content/playground'
};

return pathMap[path] || path;
}

Expand All @@ -124,9 +120,10 @@ function updatePageTitle(path) {
const titleMap = {
'/': 'MSAL Express Sample - Home',
'/profile': 'MSAL Express Sample - Profile',
'/logout': 'MSAL Express Sample - Logout'
'/logout': 'MSAL Express Sample - Logout',
'/playground': 'MSAL Express Sample - Playground'
};

const newTitle = titleMap[path] || 'MSAL Express Sample';
document.title = newTitle;
}
Expand All @@ -141,11 +138,19 @@ async function handlePageSpecificLogic(path) {
} catch (error) {
console.error('Error loading profile data:', error);
}
} else if (path === '/playground') {
// Initialize playground page
try {
await waitForElement('.playground-container');
initializePlayground();
} catch (error) {
console.error('Error initializing playground:', error);
}
} else if (path === '/logout') {
try {
// Show immediate feedback to user
showSuccess('Signing you out...');

// Proceed with logout immediately
if (msalInstance.getActiveAccount()) {
signOutRedirect();
Expand All @@ -164,31 +169,31 @@ async function handlePageSpecificLogic(path) {
// Setup SPA navigation event listeners
export function setupSPANavigation() {
// Handle all SPA links
document.addEventListener('click', async function(e) {
document.addEventListener('click', async function (e) {
const target = e.target.closest('.spa-link');
if (target && target.classList.contains('spa-link')) {
e.preventDefault();

const path = target.getAttribute('href');
if (path) {
// Refresh authentication state before navigation
updateUI(msalInstance.getActiveAccount());

// Check authentication for protected routes
if (path === '/profile' && !msalInstance.getActiveAccount()) {
const authSuccess = await handleProtectedRouteAuth(path);
if (!authSuccess) {
return;
}
}

navigate(path);
}
}
});

// Handle browser back/forward navigation
window.addEventListener('popstate', function(e) {
window.addEventListener('popstate', function (e) {
const path = window.location.pathname;
loadPageContentSPA(path);
});
Expand Down
Loading
Loading