Spotify API Authorization Grant Flow status 415 - node.js

I am trying to build a web app that integrates with the spotify API. For this, I am using the Authorization grant flow.
I managed to get an authorization code, but on the back end when I am testing the endpoint that should exchange the auth code with an access token, I keep getting a 415 response status.
Here is the service that the endpoint is using:
export async function getAccessAndRefresh(code: string): Promise<any> {
return axios.post(ACCESS_URL, {
data: {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI
},
headers: {
"Authorization": " Basic " + Buffer.from(CLIENT_ID + ":" + CLIENT_SECRET).toString("base64"),
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
json:true
})
}
Also, I wrote this unit test in order to test the service(I got the 415 while running this unit test):
describe("Request tests", () => {
let server: Server;
function initServer() {
server = createServer(App);
server.listen(5000);
}
function destroyServer() {
server.close();
}
test("Test refresh and access token returned by spotify api", () => {
return getAccessAndRefresh(AUTH_CODE).then((value)=>{
expect(value).toHaveProperty("access_token");
})
})
beforeAll(() => {
initServer();
});
afterAll(()=>{
destroyServer();
})
})
In the test, AUTH_CODE is a code that I obtained manually in a browser by accessing the https://accounts.spotify.com/authorize endpoint with my API Key.
Can anyone help me figure this one out please? Thanks!

In the Spotify Dashboard you might need to set the Redirect URI to the URL you're using in your code, these need to match if getting the following error:
{ "error": "invalid_grant", "error_description": "Invalid redirect URI" }
That's all you need to do, just go to the Dashboard where you get the Client ID and Client Secret and then go to the Edit Settings and you'll see the option to set the Redirect URI

Related

'Full authentication is required to access this resource' Clockify API with Node JS

I am unable to add new clients using the Clockify API in Node js. The following axios post request returns the error message: 'Full authentication is required to access this resource'. However, I am able to get a list of clients using the axios.get() method with the same API key, so I'm not sure why the post request says I'm not authenticated. Please let me know what I'm missing
async function addNewClient(clientName) {
return response = await axios.post(`${url}/workspaces/${workspaceId}/clients`, {
data: {
'name': clientName
},
headers: {
'X-Api-Key': CLOCKIFY_API_KEY,
'Content-Type': 'application/json',
},
}).catch(function (error) {
console.log(error.response.data.message);
return
});
}
I had the same issue. Regenerating API_KEY solved it.

nodejs express + create-react-app oauth2.0 error 401 'Invalid access token' Spotify API

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.

Spotify Web API Error 400: "Only valid bearer authentication supported"

I'm trying to use the Spotify Web API to search for songs through node, but it keeps sending me a status 400 error with the message: "Only valid bearer authentication supported". This is my code:
app.get("/", (req, res) => {
let searchurl = "https://api.spotify.com/v1/search?";
request.post(
{
url: searchurl,
data: {
q: "john",
type: "album",
},
headers: {
"Content-Type": "application/json",
Authorization:
"Basic " +
Buffer.from(client_id + ":" + client_secret).toString("base64"),
},
method: "POST",
},
function (e, r, body) {
console.log(body);
}
);
});
I don't understand what the issue is and have read through everything I could find, but got nowhere. Am I supposed to use a different access key?
Have a look at the Authorisation Guide for Spotify, you can use the Client Id and Client Secret to get an Access Token and it is that you send as part of an Authorization header when making the request to the Search for an Item endpoint you're trying to use

cypress retrieve JWToken and set as header for further requests

When trying to test a express webapplication i build the test should first authenticate to retrieve a JWT token, the token retrieving works but the method to set the header for further requests doesn't work.
This code creates the method to run when authentication is needed,
Cypress.Commands.add("userRequest", function(requestObj) {
// Make a login request, returning a JWT token
cy.request({
method: 'POST',
url: '/credentials',
body: {
"name": "test_user",
"password": "test123_"
}
}).then(function(response) {
requestObj.headers = requestObj.headers || {};
requestObj.headers.Authentication = response.body.token;
return cy.request(requestObj);
});
This is the code that runs
it('retrieves one user', function () {
cy.userRequest(cy.request({
url: '/users/1'
})).then(function (response) {
expect(response.body.name).to.equal("admin");
});
});
BUT when the test runs the console tells me
CypressError: cy.request() failed on:
http://localhost:3000/users/1
The response we received from your web server was:
401: Unauthorized
This was considered a failure because the status code was not '2xx' or '3xx'.
Can anyone help me?
thanks in advance!
As I said in my comment that cypress works on async programming, so try with in support/command.js
Cypress.Commands.add('userRequest', (email, password) => {
Cypress.log({
name: 'loginViaAuth0',
});
const options = {
method: 'POST',
url: '/credentials',
"body":'userName='+email+'&Password='+password+'
};
cy.request(options)
});
Then in your code
cy.userRequest(userName,password)
.its('body').then((response)=>{
// Do your stuffs
})

Configuring request to Google My Business API to avoid Unauthenticated error

I keep getting an unauthenticated error back when submitting my request to the Google My Business API in my Node.js app. The response:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
For what it's worth, I'm using the request-promise client to make the request. My function is below. I just received the access token, so I'm fairly certain that it's good, and I can see it through the err.options.Authorization log. I know the location ID doesn't exist yet, but I don't think that's what the error is telling me.
const request = require('request-promise');
...
function checkLocation (loc) {
return request({
method: 'GET',
uri: `https://mybusiness.googleapis.com/v4/accounts/${ACCOUNT_ID}/locations/${loc._id}`,
Authorization: `OAuth ${ACCESS_TOKEN}`
})
.then(function (response) {
console.log(response);
})
.catch(function (err) {
console.error(err.error, err.options.Authorization);
});
}
Is my request formatted incorrectly or am I not sending all I need to be?
Update: I left out possibly crucial information that this is through the one time authorization process for server-side apps like outlined here: https://developers.google.com/identity/protocols/OAuth2. My trouble is wrapped up in the sentence on that page, "After an application obtains an access token, it sends the token to a Google API in an HTTP authorization header."
Turned out my header wasn't formatted correctly. The Authorization key should be in a headers object:
{
method: 'GET',
uri: `https://mybusiness.googleapis.com/v4/accounts/${ACCOUNT_ID}/locations/${loc._id}`,
headers: {
Authorization: `OAuth ${ACCESS_TOKEN}`
}
}

Resources