0

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": "user@example.com",
    "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": "user@example.com",
                        "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": "users@email.com"
},
"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...

Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
Scottish Smile
  • 455
  • 7
  • 22
  • You'll need to persist the additional info through callbacks, check if this message I posted a while ago can help: https://stackoverflow.com/a/67103566/941770 – Nelloverflow Feb 14 '23 at 13:38
  • Thanks @Nelloverflow, callbacks are the right idea! I'm having trouble with the signIn() callback now. Can it return custom error messages or just true/false ? The user will need to know if their account is locked. – Scottish Smile Feb 15 '23 at 00:17

1 Answers1

0

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()

Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
  • Yes this is correct. I am just curious, can you return a custom error message using the signIn() callback? I have only read about it returning true or false. – Scottish Smile Feb 15 '23 at 00:10
  • take a look [here](https://stackoverflow.com/questions/75202116/how-to-catch-next-auth-email-providers-sign-in-errors-in-the-client-with-redire) – Ahmed Sbai Feb 15 '23 at 00:16