Skip to content

Instantly share code, notes, and snippets.

@andrIvash
Created June 21, 2019 11:23
Show Gist options
  • Save andrIvash/c5db02a83c0f78550cde78b71a971fc1 to your computer and use it in GitHub Desktop.
Save andrIvash/c5db02a83c0f78550cde78b71a971fc1 to your computer and use it in GitHub Desktop.
Auth class for api call with jwt-token logic
import decode from 'jwt-decode';
import {
API_BASE_URL,
ACCESS_TOKEN,
REFRESH_TOKEN,
ACTIVE_USER } from '../../helpers/constants.js';
/**
* Authentification service.
* Performs api calls sending the required authentication headers.
* Check and validate tokens
* Perfom login and logout of the user
*/
export class Auth {
constructor() {
this.status = false;
this.refreshingTokenPromise = null;
}
/**
* Retailer login
* Get a token from api server using the fetch api
* @param {String} username
* @param {String} password
* @return {Promise}
*/
login(username, password) {
return fetch(API_BASE_URL + '/license-manager/login', {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'POST',
body: JSON.stringify({
username,
password
})
})
.then(this.checkResponseStatus)
.then(response => response.json())
.then(user => {
this.setUser(user);
return user;
});
}
/**
* Registration new user
* @param {String} username
* @param {String} password
* @return {Promise}
*/
signup(username, password) {
return fetch(API_BASE_URL + '/users/user/register', {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'POST',
body: JSON.stringify({
login: username,
email: username,
password
})
})
.then(this.checkResponseStatus)
.then(response => response.json());
}
/**
* Confirmation new user registration
* @param {String} token
* @return {Promise}
*/
confirmate(token) {
return fetch(API_BASE_URL + '/users/registrationConfirm/' + token, {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'GET'
})
.then(this.checkResponseStatus)
.then(response => response.json());
}
/**
* Send email to recover a password
* @param {String} email
* @return {Promise}
*/
recover(email) {
return fetch(`{API_BASE_URL}/users/sendRecoverEmail?email=${email}`, {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'GET'
})
.then(this.checkResponseStatus)
.then(response => response.json());
}
/**
* Send new password to update
* @param {Object} credentials
* @return {Promise}
*/
passwdUpdate(credentials) {
return fetch(API_BASE_URL + '/users/updatePassword', {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'POST',
body: JSON.stringify(credentials)
})
.then(this.checkResponseStatus)
.then(response => response.json());
}
/**
* Check Login credentials if exist
* @param {String} login
* @return {Promise}
*/
checkLogin(login) {
return fetch(`${API_BASE_URL}/users/checkEmailAvailability?email=${login}`, {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'GET'
})
.then(this.checkResponseStatus)
.then(response => response.json());
}
/**
* Checks if the Retailer is logged in otherwise if there is a saved token and it's still valid
* If no, is trying to get a new one
* @return {Promise} with true or false result
*/
isLoggedIn() {
const token = this.getToken(); // Getting token from localstorage
const refreshToken = this.getRefreshToken(); // Getting refresh token from localstorage
if (typeof token === 'undefined' || typeof refreshToken === 'undefined') {
return Promise.resolve(false);
}
if (!token || !refreshToken) {
return Promise.resolve(false);
}
if (token && this.getProfileFromToken()) {
return Promise.resolve(true);
} else {
this.logout();
return Promise.resolve(false);
}
}
/**
* Checking if token is expired
* @param token
* @returns {boolean}
*/
isTokenExpired(token) {
try {
const decoded = decode(token);
if (decoded.exp < Date.now() / 1000) { // Checking if token is expired
return true;
}
else
return false;
}
catch (err) {
return false;
}
}
/**
* Set active user to localStorage
* @param user
* @param refreshToken
*/
setUser(user) {
localStorage.setItem(ACTIVE_USER, user);
}
/**
* Saves user tokens to localStorage
* @param accessToken
* @param refreshToken
*/
setToken(accessToken, refreshToken) {
localStorage.setItem(ACCESS_TOKEN, accessToken);
localStorage.setItem(REFRESH_TOKEN, refreshToken);
}
/**
* Retrieves the user token from localStorage
* @returns {string}
*/
getToken() {
return localStorage.getItem(ACCESS_TOKEN)
}
/**
* Retrieves the user token from localStorage
* @returns {string}
*/
getRefreshToken() {
return localStorage.getItem(REFRESH_TOKEN)
}
/**
* Retailer logout. Clear user token and profile data from localStorage
*/
logout() {
localStorage.removeItem(ACCESS_TOKEN);
localStorage.removeItem(REFRESH_TOKEN);
localStorage.removeItem(ACTIVE_USER);
}
/**
* Using jwt-decode npm package to decode the token
*/
getProfileFromToken() {
return decode(this.getToken());
}
/**
* Refreshing Active Token Query
*/
shouldRefreshToken = error => error.response.status === 401;
/**
* Performs download action with sending the required authentication headers
* inner param 'methods' links selected available methods for fetch, should refresh query and token refresh method
* @param url
* @param options request options
* @param addHeaders request headers
* @returns {Promise}
*/
async secureDownload (url, options, addHeaders) {
const methods = {
fetch: this.customFetchDownload,
shouldRefreshToken: this.shouldRefreshToken,
tokenRefreshing: this.tokenRefreshing,
};
return this.fetchWithTokenWrapper(url, options, addHeaders, methods)
}
/**
* Performs api calls sending the required authentication headers
* inner param 'methods' links selected available methods for fetch, should refresh query and token refresh method
* @param url
* @param options request options
* @param addHeaders request headers
* @returns {Promise}
*/
async secureFetch (url, options, addHeaders) {
const methods = {
fetch: this.customFetch,
shouldRefreshToken: this.shouldRefreshToken,
tokenRefreshing: this.tokenRefreshing,
};
return this.fetchWithTokenWrapper(url, options, addHeaders, methods)
}
/**
* Wrap api calls sending with required authentication headers
* @param url
* @param options request options
* @param addHeaders request headers
* @param methods methods for fetch action, should refresh query and token refresh method
* @returns {Promise}
*/
async fetchWithTokenWrapper (url, options, addHeaders, methods) {
if (this.refreshingTokenPromise !== null) {
return this.refreshingTokenPromise
.then(() => methods.fetch(url, options, addHeaders))
.catch(() => methods.fetch(url, options, addHeaders))
}
return methods.fetch(url, options, addHeaders).catch(error => {
if (methods.shouldRefreshToken(error)) {
if (this.refreshingTokenPromise === null) {
this.refreshingTokenPromise = new Promise(async (resolve, reject) => {
methods.tokenRefreshing.call(this).then(() => {
this.refreshingTokenPromise = null;
resolve();
}, (refreshTokenError) => {
this.refreshingTokenPromise = null;
reject(refreshTokenError)
})
})
}
return this.refreshingTokenPromise.then(async () => {
return await methods.fetch(url, options, addHeaders);
}).catch(err => {
this.logout();
window.location.href='./';
throw err
})
} else {
throw error
}
})
}
/**
* Custom fetch (for CRUD operation) with specific data and headers
* @param url
* @param options request options
* @param addHeaders request headers
* @returns {Promise}
*/
customFetch = (url, options, addHeaders) => {
let headers = null;
if (addHeaders) {
//headers[addHeader.name] = addHeader.value;
headers = addHeaders;
} else {
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
headers['Authorization'] = 'Bearer ' + this.getToken();
return fetch(url, {
headers,
...options
})
.then(this.checkResponseStatus)
.then(response => response.json())
};
/**
* Performs api calls sending the required authentication headers
* @param url
* @param options
* @param addHeaders request headers
* @returns {*}
*/
async customFetchDownload(url, options, addHeaders) {
let headers = null;
if (addHeaders) {
//headers[addHeader.name] = addHeader.value;
headers = addHeaders;
} else {
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
headers['Authorization'] = 'Bearer ' + this.getToken();
return fetch(url, {
headers,
...options
})
.then(this.checkResponseStatus)
.then(response => response.blob())
}
/**
* Refresh expired token
* @returns {Promise}
*/
tokenRefreshing() {
return fetch(API_BASE_URL + '/license-manager/refresh', {
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
method: 'POST',
body: JSON.stringify({
refreshToken: this.getRefreshToken()
})
})
.then(this.checkResponseStatus)
.then(response => response.json())
.then(res => {
console.log('res tokens', res)
if (res.accessToken && res.refreshToken ) {
this.setToken(res.accessToken, res.refreshToken) // Setting the token in localStorage
}
return res;
})
}
/**
* Raises an error in case response status is not a success
* @param response
* @returns {response}
*/
checkResponseStatus(response) {
if (response.status >= 200 && response.status < 300) { // Success status lies between 200 to 300
return response
} else {
const error = new Error(response.statusText);
console.error(response.statusText);
error.response = response;
throw error
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment