src/azurechat/features/auth/auth-api.ts (91 lines of code) (raw):

import NextAuth, { NextAuthOptions } from "next-auth"; import { Provider } from "next-auth/providers"; import AzureADProvider from "next-auth/providers/azure-ad"; import GitHubProvider from "next-auth/providers/github"; import CredentialsProvider from "next-auth/providers/credentials"; import { hashValue } from "./helpers"; const configureIdentityProvider = () => { const providers: Array<Provider> = []; const adminEmails = process.env.ADMIN_EMAIL_ADDRESS?.split(",").map(email => email.toLowerCase().trim()); if (process.env.AUTH_GITHUB_ID && process.env.AUTH_GITHUB_SECRET) { providers.push( GitHubProvider({ clientId: process.env.AUTH_GITHUB_ID!, clientSecret: process.env.AUTH_GITHUB_SECRET!, async profile(profile) { const newProfile = { ...profile, isAdmin: adminEmails?.includes(profile.email.toLowerCase()) } return newProfile; } }) ); } if ( process.env.AUTH_ENTRA_CLIENT_ID && process.env.AUTH_ENTRA_CLIENT_SECRET && process.env.AZURE_TENANT_ID ) { providers.push( AzureADProvider({ clientId: process.env.AUTH_ENTRA_CLIENT_ID!, clientSecret: process.env.AUTH_ENTRA_CLIENT_SECRET!, tenantId: process.env.AZURE_TENANT_ID!, async profile(profile) { const newProfile = { ...profile, // throws error without this - unsure of the root cause (https://stackoverflow.com/questions/76244244/profile-id-is-missing-in-google-oauth-profile-response-nextauth) id: profile.sub, isAdmin: adminEmails?.includes(profile.email.toLowerCase()) || adminEmails?.includes(profile.preferred_username.toLowerCase()) } return newProfile; } }) ); } // If we're in local dev, add a basic credential provider option as well // (Useful when a dev doesn't have access to create app registration in their tenant) // This currently takes any username and makes a user with it, ignores password // Refer to: https://next-auth.js.org/configuration/providers/credentials if (process.env.NODE_ENV === "development") { providers.push( CredentialsProvider({ name: "localdev", credentials: { username: { label: "Username", type: "text", placeholder: "dev" }, password: { label: "Password", type: "password" }, }, async authorize(credentials, req): Promise<any> { // You can put logic here to validate the credentials and return a user. // We're going to take any username and make a new user with it // Create the id as the hash of the email as per userHashedId (helpers.ts) const username = credentials?.username || "dev"; const email = username + "@localhost"; const user = { id: hashValue(email), name: username, email: email, isAdmin: false, image: "", }; console.log("=== DEV USER LOGGED IN:\n", JSON.stringify(user, null, 2)); return user; } }) ); } return providers; }; export const options: NextAuthOptions = { secret: process.env.NEXTAUTH_SECRET, providers: [...configureIdentityProvider()], callbacks: { async jwt({token, user, account, profile, isNewUser, session}) { if (user?.isAdmin) { token.isAdmin = user.isAdmin } return token }, async session({session, token, user }) { session.user.isAdmin = token.isAdmin as string return session } }, session: { strategy: "jwt", }, }; export const handlers = NextAuth(options);