Implementing role-based authorization in a Vue 3 SPA and a .NET Core API using Entra ID (formerly Azure AD) involves several steps. Here’s a detailed guide to set it up...
---
title: How to implement role-based authorization in Vue 3 using Entra ID authentication
subtitle: How can I handle token expiration and automatic token renewal in a Vue 3 SPA and a .NET Core API using Entra ID authentication?
author: Jon LaBelle
date: September 15, 2024
notoc: false
---
Implementing role-based authorization in a Vue 3 SPA and a .NET Core API using
Entra ID (formerly Azure AD) involves several steps. Here's a detailed guide to
set it up:
## Step 1: Register Applications in Entra ID
1. **Register the API**:
- Go to the Azure portal.
- Navigate to "Azure Active Directory" > "App registrations" > "New registration".
- Name your application (e.g., `MyApi`).
- Set the "Supported account types" to "Accounts in this organizational directory only".
- Click "Register".
- Note the "Application (client) ID" and "Directory (tenant) ID".
- Under "Expose an API", add a scope (e.g., `api://{client-id}/access_as_user`).
2. **Define App Roles**:
- Go to "Azure Active Directory" > "App registrations" > your API application.
- Under "App roles", define roles (e.g., `Admin`, `User`).
- Assign these roles to users in your Azure AD tenant.
3. **Register the SPA**:
- Go to "Azure Active Directory" > "App registrations" > "New registration".
- Name your application (e.g., `MySpa`).
- Set the "Supported account types" to "Accounts in this organizational directory only".
- Under "Redirect URI", select "Single-page application (SPA)" and enter your SPA URL (e.g., `http://localhost:8080`).
- Click "Register".
- Note the "Application (client) ID".
- Under "Authentication", add the redirect URI and enable "ID tokens" and "Access tokens".
- Under "API permissions", add the scope you defined for the API (e.g., `api://{api-client-id}/access_as_user`).
## Step 2: Configure the .NET Core API
1. **Install Required Packages**:
```bash
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.Identity.Web
```
2. **Configure Authentication in `appsettings.json`**:
```json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "yourdomain.onmicrosoft.com",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Audience": "api://your-api-client-id"
}
}
```
3. **Configure Authentication and Authorization in `Startup.cs`**:
```csharp
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy =>
policy.RequireRole("User"));
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
```
4. **Protect API Endpoints with Roles**:
```csharp
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProtectedController : ControllerBase
{
[HttpGet("admin")]
[Authorize(Policy = "AdminPolicy")]
public IActionResult GetAdminData()
{
return Ok("This is admin data.");
}
[HttpGet("user")]
[Authorize(Policy = "UserPolicy")]
public IActionResult GetUserData()
{
return Ok("This is user data.");
}
}
```
## Step 3: Configure the Vue 3 SPA
1. **Install Required Packages**:
```bash
npm install @azure/msal-browser @azure/msal-vue
```
2. **Configure MSAL in Vue**:
```javascript
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import { msalPlugin } from '@azure/msal-vue';
import { PublicClientApplication } from '@azure/msal-browser';
const msalConfig = {
auth: {
clientId: 'your-spa-client-id',
authority: 'https://login.microsoftonline.com/your-tenant-id',
redirectUri: 'http://localhost:8080'
},
cache: {
cacheLocation: 'localStorage', // This is important to persist the tokens
storeAuthStateInCookie: true
}
};
const msalInstance = new PublicClientApplication(msalConfig);
const app = createApp(App);
app.use(msalPlugin, msalInstance);
app.mount('#app');
```
3. **Add Login and Logout Methods**:
```javascript
// src/components/Login.vue
<template>
<div>
<button @click="login">Login</button>
<button @click="logout">Logout</button>
</div>
</template>
<script>
import { useMsal } from '@azure/msal-vue';
export default {
setup() {
const { instance } = useMsal();
const login = () => {
instance.loginPopup({
scopes: ['openid', 'profile', 'api://your-api-client-id/access_as_user']
});
};
const logout = () => {
instance.logoutPopup();
};
return { login, logout };
}
};
</script>
```
4. **Check User Roles and Protect Routes**:
```javascript
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import Admin from '../views/Admin.vue';
import { useMsal } from '@azure/msal-vue';
const routes = [
{ path: '/', name: 'Home', component: Home },
{
path: '/admin',
name: 'Admin',
component: Admin,
meta: { requiresAuth: true, roles: ['Admin'] }
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
router.beforeEach(async (to, from, next) => {
const { instance, accounts } = useMsal();
const account = accounts.value[0];
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!account) {
next('/');
} else {
const response = await instance.acquireTokenSilent({
scopes: [
'openid',
'profile',
'api://your-api-client-id/access_as_user'
],
account: account
});
const token = response.idTokenClaims;
const roles = token.roles || [];
if (
to.meta.roles &&
!to.meta.roles.some((role) => roles.includes(role))
) {
next('/');
} else {
next();
}
}
} else {
next();
}
});
export default router;
```
5. **Display User Roles in Components**:
```javascript
// src/components/UserInfo.vue
<template>
<div>
<p v-if="roles.includes('Admin')">You are an Admin</p>
<p v-else>You are a User</p>
</div>
</template>
<script>
import { useMsal } from '@azure/msal-vue';
export default {
setup() {
const { accounts } = useMsal();
const account = accounts.value[0];
const roles = account.idTokenClaims.roles || [];
return { roles };
}
};
</script>
```
## Summary
1. **Register Applications in Entra ID**: Register both the API and SPA applications in Azure AD and define roles.
2. **Configure .NET Core API**: Set up JWT authentication, define authorization policies, and protect API endpoints based on roles.
3. **Configure Vue 3 SPA**: Set up MSAL for authentication, implement login and logout methods, check user roles, and protect routes based on roles.
By following these steps, you can implement role-based authorization in your Vue
3 SPA and .NET Core API using Entra ID authentication.