Reputation: 23
I am new to SvelteKit and i am trying to use MSAL.js with SvelteKit, the issue is i want to implement something similar to an AuthGuard/HttpInterceptor which checks to see if the user is still logged in as they navigate around the SPA or call the external API.
I am using the OAuth 2.0 authorization code flow in Azure Active Directory B2C
within in my auth.ts
file i have the following code
let accountId: string = "";
const signIn = () => {
try {
msalInstance.loginRedirect(loginRequestWithApiReadWrite);
} catch (error) {
isAuthenticated.set(false);
console.warn(error);
}
}
// This captures the response from using the redirect flow to login
await msalInstance.handleRedirectPromise()
.then(response => {
if (response) {
if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) {
handleResponse(response);
}
}
})
.catch(error => {
console.log(error);
});
async function handleResponse(response: msal.AuthenticationResult) {
if (response !== null) {
user.set(response);
isAuthenticated.set(true);
setAccount(response.account);
} else {
selectAccount();
}
}
function selectAccount() {
const currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts.length < 1) {
return;
} else if (currentAccounts.length > 1) {
const accounts = currentAccounts.filter(account =>
account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase())
&&
account.idTokenClaims?.iss?.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase())
&&
account.idTokenClaims.aud === msalConfig.auth.clientId
);
if (accounts.length > 1) {
if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) { console.log("Multiple accounts belonging to the user detected. Selecting the first one.");
setAccount(accounts[0]);
} else {
console.log("Multiple users accounts detected. Logout all to be safe.");
signOut();
};
} else if (accounts.length === 1) {
setAccount(accounts[0]);
}
} else if (currentAccounts.length === 1) {
setAccount(currentAccounts[0]);
}
}
// in case of page refresh
selectAccount();
function setAccount(account: msal.AccountInfo | null) {
if (account) {
accountId = account.homeAccountId;
}
}
const authMethods = {
signIn,
getTokenRedirect
}
In a +page.svelte
file i can then import the authMethods
no problem, MSAL redirects me to the microsoft sign in page, i get redirected back and can then request an access token and call external API, great all is well.
<script lang='ts'>
import authMethods from '$lib/azure/auth';
<script>
<button on:click={authMethods.signIn}>Sign In</button>
However, the issue i am having is trying to implement this so i can check to see if the user is logged in against Azure B2C using a hook.server.ts
file automatically. I would like to check a variable to see if the user is authenticated and if they arnt the hooks.server will redirect them to signUp by calling the authMethod within the hook, and the user will be automatically redirected to the sign in page.
In the hooks.server.ts
i have the following code:
export const handle: Handle = (async ({ event, resolve }) => {
if (isAuthenticated === false) {
authRedirect.signIn();
msalInstance.handleRedirectPromise().then((response) => {
if (response) {
console.log('login with redirect succeeded: ', response)
isAuthenticated = true;
}
}).catch((error) => {
console.log('login with redirect failed: ', error)
})
}
const response = await resolve(event);
return response;
}) satisfies Handle;
When i navigate around the SvelteKit SPA, MSAL.js keeps throwing the error below, which i know is because i am running the code from the server flow rather than in the browser, so it was my understanding that if i implement the handleRedirectPromise() in both the auth.ts
file and hooks.server.ts
this would await the response from the signIn event and so long as i got a response i can then set isAuthenticated to true.
errorCode: 'non_browser_environment',
errorMessage: 'Login and token requests are not supported in non-browser environments.',
subError: ''
Upvotes: 2
Views: 2608
Reputation: 3
Are you required to use the MSAL library? I have got it working with https://authjs.dev/. I was using Active Directory -https://authjs.dev/reference/oauth-providers/azure-ad but there is also a flow for B2C https://authjs.dev/reference/oauth-providers/azure-ad-b2c which I haven't tried.
Then in the hooks.server.js you can do something like the below.
import { sequence } from '@sveltejs/kit/hooks';
import { redirect } from '@sveltejs/kit';
import { SvelteKitAuth } from '@auth/sveltekit';
import AzureADProvider from '@auth/core/providers/azure-ad';
import {
AZURE_AD_CLIENT_ID,
AZURE_AD_CLIENT_SECRET,
AZURE_AD_TENANT_ID
} from '$env/static/private'
const handleAuth = SvelteKitAuth({
providers: [
AzureADProvider({
clientId: AZURE_AD_CLIENT_ID,
clientSecret: AZURE_AD_CLIENT_SECRET,
tenantId: AZURE_AD_TENANT_ID
})
]
});
async function isAuthenticatedUser({ event, resolve }) {
const session = await event.locals.getSession();
if (!session?.user && event.url.pathname !== '/') {
throw redirect(302, '/');
} else if (session?.user && event.url.pathname === '/') {
throw redirect(302, '/dashboard');
}
const response = await resolve(event);
return response;
}
export const handle = sequence(handleAuth, isAuthenticatedUser);
Upvotes: 0