Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation on Local Dev / Credentials Incomplete #10711

Closed
mhg-sov opened this issue Apr 24, 2024 · 4 comments
Closed

Documentation on Local Dev / Credentials Incomplete #10711

mhg-sov opened this issue Apr 24, 2024 · 4 comments
Labels
documentation Relates to documentation triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@mhg-sov
Copy link

mhg-sov commented Apr 24, 2024

What is the improvement or update you wish to see?

Desired Improvements: Testing missing examples on how to deal with dev / prod

https://authjs.dev/guides/testing

  1. NEEDS: Full examples of both /api/auth/[...nextauth]/route.js and /auth.ts
  2. NEEDS: A documented pattern for developers to have an app that communicates with an OAuth provider in production and does not require custom logic to develop locally against a different auth provider (Credentials / Password).

This feels like either a gaping hole of functionality / documentation re: local dev... or more likely I am REALLY missing something that should be obvious in the docs but I'm not getting it.

Problem:
Currently only a small snippet of /auth.js is available on the testing guide and there is not enough context or information to get a working solution.

What do I put in the [...nextauth]/route.js? For Azure AD I define a clientID, secret and tennant... but for credentials?

Also of note, the Credentials provider has zero documentation on what to put in that route. Should I just remove it?

https://authjs.dev/getting-started/providers/credentials

Is there any context that might help us understand?

When using an OAuth provider, it's not possible or easy (or documented how) to setup a next.js app to authenticate against a system like Azure AD or Auth0 because your system needs to do the callback stuff to process the flow correctly.

The solution provided in the docs makes sense... use a different provider.

I get tons of errors and things break and explode when I try to configure things as described in the if (process.env.NODE_ENV === "development") solution.

What it proposes is EXACTLY what I need, unfortunately the docs advertise code that doesn't work.

Does the docs page already exist? Please link to it.

https://authjs.dev/guides/testing

@mhg-sov mhg-sov added documentation Relates to documentation triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Apr 24, 2024
@mhg-sov
Copy link
Author

mhg-sov commented Apr 24, 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: "test@test.com",
            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.

@ndom91
Copy link
Member

ndom91 commented Apr 26, 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

@ndom91 ndom91 closed this as completed Apr 26, 2024
@ndom91
Copy link
Member

ndom91 commented Apr 26, 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

@ndom91
Copy link
Member

ndom91 commented Apr 26, 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: "test@test.com",
            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 next-auth@5.0.0-beta.17 the other day

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Relates to documentation triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests

2 participants