Bearer token in Angular - javascript

I building web app which is using OAuth authorization. To get access to data i need ask endpoint for token and place it in Authorization header.
I have made interceptor in Angular which put right header for API call and that part works just fine.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if(!(req.headers.get("Content-Type") === "application/x-www-form-urlencoded")) {
const authReq = req.clone({
headers: req.headers.set('Authorazation', 'Bearer ' + this.callToken()
.then(resp => resp)
.catch(err => console.log(err)))
});
return next.handle(authReq)
}else {
return next.handle(req);
}
}
but i have this method callToken() where I used promis to call for token.
Now this method return promise I cant put it in header in right type its always appear like
callToken(): Promise<any> {
let url = 'url';
let body = "body";
let promise = fetch(url, {
body: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
});
return promise
.then(resp => resp.json())
.then(json => json.access_token);
}
funny thing is that when I say resp => console.log(resp.acces_token) token just pretty printed.
I try use callbacks as well
callForToken(): string {
let url = 'url';
let body = 'body';
let option = {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};
let token;
this.http.post<Token>(url, body, option).subscribe(
res => {
token = res.access_token;
}
);
return token;
}
but because of async call I can't reach token data outside of method

I still try to found solution on this.
Now I end up with
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!(req.headers.get('Content-Type') === 'application/x-www-form-urlencoded')) {
return Observable.fromPromise(this.handleAccess(req, next));
}
}
private async handleAccess(req: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
const token = await this.callToken();
console.log(token);
let changeRequest;
if (token) {
changeRequest = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + token)
});
}
return next.handle(changeRequest).toPromise();
}
callToken(): Promise<Token> {
let url = 'url';
let body = 'body';
return fetch(url, {
body: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
}).then(resp => resp.json())
.then(json => json.acces_token)
.catch(err => console.error(err));
}
but still token is undefined
EDIT: actually that's work. I messed up with URL and body

Related

try to fetch and read the response body as json or fallback to plain text

I'm trying to fetch data from the server, and I want to try to resolve the response body as json, if failed return it as plain text
fetch(`/devapi/${url}`, {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${localStorage.getItem("token")}`,
}),
})
.then((res) =>
res
.json()
.then((res) => res?.body?.data || res?.body || res)
.catch((err) => res.text())
)
.then((val) => console.log(val));
when the response is NOT a valid json, ten res.text() is called, but it seems that calling .text() after .json causes an error
Failed to execute 'text' on 'Response': body stream already read
You could clone the response, and then use it in error handler:
Try this:
fetch(`/devapi/${url}`, {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${localStorage.getItem("token")}`,
}),
})
.then((res) => {
const clone = res.clone();
return res
.json()
.then((res) => res?.body?.data || res?.body || res)
.catch((err) => clone.text())
})
.then((val) => console.log(val));
or use XHR and process response in a try/catch:
const xhr = new XMLHttpRequest();
xhr.open('POST', `/devapi/${url}`, true);
xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem("token")}`);
xhr.onload = function() {
let res;
try {
res = JSON.parse(xhr.response);
} catch (err) {
res = xhr.responseText;
}
console.log(res);
};
xhr.send();

How can I add an aditional header to global instance of Axios in a custom request?

This is my global Axios
import axios from 'axios';
import { storage } from 'containers/login/utils/local-storage';
const token = storage.getToken();
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
axiosInstance.interceptors.response.use((response) => {
response.config.headers = {
Authorization: `Bearer ${token}`,
};
return response;
}, (error) => Promise.reject(error));
axiosInstance.interceptors.request.use((request) => {
request.headers = {
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
export default axiosInstance;
In this request I need to add new header: invoiceLimit = `${-invoiceLimit}`
export const updateInvoiceLimit = async (
invoiceLimit: string,
)
: Promise<ReturnDataType> => {
let result: ReturnDataType = {} as ReturnDataType;
try {
axios.defaults.headers.common.invoiceLimit = `${-invoiceLimit}`;
result = await axios.put(`${CREDITS_URL.CREDITS}/invoice/limit`);
return result;
} catch (error) {
SnackBarUtils.error(`${(error as Error).message}. ${result.data.message}`);
}
return result;
};
When I use this: axios.defaults.headers.common.invoiceLimit = `${-invoiceLimit}`;
header adds to the Axios defaults, but then when I call axios.put so this custom header goes away and left only global header from interceptors.
I know it's not best practice, but its customer API and I want not to make another instance of Axios but use one global instance.
I think the problem is here:
axiosInstance.interceptors.request.use((request) => {
request.headers = {
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
You are overriding all request headers with just this one:
{ Authorization: `Bearer ${token}` }
So try spreading headers before adding the new one like this:
axiosInstance.interceptors.request.use((request) => {
request.headers = {
...request.headers,
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
Did this solve the problem?
I thought you could just reuse the initial axiosInstance and the headers will be merged when you do:
await axios.put(`${CREDITS_URL.CREDITS}/invoice/limit`,{}, {
headers: {
invoiceLimit: `${-invoiceLimit}`
}
})

How to set Authorization token with axios in node.js

I want to use axios in api test. To initialize client I need first to set auth token, which I expect to get with axios as well.How to get it form async code?
const a = require('axios');
getToken = () => {
var axios =a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
return axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
.then(function (response) {
return response.data.data.token;
})
.catch(function (error) {
console.log(error);
});
}
const client=a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json',
'Authorization': getToken()
},
});
module.exports = client;
First, getToken should be async (or just return promise):
async getToken() {
var axios = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
try {
const response = await axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
return response.data.data.token;
} catch (error) {
console.error(error);
}
}
Then you can mock request:
const client = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json'
}
})
const request = client.request
const addToken = (token) => (data, headers) => {
headers.Authorization = token
return data
}
client.request = (config = {}) => new Promise(async (resolve, reject) => {
token = await getToken()
if (!config.transformRequest) config.transformRequest = []
config.transformRequest.push(addToken(token))
request(config).then(resolve, reject)
})
module.exports = client
Yes, bit messy, feel free to refactor!
axios has request function which makes requests, others like get, post are aliases. This code patches request to get token first and then continue request.
transformRequest is took from axious readme on github, there is a comment "You may modify the headers object" so it is legal.
P.S good question, don't know why -1

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.

Resolve Http Request on Angular2 before executing code

I've encountered problems trying to resolve an http.post request before doing some other code.
Here's the place:
getRefresh(token){
debugger;
console.log("sono dentro get refresh");
console.log(token);
let params = new URLSearchParams();
params.append('grant_type','refresh_token');
params.append('refresh_token',token);
params.append('client_id','web_app');
let headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8','Authorization': 'Basic '+btoa("web_app:")});
let options = new RequestOptions({headers: headers});
this._http.post('http://localhost:9997/oauth/token',params.toString(),options)
.map(res => res.json())
.subscribe(
data => this.saveToken(data),
err => alert('Invalid Credentials')
);
}
Following the control flow via Browser Console, I've noticed that saveToken method is not executed asap.
The fact is that I need that method for registering some cookies, reused here:
if (!Cookie.get("access_token")) {
this.getRefresh(Cookie.get("refresh_token"));
cookie = Cookie.get("access_token");
} else {
cookie = Cookie.get("access_token");
}
var headers = new Headers({
'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
'Authorization': 'Bearer ' + cookie
});
var options = new RequestOptions({headers: headers});
return this._http.get(resourceUrl, options)
.map((res: Response) => res.text())
.catch((error: any) => Observable.throw(error().json().error || 'Server error'));
How can I resolve the async problem of observable?
Change your getRefresh(token) method to return the observable and use that in your calling code:
function getRefresh(token) {
debugger;
console.log("sono dentro get refresh");
console.log(token);
let params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('refresh_token', token);
params.append('client_id', 'web_app');
let headers = new Headers({ 'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Basic ' + btoa("web_app:") });
let options = new RequestOptions({ headers: headers });
return this._http.post('http://localhost:9997/oauth/token', params.toString(), options)
.map(res => res.json())
.flatMap(
data => this.saveToken(data),
err => alert('Invalid Credentials')
);
}
function getResource() {
var cookie = Cookie.get("access_token");
var headers = new Headers({
'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
'Authorization': 'Bearer ' + cookie
});
var options = new RequestOptions({ headers: headers });
return this._http.get(resourceUrl, options)
.map((res: Response) => res.text())
.catch((error: any) => Observable.throw(error().json().error || 'Server error'));
}
function callingCodeFunction() {
if (!Cookie.get("access_token")) {
this.getRefresh(Cookie.get("refresh_token"))
.flatMap((result) => {
getResource();
});
} else {
getResource()
}
}
*I've not run this code - so there may be errors, but it should serve to demonstrate the principle.

Categories