I am trying to set up a simple login system for a small project. I have managed to connect the website to an login api hosted locally via mysql database.
I'm using express/nodejs on backend and Vue for front end. Also using axios to send http requests.
The error i get is POST http://localhost:3001/api/get-user 422 (Unprocessable Entity)
"{"message":"Please provide the token"}"
Client side part of code.
finally{
const auth = await axios.post(`/api/get-user`, {
headers: {
Authorization: `Bearer ${this.Token}`
}
})
}
Server side part.
router.post('/get-user', signupValidation, (req, res, next) => {
if(
!req.headers.authorization ||
!req.headers.authorization.startsWith('Bearer') ||
!req.headers.authorization.split(' ')[1]
){
return res.status(422).json({
message: "Please provide the token",
});
}
const theToken = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(theToken, 'the-super-strong-secrect');
db.query('SELECT * FROM users where id=?', decoded.id, function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results[0], message: 'Fetch Successfully.' });
});
});
I have put a base URL.
The login is working 100% and I am able to extract the token from the response data.
I used Postman to send the request to the server to get the user and it works perfectly. I believe the issue is in the code of the client side or maybe the client side is sending the token incorrectly where the server side cant read it... I'm not sure please help.
The second parameter in axios post is the body
finally{
const auth = await axios.post(`/api/get-user`,{}, {
headers: {
Authorization: `Bearer ${this.Token}`
}
})
}
Related
I'm trying to build an app that allows me to call Spotify's API from a create-react-app client through a nodejs express server. I'm trying to use the Authorization Code Flow.
It works getting the authorization code using the following code to generate the URL, completely on client side (if and how using server-side is another question):
getSpotifyCodeUrl() {
const authEndPoint = 'https://accounts.spotify.com/authorize'
const clientId = CLIENT_ID;
const responseType = 'code';
const redirectUrl = 'http://localhost:3000/';
// TODO: state for cross-site request forgery protection
// cont state = '...';
const scope = 'user-read-private user-read-email';
return(
authEndPoint +
'?response_type=' + responseType +
'&client_id=' + clientId +
(scope ? '&scope=' + encodeURIComponent(scope) : '') +
'&redirect_uri=' + encodeURIComponent(redirectUrl)
)
}
The user simply clicks a link with the href as generated above.
{!this.state.token ? <a className="btn btn--loginApp-link" href={this.getSpotifyCodeUrl()}>
Login to Spotify
</a> : ""}
After the user gets redirected back, I use the following function to extract the authorization code from
componentDidMount() {
this.setState({code: new URLSearchParams(window.location.search).get('code')});
}
With the code I retrieve the access token. Call from client:
getSpotifyAccessToken() {
fetch('/auth?code=' + this.state.code)
.then((res) => res.json())
.then(data => {
this.setState({token: data.token});
localStorage.setItem('token', this.state.token);
});
}
API call on server:
app.get("/auth", (req, res) => {
let code = req.query.code;
let authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: 'http://localhost:3000/',
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer.from(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body){
if (!error && response.statusCode === 200) {
token = body.access_token;
res.json({ token: "Token: " + body.access_token});
} else {
console.log("/auth response body")
console.log(body)
}
});
});
Strange thing is I get a token, but can also see the following error in my server terminal:
{
error: 'invalid_grant',
error_description: 'Invalid authorization code'
}
If I then try to use the token to do a (simple) request from client:\
getSpotifyMe() {
fetch('/me?token=' + this.state.token)
.then((res) => res.json())
.then(data => {
console.log(data);
});
}
And corresponding server call:
app.get("/me", (req, res) => {
let token = req.query.token;
console.log("Token: " + token);
let options = {
url: 'https://api.spotify.com/v1/me',
headers: { 'Authorization': 'Bearer ' + token },
json: true
}
request.get(options, function(error, response, body) {
console.log("/me request body");
console.log(body);
if (!error && response.statusCode === 200) {
res.json(body);
} else {
}
})
})
Which gives me a 401 error:
{ error: { status: 401, message: 'Invalid access token' } }
I've tried some things. Doing the call from client, no success. Refreshing tokens, deleting cookies, authorizations from account, but no success. The strange thing is I can use the token said to be invalid in the Spotify Web Console, doing the exact same call I'm trying to do in the application.
Do you know where I'm causing these errors (invalid_grant and 401) in my application? And how I could solve them?
I ended up following the example on Spotify GitHub more closely.
I changed my create-react-app to simply call the /login server route. Don't use fetch like I tried, you'll end up with a cross-origin error once the server calls the Spotify API from a different origin. For some unapparent reason I can't use href="/login", the server simply doesn't respond, but that's fruit for another SO question.
Login to Spotify
The server index.js is now simply the Authorization Code Flow app.js with my own variables and one minor tweak.
My redirect_uri is http://localhost:3001/callback, I struggled long and tiresome with redirecting back to my client create-react-app at http://localhost:3000, perhaps this is where something goes wrong with the auth code and access token, IDK. You wanna go straight into the server-side callback. Much more intuitive for the user as well: one click and bam, logged in, no messy redirects in between.
In the /callback route on the server, when the access_token and refresh_token have either been successfully retrieved or not, that's when you want to redirect back to your client, http://localhost:3000 for me. With the tokens of course. The example uses URL params, I'm guessing setting a cookie should also work, but have to do some research if that's a security no-go.
The small little tweak I made is to the CORS clause for the app. There's no need for incoming request at the server other than coming from my client, so I added a {origin: 'http://localhost:3000'} there, just in case.
app.use(express.static(__dirname + '../client/build'))
.use(cors({origin: 'http://localhost:3000'}))
.use(cookieParser());
That's it, works like a charm, I can see the response body of the /v1/me call coming in at the server (there's a console log in the example code) and tokens are coming back to the client.
I use nextjs API Routes to connect to an external API
when I use a POST request to send a file, the file doesn't handled correctly in API Routes and I get 422 Unprocessable Entity Error
when I send only text it's OK, but when I add a file this error occur.
my request
const data = new FormData();
data.append('first_name', firstName);
data.append('last_name', lastName);
data.append('phone_number', phoneNumber);
image && data.append('avatar', image);
axios.post(`${API_ROUTE}`, data, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res=>{
alert(JSON.stringify(res.data, null, 2));
})
my API Route
const config = {
headers: {
'Content-Type': req.headers['content-type']
}
};
axios.post(address, req.body, config)
.then(r=>{
res.statusCode = 200
res.json( r.data )
})
.catch(err=>{
res.json(err)
})
in the request that API Route sending to the external API, there are some ? in the content of the file, but in the request that sending from browser to the API Route seems there are different characters
I am having problems getting the correct token for triggering my cloud function.
When testing through POSTMAN I get the token by running the following command:
gcloud auth print-identity-token
and my functions works correctly.
But on my server I am using the following code. I also do see the token but I get 401 with this token.
// Constants------------
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
async function gToken(){
let token='';
try{
// Fetch the token
const tokenResponse = await fetch(metadataServerTokenURL + 'https://'+process.env.CLOUD_URL, { //URL WITHOUT THE PATH
headers: {
'Metadata-Flavor': 'Google',
},
});
token = await tokenResponse.text();
} catch (err){
console.log(err);
}
return token;
}
---------EDIT-------
The calling function::
app.get("/", async function(req , res){
try {
const token = await getToken();
console.log(`Token: ${token}`);
const functionResponse = await fetch('https://'+process.env.CLOUD_URL+process.env.PATH_TO_FUNC, {
method: 'post',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`},
});
console.log(`Status: ${await functionResponse.status}`);
res.sendStatus(200);
} catch (err){
console.log(err);
res.status(400).send('Something went wrong')
}
})
My server is my NodeJS code running on AppEngine.
What am I doing wrong please?
----------EDIT 2--------------
I entered the two tokens received using two different ways, they show different information for some reason. Please see below::
Token from the server
Token using gcloud command locally (which works)::
Server code and cloud functions are both hosted in the same region, and are a part of the same project.
process.env.CLOUD_URL > "e****-***2-c******-e******2.cloudfunctions.net"
What #Charles and #John mentioned in the comment is correct. You should include the name of the receiving function in the audience:
As mentioned in the docs:
In the calling function, you'll need to create a Google-signed OAuth ID token with the audience (aud) set to the URL of the receiving function.
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
...
// Fetch the token
const tokenResponse = await fetch(metadataServerTokenURL + `https://${process.env.CLOUD_URL}/${FUNCTION_NAME}`, {
headers: {
'Metadata-Flavor': 'Google',
}
The audience should look like your HTTP trigger URL. If you decode your JWT ID token, aud looks like this:
{
"aud": "https://[REGION]-[PROJECT_ID].cloudfunctions.net/func-post",
"azp": "117513711437850867551",
"exp": 1614653346,
"iat": 1614649746,
"iss": "https://accounts.google.com",
"sub": "117513711437850867551"
}
that is my code in helper signin router
so I'm trying to set cookie to browser after confirm sign in
exports.signin = (req,res) => {
db.User.findOne({email:req.body.email}).then(user => {
user.comparePassword(req.body.password,function(err,isMatch){
if(isMatch){
let token = jwt.sign({userId: user.id},process.env.SECRET_KEY,{ expiresIn: 60*5 });
res.setHeader() //here I wanna send header Bearer to the browser
} else {
res.status(400).json({message:"Incorrect Password!"});
}
})
})
.catch(err => {
return res.status(404).send('No user found.');
})**strong text**
}
Authorization header is a client header, you cannot set it on the server(Server does not need authorization). You need to send it in a JSON response and then handle it in the client side.
Your server sends a JSON response with the token.
Your client sets the Authorization header and send it to any route that reaquires authorization
javascript client example:
var myHeaders = new Headers()
/**you need to get the token and put it in myToken var.
Where to store the token is the real question, you need
to take care about the storage you choose beacause of
the security risks*/
myHeaders.append('Content-Type','application/json; charset=utf-8');
myHeaders.append('Authorization', 'Bearer ' + myToken);
fetch( '/myurl', {
credentials: 'include',
headers: myHeaders,
method: 'GET'
}).then( res => {
return res.json();
}).then( res => {
/**your stuff*/
});
Then in your server check for the headers and you will see the Bearer
In Node.js res.setHeader() and Express js res.header() is an alias of res.set() method.
you can use in following ways :
res.setHeader('Authorization', 'Bearer '+ token);
res.header('Authorization', 'Bearer '+ token);
But I recommend you to read jwt example (angularjs & node) : https://code.tutsplus.com/tutorials/token-based-authentication-with-angularjs-nodejs--cms-22543
I'm trying to get a response from the imgur api, and I'm having trouble getting anything. I keep getting 401 not authorized errors. I've also tried using ".../gallery/authorization?client_id=###########/search="+req.body.search and that doesn't work either. I'm not really understanding the documentation. Can anyone tell me what I am doing wrong? Oh, btw this is written in express/node.js.
router.post('/getimages', function(req,res,next){
console.log('successfully got to /getimages backend :P');
console.log('the value of the search query is ', req.body.search);
var url = 'https://api.imgur.com/3/gallery/search?q='+req.body.search;
axios.get(url)
.then(function (response) {
console.log('YATA from axios to imgur');
console.log(response);
res.json({'YATA':'YATA from getimages'});
})
.catch(function (error) {
console.log('Fail from axios request to imgur');
console.log(error);
res.json({'OHNOES':'NOOOOYATA from getimages'});
});
})
According to the documentation, you have to register your app to use the API. This is even if you'll be using it in an anonymous capacity, which they seem to allow.
Put the clientId in the authorization header :
axios = require("axios");
search = "monkey";
clientId = "YOUR_CLIENT_ID";
axios({
method: 'get',
url: 'https://api.imgur.com/3/gallery/search?q=' + search,
headers: { 'authorization': 'Client-ID ' + clientId }
}).then(function(response) {
console.log(response.data);
}).catch(function(error) {
console.log(error);
});