Created
June 21, 2019 11:23
-
-
Save andrIvash/c5db02a83c0f78550cde78b71a971fc1 to your computer and use it in GitHub Desktop.
Auth class for api call with jwt-token logic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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