How can I handle refresh tokens in a Vue 3 SPA and a .NET Core API using Entra ID authentication?
---
title: How to handle refresh tokens in a Vue 3 SPA and .NET Core API using Entra ID
subtitle: How can I handle refresh tokens in a Vue 3 SPA and a .NET Core API using Entra ID authentication?
author: Jon LaBelle
date: September 15, 2024
notoc: false
---
Handling refresh tokens 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. **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 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();
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**:
```csharp
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProtectedController : ControllerBase
{
[HttpGet]
[Authorize]
public IActionResult GetProtectedData()
{
return Ok("This is protected 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: ['api://your-api-client-id/access_as_user']
});
};
const logout = () => {
instance.logoutPopup();
};
return { login, logout };
}
};
</script>
```
4. **Call Protected API Endpoints and Handle Token Refresh**:
```javascript
// src/components/ProtectedData.vue
<template>
<div>
<button @click="getProtectedData">Get Protected Data</button>
<p>{{ data }}</p>
</div>
</template>
<script>
import { useMsal } from '@azure/msal-vue';
import axios from 'axios';
export default {
data() {
return {
data: ''
};
},
setup() {
const { instance, accounts } = useMsal();
const getProtectedData = async () => {
const account = accounts.value[0];
try {
const response = await instance.acquireTokenSilent({
scopes: ['api://your-api-client-id/access_as_user'],
account: account
});
const token = response.accessToken;
const result = await axios.get('http://localhost:5000/api/protected', {
headers: {
Authorization: `Bearer ${token}`
}
});
this.data = result.data;
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
instance.acquireTokenPopup({
scopes: ['api://your-api-client-id/access_as_user'],
account: account
}).then(response => {
const token = response.accessToken;
axios.get('http://localhost:5000/api/protected', {
headers: {
Authorization: `Bearer ${token}`
}
}).then(result => {
this.data = result.data;
});
});
}
}
};
return { getProtectedData };
}
};
</script>
```
### Summary
1. **Register Applications in Entra ID**: Register both the API and SPA applications in Azure AD.
2. **Configure .NET Core API**: Set up JWT authentication and protect API endpoints.
3. **Configure Vue 3 SPA**: Set up MSAL for authentication, persist tokens, and handle token refresh.
By following these steps, you can handle refresh tokens in your Vue 3 SPA and .NET Core API using Entra ID authentication.