Search code examples
next.jsnext-auth

Next Auth Credentials Provider - SignIn() and Mapping to my API Dto


I am using Next.js and Next Auth to talk to my backend C# ASP.NET API.

My API's response is the following DTO:

{
  "data": {
    "accessToken": "string",
    "refreshToken": "string",
    "email": "[email protected]",
    "username": "string",
    "roles": [
    "string"
    ]
  },
  "success": true,
  "message": "string"
 }

I am having a hard time getting that info into the next auth session so that I can grab it with useSession().

I'd also like to be able to display the API "message" to the user in the login form. Incase their account is locked or whatever.

This is what I have:

[...nextauth].js

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { API_URL } from "@/constants";

export const authOptions = {
  // Configure one or more authentication providers
  providers: [
    // Add Your Providers Here
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        username: { label: "Username", type: "text", placeholder: "jsmith" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials, req) {
        const { usernme, password } = credentials;

        const body = JSON.stringify({
          username,
          password,
        });

        // Login request to our API.
        const res = await fetch(`${API_URL}/Login`, {
          method: "POST",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json; charset=utf-8",
          },
          body: body,
        });

        const data = await res.json();

        // Our API's response contains
        /*
                    {
                    "data": {
                        "accessToken": "string",
                        "refreshToken": "string",
                        "email": "[email protected]",
                        "username": "string",
                        "roles": [
                        "string"
                        ]
                    },
                    "success": true,
                    "message": "string"
                    }
                */

        const user = {
          success: data.success,
          message: data.message,
          email: data.data.email,
          username: data.data.username,
          accessToken: data.data.accessToken,
          refreshToken: data.data.refreshToken,
          roles: data.data.roles,
        };

        // EVERYTHING TO HERE IS GOOD!
        // I CAN GET THE user OBJECT FILLED.

        if (res.ok && user) {
          return user; //<---- is this actually returning the full user object to the session?
        } else {
          return null;
        }
      },
    }),
  ],
  pages: { signIn: "/login" },
};

export default NextAuth(authOptions);

Navbar Links:

<Nav.Link as={Link} href='/login' onClick={() => signIn()}>Login</Nav.Link>
<Nav.Link as={Link} href='/signout' onClick={() => signOut({callbackUrl: '/'})}>Signout</Nav.Link>

Login form:

// Get data from the form.
const nextAuthSettings = {
  username: event.target.username.value,
  password: event.target.password.value,
  redirect: true,
  callbackUrl: "/dashboard",
};

// Send the form data to Next Auth
const result = await signIn("credentials", nextAuthSettings);

// Error Handling
// THIS DOES NOT WORK
// I don't think signIn() returns a copy of the user object unfortunately...
if (!result.success) {
  // Display the API Error Message On The Page
  setErrorMsg(result.message);
}

And then in various pages, when I want to access the user object I am doing this :

import { useSession } from "next-auth/react";

const { data: session } = useSession();

// This only shows the email
<span>{session?.user.email}</span>;

// It looks like a JWT or something when I console log it
{
"user": {
  "email": "[email protected]"
},
"expires": "2023-03-16T12:39:28.120Z"
}

Any help appreciated! I need to be able to access the user object my API is returning throughout my app. At the moment I'm just getting this session.user.email and nothing else ?? it's like I am not mapping the API's response to whatever Next Auth wants me to create...


Solution

  • you have to use callbacks :

    callbacks: {
        async jwt({ user, token }) {
          //   update token from user
          if (user) {
            token.user = user;
          }
          //   return final_token
          return token;
        },
        async session({ session, token }) {
          // update session from token
          session.user = token.user;
          return session;
        },
      },
    

    Now you can access your session with useSession() and your token with getToken()