I have a peculiar problem here guys. While working with strapi (first timer), I'm attempting to set up login and register, but I keep running into the same error, until I used the exact code example from the documentation, even then it only works on a condition.
When I do this, it works. that is when I hardcode the identifier and password. it works and I get the jwt.
function fetchLogin(){
await axios
.post(url, {
identifier: "user",
password: "123456"
})
.then((response) => {
// Handle success.
console.log('Well done!');
console.log('User profile', response.data.user);
setUser(response.data.user)
console.log('User token', response.data.jwt);
setUserToken(response.data.jwt)
})
.catch((error) => {
// Handle error.
console.log('An error occurred:', error.response);
setLoginError(error.response)
});
}
But I used the input fields from the client, and I collect the correct data, I get an error. Here.
function fetchLogin(data){
console.log(data)
await axios
.post(url, {
data
})
.then((response) => {
// Handle success.
console.log('Well done!');
console.log('User profile', response.data.user);
setUser(response.data.user)
console.log('User token', response.data.jwt);
setUserToken(response.data.jwt)
})
.catch((error) => {
// Handle error.
console.log('An error occurred:', error.response);
setLoginError(error.response)
});
}
//Heres the function that collects the input
const LoginSubmit = (e) =>{
e.preventDefault()
const data = {
identifier: login.email,
password: login.password
}
const details = JSON.stringify(data)
fetchLogin(details)
}
// useState
const [login, setLogin] = useState({
email: '',
password: ""
})
// input fields
<div className="input-group input-group-sm mb-3">
<span className="input-group-text" id="inputGroup-sizing-sm">Password</span>
<input type="password" className="form-control"
aria-label="Sizing example input"
value={login.password}
onChange={(e)=> setLogin({...login, password: e.target.value})}
aria-describedby="inputGroup-sizing-sm"/>
</div>
Everything checks out when I log data to console, but I get a validation error
POST http://localhost:1337/api/auth/local 400 (Bad Request)
An error occurred: {data: {…}, status: 400, statusText: 'Bad Request', headers: {…}, config: {…}, …}
//the error message
errors: Array(2)
0: {path: Array(1), message: 'identifier is a required field', name: 'ValidationError'}
1: {path: Array(1), message: 'password is a required field', name: 'ValidationError'}
What I'm I doing wrong?
should remove Json.Stringfy in LoginSubmit when call fetchLogin and use spread operator. Body of request must be {id:"", password:""}, not like {data: "{id:"",password:""}"}
so should use spread operator {...}, it do unpack elements of an object/array
ex:
const data = {name:"foo"}
const newData = {
...data,
color: 'black'
};
//newData is {name: "foo",color:'black'};
solution:
//data is {identifier: "user",password: "123456"}
function fetchLogin(data){
await axios.post(url, { ...data })
equal:
function fetchLogin(data){
await axios.post(url, {identifier: "user",password: "123456"})
Related
Hello I'm trying to add custom validation to my form using the Joi library.Basically i want to reach to an api and either return error message or not based on the data.
here is my Joi schema
const schema = Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } })
.required(),
firstName: Joi.string().required(),
lastName: Joi.string().required(),
description: Joi.string().min(10).max(250).required().custom(isSad).message({'description.invalid':`the value provided in the description field is sad, please redact it`}),
});
the isSad function passed in the custom() argument
const isSad = (value,helpers) => {
fetch('api url',{
method: "POST",
headers: {
"apikey": "key"
},
body:value
}).then(data => {
return data.json()
}).then(data => {
if(data.Sad > 0.49) {
return helpers.error('description.invalid');
}
}).catch(error => {
console.log('logging the error in catch', error)
})
}
As far as I understand I'm sending 'description.invalid' to the .message() function in the schema where I should use it in the passed object to display a custom message, but for some reason I'm not getting the error message displayed. The field seems to be validated as valid which it shouldn't be in my case if the value received is > 0.49
EDIT: Tried using schema.validateAsync with .external() like so
const isSad = (value,helpers) => {
console.log('logging value',value)
console.log('logging helpers',helpers)
fetch('api',{
method: "POST",
headers: {
"apikey": "apikey"
},
body:value
}).then(data => {
return data.json()
}).then(data => {
if(data.Sad > 0.49) {
throw new Error('Ur description is sad please edit it')
}
}).catch(error => {
console.log('logging the error in catch', error)
})
}
and to the schema i just attach .external(isSad) like so
const schema = Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } })
.required(),
firstName: Joi.string().required(),
lastName: Joi.string().required(),
description: Joi.string().min(10).max(250).required().external(isSad)
});
I also had to convert the code where I use the schema.validateAsync since it now returns data as HTTP response.BUT it still doesn't work I get no response whatsoever from the .external() and the description field is validated ( It's like the .external() is not there at all ).
Found an issue, it says that custom is only for synchronous functions, for async you need to use external.
EDIT1
If I understand it right, and please correct me if not, the problem is that error is not thrown, when it should.
In that case I have done the following. Changed the request and the data.
The console says: logging the error in catch Error: Ur description is sad please edit it. Which looks to me as the expected behavior.
const isSad = (value) => {
console.log("value: ", value);
fetch("https://api.coindesk.com/v1/bpi/currentprice.json", {
method: "GET"
})
.then((data) => data.json())
.then((data) => {
console.log("request data: ", data);
if (value.startsWith(data.chartName)) {
throw new Error("Ur description is sad please edit it");
}
})
.catch((error) => {
console.log("logging the error in catch", error);
});
};
const schema = Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } })
.required(),
firstName: Joi.string().required(),
lastName: Joi.string().required(),
description: Joi.string().min(10).max(250).required().external(isSad)
});
schema.validateAsync({
email: "asf#adf.asdf",
firstName: "adfsdafsdf",
lastName: "asdfasdf",
description: "Bitcoin111"
});
I ended up using .validate() not .validateAsync() and made my own custom function check after Joi has already validated the form.
I'm integrating next-auth package to my fresh Next.js project. I have followed all of the Next.js and next-auth documentations but not able to find a solution.
The issue I'm facing goes like this:
I want to Login to my Next.js app using Email & Password submitted to my API Server running on Laravel.
When submitting the login form I'm executing the below function.
import { signIn } from "next-auth/client";
const loginHandler = async (event) => {
event.preventDefault();
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;
const result = await signIn("credentials", {
redirect: false,
email: enteredEmail,
password: enteredPassword,
});
console.log("finished signIn call");
console.log(result);
};
And code shown below is in my pages/api/auth/[...nextauth].js
import axios from "axios";
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
export default NextAuth({
session: {
jwt: true,
},
providers: [
Providers.Credentials({
async authorize(credentials) {
axios
.post("MY_LOGIN_API", {
email: credentials.email,
password: credentials.password,
})
.then(function (response) {
console.log(response);
return true;
})
.catch(function (error) {
console.log(error);
throw new Error('I will handle this later!');
});
},
}),
],
});
But when try to login with correct/incorrect credentials, I get the below error in Google Chrome console log.
POST http://localhost:3000/api/auth/callback/credentials? 401 (Unauthorized)
{error: "CredentialsSignin", status: 401, ok: false, url: null}
Am I missing something here?
From the documentation (https://next-auth.js.org/providers/credentials#example)
async authorize(credentials, req) {
// Add logic here to look up the user from the credentials supplied
const user = { id: 1, name: 'J Smith', email: 'jsmith#example.com' }
if (user) {
// Any object returned will be saved in `user` property of the JWT
return user
} else {
// If you return null or false then the credentials will be rejected
return null
// You can also Reject this callback with an Error or with a URL:
// throw new Error('error message') // Redirect to error page
// throw '/path/to/redirect' // Redirect to a URL
}
}
You are not currently returning a user or null from the authorize callback.
Answer posted by shanewwarren is correct, but here is more elaborated answer,
Using axios to solve this
async authorize(credentials, req) {
return axios
.post(`${process.env.NEXT_PUBLIC_STRAPI_API}/auth/login`, {
identifier: credentials.identifier,
password: credentials.password,
})
.then((response) => {
return response.data;
})
.catch((error) => {
console.log(error.response);
throw new Error(error.response.data.message);
}) || null;
},
I'm trying to work out how to receive helpful error messages on the client side, but keep getting generic error messages. For example, trying to sign up with an email that is not available should result in the email#email.com is already in use error message. I, however, get the generic Request failed with status code 409 message, which is obviously unhelpful to the user. The network response is as expected as seen in the screenshot below. What gives? Why am I not getting the same error message as my (Redux) payload?
Below are the relevant code snippets.
Sign up controller
export default {
signup: async (req, res, next) => {
try {
const { fullname, username, email, password } = req.body;
// Check if there is a user with the same email
const foundUser = await User.findOne({ email });
if (foundUser) {
return res.status(409).send({ error: `${email} is already in use` });
}
const newUser = await User.create({
fullname,
username,
email,
password,
});
// Assign token to succesfully registered user
const token = authToken(newUser);
return res.status(200).send({ token, user: newUser });
} catch (error) {
next(error);
}
},
};
Sign up action
export const createAccount = ({
fullname,
username,
email,
password,
history
}) => async dispatch => {
dispatch({
type: actionTypes.CREATE_ACCOUNT_REQUEST,
});
try {
const {
data: {
newUser: { token, user },
},
} = await request.post('/auth/signup', {
fullname,
username,
email,
password,
});
localStorage.setItem('auth-token', token);
dispatch({
type: actionTypes.CREATE_ACCOUNT_SUCCESS,
payload: user
});
// Redirect to home
history.push('/home');
} catch (error) {
dispatch({
type: actionTypes.CREATE_ACCOUNT_FAILURE,
payload: error.message
});
}
};
Sign up network response
Redux sign up error payload
Try 'error.response.data.error' instead of 'error.message'
I have a problem regarding push notifications. I have this error in firebase functions logs:
Error: To send a message with a payload, the subscription must have
'auth' and 'p256dh' keys.
exports.storePostData = functions.https.onRequest(
(request, response) => {
cors(request, response, () => {
admin.database().ref('posts').push({
id: request.body.id,
title: request.body.title,
location: request.body.location,
image: request.body.image
}).then(() => {
webpush.setVapidDetails('mailto: xxxx#gmail.com', 'BLl7xIPAyJNzsMi5vo_aG-4RdXdyZ4Q4ZFpTgnm902qN79MIiSORBk9N-rfFEGiKNPuJu5SJmUX35Wwce9nuH94', 'M8E6hw7jCmu7qNQJ88FV5o02OAiLefEFJK8jyJimk7g')
return admin.database().ref('subscriptions').once('value');
}).then(subscriptions => {
subscriptions.forEach(sub => {
var pushConfig = {
endpoint: sub.val().endpoint,
keys: {
auth: sub.val().keys,
p256dh: sub.val().p256dh
}
}
webpush.sendNotification(pushConfig, JSON.stringify({
title: 'New Post',
content: 'New post added',
openUrl: '/help'
})).catch(err => {
console.log(err)
})
})
response.status(201).json({
message: 'Data stored',
id: request.body.id
})
}).catch(err => {
response.status(500).json({
error: err
})
})
})
});
This is my function for storing post data, and i think the problem is here because it can't even get to push event in serviceWorker (i don't get any logs there).
On the client side, you should be able to retrieve subscription
On Angular it would be like
const sw = await navigator.serviceWorker.register('/assets/js/service-worker.js', { scope: '/assets/js/' });
let subscription = await sw.pushManager.getSubscription();
Through this way, you should be able to get subscription and subscription object contains p256dh and auth keys.
You should be able to see the values by
console.log("subscription: ", subscription?.toJSON());
Ye i figured it need to go as:
keys: {
auth: sub.val().keys.auth,
p256dh: sub.val().keys.p256dh
}
I'm trying to pass form data from login page to signin page via post using fetch with this pug code:
form(id="form-login")
input(type="text", name="email", value="", placeholder="Tu email")
br
input(type="password", name="password", value="", placeholder="Tu contraseña")
br
input(type="submit" value="Conectar")
script.
const formLogin = document.querySelector('#form-login');
const formData = new FormData(formLogin);
formLogin.addEventListener('submit', function(event) {
console.log('Form Data: ', formData);
event.preventDefault();
fetch('/signin', {
method: 'POST',
body: formData
})
.then(function(res) {
res.json();
})
.then(function(data) {
console.log(data)
localStorage.setItem('token', data.token)
})
});
The problem is an empty req.body reaching to signin.. After trace it gives this console.log
Form Data: FormData {}
and also an undefined req.body.
If I comment this script and just send it through form adding action="/signin" and method="post", it works and the answer is printed, but calling storage.setItem({ token: <token> }) returns an Uncaught (in promise) TypeError: Cannot read property 'token' of undefined
I'm wondering why this script is not sending the data... can't figure out... so any help will be much apreciated.
Signin function:
function signIn (req, res) {
if (!req.body.email) return res.status(200).send({message: 'No recibo el usuario'})
User.findOne({ email: req.body.email }, (err, user) => {
if(err) return res.status(500).send({ message: err })
if(!user) return res.status(404).render('login', { title: 'Intenta loguearte de nuevo' })
user.comparePassword(req.body.password, (error, isMatch) => {
if (error) return res.status(500).send({ message: error })
if (!isMatch) {
return res.redirect('login')
} else {
req.user = user
res.status(200).send({
message: 'Te has logueado correctamente',
token: service.createToken(user)
})
//$window.localStorage.setItem({token: service.createToken(user)}); // NO WORKS
return res.body = service.createToken(user) // TRYING THIS WITHOUT KNOWLEDGE ABOUT WHAT AM I DOING :O
}
})
})
}
Thanks in advance.
****EDIT****
As #MichałSałaciński suggest, commenting first .then res.json().... At least gives a response, but still don't undestand what's hapenning here and in order to learn properly and make things better, also hope someone can explain how to correctly do stuff like this.
Response: body : ReadableStream
locked : false
__proto__ : Object
bodyUsed : false
headers : Headers
__proto__ : Headers
ok : true
redirected : false
status : 200
statusText: "OK"
type : "basic"
So I was having the same issue where the POST request from my pug form was sending back an empty {} as the req.body object. The code was a simple create action using these:
bookController.js
exports.createBookForm = (req,res) => {
res.render("create_book_form", { title: "Add A New Book"})
}
exports.createBook = (req,res) => {
const reqFields = ["title", "author"];
for (let i = 0; i < reqFields.length; i++) {
const field = reqFields[i];
if (!field in req.body) {
const message = `Missing ${field} in the request body`;
console.log(message)
return res.status(400).send(message)
}
}
Book
.create({
title: req.body.title,
author: req.body.author,
summary: req.body.summary
})
.then((book) => {
res.status(201).json(book.serialize())
})
.catch(err => {
console.log(err);
})
}
And the create book form:
block content
h1 Add a Book
h3 Do use real details. Otherwise, what's the point?
form(method="POST" action="/books")
div.form-group
label(for="title") Title:
input#title.form-control(type="text", placeholder="Small Gods" name="title")
label(for="author") Author:
input#author.form-control(type="text", placeholder="Terry Pratchett" name="author")
label(for="summary") Summary:
textarea#summary.form-control(type="text", placeholder="God is turtle, world is flat" name="summary")
div.form-group
button.btn.btn-primary(type="submit" role="submit") Add Book
What finally fixed getting the actual req.body to show up for the POST action was adding (within server.js)
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
Let me know if this works for you. Took me a couple hours to come to this conclusion and I hate seeing questions go unanswered.
You should move "new FormData" inside "send" event listener. Also, there's missing comma after type="submit", but overall, the problem got nothing to do with pug :)
form(id="form-login")
input(type="text", name="email", value="", placeholder="Tu email")
br
input(type="password", name="password", value="", placeholder="Tu contraseña")
br
input(type="submit",value="Conectar")
script.
const formLogin = document.querySelector('#form-login');
formLogin.addEventListener('submit', function(event) {
const formData = new FormData(formLogin);
console.log('Form Data: ', formData);
event.preventDefault();
fetch('/signin', {
method: 'POST',
body: formData
})
.then(function(res) {
res.json();
})
.then(function(data) {
console.log(data)
localStorage.setItem('token', data.token)
})
});