Authentication with MSAL in Nuxt3
Unlocking the Power of MSAL: Seamlessly Secure Your Nuxt 3 App
If you’re here, you’re probably acquainted with MSAL, the authentication library for Microsoft services. If not, don’t fret! Get set for a journey full of discovery.
Table of Content
- What is MSAL?
- Advantages of MSAL
- Creating a Nuxt 3 Project Setup
- Registering an application on Microsoft Entra Admin Center
- Configuring MSAL in the Nuxt3 application
What is MSAL?
MSAL stands for Microsoft Authentication Library. It’s a set of libraries that developers can use to enable authentication and access Microsoft services in their applications. MSAL supports various authentication scenarios, including sign-in, sign-out, and token acquisition.
Advantages of MSAL
- Simplified authentication
- Integration with Microsoft Identity platform
- Cross-platform support
- Token management
- Secure authentication flows
- Scalability and performance
Creating a Nuxt 3 project setup
Step 1: Create a new Nuxt 3 project
Navigate to the desired directory, then open a terminal and use the following command to create a new starter project:
npx nuxi@latest init msal-nuxt3
After executing this command in the terminal, you’ll see the following question: “Which package manager would you like to use?” The options are “npm, pnpm, yarn, bun”. You’ll then need to select your preferred package manager. In this case, I am choosing npm.
- Open your project folder in Visual Studio Code.
code msal-nuxt3
2. Or change the directory into your new project from your terminal.
cd msal-nuxt3
Step 2: Installing dependencies and establishing folder structure
- Install dependencies: We will install @azure/msal-browser. The
@azure/msal-browser
package is a part of the Microsoft Authentication Library (MSAL) designed specifically for browser-based applications.
npm i @azure/msal-browser
2. Folder structure: Create the files following the folder structure shown below:
.
├── app.vue
├── assets
│ ├── css
│ │ └── main.css
│ └── images│
│ └── microsoft.png
├── middleware
│ └── auth.global.js
├── nuxt.config.ts
├── package.json
├── package-lock.json
├── pages
│ ├── index.vue
│ └── login.vue
├── plugins
│ └── msal.js
├── public
│ └── favicon.ico
├── README.md
├── server
│ └── tsconfig.json
└── tsconfig.json
If you prefer using the starter template instead of creating the files and folders from scratch, you can clone the template from this GitHub repository: GitHub.
After cloning the repository, switch to starter-template
branch, and run the command npm i
and you’re all set to blast off! 🚀
Now, we need to configure nuxt.config.ts
as follows:
//nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
css: ['~/assets/css/main.css'],
plugins: [
{ src: '~/plugins/msal.js', mode: 'client' },
],
})
Registering an application on Microsoft Entra Admin Center
First, log in to Microsoft Entra Admin Center: https://entra.microsoft.com/. Then you need to register your application. Here’s the video how you can register your application on Microsoft Entra Admin Center:
Configuring MSAL in the Nuxt3 application
Step1: Configuration of MSAL instance
Within the root folder, create a file named .env
and configure it as follows:
VITE_CLIENT_ID=/*Your Application (client) ID */
VITE_REDIRECT_URI= /* Your Redirect URI */
You’ll find the Application (client) ID in the application you registered on the MSAL Admin Center. Additionally, the Redirect URI should be the one you provided during the application registration process.
Now, we need to import PublicClientApplication from ‘@azure/msal-browser.’
The PublicClientApplication class is the object exposed by the library to perform authentication and authorization functions in Single Page Applications to obtain JWT tokens as described in the OAuth 2.0 Authorization Code Flow with PKCE specification.
Create a config object in plugins/msal.js
that contains configuration settings for authentication using Microsoft Azure Active Directory (Azure AD).
//plugins/msal.js
import { PublicClientApplication } from '@azure/msal-browser'
export default defineNuxtPlugin(async () => {
const config = {
auth:
{
clientId: import.meta.env.VITE_CLIENT_ID,
authority: 'https://login.microsoftonline.com/organizations/',
redirectUri: import.meta.env.VITE_REDIRECT_URI,
}
}
})
Now create the new instance by using the above config object.
// plugins/msal.js
const msal = new PublicClientApplication(config)
await msal.initialize()
Step 2: SignIn setup
You need an authenticated user context before you can get tokens to access APIs in your application. You can sign in users to your application in MSAL.js in two ways:
- Pop-up window, by using the
loginPopup
method - Redirect, by using the
loginRedirect
method
Here, we are going to use the loginPopup method.
//plugins/msal.js
const login = async () => {
let loginRequest = {
scopes: ['user.read'],
}
try {
let loginResponse = await msal.loginPopup(loginRequest)
return loginResponse
}
catch (err) {
console.log(err);
}
}
Step 3: Acquire a token to call an API
To silently acquire an access token for a given set of scopes and to return currently processing promise if parallel requests are made, we will use the acquireTokenSilent()
method.
// plugins/msal.js
let tokenResponse
const acquireTokenSilent = async () => {
const account = msal.getAllAccounts()
if (account.length > 0) {
let tokenRequest = {
scopes: ['user.read'],
account: account[0]
}
tokenResponse = await msal.acquireTokenSilent(tokenRequest)
return tokenResponse
}
else {
return null
}
}
const getAccounts = () => {
return msal.getAllAccounts()
}
In MSAL (Microsoft Authentication Library), getAllAccounts()
is a method used to retrieve all the accounts that are currently signed in or have interacted with the application.
Step 4: Retrieving the profile information and image of the user
To retrieve the profile information, we use Microsoft Graph API.
Microsoft Graph: It is a RESTful web API that enables you to access Microsoft Cloud service resources. After you register your app and get authentication tokens for a user or service, you can make requests to the Microsoft Graph API.
To get the profile information of a specific user, we will hit this endpoint: https://graph.microsoft.com/v1.0/me
Below is the method for retrieving the profile information:
// plugins/msal.js
const profileInfo = async () => {
let payload = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${tokenResponse.accessToken}`,
},
})
let json = await payload.json()
return json
}
To get the photo of the currently authenticated user, we will use the following Microsoft Graph API: https://graph.microsoft.com/v1.0/me/photo/$value
Here is how you can retrieve the profile information:
// plugins/msal.js
const profileImg = async () => {
try {
let profileImageResponse = await fetch(
`https://graph.microsoft.com/v1.0/me/photo/$value`,
{
headers: {
Authorization: `Bearer ${tokenResponse.accessToken}`,
},
}
)
let imageUrl
if (profileImageResponse.ok) {
let blob = await profileImageResponse.blob()
imageUrl = URL.createObjectURL(blob)
} else {
console.error(
'Failed to fetch profile image:',
profileImageResponse.statusText
)
}
return { data: imageUrl, error: null }
}
catch (error) {
return { data: null, error: error }
}
}
Step 5: Sign out user
The logout process for MSAL takes two steps:
- Clear the MSAL cache
- Clear the session on the identity server
You can sign out the user to your application in MSAL.js in two ways:
- Pop-up window, by using the
logoutPopup
method - Redirect, by using the
logoutRedirect
method
Here, we will use the logoutRedirect()
method:
// plugins/msal.js
const logout = async () => {
const token = await acquireTokenSilent()
const homeAccountId = token.account.homeAccountId
const currentAccount = msal.getAccount(homeAccountId)
await msal.logoutRedirect({ account: currentAccount })
}
Ensure all the functions are properly exported from the plugin, like this:
// plugins/msal.js
return {
provide: {
login,
acquireTokenSilent,
getAccounts,
profileInfo,
profileImg,
logout
}
}
Here’s the link to the plugins/msal.js file. Your plugins/msal.js file should look similar to that structure.
If you’ve made it this far in my blog, you’ve already conquered more than half the battle.
Step 6: Middleware configuration
Now, it’s time to configure the middleware. Create a folder named middleware
under the root folder, and within it, create a file named auth.global.js
.
// middleware/ auth.global.js
export default defineNuxtRouteMiddleware(async (to, from) => {
if (process.server) return;
const { $acquireTokenSilent } = useNuxtApp();
const accessToken = (await $acquireTokenSilent())?.accessToken;
if (to.name !== "login" && !accessToken) {
return navigateTo("/login");
} else if (to.name === "login" && accessToken) {
return navigateTo("/");
} else {
return;
}
});
This middleware function checks if a user is authenticated by attempting to acquire an access token silently by using $acquireTokenSilent()
method. If the user is not authenticated and tries to access a page other than the login page, they are redirected to the login page. Conversely, if the user is authenticated and tries to access the login page, they are redirected to the home page. This middleware ensures that unauthorized users are directed to the login page and authenticated users can access authorized routes seamlessly.
Step 7: Configure pages and assets
Now it's time to configure index.vue and login.vue files.
pages/ login.vue
This component renders a button allowing users to log in using Microsoft 365 authentication. Upon clicking the button, the $login
function is called, triggering the authentication process. After successful authentication, users are redirected to the index page. Additionally, the clearSiteData
function is invoked to clear any existing cookies, local storage, and session storage to ensure a clean authentication state.
<template>
<ClientOnly >
<div class="login-container">
<div class="image">
<img src="~/assets/images/microsoft.png" height="180"/>
</div>
<div class="text">
<h1>MSAL Authentication in Nuxt3</h1>
</div>
<div class="button">
<button class="btn" @click="loginUser">Login with Microsoft 365</button>
</div>
</div>
</ClientOnly>
</template>
<script setup>
const {$login} = useNuxtApp()
const loginUser =async () =>{
clearSiteData()
const loginResponse = await $login()
if (loginResponse) navigateTo('/')
}
function clearSiteData() {
document.cookie.split(";").forEach((cookie) => {
const [name, _] = cookie.split("=").map((c) => c.trim());
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
});
localStorage.clear();
sessionStorage.clear();
}
</script>
pages/index.vue
This Nuxt.js component displays user profile information, including the user’s profile image, name, email, job title, and phone number, if available. Additionally, it provides a logout button and shows a loading spinner while fetching the user profile data.
<template>
<div v-if="loading" class="loadData">
<div class="loader"></div>
</div>
<div class="home-container" v-else>
<div class="profile" v-if="profileImg">
<img :src="profileImg" width="250" class="profile-image"/>
</div>
<div class="initials-container" v-else>
<div class="initials">{{initials}}</div>
</div>
<div class="name">
<h1>Welcome, {{profile.displayName}}!</h1>
</div>
<div class="email">
<h4>{{profile.mail}}</h4>
</div>
<div class="username">
<h2>{{profile.jobTitle}}</h2>
</div>
<div class="phone" v-if="profile.mobilePhone">
<h4>Phone no. : {{profile.mobilePhone}}</h4>
</div>
<div class="button" @click="logout">
<button class="logout-btn">Logout</button>
</div>
</div>
</template>
<script setup>
const {$profileInfo,$profileImg,$logout} = useNuxtApp()
const profile = ref()
const profileImg = ref()
const loading = ref(true)
const logout =async ()=>{
await $logout()
}
onMounted(async()=>{
loading.value = true
const profileInfo = await $profileInfo()
profile.value = profileInfo
const {data,error} = await $profileImg()
if(data){
profileImg.value = data
}
if(error){
getInitials()
}
loading.value = false
})
const initials = ref()
const getInitials = () =>{
const nameArray = profile.value.displayName.split(' ')
const fletter = nameArray[0].charAt(0)
const lletter = nameArray[1].charAt(0)
initials.value = fletter+lletter
}
</script>
You have the option to customize the style according to your preferences by modifying the main.css
file located in assets/css/, or you can visit this link to copy the contents of the main.css file.
It’s finally time to see the application in action! You can do so by running the following command:
npm run dev
Take a look at the screenshots below to see MSAL authentication in action on Nuxt 3:
You can access the full source code on the GitHub repository using the following link: GitHub Repository 📌
You can see the demo here : https://msal-nuxt3.netlify.app/
Conclusion
In this guide, we explored integrating MSAL authentication into Nuxt 3 projects. We covered setting up, configuring MSAL, implementing sign-in/sign-out, profile information retrieval, profile image retrieval, and protecting routes. With MSAL, developers can seamlessly leverage Microsoft’s authentication capabilities, ensuring secure user experiences without complexity.
For more updates on the latest tools and technologies, follow the Simform Engineering blog.