I have two websites:
1. Main Site: Developed using Next.js with NextAuth for authentication.
2. Child Site: Developed using React with MSAL-React for authentication.
Both applications share the same Azure AD client ID and have the same subdomain.
Authentication Flow
• When a user logs in on the main site, it stores the accessToken in a cookie with the subdomain scope.
• The child site can retrieve this accessToken from the cookie.
Goal
I want the child site to skip the login process in MSAL-React and use the accessToken stored in the cookie instead.
Question
Can I achieve this authentication bypass using only an accessToken, or do I need additional configurations?
Current Approach in the Child Site
• Retrieve accessToken from the cookie.
• If the token exists, store it in sessionStorage and skip MSAL authentication.
• Otherwise, proceed with the MSAL authentication flow, attempting ssoSilent before falling back to a redirect-based login.
my msal-react config looks like this
export const msalConfig: Configuration = {
auth: {
// 'Application (client) ID' of app registration in Azure portal - this value is a GUID
clientId: import.meta.env.VITE_MSAL_CLIENT_ID,
// Full directory URL, in the form of https://login.microsoftonline.com/<tenant-id>
authority: `https://login.microsoftonline.com/${import.meta.env.VITE_MSAL_TENANT_ID}`,
// Full redirect URL, in form of http://localhost:3000
redirectUri: isMobile
? window.location.origin + '/login'
: window.location.origin + '/oauth2/redirect',
navigateToLoginRequestUrl: true,
},
cache: {
cacheLocation: 'localStorage', // This configures where your cache will be stored
storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
},
system: {
allowNativeBroker: false, // Disables WAM Broker
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return
}
if (!(window as any).showMsalLog) {
return
}
switch (level) {
case LogLevel.Error:
console.error(message)
return
case LogLevel.Info:
console.info(message)
return
case LogLevel.Verbose:
console.debug(message)
return
case LogLevel.Warning:
console.warn(message)
return
default:
return
}
},
},
allowRedirectInIframe: true,
tokenRenewalOffsetSeconds: 300, // 5 minutes before token expiry
// pollIntervalMilliseconds: 0,
},
}
and what I’m trying in child project is like this
useEffect(() => {
const initializeAuth = async () => {
try {
// Get token from cookie if available
const getCookieValue = (name: any) => {
const value = `; ${document.cookie}`
const parts = value.split(`; ${name}=`)
if (parts.length === 2) return parts?.pop()?.split(';').shift()
return null
}
// Check if accessToken exists in cookies
const tokenFromCookie = getCookieValue('accessToken')
if (tokenFromCookie) {
// Use token from cookie and skip MSAL auth
console.log('Using token from cookie')
sessionStorage.setItem('accessToken', tokenFromCookie)
setIsInitialized(true)
return
}
// Proceed with normal MSAL authentication flow
await instance.handleRedirectPromise()
const accounts = instance.getAllAccounts()
if (accounts.length > 0) {
instance.setActiveAccount(accounts[0])
setIsInitialized(true)
} else {
try {
const silentRequest = {
...loginRequest,
loginHint: '',
redirectUri: window.location.origin + '/oauth2/redirect',
}
await instance
.ssoSilent(silentRequest)
.then((response) => {
console.log('Silent login success', response.accessToken)
sessionStorage.setItem('accessToken', response.accessToken)
})
.catch((error) => {
console.error('Silent login failed', error)
throw error
})
const accountsAfterSso = instance.getAllAccounts()
if (accountsAfterSso.length > 0) {
instance.setActiveAccount(accountsAfterSso[0])
}
} catch (e) {
console.error('Silent login failed', e)
}
}
} catch (error) {
console.error('Auth initialization failed:', error)
} finally {
setIsInitialized(true)
}
}
initializeAuth()
}, [instance])