I've set up a Next.js app to authenticate to Azure AD using Next-Auth, and this is successfully returning a JWT token. I'd like to use this token for authentication in a .NET Core Web API project but I'm getting a 401 Unauthorized error with the WWW-Authenticate header returning Bearer error="invalid_token", error_description="The signature key was not found". I'm using the same secret for the value of both the NEXTAUTH_SECRET and the IssuerSigningKey, as I assume this is the key that Next-Auth signs the JWT with, but I don't know if this is even correct or not. If anyone knows a resource that shows the proper configuration for this or if they can spot a configuration error in my code it's appreciated.
[...nextauth].ts:
import NextAuth, { NextAuthOptions } from "next-auth"
import AzureADProvider from "next-auth/providers/azure-ad";
export const authOptions: NextAuthOptions = {
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID!,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
tenantId: process.env.AZURE_AD_TENANT_ID,
authorization: {
params: {
scope:
`access_as_user api://${process.env.AZURE_AD_CLIENT_ID}/access_as_user`,
},
}
}),
],
theme: {
colorScheme: "light",
},
callbacks: {
async jwt({ token, user, account, profile, isNewUser }) {
if (account) {
token.access_token = account.access_token;
}
return token;
},
async session({ session, token }) {
return { ...session };
},
},
}
export default NextAuth(authOptions)
Program.cs:
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])), // Same as NEXTAUTH_SECRET
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidIssuer = "localhost"
};
}).AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"), "AzureAd");
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(o => o.AddPolicy("default", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
weather.ts:
import { getToken } from 'next-auth/jwt'
import type { NextApiRequest, NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET }) // get JWT token from request
try {
return new Promise(async (resolve, reject) => {
var apiUrl = `${process.env.API_BASE_URL}/weatherForecast/get`;
const response = await fetch(apiUrl, { // example URL
method: "GET",
credentials: "include",
headers: {
Authorization: `Bearer ${token?.access_token}`, // <-- add token to request
"Content-Type": "application/json",
}
});
const json = await response.json();
res.send(JSON.stringify(json, null, 2));
});
} catch (e) {
var error = e;
}
}