I am trying to authenticate Spotify API using the OAuth2.0 client credentials flow. Generally the flow is as follows:
Send GET request to /authenticate endpoint with required parameters, including a callback endpoint that i have set to my http://localhost:8888/callback
const open = require('open')
responseType = 'token'
clientID = 'CLIENT_ID'
redirectURI = 'http%3A%2F%2Flocalhost%3A8888%2Fcallback'
scope = 'streaming+user-read-email+user-modify-playback-state+user-read-private+user-library-read+user-library-modify+user-read-currently-playing'
contentType = 'application/json'
params = {
'response_type' : responseType,
'client_id' : clientID,
'redirect_uri' : redirectURI,
'scope' : scope
}
headers = {
'Content-Type' : contentType
}
// send user to browser to grant data permissions
open(`https://accounts.spotify.com/en/authorize?response_type=${responseType}&client_id=${clientID}&redirect_uri=${redirectURI}&scope=${scope}&show_dialog=true`)
-- this part is fine, i have no issues sending user to browser to login and grant my app permission and the Express server i have listening on port 8888 lets me know when the callback pings with the response
Receive Authentication Token in params of callback from Authentication endpoint. Spotify API handles this part and sends the authentication token that i need as part of the header in the response. it looks like this
http://localhost:8888/callback#authentication_token=2iais820zg...fh9IHkLDI&timeout=3600
-- this is what i am having problems with.
here is my how i am handling the api callback in my Express server's /callback endpoint:
app.get('/callback', function (req, res) {
console.log('someone made a request!\n')
/// ////
console.log(req + '\n')
console.log(res + '\n')
/// ////
console.log(req.body)
res.json(req.body)
res.send('GET request to callback endpoint took you here')
res.end()
})
but the console output is as follows:
> server is listening on port 8888 ...
someone made a request!
[object Object]
[object Object]
{}
where everything is empty.
how can i access the URL parameters of the callback GET request? i have tried changing the handling from GET to POST as people have told me that a GET request doesn't contain params but the callback can't find the endpoint (bc it's not making a POST request..)
you should put the parameters into request body not in the header as you mentioned
to let you use req.body with no problem
Related
I am trying to set a JWT cookie for the response of a successful login in my Express app.
In the following code the user has been returned correctly from the DB and the jwt.sign() function is working correctly, ie. the token prints in the console.log
app.get('/api/login', (req, res) => {
... // perform database lookup here
// Set up JWT for the returned user
const secret = 'secretKey';
const options = { expiresIn: '1h' };
const token = jwt.sign(user, secret, options);
console.log('the token -> ', token);
res.cookie('token', token, { httpOnly: true });
console.log('res.headers (check for cookie) -> ', res);
// this logs the entire response object at the end of
// which Set-Cookie is present and contains the previously
// created JWT.
res.send(user)
}
The JWT is clearly being set in the response headers under Set-Cookie.
The problem is the cookie is not appearing in the dev tools of the client. As a result it is not being sent in the headers of subsequent requests.
It's the first time I have tried to implement JWT so I could be missing something basic. If anyone has any suggestions how to remedy this I'd much appreciate the help.
EDIT
I have tested the JWT that is being produced in the server route using the tool at jwt.io and it comes back as an invalid signature even though it shows the correct user data in the decoded window. I'm not sure if this itself is causing the token not to be sent to/stored on the client but there is obviously something going wrong while encoding the JWT.
In Nuxt.js this is one way to implement authentication :
The client authenticates by sending an HTTP request with its credentials in the body to an API route of the Nuxt backend ;
The Nuxt backend responds with a JWT token that allows the client to access protected routes ;
Finally, when the authenticated user tries to access such a route, they make an HTTP request to the Nuxt backend with their JWT token inserted in the header ;
The backend validates the JWT token and responds with the requested page JSON data to the client.
What I don't understand is how to make the Nuxt backend aware that for some protected routes it has to check the JWT token of the client before providing the page JSON data. I mean, where exactly in Nuxt can I implement this kind of validation ?
Well i am confused a bit first you say API data the other sentece you say JSON page.. however. If you want to protect an PAGE then you create an middleware
middleware/auth.js
export default async function ({ store, $axios, redirect }) {
let valid = await $axios.$post('/api/checktoken')
if (!valid) {
redirect('/')
}
}
You need to create an API where you check the token. Usually you need to put the token in your header like Authentication: Bearer token... however i simply save my token inside an cookie. Because if you send an HTTP request to the server the cookies gets automatically sended with it so i dont need to do some extra work.
Next step is you go to some page and set your middleware auth.
page.vue
<script>
export default {
middleware: "auth"
}
</script>
However if you want to protect some backend routes you can do it like this. Create again an middleware
async authenticate(req, res, next) {
let token = await cookieService.getTokenFromCookie(req)
if (!token) return errorService.resError(res, 400, 'Authorization failed')
let tokenValid = await tokenService.verifyToken(token)
if (!tokenValid)
return errorService.resError(res, 400, 'Authorization failed')
let decodedData = tokenService.decode(token)
if (!decodedData)
return errorService.resError(res, 400, 'Authorization failed')
res.locals.userId = decodedData.userId
res.locals.role = decodedData.role
next()
}
In this case you basically need to read the token out of your cookie. (in case you dont use cookies you will need to read it out of your header so for this you should create an function that reads your token out of the header)
Check if token is even there.
Verify if token is valid.
Decode the token so you can access the data in it
Now you can also put the data to your res.locals. The advantage is that this data is scoped to this current request and you can access it in the next middleware / endpoint.
then you call next() to step to the next middleware / endpoint
function endpoint(req, res) {
let { userId, role } = res.locals
do something....
}
So the route looks something like this:
app.use("/some/api/point", authenticate, endpoint)
The good thing about is you can put authenticate in every API route you want to protect.
I'm trying to display the JSON data form instagram's URL: https://www.instagram.com/apple/?__a=1.
If I put this in browser, it shows the JSON, but I want to be able to get it with express so I can attempt to capture any data I need to with that URL
const express = require("express");
const Instagram = require("node-instagram").default;
const keys = require("./config/keys");
const app = express();
const request = require("request");
app.get("/", (req, res) => {
const url = "https://www.instagram.com/apple/?__a=1";
request(url, (err, response, body) => {
const json = JSON.parse(body);
console.log(json);
res.json(request.json);
});
});
app.listen(5000);
When I'm trying to deploy this in localhost, I'm getting an error:
SyntaxError: Unexpected end of JSON input
If you are logged in to instagram in your browser, you are getting authentication via cookies, and everything works fine, but if you are not logged in, the url https://www.instagram.com/apple/?__a=1 responds wit a 403 (access denied) error. This is the case for your back-end code since you are not sending cookies or authentication headers.
You are probably getting an error but not checking for errors in your request callback (the err parameter).
According to the instagram developer documentation, you need to register your app and authenticate before you can perform the request with OAuth credentials.
Check request's documentation on OAuth signing for more info.
I need to retrieve some data from Google Search Console (Webmaster Tools) using a service account.
So far I've been able to retrieve an access_token for the service account which I need to append to the url of the request. The problem is that I can't find a way to do so, this is the code i'm using:
function retrieveSearchesByQuery(token)
{
gapi.client.webmasters.searchanalytics.query(
{
'access_token': token,
'siteUrl': 'http://www.WEBSITE.com',
'fields': 'responseAggregationType,rows',
'resource': {
'startDate': formatDate(cSDate),
'endDate': formatDate(cEDate),
'dimensions': [
'date'
]
}
})
.then(function(response) {
console.log(response);
})
.then(null, function(err) {
console.log(err);
});
}
This is the url called by the function:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json"
Instead it should be something like this:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json&access_token=XXX"
The gapi.client.webmasters.searchanalytics.query doesn't recognize 'access_token' as a valid key thus it doesn't append it to the url and that's why I get a 401 Unauthorized as response.
If I use 'key' instead of 'access_token' the parameter gets appended to the url but 'key' is used for OAuth2 authentication so the service account token I pass is not valid.
Does anyone have a solution or a workaround for this?
If your application requests private data, the request must be authorized by an authenticated user who has access to that data. As specified in the documentation of the Search Console API, your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
If you application is correctly configured, when using the Google API, an authenticated request looks exactly like an unauthenticated request. As stated in the documentation, if the application has received an OAuth 2.0 token, the JavaScript client library includes it in the request automatically.
You're mentioning that you have retrieved an access_token, if correctly received, the API client will automatically send this token for you, you don't have to append it yourself.
A very basic workflow to authenticate and once authenticated, send a request would looks like the following code. The Search Console API can use the following scopes: https://www.googleapis.com/auth/webmasters and https://www.googleapis.com/auth/webmasters.readonly.
var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'https://www.googleapis.com/auth/webmasters';
function auth() {
// Set the API key.
gapi.client.setApiKey(apiKey);
// Start the auth process using our client ID & the required scopes.
gapi.auth2.init({
client_id: clientId,
scope: scopes
})
.then(function () {
// We're authenticated, let's go...
// Load the webmasters API, then query the API
gapi.client.load('webmasters', 'v3')
.then(retrieveSearchesByQuery);
});
}
// Load the API client and auth library
gapi.load('client:auth2', auth);
At this point, your retrieveSearchesByQuery function will need to be modified since it doesn't need to get a token by argument anymore in order to pass it in the query. The JavaScript client library should include it in the request automatically.
You can also use the API Explorer to check what parameters are supported for a specific query and check the associated request.
If you need to use an externally generated access token, which should be the case with a Service Account, you need to use the gapi.auth.setToken method to sets the OAuth 2.0 token object yourself for the application:
gapi.auth.setToken(token_Object);
I have two applications, both on Nodejs. One front-end and other back-end.
My back-end app is protected with token access using express-jwt and jsonwebtoken middlewares.
My problem is: I am making a request from front-end to back-end passing the token on header, back-end accepts the request and respond properly. Then in the front-end I redirect the response to an specific page (res.redirect('/')), in that moment I get the error UnauthorizedError: No authorization token was found
My front-end request:
/* Authentication */
router.post('/', function(req, res, next) {
// request login service
request({
uri: env.getUrl() + "/user",
method: 'POST',
timeout: 10000,
headers: {
'Authorization': 'Bearer '.concat(global.token)
},
form: { login : req.body.login, pwd : req.body.pwd }
}, function(error, response, body){
if(error) {
logger.error(error);
res.render("error", {message: "Error getting user" });
}
else {
if(body){
req.session.usuario = JSON.parse(body);
res.redirect("/");
} else {
res.render("login", {message: "Login Failed" });
}
}
});
});
I don't know why this happen. Could you help me?
Thanks in advance.
A redirect (via res.redirect) issues a new HTTP request. This means that the Authorization header is empty. This results in the UnauthorizedError error.
To fix this, you have two options:
1. Pass the token in the URI
You can issue the redirect with the token passed in the URL in this way:
res.redirect("/?access_token=" + global.token);
2. Set the header before the redirect
You can set the 'Authorization' header before making the redirect request:
req.session.access_token = global.token;
Problem found.
Anytime the my front-end app makes a request to the back-end side (api) the user logged in front-end is validated against back-end and so the fron-end's session is updated as well. Which means that every request is actually two requests:
One as the real request the app is doing.
The request validating the user logged on front-end in order to be sure that user exists.
This update (second point) was made without providing a token.