Next-Auth : How the registration is handled with a email + password credential provider?
Asked Answered
N

3

26

How the registration is handled with a custom credential provider ( email + password)?

Currently my [...nextauth].js looks like this:

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import axios from 'axios'
import jwt from "next-auth/jwt";

const YOUR_API_ENDPOINT = process.env.NEXT_PUBLIC_API_ROOT + 'auth/store'

const providers = [
    Providers.Credentials({
        name: 'Credentials',
        authorize: async (credentials) => {

            try {
                const user = await axios.post(YOUR_API_ENDPOINT,
                    {

                            password: credentials.password,
                            username: credentials.email

                    },
                    {
                        headers: {
                            accept: '*/*',
                            'Content-Type': 'application/json'
                        }
                    })

                console.log('ACCESS TOKEN ----> ' + JSON.stringify(user.data.access_token, null, 2));

                if (user) {

                    return {status: 'success', data: user.data}
                }

            } catch (e) {
                const errorMessage = e.response.data.message
                // Redirecting to the login page with error messsage in the URL
                throw new Error(errorMessage + '&email=' + credentials.email)
            }

        }
    })
]

const callbacks = {

    /*
    |--------------------------------------------------------------------------
    | Callback : JWT
    |--------------------------------------------------------------------------
    */
    async jwt(token, user, account, profile, isNewUser) {

        if (user) {
            token.accessToken = user.data.access_token
        }

        return token
    },

    /*
    |--------------------------------------------------------------------------
    | Callback : Session
    |--------------------------------------------------------------------------
    */
    async session(session, token) {

        // Store Access Token to Session
        session.accessToken = token.accessToken

        /*
        |--------------------------------------------------------------------------
        | Get User Data
        |--------------------------------------------------------------------------
        */

        const API_URL = 'http://localhost:3000/api';

        const config = {
            headers: { Authorization: `Bearer ${token.accessToken}` }
        };

        let userData;

        await axios.get(`${API_URL}/user`, config)
            .then(response => {

                userData = {
                    id:                             response.data.id,
                    uuid:                           response.data.uuid,
                    username:                       response.data.username,
                    avatar_location:                response.data.avatar_location,
                    gender_id:                      response.data.gender_id,
                    date_of_birth:                  response.data.date_of_birth,
                    country_id:                     response.data.country_id,
                    location:                       response.data.location,
                    about_me:                       response.data.about_me,
                    interests:                      response.data.interests,
                    website:                        response.data.website,
                    timezone:                       response.data.timezone,
                    full_name:                      response.data.full_name,
                    formatted_created_at:           response.data.formatted_created_at,
                    formatted_last_seen:            response.data.formatted_last_seen,
                    album_count:                    response.data.album_count,
                    total_unread_message_count:     response.data.total_unread_message_count,
                };

                // Store userData to Session
                session.user = userData

            }).catch((error) => {

                // Error
                if (error.response) {
                    // The request was made and the server responded with a status code
                    // that falls out of the range of 2xx
                    // console.log(error.response.data);
                    // console.log(error.response.status);
                    // console.log(error.response.headers);
                    console.log('error.response: ' + error.request);

                } else if (error.request) {
                    // The request was made but no response was received
                    // `error.request` is an instance of XMLHttpRequest in the
                    // browser and an instance of
                    // http.ClientRequest in node.js
                    console.log('error.request: ' + error.request);



                } else {
                    // Something happened in setting up the request that triggered an Error
                    console.log('Error', error.message);
                }
                console.log(error.config);

            });

        return session
    }
}

const options = {
    providers,
    callbacks,
    session: {
        // Use JSON Web Tokens for session instead of database sessions.
        // This option can be used with or without a database for users/accounts.
        // Note: `jwt` is automatically set to `true` if no database is specified.
        jwt: true,

        // Seconds - How long until an idle session expires and is no longer valid.
        maxAge: 30 * 24 * 60 * 60, // 30 days

        // Seconds - Throttle how frequently to write to database to extend a session.
        // Use it to limit write operations. Set to 0 to always update the database.
        // Note: This option is ignored if using JSON Web Tokens
        updateAge: 24 * 60 * 60, // 24 hours
    },
    secret: process.env.SECRET,
    jwt: {
        // signingKey: process.env.JWT_SIGNING_PRIVATE_KEY,
        //
        // // You can also specify a public key for verification if using public/private key (but private only is fine)
        // verificationKey: process.env.JWT_SIGNING_PUBLIC_KEY,
        //
        // // If you want to use some key format other than HS512 you can specify custom options to use
        // // when verifying (note: verificationOptions should include a value for maxTokenAge as well).
        // // verificationOptions = {
        // //   maxTokenAge: `${maxAge}s`, // e.g. `${30 * 24 * 60 * 60}s` = 30 days
        // //   algorithms: ['HS512']
        // // },

        secret: process.env.JWT_SECRET,
    },
    pages: {
        error: '/login' // Changing the error redirect page to our custom login page
    }
}

export default (req, res) => NextAuth(req, res, options)

All tutorials I found online only shows the login/signIn without details on how to implement registration

Neogene answered 30/6, 2021 at 5:23 Comment(2)
answered here I guess: github.com/nextauthjs/next-auth/discussions/2285Carbonate
github.com/nextauthjs/next-auth/discussions/… Seems to be discouraged with credentialsDisney
P
30

Registration is the process of registering the user, saving new users' credentials into the database. It is independent of next-auth. You create a form, submit the form and save the form data properly into the database.

next-auth takes over when you are logged in, in other words, you are authenticated meaning that you are already a registered, genuine user. Once you pass the security checks and successfully logged in, next-auth creates a session for the user.

You can view the set up demo for v4: next-auth 4 session returns null, next.js

Propagable answered 22/4, 2022 at 15:26 Comment(3)
If you select strategy: "database", next-auth will save a new user in your database. So it does provide its own "out-of-the-box" version of a registration. I'm wondering whether I can then update and rely on those "user" records it creates or whether next-auth can delete any of them any moment (!)Sere
@SproutCoder, Can you provide some working registration demo example for reference?Fern
i d[ont see a strategy attribute in the optionsHomiletic
W
2

ANS 1

You can add multiple providers Do the same thing like login store data from the backend in session as we do in the login

providers: [
    GoogleProvider({
        
    }),
    CredentialsProvider({
        id: "custom-login",
        type: "credentials",
        credentials: {},
        
    }),
    CredentialsProvider({
        id: "custom-signup",
        type: "credentials",
        credentials: {},
        name: 'credentials',
        async authorize(credentials: any, req) {
            const payload = {
                firstName: credentials.firstName,
                lastName: credentials.lastName,
                
            }

            // api call to backend
            const res = await signUpWithCred(payload)
            if (!res.success) {
                throw new Error(JSON.stringify({ errors: { ...res } }))
            } else { 
                // return data to be used in callback signIn
                return res.data
            }
        }
    })
]


// How to use in page   
const handleSubmit = async (e: any) => {
    e.preventDefault();
    let result: any = await signIn('custom-signup', {
      ...credential,
      redirect: false,
    })
    if (result?.error) {
      
      // error handling

    }
  }

ANS 2

    const handleRegister = (e) => {

        e.preventDefault()

        
        
    await axios.post(ajax_register_url, ajax_register_post_params)
    .then(response => {

        // Registration Success
        if(response.data.status === 'success') {
        
            // Login
            signIn('credentials',
                {
                    email,
                    password,
                    callbackUrl: callbackUrl
                }
            )
        
        } else {
        
            // Error
        
        } 

    }).catch((error) => {

        // Error

        console.log(error.config);

    });

}
Willing answered 29/6, 2023 at 14:47 Comment(4)
How do I invoke the sign up? I don't really understand how do you do this. and how do I store the JWT token from the backend server in the sign up procces?Fireboat
signIn('custom-signup') just mimic the signin for signupWilling
thanks. is it considered "correct" to do it this way or better to have my backend handles all of this with passportJS + storing the accessToken in the cookies (and the name + maybe email of the user in useContext / Zustand)?Fireboat
Do you recommend using server actions to invoke signup?N
S
-4

next-auth only supports Sign In, Sign Up and Sign Out. When a user creates an account for the first time, It registers them automatically without the need for a new set of registration process.

Sunbreak answered 19/8, 2021 at 21:54 Comment(1)
Although I wasn't the downvoter here, this answer is only true with regard to oauth, not in regard to credentials provider.Fons

© 2022 - 2024 — McMap. All rights reserved.