Before you begin implementing Single Sign-On (SSO) using Azure Active Directory (Azure AD) in your Vue.js application, ensure you have the following:
npm install -g @vue/cli
Navigate to the Azure Portal and sign in with your Azure account credentials.
Once signed in, go to the sidebar and select Azure Active Directory.
In the Azure Active Directory pane, select App registrations from the sidebar, then click on New registration.
Fill out the registration form:
http://localhost:8080 for local development.Click Register to create the application.
After registration, navigate to the Authentication section.
Under Redirect URIs, ensure your application's URI is listed. If not, add it.
Enable ID tokens by checking the box for Access tokens and ID tokens as part of the implicit grant flow.
From the application's overview page, note the following values as they will be needed in your Vue.js application:
vue create my-sso-app
cd my-sso-app
Install the Microsoft Authentication Library (MSAL) for JavaScript using npm:
npm install @azure/msal-browser
Create a new file named authConfig.js inside a dedicated src/auth directory to store your Azure AD configuration:
export const msalConfig = {
auth: {
clientId: "YOUR_CLIENT_ID", // Replace with your Azure AD app's Client ID
authority: "https://login.microsoftonline.com/YOUR_TENANT_ID", // Replace with your Tenant ID
redirectUri: "http://localhost:8080", // Replace with your app's redirect URI
},
cache: {
cacheLocation: "sessionStorage", // Can also be 'localStorage'
storeAuthStateInCookie: true, // Recommended for IE/Edge
},
};
export const loginRequest = {
scopes: ["User.Read"], // Define the permissions your app requires
};
Modify src/main.js to initialize MSAL and make it available throughout your Vue application:
import { createApp } from 'vue';
import App from './App.vue';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './auth/authConfig';
const msalInstance = new PublicClientApplication(msalConfig);
const app = createApp(App);
// Optionally, add MSAL instance to Vue's global properties
app.config.globalProperties.$msal = msalInstance;
app.mount('#app');
Create a new file named authService.js inside src/auth to manage authentication logic:
import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig, loginRequest } from "./authConfig";
class AuthService {
constructor() {
this.msalInstance = new PublicClientApplication(msalConfig);
}
async login() {
try {
const loginResponse = await this.msalInstance.loginPopup(loginRequest);
return loginResponse;
} catch (error) {
console.error("Login failed:", error);
return null;
}
}
logout() {
this.msalInstance.logout();
}
getAccount() {
const accounts = this.msalInstance.getAllAccounts();
if (accounts.length > 0) {
return accounts[0];
}
return null;
}
async acquireToken() {
const account = this.getAccount();
if (!account) {
return null;
}
const tokenRequest = {
scopes: ["User.Read"],
account: account,
};
try {
const tokenResponse = await this.msalInstance.acquireTokenSilent(tokenRequest);
return tokenResponse.accessToken;
} catch (error) {
console.warn("Silent token acquisition failed, acquiring token via popup:", error);
try {
const tokenResponse = await this.msalInstance.acquireTokenPopup(tokenRequest);
return tokenResponse.accessToken;
} catch (popupError) {
console.error("Token acquisition via popup failed:", popupError);
return null;
}
}
}
}
export const authService = new AuthService();
Create a new Vue component named Login.vue inside src/components to handle user login and logout:
<template>
<div>
<button v-if="!isAuthenticated" @click="login">Login</button>
<div v-else>
<p>Welcome, {{ userName }}!</p>
<button @click="logout">Logout</button>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { authService } from '../auth/authService';
export default {
name: 'Login',
setup() {
const isAuthenticated = ref(false);
const userName = ref('');
const login = async () => {
const response = await authService.login();
if (response) {
isAuthenticated.value = true;
userName.value = response.account.username;
}
};
const logout = () => {
authService.logout();
isAuthenticated.value = false;
userName.value = '';
};
onMounted(() => {
const account = authService.getAccount();
if (account) {
isAuthenticated.value = true;
userName.value = account.username;
}
});
return {
isAuthenticated,
userName,
login,
logout
};
}
};
</script>
If Vue Router isn't already installed in your project, install it using npm:
npm install vue-router@4
Create or modify src/router/index.js to set up route protection:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../components/Home.vue';
import Login from '../components/Login.vue';
import { authService } from '../auth/authService';
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { requiresAuth: true }
},
{
path: '/login',
name: 'Login',
component: Login
},
// Add more routes as needed
];
const router = createRouter({
history: createWebHistory(),
routes
});
// Navigation Guard to Protect Routes
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth) {
const account = authService.getAccount();
if (!account) {
await authService.login();
const newAccount = authService.getAccount();
if (newAccount) {
next();
} else {
next('/login');
}
} else {
next();
}
} else {
next();
}
});
export default router;
Modify src/main.js to use the router:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './auth/authConfig';
const msalInstance = new PublicClientApplication(msalConfig);
const app = createApp(App);
app.config.globalProperties.$msal = msalInstance;
app.use(router);
app.mount('#app');
To make authenticated API calls, you need to acquire access tokens. Use the authService created earlier to handle token acquisition:
async function fetchUserProfile() {
const token = await authService.acquireToken();
if (token) {
try {
const response = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
console.log('User Profile:', data);
} catch (error) {
console.error('Error fetching user profile:', error);
}
} else {
console.error('No access token available.');
}
}
Modify Home.vue to fetch and display the user's profile:
<template>
<div>
<h1>Home</h1>
<div v-if="userProfile">
<p>Name: {{ userProfile.displayName }}</p>
<p>Email: {{ userProfile.mail || userProfile.userPrincipalName }}</p>
</div>
<div v-else>
<p>Loading user profile...</p>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { authService } from '../auth/authService';
export default {
name: 'Home',
setup() {
const userProfile = ref(null);
const fetchUserProfile = async () => {
const token = await authService.acquireToken();
if (token) {
try {
const response = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: {
'Authorization': `Bearer ${token}`
}
});
userProfile.value = await response.json();
} catch (error) {
console.error('Error fetching user profile:', error);
}
}
};
onMounted(() => {
fetchUserProfile();
});
return {
userProfile
};
}
};
</script>
Start the development server to test your SSO integration:
npm run serve
Open your browser and navigate to http://localhost:8080. You should see the login button if you're not authenticated.
If you encounter issues during authentication, consider the following troubleshooting steps:
authConfig.js.
Implementing Single Sign-On (SSO) using Azure Active Directory in a Vue.js application enhances security and provides a seamless authentication experience for users. By following the steps outlined in this guide, you can integrate Azure AD authentication effectively, manage user sessions securely, and protect routes within your application. Ensure to keep your dependencies updated and monitor authentication flows to maintain a robust and secure application.
This guide provides a comprehensive approach to implementing Single Sign-On (SSO) using Azure Active Directory in a Vue.js application. By following these steps, you can ensure secure authentication flows, protect sensitive routes, and provide a seamless user experience.