We are doing OTP auth in our website. So in order to get authorized, a visitor enter his phone number in input and we send him a OPT number and he again enters the sent opt, then if it matches, we send him his account credendials (token, userID) if exists or we create new and we want to save that credentails in session useing next-auth.
This is where i got so far:
export default NextAuth({
providers: [
CredentialsProvider({
credentials: {
phoneNumber: { label: 'PhoneNumber', type: 'text' },
code: { label: 'Code', type: 'text' },
type: { label: 'Type', type: 'text' },
},
async authorize(credentials, req) {
const user_needs = await requests.auth.signInEnterOtp(
credentials.phoneNumber,
credentials.code,
credentials.type
)
return user_needs.ok ? true : null
},
}),
],
callbacks: {
async session({ session, user, token }) {
return session
},
},
secret: process.env.JWT_SECRET,
})
I need to save the user_needs in session but how can i pass it throuh authorize to session?
I tried returning the user_need in authorize but it was not passed to session callback.
Eventually i figured it out like this:
async authorize(credentials, req) {
const res = fetchUserInfo(credentials.opt)
if(res.ok) return {user: res.data} // res.data contains whatever received from DB call => fetchUserInfo(credentials.opt)
return null
},
callbacks: {
async jwt({ token, user }) {
// the user present here gets the same data as received from DB call made above -> fetchUserInfo(credentials.opt)
return { ...token, ...user }
},
async session({ session, user, token }) {
// user param present in the session(function) does not recive all the data from DB call -> fetchUserInfo(credentials.opt)
return token
},
},
Edit: 2023 Feb 15
I myself understood callback cycle better now:
authorize --> jwt --> session
jwt callback(cb) accepts the user object that authorize cb returns.
By default jwt retuns the token and things return from there is then available in the token object of session cb
Example:
async authorize(credentials, req) {
return { user: { role: 'ADMIN' } }
},
async jwt({ token, user }) {
return { ...token, role: user.role }
},
async session({ session, token }) {
return { ...session, token.role }
}
But when i use Providers, i won't have authorize cb, so to get user's role i need to query db in jwt cb but this callback runs a lot and i don't know what is the better option
A.Anvarbekov, i think it works but maybe in callbacks we should also pass other session properties? something like:
async session({ session, token }) {
return {
...session,
user:{...token.user}
},
Related
I have a Next application that let's the user signup using their discord account. The signup part is working. Now I am wondering how can I let the user sign-in using the discord account without going through the authorize part again, if they had already done the signup.
In the /pages/api/auth/[...nextauth].ts file
export default (req: NextApiRequest, res: NextApiResponse<any>) => {
if (req.query.firstName && req.query.lastName) {
firstName = req.query.firstName;
lastName = req.query.lastName;
}
return NextAuth(req, res, {
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET,
authorization: { params: { scope: DISCORD_SCOPES } },
}),
],
session: { strategy: "jwt" },
callbacks: {
async session({ session, token, user }) {
session.jwt = token.jwt;
session.id = token.id;
return session;
},
async jwt({ token, user, account }) {
}
}
});
}
Using above logic I am saving the user data to strapi after signup.
How to let the user sign-in with discord without getting a new access token and going through authorization
If the user already authorized your app to log in. You can use the following in your front-end client.
import { signIn } from "next-auth/react";
signIn("discord", { callbackUrl: '', redirect: true }, { prompt: "none" });
References:
https://next-auth.js.org/getting-started/client#additional-parameters
i am using CredentialsProvider to auth users into my app. But in authorize function, even if i give the user variables coming from my API Endpoint: NextAuthJS only catches e-mail variable.
Is there a way to pass all variables inside session?
async authorize(credentials, req){
const res = await fetch('http://localhost:3000/api/login', {
method: 'POST',
body: JSON.stringify(credentials),
headers: {"Content-Type": 'application/json'}
})
const {user} = await res.json()
console.log(user)
if(res.ok && user){
return user
}
return null
}
Try to override the jwt and session callbacks:
providers: [ ... ],
callbacks: {
async jwt({ token, user }) {
if (user) {
return {
...token,
user: user.user,
};
}
return token;
},
async session({ session, token }) {
if (token.user) {
session.user = token.user;
}
return session;
},
},
This is how we are authorizing users in our website
signIn('credentials', {
phoneNumber: verifiedPhone,
code: otp.data,
type: 'phone',
redirect: false,
}).then((res) => {
console.log(res) // default response {error,status,ok,url}
// How can i add additional data to this response, in my case user session
// if (!res.user.name) openModal('add_name')
// else toast.success('You are all set!')
});
By default, signIn will then return a Promise, that resolves:
{
error: string | undefined
status: number
ok: boolean
url: string | null
}
And we wanna add custom data to this promise return.
Actually what we wanna do is to sign user in and if the user is new, he/she is supposed to have no username so a modal opens up, enters his/her username and we update the next-auth session.
[...nextauth].js:
...
async authorize(credentials, req) {
// check the code here
const res = await requests.auth.signInEnterOtp(
credentials.phoneNumber,
credentials.code,
credentials.type
);
if (!res.ok) return null
return {
user: {
access_token: res.data?.access_token,
token_type: res.data?.token_type,
expires_at: res.data?.expires_at,
user_info: {
id: res.data?.user.id,
name: res.data?.user.name,
phone: res.data?.user.phone,
user_type: res.data?.user.user_type,
},
},
};
},
...
I eventually figured it like this:
...
.then(async (res) => {
const session = await getSession()
...
})
...
Bu i have another problem, it is to update the session with new username (
EDIT
i found a way of how to change session after sign in
[...nextauth].js :
...
async authorize(credentials, req){
...
if(credentials.type === 'update_name'){
const session = await getSession({ req })
return session.user.name = credentails.name
}
...
}
on the client :
signIn('credentials', {
name: newName,
type: 'name_update',
redirect: false
)
I try to get credentials auth with next-auth, but I have no experience to use it and whatever I do, i get following message:
[next-auth][error][callback_credentials_jwt_error] Signin in with credentials is only supported if JSON Web Tokens are enabled
https://next-auth.js.org/errors#callback_credentials_jwt_error
This is my src/pages/api/auth/[...nextauth].js file.
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import User from "#models/User"
const options = {
NEXTAUTH_URL:process.env.NEXTAUTH_URL,
providers: [
Providers.Credentials({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'Avista',
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
credentials: {
email: {label: "Email", type: "text"},
password: {label: "Password", type: "password"}
},
authorize: async (credentials) => {
// Add logic here to look up the user from the credentials supplied
// const user = {id: 1, name: 'J Smith', email: 'jsmith#example.com'}
const user = await User.findOne()
console.log("Данные входа", credentials)
if (user) {
// Any object returned will be saved in `user` property of the JWT
return Promise.resolve(user)
} else {
// If you return null or false then the credentials will be rejected
return Promise.resolve(null)
// You can also Reject this callback with an Error or with a URL:
// return Promise.reject(new Error('error message')) // Redirect to error page
// return Promise.reject('/path/to/redirect') // Redirect to a URL
}
}
}),
Providers.Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM
}),
],
// A database is optional, but required to persist accounts in a database
database: process.env.MONGO_URI,
secret: process.env.JWT_SIGNING_PRIVATE_KEY,
},
}
I don't know what I do wrong.
try adding this to your [...nextauth].js page
session: {
jwt: true,
// Seconds - How long until an idle session expires and is no longer valid.
maxAge: 30 * 24 * 60 * 60, // 30 days
},
read more about the options here: https://next-auth.js.org/configuration/options#jwt
This might be useful for someone else.
You need to specify that you want to use the jwt auth style.
Make options.session to be:
{ jwt: true }
If you using next-auth version 4, you should instead do:
{ strategy: 'jwt'}
Add this after providers: []
callbacks: {
jwt: async ({ token, user }) => {
user && (token.user = user)
return token;
},
session: async ({ session, token }) => {
session.user = token.user
return session;
}
},
secret: "secret",
jwt: {
secret: "ksdkfsldferSDFSDFSDf",
encryption: true,
},
I am attempting to implement NextAuth in my NextJs app. I am following the official documentation. But for one reason or the other, it seems like the user session object is not generated on login.
Here is my code from my pages/api/auth/[...nextauth].js file
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import axios from "axios";
export default (req, res) =>
NextAuth(req, res, {
providers: [
Providers.Credentials({
id: 'app-login',
name: APP
authorize: async (credentials) => {
console.log("credentials_:", credentials);
try {
const data = {
username: credentials.username,
password: credentials.password
}
// API call associated with authentification
// look up the user from the credentials supplied
const user = await login(data);
if (user) {
// Any object returned will be saved in `user` property of the JWT
return Promise.resolve(user);
}
} catch (error) {
if (error.response) {
console.log(error.response);
Promise.reject(new Error('Invalid Username and Password combination'));
}
}
},
}),
],
site: process.env.NEXTAUTH_URL || "http://localhost:3000",
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: 1 * 3 * 60 * 60, // 3 hrs
// 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
},
callbacks: {
// signIn: async (user, account, profile) => { return Promise.resolve(true) },
// redirect: async (url, baseUrl) => { return Promise.resolve(baseUrl) },
// session: async (session, user) => { return Promise.resolve(session) },
// jwt: async (token, user, account, profile, isNewUser) => { return Promise.resolve(token) }
},
pages: {
signIn: '/auth/credentials-signin',
signOut: '/auth/credentials-signin?logout=true',
error: '/auth/credentials-signin', // Error code passed in query string as ?error=
newUser:'/'
},
debug: process.env.NODE_ENV === "development",
secret: process.env.NEXT_PUBLIC_AUTH_SECRET,
jwt: {
secret: process.env.NEXT_PUBLIC_JWT_SECRET,
}
});
const login = async data => {
var config = {
headers: {
'Content-Type': "application/json; charset=utf-8",
'corsOrigin': '*',
"Access-Control-Allow-Origin": "*"
}
};
const url = remote_user_url;
const result = await axios.post(url, data, config);
console.log('result', result);
return result;
};
What am I not getting it right here? Thanks for the help.
I managed to resolve the issue eventually. Something was wrong due to specifying the 'id' and 'name' options for the custom credential provider
I have removed them and the code is working now.