React-Admin Simple Refresh JWT Token - reactjs

I have an react-admin with an auth provider like the below.
I want to refresh my token, but I don't know how to do it.
I tried to follow this blog post, but my auth is a little different and I can't make it work (the error "httpClient(...).then" is not a function and others make me leave it).
I can make it with a more simple solution, does not need to be in memory. I tried to call my refresh endpoint to get my refresh token, but my call go without the current token.
My endpoint to refresh the token is:
/auth/jwt/refresh
I need to call it like this:
curl -X 'GET' \
'http://localhost:8000/auth/jwt/refresh' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
And My response body would be: (and I need to save it to my localstorage or the in memory way)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZTUwZDdhZDctOWE5Ni00NzQyLTgxNWEtZTNmZmJmNGRiMTVjIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE2Mzk4NDE1MDF9.-o2yk56sCj_MZx_VA6PxH7gZ-KKSMmopbDNDiapHmn0",
"token_type": "bearer"
}
My inMemoryJWTManager file:
const inMemoryJWTManager = () => {
let inMemoryJWT = null;
let isRefreshing = null;
let logoutEventName = 'ra-logout';
let refreshEndpoint = '/auth/jwt/refresh';
let refreshTimeOutId;
const setLogoutEventName = name => logoutEventName = name;
const setRefreshTokenEndpoint = endpoint => refreshEndpoint = endpoint;
// This countdown feature is used to renew the JWT before it's no longer valid
// in a way that is transparent to the user.
const refreshToken = (delay) => {
refreshTimeOutId = window.setTimeout(
getRefreshedToken,
delay * 1000 - 5000
); // Validity period of the token in seconds, minus 5 seconds
};
const abordRefreshToken = () => {
if (refreshTimeOutId) {
window.clearTimeout(refreshTimeOutId);
}
};
const waitForTokenRefresh = () => {
if (!isRefreshing) {
return Promise.resolve();
}
return isRefreshing.then(() => {
isRefreshing = null;
return true;
});
}
// The method make a call to the refresh-token endpoint
// If there is a valid cookie, the endpoint will set a fresh jwt in memory.
const getRefreshedToken = () => {
const request = new Request(refreshEndpoint, {
method: 'GET',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'include',
});
isRefreshing = fetch(request)
.then((response) => {
if (response.status !== 200) {
ereaseToken();
global.console.log(
'Token renewal failure'
);
return { token: null };
}
return response.json();
})
.then(({ token, tokenExpiry }) => {
if (token) {
setToken(token, tokenExpiry);
return true;
}
ereaseToken();
return false;
});
return isRefreshing;
};
const getToken = () => inMemoryJWT;
const setToken = (token, delay) => {
inMemoryJWT = token;
refreshToken(delay);
return true;
};
const ereaseToken = () => {
inMemoryJWT = null;
abordRefreshToken();
window.localStorage.setItem(logoutEventName, Date.now());
return true;
}
// This listener will allow to disconnect a session of ra started in another tab
window.addEventListener('storage', (event) => {
if (event.key === logoutEventName) {
inMemoryJWT = null;
}
});
return {
ereaseToken,
getRefreshedToken,
getToken,
setLogoutEventName,
setRefreshTokenEndpoint,
setToken,
waitForTokenRefresh,
}
};
export default inMemoryJWTManager();
This is my auth provider: (updated, using inMemoryJWTManager)
import inMemoryJWTManager from './inMemoryJWT'
const apiUrl = 'http://localhost:8000'
const authProvider = {
login: ({username, password}) => {
const oAuthParams = {
username,
password
}
const body = Object.keys(oAuthParams).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(oAuthParams[key]);
}).join('&');
const request = new Request(`${apiUrl}/auth/jwt/login`, {
method: 'POST',
body: body,
headers: new Headers({'Content-Type': 'application/x-www-form-urlencoded'}),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(( {access_token} ) => {
inMemoryJWTManager.setToken(access_token);
});
},
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
inMemoryJWTManager.ereaseToken();
return Promise.reject({redirectTo: '/login'});
}
// other error code (404, 500, etc): no need to log out
return Promise.resolve();
},
checkAuth: () => inMemoryJWTManager.getToken()
? Promise.resolve()
: Promise.reject({ message: 'Login necessário', redirectTo: 'login' }),
logout: () => {
inMemoryJWTManager.ereaseToken();
return Promise.resolve();
},
getPermissions: () => {
return inMemoryJWTManager.getToken() ? Promise.resolve() : Promise.reject();
},
};
export default authProvider;
My updated httpClient code using inMemoryJWTManager: (and I'm using: const dataProvider = jsonServerProvider(apiUrl, httpClient); with modifications to it, but I think it is irrelevant)
const httpClient = (url) => {
const options = {
headers: new Headers({ Accept: 'application/json' }),
};
const token = inMemoryJWTManager.getToken();
console.log(token)
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
} else {
inMemoryJWTManager.setRefreshTokenEndpoint(`${apiUrl}/auth/jwt/refresh`);
return inMemoryJWTManager.getRefreshedToken().then((gotFreshToken) => {
if (gotFreshToken) {
options.headers.set('Authorization', `Bearer ${inMemoryJWTManager.getToken()}`);
};
return fetchUtils.fetchJson(url, options);
});
}
};
My problem is that, when I call my refresh token endpoint, my request go without the {'Authorization': Bearer... and it is not renewed and I got logged out. The other endpoints are fine, they go with the token.

You must check token expire before each requests, if token expired you must get new from /auth/jwt/refresh, then you can send current request. All this information is in the article post. Example:
const httpClient = (url) => {
const options = {
headers: new Headers({ Accept: 'application/json' }),
};
const token = inMemoryJWT.getToken();
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
} else {
inMemoryJWT.setRefreshTokenEndpoint('http://localhost:8001/refresh-token');
return inMemoryJWT.getRefreshedToken().then((gotFreshToken) => {
if (gotFreshToken) {
options.headers.set('Authorization', `Bearer ${inMemoryJWT.getToken()}`);
};
return fetchUtils.fetchJson(url, options);
});
}
};

Related

Axios Interceptor is not working in React JS

I am using the below code as an interceptor in my React JS app for getting token back but unfortunately, it is not working. Refresh token returns new idToken and updates local storage data correctly. The same code I'm using some other application which works fine. One main difference is that I currently use React 18 and the previous 16. I struggled to identify the problem but failed. Your help will be appreciable.
axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response.status === 401) {
// console.log(error.response.data.code)
let usersData = JSON.parse(localStorage.getItem("userData"));
const refreshToken = usersData.refreshToken;
return axios
.post(
`${api_base_url}/auth/authentication/refresh_token`,
JSON.stringify({
refresh_token: refreshToken,
})
)
.then((response) => {
usersData["accessToken"] = response.data.data.accessToken;
usersData["idToken"] = response.data.data.idToken;
setSessionStorage("userData", usersData);
error.response.config.headers[
"Authorization"
] = `Bearer ${response.data.data.idToken}`;
return axios(error.response.config);
})
.catch((error) => {
if (error.response.data.code !== "TOKEN_EXPIRED") {
return;
}
localStorage.clear();
window.location = "/login";
});
}
return Promise.reject(error);
}
);
function getIRequestProp(severType, isMultipart, isSocial) {
const serverUrl = severType ? social_api_base_url : api_base_url;
let userData = JSON.parse(localStorage.getItem('userData'));
let idToken;
idToken = userData !== null ? userData['idToken'] : '';
let content_type;
if (isSocial) {
content_type = 'application/x-www-form-urlencoded'
} else {
content_type = isMultipart ? 'multipart/form-data' : 'application/json'
}
return {
serverUrl: serverUrl,
requestHeader: {
'Content-Type': content_type,
'Accept-Language': DEFAULT_LANGUAGE,
Authorization: `Bearer ${idToken}`
}
};
}
async function post(url, body, isSocialServer, isMultipart) {
const {serverUrl, requestHeader} = getIRequestProp(isSocialServer, isMultipart);
return axios.post(serverUrl + url, body, {
headers: requestHeader
});
}
So, I call API like this:
AxiosServices.post(ApiUrlServices.SOCIALS_UPDATE_LINKS(UserInfo.userId), payload, false)
.then(response => {})
What i figured out that return axios(error.response.config); is not sending authorization token in API request headers and trying request infinitely. But consoling error.response.config shows token sets in the config correctly.
Adding an additional modification of axios request, I solved my problem.
axios.interceptors.request.use(request => {
// Edit request config
let usersData = JSON.parse(localStorage.getItem('userData'));
request.headers['Authorization'] = `${usersData.idToken}`;
return request;
}, error => {
return Promise.reject(error);
});

How to programatically call an api in react js

I'm using react js with fetch ,I wanna call an api of refresh token Before my token be expired ,then I calculate the difference between my local date and expire date of the token . I want to call my refresh token api every (expire - localData) - 5 min and update my token in my localStorage.
Is that possible ?
A call my refresh token in Api.js to work globally ,Is that right ?
This is my code but doesn't work like excepting:
Api.js
const duration = localStorage.getItem("duration")
useEffect(() =>{
if(token)
setInterval(() => {
RefreshApi.getRefreshToken().then(res => {
if (typeof res === "string") {
console.log("Error",res)
} else {
var now = moment(new Date(), "DD/MM/YYYY HH:mm:ss");
var expire = moment(new Date(res.expire), "DD/MM/YYYY HH:mm:ss");
var diffTime = moment.duration(expire.diff(now));
localStorage.setItem("token",res.token);
localStorage.setItem("expireDuration",diffTime.asMilliseconds());
localStorage.setItem("expire",res.expire);
}
}).catch(err => {
console.log(err);
})
},(duration - 300000))
},[])
refreshApi.js
getRefreshToken: function () {
return api.get("/refreshToken",{"Authorization": `Bearer ${localStorage.getItem("token")}`}).then(res => {
if (res.ok) {
return res.json();
} else {
return res.text();
}
});
},
Any help please ?
You can do this in different ways.
You can use the fetch API provided by JavaScript
SomeJsxFile.jsx
import { getRefreshToken } from './RefreshToken'
const refreshApi = async () => {
const token = localStorage.getItem("token");
if(token) {
const response = await getRefreshToken(token)
const now = Date.now() // this will return the Epoch time in milliseconds
const expire = Date.now(response.expire)
localStorage.setItem("token", response.token);
localStorage.setItem("expireDuration", now - expire);
localStorage.setItem("expire", response.expire);
return
}
// if dont have the token do something else
}
useEffect(() => {
// 10min = 600000 milleseconds
setInterval(refreshApi(), 600000)
},[])
RefreshToken.js
module.exports = () => ({
getRefreshToken: async (token) => {
return new Promise((resolve, reject) => {
fetch("https://localhost:3000/refreshToken", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
},
cache: "default"
})
.then(response => response.json())
.then(responseInJSON => resolve(responseInJSON))
.catch(err => reject(err))
})
}
})
You can use a third-party library, one that is used a lot is the axios.js, you can follow this example:
Api.js
import axios from 'axios'
const api = axios.create({ baseURL: 'http://localhos:3000' })
module.exports = {
api
}
RefreshToken.js
import { api } from './Api'
module.exports = () => ({
getRefreshToken: async (token) => {
return api.get('/refreshToken', {
headers: {
"Authorization": `Bearer ${token}`
}
})
}
})

How to create a Spotify playlist using react?

I am trying to create a playlist on localhost and then have the list I created to be saved to Spotify. Can someone help why Save to Spotify button might not be working? Everything else seems fine, I have doubts about the fetching part I used but can't figure out what the issue might be.
Screenshot of the page:
And there is the Spotify.js code:
import { SearchBar } from '../components/SearchBar/SearchBar';
const clientId = 'I've put my client id';
const redirectUri = 'http://localhost:3000/callback/';
let accessToken;
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
//check for access token match
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
let expiresIn = Number(expiresInMatch[1]);
//This clears the parameters, allowing to grab new access token then it expires
window.setTimeout(() => (accessToken = ''), expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
search(term) {
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
headers: { Authorization: `Bearer ${accessToken}` },
})
.then((response) => {
return response.json();
})
.then((jsonResponse) => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map((track) => ({
id: track.id,
name: track.name,
artists: track.artists[0].name,
album: track.album.name,
uri: track.uri,
}));
});
},
savePlaylist(name, trackUris) {
if (!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
let userId;
return fetch(`https://api.spotify.com/v1/me`, { headers: headers })
.then((response) => response.json())
.then((jsonResponse) => (userId = jsonResponse.id))
.then((userId) => {
return fetch(`/v1/users/${userId}/playlists`, {
headers: headers,
method: 'POST',
body: JSON.stringify({ name: name }),
})
.then((response) => response.json())
.then((jsonResponse) => {
const playlistId = jsonResponse.id;
return fetch(`/v1/users/${userId}/playlists/${playlistId}/tracks`, {
headers: headers,
method: 'POST',
body: JSON.stringify({ uris: trackUris }),
});
});
});
},
};
export default Spotify;
Here is the screenshot of Element > Console:
I had an fetch error, updated as below and working now.
let accessToken;
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
//check for access token match
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
let expiresIn = Number(expiresInMatch[1]);
//This clears the parameters, allowing to grab new access token then it expires
window.setTimeout(() => (accessToken = ''), expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
search(term) {
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
headers: { Authorization: `Bearer ${accessToken}` },
})
.then((response) => {
return response.json();
})
.then((jsonResponse) => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map((track) => ({
id: track.id,
name: track.name,
artists: track.artists[0].name,
album: track.album.name,
uri: track.uri,
}));
});
},
savePlaylist(name, trackUris) {
if (!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
let userID;
return fetch('https://api.spotify.com/v1/me', { headers: headers })
.then((response) => response.json())
.then((jsonResponse) => {
userID = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/users/${userID}/playlists`, {
method: 'POST',
headers: headers,
body: JSON.stringify({ name: name }),
})
.then((response) => response.json())
.then((jsonResponse) => {
const playlistID = jsonResponse.id;
return fetch(
`https://api.spotify.com/v1/users/${userID}/playlists/${playlistID}/tracks`,
{
method: 'POST',
headers: headers,
body: JSON.stringify({ uris: trackUris }),
}
);
});
});
}, // end of savePlaylist method
}; // end of Spotify object
export default Spotify;

React-admin JWT authentication refresh token problem

I want to implement my own authProvider for react-admin but I'm stuck.
I use a Django-Rest-Framework backend and a JWT token authentication system.
I want to refresh the JWT token if it's almost expired before every request. According to the documentation the authProvider's checkAuth function gets called before every API call, which is true. My problem is that with my code it doesn't wait for the promise to finish and it uses the old access token which results in a 401 and I get redirected to the login page. Any guidance what am I missing?
import jwt from 'jsonwebtoken';
export default {
login: async ({ username, password }) => {
const request = new Request('http://localhost:8000/api/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { refresh, access } = await response.json();
localStorage.setItem("refreshToken", refresh);
localStorage.setItem("accessToken", access);
},
logout: params => {
console.log("logout");
localStorage.setItem('accessToken', "");
localStorage.setItem('refreshToken', "");
return Promise.resolve();
},
checkAuth: (params) => {
const accessToken = localStorage.getItem('accessToken');
const refreshToken = localStorage.getItem('refreshToken');
if (accessToken && refreshToken) {
console.log(accessToken);
const { exp } = jwt.decode(accessToken);
if (exp > (new Date().getTime() / 1000) - 10) {
return Promise.resolve();
} else {
const request = new Request('http://localhost:8000/api/token/refresh/', {
method: 'POST',
body: JSON.stringify({ "refresh": refreshToken }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = fetch(request)
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem('accessToken', token);
return Promise.resolve();
});
return response;
}
}
return Promise.reject();
},
checkError: error => {
if (error.status === 401 || error.status === 403) {
return Promise.reject();
}
return Promise.resolve();
},
getPermissions: params => Promise.resolve(),
}
Can you try something like that
checkAuth: async (params) =>
And
const request = new Request(...);
let data;
const response = await fetch(request);
if (response.ok) data = await response.json()
else throw new Error(response.statusText);
if (data && data.token) {
localStorage.setItem('accessToken', data.token);
console.log(data.token);
return Promise.resolve();
} else return Promise.reject();

How can I use a fake backend that is localhost on development when on production environment

I am following this tutorial https://jasonwatmore.com/post/2017/09/16/react-redux-user-registration-and-login-tutorial-example#user-service-js
It creates a fake backend using localhost, and then it mocks a data base on localstorage
I would like to deploy my app on heroku or something similar
Is it possible?
Here is the service code, where config is "http://localhost:4000".
Thanks in advance
import config from 'config';
import { authHeader } from '../_helpers';
export const userService = {
login,
logout,
register,
getAll,
getById,
update,
delete: _delete
};
function login(username, password) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
};
return fetch(`${config.apiUrl}/users/authenticate`, requestOptions)
.then(handleResponse)
.then(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
return user;
});
}
function logout() {
// remove user from local storage to log user out
localStorage.removeItem('user');
}
function getAll() {
const requestOptions = {
method: 'GET',
headers: authHeader()
};
return fetch(`${config.apiUrl}/users`, requestOptions).then(handleResponse);
}
function getById(id) {
const requestOptions = {
method: 'GET',
headers: authHeader()
};
return fetch(`${config.apiUrl}/users/${id}`, requestOptions).then(handleResponse);
}
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`${config.apiUrl}/users/register`, requestOptions).then(handleResponse);
}
function update(user) {
const requestOptions = {
method: 'PUT',
headers: { ...authHeader(), 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`${config.apiUrl}/users/${user.id}`, requestOptions).then(handleResponse);;
}
// prefixed function name with underscore because delete is a reserved word in javascript
function _delete(id) {
const requestOptions = {
method: 'DELETE',
headers: authHeader()
};
return fetch(`${config.apiUrl}/users/${id}`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if (response.status === 401) {
// auto logout if 401 response returned from api
logout();
location.reload(true);
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
Fake back end
// array in local storage for registered users
let users = JSON.parse(localStorage.getItem('users')) || [];
export function configureFakeBackend() {
let realFetch = window.fetch;
window.fetch = function (url, opts) {
return new Promise((resolve, reject) => {
// wrap in timeout to simulate server api call
setTimeout(() => {
// authenticate
if (url.endsWith('/users/authenticate') && opts.method === 'POST') {
// get parameters from post request
let params = JSON.parse(opts.body);
// find if any user matches login credentials
let filteredUsers = users.filter(user => {
return user.username === params.username && user.password === params.password;
});
if (filteredUsers.length) {
// if login details are valid return user details and fake jwt token
let user = filteredUsers[0];
let responseJson = {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
token: 'fake-jwt-token'
};
resolve({ ok: true, text: () => Promise.resolve(JSON.stringify(responseJson)) });
} else {
// else return error
reject('Username or password is incorrect');
}
return;
}
// get users
if (url.endsWith('/users') && opts.method === 'GET') {
// check for fake auth token in header and return users if valid, this security is implemented server side in a real application
if (opts.headers && opts.headers.Authorization === 'Bearer fake-jwt-token') {
resolve({ ok: true, text: () => Promise.resolve(JSON.stringify(users))});
} else {
// return 401 not authorised if token is null or invalid
reject('Unauthorised');
}
return;
}
// get user by id
if (url.match(/\/users\/\d+$/) && opts.method === 'GET') {
// check for fake auth token in header and return user if valid, this security is implemented server side in a real application
if (opts.headers && opts.headers.Authorization === 'Bearer fake-jwt-token') {
// find user by id in users array
let urlParts = url.split('/');
let id = parseInt(urlParts[urlParts.length - 1]);
let matchedUsers = users.filter(user => { return user.id === id; });
let user = matchedUsers.length ? matchedUsers[0] : null;
// respond 200 OK with user
resolve({ ok: true, text: () => JSON.stringify(user)});
} else {
// return 401 not authorised if token is null or invalid
reject('Unauthorised');
}
return;
}
// register user
if (url.endsWith('/users/register') && opts.method === 'POST') {
// get new user object from post body
let newUser = JSON.parse(opts.body);
// validation
let duplicateUser = users.filter(user => { return user.username === newUser.username; }).length;
if (duplicateUser) {
reject('Username "' + newUser.username + '" is already taken');
return;
}
// save new user
newUser.id = users.length ? Math.max(...users.map(user => user.id)) + 1 : 1;
users.push(newUser);
localStorage.setItem('users', JSON.stringify(users));
// respond 200 OK
resolve({ ok: true, text: () => Promise.resolve() });
return;
}
// delete user
if (url.match(/\/users\/\d+$/) && opts.method === 'DELETE') {
// check for fake auth token in header and return user if valid, this security is implemented server side in a real application
if (opts.headers && opts.headers.Authorization === 'Bearer fake-jwt-token') {
// find user by id in users array
let urlParts = url.split('/');
let id = parseInt(urlParts[urlParts.length - 1]);
for (let i = 0; i < users.length; i++) {
let user = users[i];
if (user.id === id) {
// delete user
users.splice(i, 1);
localStorage.setItem('users', JSON.stringify(users));
break;
}
}
// respond 200 OK
resolve({ ok: true, text: () => Promise.resolve() });
} else {
// return 401 not authorised if token is null or invalid
reject('Unauthorised');
}
return;
}
// pass through any requests not handled above
realFetch(url, opts).then(response => resolve(response));
}, 500);
});
}
}

Resources