ui/shared/auth/AuthService.js (82 lines of code) (raw):
import {
userSessionFromAuthResult,
renew,
loggedOutUser,
} from '../../helpers/auth';
import { getApiUrl } from '../../helpers/url';
import UserModel from '../../models/user';
export default class AuthService {
constructor(setUser) {
this.renewalTimer = null;
this.setUser = setUser;
}
_fetchUser(userSession) {
const loginUrl = getApiUrl('/auth/login/');
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
const userResponse = await fetch(loginUrl, {
headers: {
Authorization: `Bearer ${userSession.accessToken}`,
'Access-Token-Expires-At': userSession.accessTokenExpiresAt,
'Id-Token': userSession.idToken,
},
method: 'GET',
credentials: 'same-origin',
});
const user = await userResponse.json();
if (!userResponse.ok) {
reject(new Error(user.detail || userResponse.statusText));
}
resolve(new UserModel(user));
});
}
_clearRenewalTimer() {
if (this.renewalTimer) {
clearTimeout(this.renewalTimer);
this.renewalTimer = null;
}
}
async _renewAuth() {
try {
if (!localStorage.getItem('userSession')) {
return;
}
const authResult = await renew();
if (authResult) {
await this.saveCredentialsFromAuthResult(authResult);
return this.resetRenewalTimer();
}
} catch (err) {
// instance where a new scope was added and is now required in order to be logged in
if (err.error === 'consent_required') {
this.logout();
}
// if the renewal fails, only log out the user if the access token has expired
const userSession = JSON.parse(localStorage.getItem('userSession'));
if (new Date(userSession.accessTokenExpiresAt * 1000) < new Date()) {
this.logout();
}
/* eslint-disable no-console */
console.error('Could not renew login:', err);
}
}
resetRenewalTimer() {
const userSession = JSON.parse(localStorage.getItem('userSession'));
// if a user has multiple treeherder tabs open and logs out from one of them,
// we make sure to clear each tab's timer without renewing
this._clearRenewalTimer();
if (userSession) {
let timeout = Math.max(0, new Date(userSession.renewAfter) - Date.now());
// apply up to a few minutes to it randomly. This avoids
// multiple tabs all trying to renew at the same time.
if (timeout > 0) {
timeout += Math.random() * 5 * 1000 * 60;
}
// create renewal timer
this._clearRenewalTimer();
this.renewalTimer = setTimeout(() => this._renewAuth(), timeout);
}
}
logout() {
localStorage.removeItem('userSession');
localStorage.setItem('user', JSON.stringify(loggedOutUser));
if (this.setUser) this.setUser(loggedOutUser);
}
async saveCredentialsFromAuthResult(authResult) {
const userSession = userSessionFromAuthResult(authResult);
const user = await this._fetchUser(userSession);
localStorage.setItem('userSession', JSON.stringify(userSession));
localStorage.setItem('user', JSON.stringify(user));
}
}