Azure AD Authentication, Next-Auth JWT and .NET Core Web API

817 Views Asked by At

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;
  }
}
0

There are 0 best solutions below