What's wrong with the below code? - javascript

I am working on a sample Angular2 application which uses Spotify API to get data. When I run the code, I'm getting response Error in console as "Invalid Access Token". I have provided the correct access token, still the error persists, I'm not getting how to resolve it and what's wrong I'm doing.
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class SpotifyService {
private searchUrl: string;
constructor(private _http: Http) { }
searchMusic(str: string, type = 'artist') {
const access_token = '<My Access Token Here>';
const headers = new Headers({ 'Authorization': 'Bearer ' + access_token });
this.searchUrl = 'https://api.spotify.com/v1/search?query='+str+'&offset=0&limit=20&type='+type+'&market=US';
return this._http
.get(this.searchUrl, { headers })
.map(res => res.json());
}
}
Error Screenshot :

You can check by adding one of the content-type in headers
var headers: Headers = new Headers({'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/x-www-form-urlencoded' });
OR
var headers: Headers = new Headers({'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json' });

were you able to get a valid response by giving your access token in their api console?
https://developer.spotify.com/web-api/console/get-search-item/?q=tania+bowra&type=artist

Related

Automatic reload import React

Currently,
I've made an axios common to calling API as:
export const API_LOCAL = axios.create({
baseURL: process.env.REACT_APP_BASEURL,
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
timeout: 30000,
});
First time when I start to import and use file with this export as API_LOCAL, I'm not use the 'Authorization', the localStorage.getItem('token') is null. Then I'll get some data and set back to localStorage.setItem('token','SOME_SAMPLE_TEXT').
After that, when in another calling which use API_LOCAL, I think it's cached because the API_LOCAL with localStorage.getItem('token') is always null.
Could I set up for the dynamic reload import file to get latest data?
Thank you for any guides.
the best way to handle Bearer token is via interceptor.
You can do something like
axios.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if ( token != null ) {
config.headers.Authorization = `Bearer ${token}`;
}
}
This way every axios request will be sent with your auth token

"grant_type parameter is missing": Spotify API PKCE OAuth Flow Troubles

I'm developing a React app that uses the Spotify API I can't figure out why I'm getting this error when trying to get an access token with the API's PKCE OAuth flow.
{
error: "unsupported_grant_type",
error_description: "grant_type parameter is missing"
}
I'm following the directions from the guide exactly and I'm able to obtain an auth code just fine. Here's my call trying to get the token.
let res = await axios.post("https://accounts.spotify.com/api/token", {}, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
params: {
"grant_type": "authorization_code",
"code": data.code,
"redirect_uri": redirectUri,
"code_verifier": verifier,
"client_id": clientId
}
}).catch(err => console.error(err));
I've tried passing the params in the body of the post request and as url params and both produce the same results. As you can see, I'm clearly providing a grant_type and I'm using the value that the guide said to use.
I've tried every method I was able to find on the internet, nothing seemed to be working, but after a few hours, this succeeded:
const headers = {
Authorization:
'Basic ' +
new Buffer(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64'),
}
const { data } = await axios.post(
'https://accounts.spotify.com/api/token',
'grant_type=client_credentials',
headers: { headers },
)
this.token = data.access_token
After this, you can simply use any endpoint as seen in the Spotify API examples.
Use querystring npm package to parse the data since we're using application/x-www-form-urlencoded in the header
And change the grant_type to grant_type: "client_credentials"
var querystring = require('querystring');
const headers = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
};
let data = {
grant_type: "client_credentials",
code: data.code,
redirectUri: "http://localhost:8000/callback",
client_id: your_client_id,
client_secret: your_client_secret,
};
we use query.stringify() for the data because the content type is application/x-www-form-urlencoded also don't use params since its a post request
axios
.post(
"https://accounts.spotify.com/api/token",
querystring.stringify(data),
headers
)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
This works for me:
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization:
'Basic ' +
Buffer.from(this.clientId + ':' + this.clientSecret).toString('base64'),
};
this.http.post(
'https://accounts.spotify.com/api/token',
'grant_type=client_credentials',
{ headers },
).subscribe(data => {
console.log(data);
});
I have the same issue, and it's resolved with stringfying request body data
const requestAccessToken = ({
code,
grantType = "authorization_code",
redirectUri = `${APP_BASE_URL}/callback`,
}) => {
const data = qs.stringify({ //query-string library
code,
grant_type: "client_credentials",
redirect_uri: redirectUri,
});
return axios.post(
[SPOTIFY_ACCOUNTS_BASE_URL, SPOTIFY_ACCOUNTS_TOKEN_URI].join(""),
data,
{
headers: {
Authorization: `Basic ${Buffer.from(
`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`,
).toString("base64")}`,
"Content-Type": "application/x-www-form-urlencoded",
},
},
);
};
Have you traced the message and verified that the request body is definitely as expected? Your OAuth fields look totally correct so I suspect this could just be an axios syntax issue.
I could be wrong but should the 'params' field be called 'data' instead, as in this class of mine.

Angular http module - post headers

I'm unable to change the headers when doing a post request with http module in Angular (with Ionic).
Here is my code:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
const apiUrl = "https://webhook.site/c2d56330-84d4-47cf-9f98-472f7eac8000";
#Injectable({
providedIn: 'root'
})
export class APIService {
constructor(private http: HttpClient) { }
getToken(){
var body = {
'data1': 'data2',
'somedata3': 'data4',
};
let headers = new HttpHeaders().append('Content-Type', 'application/json');
this.http.post(apiUrl, JSON.stringify(body), {headers}).subscribe(data =>
console.log(data));
console.log(headers.get('Content-Type')); //return 'application/json'
}
}
Everything works well, but it still sends header "content-type: text/plain" instead of "content-type: application/json".
Do I type something wrong?
I'd prefer something like:
import { HttpHeaders } from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
this.http.post<Foo>(this.apiUrl, body, httpOptions)
Also I don't see a need to stringify the body, just pass it as a "normal" object

angular 7 not sending header on request

I'm trying to send content-type headers for the below post request to allow json request, but it throws me an error Invalid CORS request with OPTIONS request method. It doesn't even send POST method.
Here, I cannot able to use RequestOptions which is depreciated.
PS: This is working fine when I send the request with postman. And, Backend code is handled with CORS request already.
From Backend java code, this the error I'm getting
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Where am I missing?
postSubmit(){
let data = { "name":"John", "age":30 };
const httpHeaders = new HttpHeaders ({
'Content-Type': 'application/json'
});
return this.http.post<any>(apiURL, data, {headers : httpHeaders})
.pipe(catchError(error => this.handleError(error)))
}
}
To define the content-type with the new HttpHeaders class you need to
Just Import import { HttpHeaders } from '#angular/common/http';
Create httpOptions object that will be passed to every HttpClient save method
import { HttpHeaders } from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
};
Call the API this.http.post<Datatype>(API url, Bady Parameter, httpOptions)

localStorage item not updating in axios headers

I am using a JWT Token auth system, and when I login I get the token like this:
axios.post('/login', data)
.then(response => {
localStorage.setItem('token', response.data.token);
});
This works well and the token is saved in localStorage. However, the token is not included in the later requests. The Authorization header is Bearer null.
This is how I set up my global axios object.
window.axios = axios.create({
baseURL: '/api/',
timeout: 10000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
});
If I refresh the site, the token is set, and is used properly.
Edit:
I got it to work by removing the Authorization header from the create() method and instead using window.axios.defaults.headers.common['Authorization']. But now the same problem appears with Laravel Echo. I create the instance like this:
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'xxx',
cluster: 'eu',
encrypted: true,
namespace: 'xxx',
auth: {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
}
});
And I update the header like this:
window.setAuthToken = (token) => {
window.axios.defaults.headers.Authorization = 'Bearer ' + token;
window.Echo.options.auth.headers.Authorization = 'Bearer ' + token;
localStorage.setItem('token', token);
}
The axios header is successfully updated, but not Echo.
Use axios interceptors for this purpose. It will run for every request call.
Better to keep axios methods in a separate file and make call to it than using it directly in all components. This way we can replace axios with another library if we want with minimal effort. Here's what I'm doing in my project.
import axios from "axios";
import AuthService from "./auth";
import config from '../config'
const instance = axios.create({
baseURL: config.apiServer.url,
timeout: config.apiServer.timeout
});
instance.interceptors.request.use(
config => {
const token = AuthService.getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
const ApiService = {
get(url) {
return instance.get(url)
.then(res => res)
.catch(reason => Promise.reject(reason));
},
post(url, data) {
return instance.post(url, data)
.then(res => res)
.catch(reason => Promise.reject(reason));
},
awaitAll() {
return axios.all(Array.from(arguments))
.then(axios.spread((...responses) => responses))
.catch(reasons => Promise.reject(reasons));
}
};
export default ApiService;
Now to use it in a component:
ApiService.get(YOUR_GET_URL)
.then(res => {
Console.log(res);
))
.catch(reason => {
console.log(reason);
})
The problem is that your are using localStorage.getItem('token') at page load. When you are setting it in localStorage, you have to update it in axios header.
window.axios = axios.create({
baseURL: '/api/',
timeout: 10000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
});
axios.post('/login', data)
.then(response => {
localStorage.setItem('token', response.data.token);
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token');
});
I faced the same problem before and I found out that the file that contains my axios config was being loaded at the time of storing the token, so it was accessing it before it is stored.
The solution is, in axios config:
const axiosInstance = axios.create({
baseURL: `${API_BASE_URL}`,
headers: {
Accepted: 'appication/json',
'Content-Type': 'application/json',
},
});
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.authorization = token;
}
return config;
},
(error) => Promise.reject(error),
);
export default axiosInstance;
After that, use this instance where you need to make a request.

Categories