I'm using next-auth credentials provider for authentication in a next.js project, it works fine in development but when I deployed the website to production I got 401 error code with the following response {url: "https://sub.domain.com/api/auth/error?error="} as I tried to login.
Everything is working fine in localhost and authentication is smooth with no errors. Wher is the error in my code?
My next-auth config
// /api/auth/[...nextauth].ts
export const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET ?? 'supersecret',
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
id: 'admin-login',
name: 'Admin login',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'test#test.com',
},
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: user } = await axios.post(
`${process.env.APP_URL}/api/auth/admin/login`,
credentials
);
return user;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
CredentialsProvider({
id: 'room-login',
name: 'Room login',
credentials: {
roomId: { label: 'Id de la chambre', type: 'text' },
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: room } = await axios.post(
`${process.env.APP_URL}/api/auth/room/login`,
credentials
);
return room;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
],
callbacks: {
async signIn() {
return true;
},
async redirect({ url, baseUrl }) {
if (url.startsWith('/')) return `${baseUrl}${url}`;
else if (new URL(url).origin === baseUrl) return url;
return baseUrl;
},
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = (user.role as Role) ?? 'GUEST';
}
return token;
},
async session({ session, token }) {
const sess: Session = {
...session,
user: {
...session.user,
id: token.id as number | string,
role: token.role as Role,
},
};
// console.log('SESSION: ', sess)
return sess;
},
},
session: {
strategy: 'jwt',
},
jwt: {
secret: process.env.JWT_SECRET ?? 'supersecret',
maxAge: 10 * 24 * 30 * 60, // 30 days
},
pages: {
signIn: '/auth/login',
signOut: '/auth/login',
newUser: '/api/auth/register',
error: '/auth/login',
},
debug: process.env.NODE_ENV === 'development',
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
return NextAuth(req, res, authOptions);
}
The issue was caused by the SSL certificate that I had to install on the domain for next-auth to work.
Related
I'm trying to change the user's password with the setPassword() passport local mongoose instance method, but I'm getting an error that says "user.setPassword is not a function".
This is my code
const change_password = (req, res) => {
const password = req.body.password
console.log(password);
User.find({
_id: req.user._id
}).then((user) => {
user.setPassword(password, function() {
console.log('user password changed')
user.save()
res.status(200).json( { msg : 'The password has been changed' })
})
}).catch(e => {
console.log(e);
})
}
And this is my user Schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
// required: true
},
googleId: {
type: String,
},
photoUrl: {
type: String,
},
githubId: {
type: String,
},
twitterId: {
type: String,
},
facebookId: {
type: String,
},
bio: {
type: String,
},
email: {
type: String,
},
phoneNumber: {
type: String,
},
})
UserSchema.plugin(passportLocalMongoose, { usernameField: 'email' });
const User = mongoose.model('User', UserSchema);
module.exports = User;
User.find() will return an array of users matching the query, hence the error.
Because there should be (at most) one user in the database matching req.user._id, use findOne instead. Also note that findOne() can return null in case there were no users matching the query:
User.findOne({ _id: req.user._id }).then(user => {
if (! user) return res.sendStatus(404);
user.setPassword(…);
});
js app with Nuxt Auth and when I want to log in. User is not set and loggedIn is false
const response = await this.$auth.loginWith('local', { data: {
email: this.form.email.value,
password: this.form.password.value,
} });
this.$auth.setUser(response.data.user);
this.$auth.strategy.token.set(response.data.jwt.access_token)
this.$auth.strategy.refreshToken.set(response.data.jwt.refresh_token)
so I wrote this and after that user is set but loggedIn is still false. Here my nuxt.config.js.
auth: {
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'access_token',
maxAge: 1800,
required: true,
type: 'Bearer',
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
maxAge: 60 * 60 * 24 * 30,
},
user: {
property: 'user',
autoFetch: true,
},
endpoints: {
login: { url: '/login', method: 'post' },
refresh: { url: '/refresh', method: 'post', propertyName: false },
logout: { url: '/logout', method: 'post' },
user: { url: '/refresh', method: 'post', propertyName: false },
},
},
},
},
Can you help me with it please?
I found solution to my answer. I wrote my own scheme
import { RefreshScheme } from '~auth/runtime'
export default class CustomScheme extends RefreshScheme {
// Override `fetchUser` method of `local` scheme
async fetchUser (endpoint) {
this.options.endpoints.user = {
...this.options.endpoints.user,
data: {
token: this.$auth.strategy.refreshToken.get()
}
}
// Token is required but not available
if (!this.check().valid) {
return
}
// User endpoint is disabled.
if (!this.options.endpoints.user) {
this.$auth.setUser({})
return
}
// Try to fetch user and then set
return this.$auth.requestWith(
this.name,
endpoint,
this.options.endpoints.user
).then((response) => {
const user = response.data.user
// Transform the user object
const customUser = {
...user,
fullName: user.name,
roles: ['user']
}
// Set the custom user
// The `customUser` object will be accessible through `this.$auth.user`
// Like `this.$auth.user.fullName` or `this.$auth.user.roles`
this.$auth.setUser(customUser)
return response
}).catch((error) => {
this.$auth.callOnError(error, { method: 'fetchUser' })
})
}
}
Apparently was the problem by getting user from propertyName. I replace code with my own response const user = response.data.user and now it seems working :)
I am developing my first NuxtJs application with the following technologies.
For Backend APIs Laravel 7.3 (Passport)
Nuxt v2.15.2
Rendering mode: Universal (SSR / SSG)
NuxtAxios v5.13.1
NuxtAuth v5
My login API response is as follows:
{
"status": true,
"code": 200,
"data": [
{
"id": 3,
"nick_name": "johndoe",
"full_name": "John Doe",
"email": "johndoe#mail.com",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNWE5MjFmMGMyMDBlZjkxYWQxY2QyNGI3NDEyYmI1OWY4ZGEyZjg2Yzc0Y2M1ZDAwOTRhOWIyZTU4MGY3MmZmODRmNGI5N2QwYjJmYjA5NjMiLCJpYXQiOjE2MTQwMzE1MDMsIm5iZiI6MTYxNDAzMTUwMywiZXhwIjoxNjQ1NTY3NTAzLCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.clQslt3CdTLoVDHzQFwQyrRLjzjTOSeipCyQAl07gzmwXFKr542hR3MUCRr8CTjueWDTNbwd-iKkQztvB7Z-0N1zaptq67UEwj3iPEEtnbV2gMOSlVdAUu0q4OPJKYI9RwJKHnK-q1bTCOUOitfLrnYOq3Lb1T8B-3en5_S7xb9ln-iOtwVc3vW1OnEbEIsn3ELeTro1zQ9IKdlHhQ50CnGU45LipzKeadtVGkm9qm467XOqlVPZdulMJTCkvunETo23hQsTsn_Fxy0IYLUA0v-_C-ARB0N672fAuHF2a8MIYHv066Omm-6WsPrCNtfOkoIgyuMLl0gaua04IJfHrDbh7CJSEUqootKTsIVvFG4OjqR3yDN2PdhjJkPYWNTMeLbKV3ewSsjCS1aMOtXYvhrVFjrunn5M74pDBzn3kW9VMFIh2BbIfUDO_ziDrsn7KAVyOm-p7gdBN_gVKcl_Hx9x4EagixWRL7GGUqEZ2AbxRkpIO4HKwqMm7WxKatSt5hkmPZ6Zt-jfMTfrj7KuF6WhhjCh1TOFJSy9BTqp9_a2eKP-YL2M6JIJXFDHwovToC96JBptbumPvB2i2KDU_XoXF2WFx_I4iNJpZVFN0u12MeyMbXlnpf4X2t79_I7QklxSfZ5LgsOdsnt9dAm9QBbSvf8AdZZNOS4p59DuQls",
}
],
"error": null,
"errors": null
}
nuxt.config.js
auth: {
strategies: {
local: {
token: {
// property: 'access_token'
// property: 'data[0].access_token'
// property: 'data.data[0].access_token'
property: false
},
user: {
property: 'data',
autoFetch: true
},
endpoints: {
login: { url: '/en/signin', method: 'post' },
logout: { url: '/user/logout', method: 'get' },
user: { url: '/en/user/profile', method: 'get' },
}
}
}
},
Login.vue
export default {
data() {
return {
login: {
email: 'johndoe#mail.com',
password: 'welcome'
}
}
},
methods: {
async userLogin() {
try {
let response = await this.$auth.loginWith('local', { data: this.login })
console.log(response)
} catch (err) {
console.log(err)
}
}
}
}
When I am setting local.property: false, auth._token.local is being set as complete json object instead of "access_token" and then the second hit goes to '/en/user/profile' with Unauthenticated Request result.
I think an alternate way would be to set local.token.required:false , and then set the user token after your login attempt was successful. Something like this:
nuxt.config.js
auth: {
strategies: {
local: {
token: {
required: false
},
user: {
property: 'data',
autoFetch: true
},
endpoints: {
login: { url: '/en/signin', method: 'post' },
logout: { url: '/user/logout', method: 'get' },
user: { url: '/en/user/profile', method: 'get' },
}
}
}
},
Login.vue
methods: {
async userLogin() {
try {
let response = await this.$auth.loginWith('local', { data: this.login })
.then((response)=>{
this.$auth.setUserToken(response.data[0].access_token,'refreshToken');
});
console.log(response);
} catch (err) {
console.log(err)
}
}
}
My auth in nuxt.config.js like this :
auth: {
redirect: {
login: '/',
home: '/home',
logout: '/'
},
strategies: {
local: {
endpoints: {
login: {
url: '/api/token',
method: 'post',
propertyName: 'response.token'
},
user: {
url: '/api/references?number=1122334433221&name=chelsea&birthDate=1995-03-18',
method: 'get',
propertyName: 'data'
},
logout: {
url: '/api/logout',
method: 'post'
}
},
tokenRequired: true,
tokenType: 'Bearer '
}
},
token: {
name: 'token'
},
cookie: {
name: 'token'
}
}
In endpoint user, I put number, name and birthDate statically. How do I make it dynamic? So the parameter is taken from the data
data () {
return {
auth: {
name: '',
number: '',
birthday: null
}
}
}
When submit login, it will call this :
methods: {
submit () {
this.$auth.loginWith('local', {
data: {
username: 'mycompany',
password: '123123123'
}
}).then((resp) => {
this.SET_IS_AUTH(true)
this.$router.push('/home')
}).catch(() => {
console.log('error')
})
}
}
Update (Using try catch)
methods: {
...mapMutations(['SET_IS_AUTH']),
async fetchUserInfo () {
const user = await this.$axios.$get(`/api/references?number=${this.auth.number}&name=${this.auth.name}&birthDate=${this.auth.birthday}`)
this.$auth.setUser(user)
},
submit () {
if (this.$refs.form.validate()) {
try {
this.$auth.loginWith('local', {
data: {
username: process.env.USERNAME,
password: process.env.PASSWORD
}
}).then((resp) => {
this.fetchUserInfo()
this.SET_IS_AUTH(true)
this.$router.push('/home')
})
} catch(err) {
commit('SET_ERROR', err)
}
}
}
}
Since you are setting the user endpoint in nuxt.config.js you won't be able to dynamically pass in the endpoint URL. However, you can achieve the outcome by passing false to the user endpoint like so:
auth: {
redirect: {
login: '/',
home: '/home',
logout: '/'
},
strategies: {
local: {
endpoints: {
login: {
url: '/api/token',
method: 'post',
propertyName: 'response.token'
},
user: false,
logout: {
url: '/api/logout',
method: 'post'
}
},
tokenRequired: true,
tokenType: 'Bearer '
}
},
token: {
name: 'token'
},
cookie: {
name: 'token'
}
}
Then you can use axios to make the request to get the user details manually passing in your dynamic values and then setting the user values for the auth module. For example perhaps when the user logs in, you can then have a method called fetchUserInfo and it would look like so:
export default {
data () {
return {
auth: {
name: '',
number: '',
birthday: null
}
}
},
methods: {
async fetchUserInfo() {
const user = await this.$axios.$get(`/api/references?number=${this.auth.number}&name=${this.auth.name}&birthDate=${this.auth.birthDate}`)
// Sets the user info using the setUser method on the auth module
this.$auth.setUser(user)
}
}
}
Here is a link to the setUser method usage. Perhaps this would help you: https://auth.nuxtjs.org/api/auth.html#setuser-user
When i boot up the hapi-server as well as the webpack-dev-server and go to localhost:3000/api/login shows a 502 bad gateway and nothing on the page! Thanks for every one who helps
Here is my webpack file:
module.exports = {
entry: ["./index.js"],
output: {
filename: "bundle.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
}
]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: {
index: 'index.html'
},
stats: 'normal',
host: process.env.HOST || 'localhost',
port: process.env.PORT || 3000,
proxy: {
'/api/*': {
target: 'http//localhost:9000',
secure: false
}
}
}
};
Here is my hapi-server:
const Hapi = require('hapi');
const Inert = require('inert');
const config = require('./config');
const CookieAuth = require('hapi-auth-cookie');
const server = new Hapi.Server();
server.connection({port: 9000});
const options = {
ops: {
interval: config.hapiGoodInterval
},
reporters: {
console: [{
module: 'good-squeeze',
name: 'Squeeze',
args: [{ log: '*', response: '*' }]
}, {
module: 'good-console'
}, 'stdout'],
file: [{
module: 'good-squeeze',
name: 'Squeeze',
args: [{ ops: '*' }]
}, {
module: 'good-squeeze',
name: 'SafeJson'
}, {
module: 'good-file',
args: ['./logs/fixtures/file_log']
}]
}
};
server.register(CookieAuth, (err) => {
if (err) {
throw err;
}
});
server.register(Inert, ()=>{});
server.register({
register: require('good'),
options,
}, (err) => {
if (err) {
return console.error(err);
}
server.start(() => {
console.info(`Server started at ${ server.info.uri }`);
});
});
const cache = server.cache({ segment: 'sessions', expiresIn: 3 * 24 * 60 * 60 * 1000 });
server.app.cache = cache;
server.auth.strategy('session', 'cookie', true, {
password: 'Vaj57zED9nsYeMJGP2hnfaxU874t6DV5',
cookie: 'sid-example',
redirectTo: '/api/login',
isSecure: false,
validateFunc: function (request, session, callback) {
cache.get(session.sid, (err, cached) => {
if (err) {
return callback(err, false);
}
if (!cached) {
return callback(null, false);
}
return callback(null, true, cached.account);
});
}
});
server.route(require('./routes'));
Here are the routes:
var Handlers = require('./handlers');
var Joi = require('joi');
var Routes = [
{
path: '/api/hello',
method: 'GET',
config: {
auth:false,
handler: function (request, reply) {
reply('Hello from the server')
}
}
},
{
method: ['GET', 'POST'],
path: '/api/login',
config: {
handler: Handlers.login,
auth: { mode: 'try' },
plugins: {
'hapi-auth-cookie': { redirectTo: false }
}
}
},
{
path: '/api/logout',
method: 'GET',
handler: Handlers.logout
}
];
And Finally the handlers:
const r = require('rethinkdb');
var {Post, User, Opinion} = require('./rethinkdb/models/all');
class Handlers {
static login(request, reply) {
let username = 'pesho';
let password = '12345';
let uuid = 1;
if (request.auth.isAuthenticated) {
return reply.redirect('/');
}
let message = '';
let account = null;
if (request.method === 'post') {
if (!request.payload.username ||
!request.payload.password) {
message = 'Missing username or password';
}
else {
if (password !== request.payload.password || username !== request.payload.username) {
message = 'Invalid username or password';
}
}
}
if (request.method === 'get' || message) {
return reply('<html><head><title>Login page</title></head><body>' +
(message ? '<h3>' + message + '</h3><br/>' : '') +
'<form method="post" action="/api/login">' +
'Username: <input type="text" name="username"><br>' +
'Password: <input type="password" name="password"><br/>' +
'<input type="submit" value="Login"></form></body></html>');
}
const sid = String(++uuid);
request.server.app.cache.set(sid, { account: account }, 0, (err) => {
if (err) {
reply(err);
}
request.cookieAuth.set({ sid: sid });
return reply.redirect('/');
});
}
static logout(request, reply) {
request.cookieAuth.clear();
return reply.redirect('/');
};
}
module.exports = Handlers;
The problem was kind-a dump, but here it is:
you must add a "/" at the end of the proxy target
proxy: {
'/api/*': {
target: 'http://localhost:9000/',<= This here
secure: false
}
},