Git Product home page Git Product logo

Comments (4)

mhg-sov avatar mhg-sov commented on May 18, 2024

Here's a workable solution I ended up with today.

//auth.js
import NextAuth from "next-auth"
import AzureAD from "next-auth/providers/azure-ad"
import Credentials from "next-auth/providers/credentials"

var providers = [];

if (process.env.NODE_ENV === "development") {
  providers.push(
    Credentials({
      id: "password",
      name: "Password",
      credentials: {
        password: { label: "", type: "password" },
      },
      authorize: (credentials) => {
        if (credentials.password === "password") {
          return {
            email: "[email protected]",
            name: "Test User",
            image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
          }
        }
      },
    })
  )
} else {

  providers.push(AzureAD({
    clientId: process.env.AUTH_AZURE_AD_ID,
    clientSecret: process.env.AUTH_AZURE_AD_SECRET,
    tenantId: process.env.AUTH_AZURE_AD_TENANT_ID,
    token: {
        url: `https://login.microsoftonline.com/${process.env.AUTH_AZURE_AD_TENANT_ID}/oauth2/v2.0/token`,
    },
    userinfo: {
        url: "https://graph.microsoft.com/oidc/userinfo",
    },
    authorization: {
        url: `https://login.microsoftonline.com/${process.env.AUTH_AZURE_AD_TENANT_ID}/oauth2/v2.0/authorize`,
        params: {
            scope: "openid email User.Read"
        }
    },
    async profile(profile, tokens) {
      const response = await fetch(
        `https://graph.microsoft.com/v1.0/me`,
        { headers: { Authorization: `Bearer ${tokens.access_token}` } }
      )
        
      const json = await response.json()
        
      return {
        id: profile.sub,
        name: profile.name,
        email: json.userPrincipalName,
      }
    },
}))

}

export const config = {
  debug: true,
  theme: {
    logo: "https://next-auth.js.org/img/logo/logo-sm.png",
  },
  providers: providers,
  callbacks: {
    authorized({ auth, request }) {

      //Allow unauthorized access to main page, /signin page
      //Users are redirected to /signin on logout or auth failures
      //https://next-auth.js.org/getting-started/client#signin
      //https://next-auth.js.org/getting-started/client#signout
      const whitelistURLs = ['/signin','/'];

      const { pathname } = request.nextUrl
      const isLoggedIn = !!auth?.user;
      if(!auth && pathname === '/signin'){
        return true;
      }

      if(!auth && !whitelistURLs.includes(pathname)){
        return false;
      }

      return true
    }
  }
  
} 

export const { handlers, auth, signIn, signOut } = NextAuth(config)
// /app/api/auth/[...nextauth]/route.js
import NextAuth from "next-auth"
import AzureAD from "next-auth/providers/azure-ad";
import Credentials from "next-auth/providers/credentials"

var providers = [];

if(process.env.ENV === 'development'){
    providers.push(Credentials)
} else {
    providers.push(AzureAD);
}

import { handlers } from "../../../../auth"
export const { GET, POST } = handlers

With these two in place i can pass the session down from the layout to the child components. Typescript was causing huge issues and changing auth.ts and [...nextauth].ts to javascript resolved all of the problems I had around types. There was no additional code required, in fact all I had to do was remove a few type statements. It took a lot of trial and error moving the provider configs between the [...nextauth].js and auth.js. Lack of examples in the docs that show the different use cases / possibilities is the issue here.

// /app/components/header.jsx
"use client"

import Link from 'next/link';
import Logo from './logo';

import { useState } from 'react';
import { Dialog, Menu } from '@headlessui/react';
import SignOutButton from './SignOutButton';
import SignInButton from './SignInButton';
import LocalSignInButton from './LocalSignInButton';

const navigation = [
  { name: 'Users', href: '/users', dropdown: false },
  { name: 'Organizations', href: '/organizations', dropdown: false },

];

export default function Header({ session }) {
  
  //From the template / component the layout started with
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
  
  const isLocalDev = ( process.env.ENV === 'development' ? true : false);
  
  //visual indicator  for dev / local  (red dev, blue prod)
  const signInElement = (isLocalDev ? <LocalSignInButton/> : <SignInButton/>);

// /src/app/layout.js
import { Inter } from 'next/font/google'
import './globals.css'
import Header from './components/header'
import Footer from './components/footer'

import { auth } from "../auth"

const inter = Inter({ subsets: ['latin'] })

export default async function RootLayout({ children }) {

  const session = await auth()
  if(session) session.environment = process.env.ENV

  return (
    <html lang="en">
      <body className={inter.className}>
        <Header session={session}/>
        <main className="mt-28 grid justify-center max-h-max">
        {children}
        </main>
        <Footer/>
      </body>
    </html>
  )
}

One of the most painful parts of this was the piecemeal nature of the information I needed to form this solution. I referenced both the v4 and v5 docs, it was extremely difficult to find more documentation on the auth options and in which files the config or just the imports belong.

The v5 docs need some sort of review from the perspective of "can somebody go through this page and actually use it".

What I have provided above is a complete enough solution that someone can get it working without spending days bouncing between v4 and v5 docs.

I hope someone can determine what from my solution is appropriate to add to the docs to address the concerns I raised above.

from next-auth.

ndom91 avatar ndom91 commented on May 18, 2024

Sorry you had such a hard time getting this setup, it looks like you've got some v4 and v5 things mixed up.

For v5 if you follow the docs step-by-step starting with a "clean" (i.e. remove all previous auth.js/next-auth related stuff) Next.js project from the "Installation" page, you should be good to go.

In v5, the .../auth/[...nextauth]/route.ts should only be 2 lines more or less:

import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers

The rest of the config goes in your ./auth.ts file, where you export the methods returned from NextAUth to be used elsewhere (like in the route file ^^). Also yuo dont need the callbacks for anything, that's all optional advanced stuff.

Also we have an example app you can always look at here

from next-auth.

ndom91 avatar ndom91 commented on May 18, 2024

For your testing use-case, I think you were on the right track.

Basically if NODE_ENV === 'development', push Credentials({ .. }) into your providers array as shown in the testing page.

image

from next-auth.

ndom91 avatar ndom91 commented on May 18, 2024

Also did you need to overwrite some AzureAD provider configuration for your specific use-case? If not, you can simplify that part to the following if you name your env vars as expected.

AUTH_AZURE_AD_ID
AUTH_AZURE_AD_SECRET
AUTH_AZURE_AD_TENANT_ID
// auth.ts
import NextAuth from "next-auth"
import AzureAD from "next-auth/providers/azure-ad"
import Credentials from "next-auth/providers/credentials"
import type { Provider } from "@auth/sveltekit/providers"

var providers: Provider[] = [];

if (process.env.NODE_ENV === "development") {
  providers.push(
    Credentials({
      id: "password",
      name: "Password",
      credentials: {
        password: { label: "", type: "password" },
      },
      authorize: (credentials) => {
        if (credentials.password === "password") {
          return {
            email: "[email protected]",
            name: "Test User",
            image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
          }
        }
      },
    })
  )
} else {
  providers.push(AzureAD)
}

export const { handlers, auth, signIn, signOut } = NextAuth({
  debug: true,
  providers,
})

I would do the authorized / session checking in the pages themselves. See: https://authjs.dev/getting-started/session-management/protecting

This is all just following the examples mostly in the "Installation", "Session Management/*" and "Testing" pages 😊

EDIT: Promise this is the last follow-up / FYI haha - we released an "Microsoft Entra ID" provider (which is mostly just a rebadged "AzureAD" provider) in [email protected] the other day

from next-auth.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.