I am trying to manually set the status on my formik instance upon receiving an error response from server:
const formik = useFormik({
initialValues: {
email: "",
password: "",
},
onSubmit: async ({ email, password }, { setStatus }) => {
try {
const response = await loginUser({
variables: {
email,
password,
},
});
} catch (error) {
await setStatus({ email: error.message, password: error.message });
console.log(formik.status);
}
formik.resetForm();
},
validationSchema: Yup.object().shape({
email: Yup.string(),
password: Yup.string(),
}),
});
Logging status prints undefined.
Why is status not being set?
Figured it out.
Using resetForm clears both form status and errors.
To get around this I have manually cleared both fields instead:
initialValues={{
email: "",
password: "",
}}
onSubmit={async ({ email, password }, { setStatus, setFieldValue }) => {
try {
const response = await loginUser({
variables: {
email,
password,
},
});
} catch (error) {
setStatus({
email: "Shit",
});
setFieldValue("email", "");
setFieldValue("password", "");
}
}}
validationSchema={Yup.object().shape({
email: Yup.string(),
password: Yup.string(),
})}
Related
I'm trying to use NextAuth and Strapi in my app, but NextAuth session only shows email for the user.
When I call the Strapi login API directly:
axios
.post(`${process.env.STRAPI_URL}/api/auth/local`, {
identifier: "email#provider.com",
password: "test123",
})
.then((response) => {
console.log("User profile", response.data.user);
})
I get this object (response.data.user) in console:
{
"id": 4,
"username": "theusername",
"email": "email#provider.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2022-09-18T17:02:43.581Z",
"updatedAt": "2022-09-27T16:39:22.993Z"
}
But when I try to sign in with NextAuth:
import axios from "axios";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
export default NextAuth({
// Configure one or more authentication providers
providers: [
CredentialsProvider({
name: "Sign in with Email",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
// return (
if (credentials == null) return null;
try {
/* I tried `...rest` but no luck. also console log doesn't work here and I can't see the actual response from the API call */
const { user, jwt } =
(await axios
.post(
`${process.env.STRAPI_URL}/api/auth/local`,
{
identifier: credentials.email,
password: credentials.password,
}
)
.then((response) => {
return response.data;
})
.catch((error) => {
console.log(error.response);
throw new Error(error.response.data.message);
})) || null;
return { jwt, ...user };
} catch (error) {
console.warn(error);
// Sign In Fail
// return null;
}
// );
},
}),
],
callbacks: {
session: async ({ session, token }) => {
session.id = token.id;
session.jwt = token.jwt;
return Promise.resolve(session);
},
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id;
token.jwt = user.jwt;
}
return Promise.resolve(token);
},
},
});
I only get email for the user:
{
"user": {
"email": "email#provider.com"
},
"expires": "2022-10-27T20:03:20.177Z",
"id": 4,
"jwt": "somejwtcodethatichangedhereforsecurity"
}
How can I have other properties, like username, in the user returned from NextAuth?
It's possible, by modifying callbacks:
callbacks: {
session: async ({ session, token }) => {
session.id = token.id;
session.jwt = token.jwt;
session.user.username = token.username /* added */
return Promise.resolve(session);
},
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id;
token.jwt = user.jwt;
token.username = user.username /* added */
}
return Promise.resolve(token);
},
},
I am running into a POST http://localhost:4000/ 400 (Bad Request) Error.
I am trying to create a new user with the following frontend.
const REGISTER_USER = gql`
mutation Mutation(
$createUser: CreateUserInput!
) {
createUser(createUserInput: $createUserInput){
email
name
token
password
}
}
`
const Register = () => {
const context = useContext(AuthContext)
let navigate = useNavigate()
const [errors, setErrors] = useState([])
function registerUserCallback() {
console.log("Callback hit")
registerUser()
}
const {onChange, onSubmit, values} = useForm(registerUserCallback, {
name: '',
email: '',
password:'',
confirmPassword: '',
})
const [registerUser, {loading}] = useMutation(REGISTER_USER, {
update(proxy, {data: {registerUser: userData}}) {
context.login(userData)
navigate('/Dashboard')
},
onError({graphQLErrors}) {
setErrors(graphQLErrors)
console.log("Error: " + graphQLErrors)
console.log(graphQLErrors)
},
variables: {createUserInput: values}
})
However, the grapQLErrors is not even being console.logged for some reason. When I run the Mutation via Apollo Studio it works. Any information would be great!
Edit: Network Tab Screenshot:
Adding Code for my httpLink:
import { ApolloClient, InMemoryCache, createHttpLink } from "#apollo/client";
import { setContext } from "#apollo/client/link/context";
const httpLink = createHttpLink({
uri: 'http://localhost:4000'
})
const authLink = setContext((_, {headers}) => {
return {
headers: {
...headers,
authorization: localStorage.getItem('token') || ""
}
}
})
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
Edit: createUser Mutation seems to be the issue. This is the Network response error: ["GraphQLError: Unknown argument "createUserInput" on field "Mutation.createUser".","
#Mutation((returns) => User)
async createUser(#Arg('data') data:CreateUserInput, #Ctx() ctx: Context) {
const oldUser = await ctx.prisma.user.findFirst({ where: { email: data.email}})
if(oldUser) {
throw new ApolloError('A user is already registered with the email' + data.email, 'USER_ALREADY_EXISTS')
}
var encryptedPassword = await bcrypt.hash(data.password, 10)
const newUser = await ctx.prisma.user.create({
data: {
name: data.name,
email: data.email,
password: encryptedPassword
}
})
return {token: jwt.sign(newUser, 'supersecret')}
}
Here is a screen shot of my Preview in my Network...I really don't get it.
export class CreateUserInput {
#Field((type) => String)
name: string
#Field((type) => String)
email: string
#Field((type) => String)
password: string
System Information
Strapi Version: 3.6.5
Operating System: MacOS 11.4
Database: SQL
Node Version: 14.17.0
NPM Version: 6.14.13
Hey there,
I wanted to have a function to change the password by passing the old password and a new one. For that, I found this solution from yohanes (https://stackoverflow.com/a/65237275/9476651). Unfortunately, if I want to execute the POST request, I get the error "Error: The model users-permissions can’t be found. It’s coming from this piece of code:
const user = await strapi.query('user', 'users-permissions').findOne({ email: params.identifier });
This is the first out of maximum three times I need to use the users-permissions plugin and I am pretty sure this error will occur at the other usages as well.
Is there anyone who is able to help me?
Have a great day!
Lucas
My full code:
"use strict";
/**
* api/password/controllers/password.js
*/
const { sanitizeEntity } = require("strapi-utils");
const formatError = (error) => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
module.exports = {
index: async (ctx) => {
// const params = JSON.parse(ctx.request.body);
const params = ctx.request.body;
// The identifier is required
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.email.provide",
message: "Please provide your username or your e-mail.",
})
);
}
// The password is required
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your password.",
})
);
}
// The new password is required
if (!params.newPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password.",
})
);
}
if (!params.confirmPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password confirmation.",
})
);
}
if (
params.newPassword &&
params.confirmPassword &&
params.newPassword !== params.confirmPassword
) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.matching",
message: "New passwords do not match.",
})
);
} else if (
params.newPassword &&
params.confirmPassword &&
params.newPassword === params.confirmPassword
) {
// Get user based on identifier
const user = await strapi
.query("user", "users-permissions")
.findOne({ email: params.identifier });
// Validate given password against user query result password
const validPassword = await strapi.plugins[
"users-permissions"
].services.user.validatePassword(params.password, user.password);
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.invalid",
message: "Identifier or password invalid.",
})
);
} else {
// Generate new hash password
const password = await strapi.plugins[
"users-permissions"
].services.user.hashPassword({
password: params.newPassword,
});
// Update user password
await strapi
.query("users-permissions")
.update({ id: user.id }, { resetPasswordToken: null, password });
// Return new jwt token
ctx.send({
jwt: strapi.plugins["users-permissions"].services.jwt.issue({
id: user.id,
}),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query("user", "users-permissions").model,
}),
});
}
}
},
};```
This part of the code works perfectly fine.
const user = await strapi.query('user', 'users-permissions').findOne({ email: params.identifier });
The issue is with the other places where users-permissions is used. You need to use "user", "users-permissions" instead of only "users-permissions". I modified the code below so it works now.
"use strict";
/**
* api/password/controllers/password.js
*/
const { sanitizeEntity } = require("strapi-utils");
const formatError = (error) => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
module.exports = {
index: async (ctx) => {
// const params = JSON.parse(ctx.request.body);
const params = ctx.request.body;
console.log("params is ", params);
// The identifier is required
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.email.provide",
message: "Please provide your username or your e-mail.",
})
);
}
// The password is required
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your password.",
})
);
}
// The new password is required
if (!params.newPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password.",
})
);
}
if (!params.confirmPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password confirmation.",
})
);
}
if (
params.newPassword &&
params.confirmPassword &&
params.newPassword !== params.confirmPassword
) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.matching",
message: "New passwords do not match.",
})
);
} else if (
params.newPassword &&
params.confirmPassword &&
params.newPassword === params.confirmPassword
) {
// Get user based on identifier
const user = await strapi
.query("user", "users-permissions")
.findOne({ email: params.identifier });
// Validate given password against user query result password
const validPassword = await strapi.plugins[
("user", "users-permissions")
].services.user.validatePassword(params.password, user.password);
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.invalid",
message: "Identifier or password invalid.",
})
);
} else {
// Generate new hash password
const password = await strapi.plugins[
("user", "users-permissions")
].services.user.hashPassword({
password: params.newPassword,
});
// Update user password
await strapi
.query("user", "users-permissions")
.update({ id: user.id }, { resetPasswordToken: null, password });
// Return new jwt token
ctx.send({
jwt: strapi.plugins[("user", "users-permissions")].services.jwt.issue(
{
id: user.id,
}
),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query("user", "users-permissions").model,
}),
});
}
}
},
};
I just want to send the request to all my resolvers through the context field, but when I access it from one of my resolvers, it returns null.
app.use('/graphql', graphqlHTTP(async (request, response, graphQLParams) => ({
schema: schema,
context:{token_1:null,test:request},
graphiql:true
})));
These are part of my schema. Firstly I Login to set the token ,but when I want to access the context.token_1 from the other resolver (BuyItems), it returns null.
BuyItems :{
type: UserType,
args: {
name: {type: GraphQLString},
points: {type: GraphQLInt}
},
resolve(parent,args,context){
console.log(context.token_1)
return UserModel.findOneAndUpdate({name:args.name},{points:args.points})
}
},
Login: {
type: AuthType,
args: {
email: {type:GraphQLString},
password: {type:GraphQLString}
},
async resolve(parent,args,context){
const user = await UserModel.findOne({ email: args.email });
if (!user) {
throw new Error('User does not exist on login!');
}
const isEqual = await bcrypt.compare(args.password, user.password);
if (!isEqual) {
throw new Error('Password is incorrect!');
}
const token = jwt.sign(
{ userId: user.id, email: user.email },
'somesupersecretkey',
{ expiresIn: '1h' }
);
context.token_1 = token;
return {tokenExpiration: 1, userId: user.id, token:token}
}
}
I add the userRecord to the database in the before function but when I try to authenticate using supertest it gives me bad combo.
I am using sails for the backend, the code is as follows
var request = require('supertest');
let should = chai.should();
require('../../boostrap.test.js');
describe('The bookedservices Controller', function() {
let userRecord,
studioRecord,
serviceRecord,
timingRecord,
bookedServiceRecord,
authenticatedUser,
authenticatedStudio,
session = null;
before(async function() {
// beforeEach(async function(done) {
userRecord = await User.create({
emailAddress: 'xx#gmail.com',
password: 'xxisnotgood123',
fullName: 'siddhant',
location: 'vellore',
image: 'not a pipi pic',
mobile: '9681901311',
description: 'words cant describe me'
}).fetch();
creditRecord = await Credits.create({
expirydate: '2019-05-25T03:23:55.016Z',
creditsPresent: 30,
passType: '1 month',
userId: userRecord.id
}).fetch();
studioRecord = await Studios.create({
emailAddress: 'yy#gmail.com',
password: 'yyisnotgood123',
fullName: 'siddhant',
location: 'vellore',
image: 'not a lili pic',
mobile: '9681901311',
description: 'words cant describe me'
}).fetch();
serviceRecord = await Services.create({
serviceName: 'zumba',
price: 1500,
creditCost: 3,
studioId: studioRecord.id
}).fetch();
timingRecord = await Timings.create({
eventInTime: '2019-05-11T03:23:55.016Z',
eventOutTime: '2019-05-13T00:00:02.001Z',
numberOfSlotsAvailable: 3,
serviceId: serviceRecord.id
}).fetch();
bookedServiceRecord = await BookedServices.create({
isComplete: 'false',
bookingDate: '2019-05-13T03:23:55.016Z',
timingId: timingRecord.id,
userId: userRecord.id,
studioId: studioRecord.id,
serviceId: serviceRecord.id
}).fetch();
authenticatedUser = await request.agent(sails.hooks.http.app);
authenticatedUser
.post('/api/v1/users/entrance/login')
.set('Accept', 'application/json')
.send({ emailAddress: 'xx#gmail.com', password: 'xxisnotgood123' })
.end((err, res) => {
if (err) {
throw err;
} else {
console.log(res);
session = res.header['Sails.sid'];
}
});
// authenticatedStudio = await request.agent(sails.hooks.http.app);
// authenticatedStudio
// .post('/api/v1/studios/login')
// .set('Accept', 'application/json')
// .send({ emailAddress: 'yy#gmail.com', password: 'yyisnotgood123' });
// done();
});
it('all the records have been added', function(done) {
userRecord.should.have.property('id');
console.log('OUTPUT: userRecord', userRecord);
studioRecord.should.have.property('id');
// console.log('OUTPUT: studioRecord', studioRecord);
serviceRecord.should.have.property('id');
// console.log('OUTPUT: serviceRecord', serviceRecord);
timingRecord.should.have.property('id');
// console.log('OUTPUT: timingRecord', timingRecord);
bookedServiceRecord.should.have.property('id');
// console.log('OUTPUT: bookedServiceRecord', bookedServiceRecord);
done();
});
it('should post and return a bookedService model document', function(done) {
timingRecordId = timingRecord.id;
// console.log(`/api/v1/timings/${timingRecordId}/bookedservices`);
authenticatedUser
.post(`/api/v1/timings/${timingRecordId}/bookedservices`)
.set('Accept', 'application/json')
.set('Cookie', session)
.send({ isComplete: false, bookedDate: '2019-05-13T00:10:02.001Z' })
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, result) {
if (err) {
done(err);
} else {
result.body.should.be.an('object');
result.body.should.have.property('bookedServiceDoc');
result.body.should.have.property('message');
createdPostId = result.body.bookedServiceDoc.should.have.property(
'id'
);
console.log(result.body);
done();
}
});
});
The error is like this
<- POST /api/v1/users/entrance/login (36ms 401)
| bad combo
| The provided email and password combination does not match any user in the database.
Edit-1:- I narrowed it down to that it is not that the user does not exist but it throws this error due to not maching the password, I am using the template that comes with user authentication and it uses the helpers.password for confirming the password. It is throwing error there.
But where is this helper in the project?
Thank you for your help
For anyone in the future, this is how I changed it.
The problem is not hashing when I add it directly but when I verify it sails hashes the incoming password, thus resulting in mismatch of passwords
let hashedPasswordUser = await sails.helpers.passwords.hashPassword(
'xxisnotgood123',
12
);
userRecord = await User.create({
emailAddress: 'xx#gmail.com',
password: hashedPasswordUser,
fullName: 'siddhant',
location: 'vellore',
image: 'not a cipdsai pic',
mobile: '9681901311',
description: 'words cant describe me'
}).fetch();