Commit d8a9fd20 authored by Nicolas Mérigot's avatar Nicolas Mérigot

refactor: little clean up

parent 6a3217dd
import 'https://unpkg.com/oidc-client@1.6';
import { SIBBase } from './core/src/index.js';
Log.logger = console;
Log.level = Log.INFO;
const defaultSettings = {
redirect_uri: location.origin,
post_logout_redirect_uri: location.origin,
// authority: 'https://test-paris.happy-dev.fr/openid/',
// client_id: '833925',
response_type: 'id_token token',
......@@ -11,6 +16,13 @@ const defaultSettings = {
};
class SIBOidc extends HTMLElement {
/**
* @typedef {Object} State
* @property {string} value 'login' or 'logout' or null
* @property {token} string CRSF token
* @property {previous_uri} string Previous uri
*/
constructor() {
super();
this.manager = null;
......@@ -19,13 +31,135 @@ class SIBOidc extends HTMLElement {
this.settings = Object.assign({}, defaultSettings);
}
/** @function
* @name connectedCallback
* When called, the OIDC manager is intancied
* with the params set in the component then install method is called
* then we process the current OIDC state
*/
async connectedCallback() {
this.settings = Object.assign({}, defaultSettings, this.dataset);
// initialize OIDC Manager
this.manager = new UserManager(this.settings);
const promise = this.getUser();
this.getUser = () => promise;
this.install();
this.processState();
}
/** @function
* @name install
* Add method and property on SIBBase class in order
* to provide auth information on all SIB component
*/
install() {
SIBBase.prototype.login = () => this.login();
SIBBase.prototype.logout = () => this.logout();
SIBBase.prototype.getUser = () => this.getUser();
}
/** @function
* @name uninstall
* Remove method and property previously added on SIBBase
*/
uninstall() {
SIBBase.prototype.login = null;
SIBBase.prototype.logout = null;
SIBBase.prototype.getUser = null;
}
/** @function
* @name getState
* Search in localStorage for previous OIDC state
* @returns {State} - The state
*/
getState() {
const state = localStorage.getItem('oidc_state');
if (state) {
return JSON.parse(state);
} else {
return {
value: null,
token: null,
previous_uri: null,
};
}
}
/** @function
* @name setState
* Set state in localStorage
* @param {string} value - 'login' or 'logout', default null
* @returns {State} - The state
*/
setState(value = null) {
const state = {
value,
token: (value === null) ? null : Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5),
previous_uri: (value === null) ? null : location.href,
};
localStorage.setItem('oidc_state', JSON.stringify(state));
return state;
}
/** @function
* @name clearState
* Reset current state and clear stale state on manager
*/
clearState() {
this.manager.clearStaleState();
this.setState();
}
/** @function
* @name processState
* Try to get user, the if a state is set, call the appropriate method
* If failed, clear state
*/
async processState() {
const user = await this.manager.getUser();
const state = this.getState();
if (user) {
this.user = user;
return;
}
if (state && state.value) {
try {
switch (state.value) {
case 'login':
this.loginCallback(state.token);
break;
case 'logout':
this.logoutCallback(state.token);
break;
default:
this.setState();
}
} catch(e) {
this.clearState();
}
}
}
/** @function
* @name disconnectedCallback
* Clear state, remove manager and user, uninstall
*/
disconnectedCallback() {
this.clearState();
this.uninstall();
this.manager = null;
this.user = null;
}
/** @function
* @name dispatchUserInfo
* Try to replace data-src by user iri on [bind-user] elements
*/
async dispatchUserInfo() {
const processDOM = async () => {
const user = await this.getUser();
const id = user.profile.website;
......@@ -35,7 +169,6 @@ class SIBOidc extends HTMLElement {
element.setAttribute('data-src', id);
}
};
// check document state and add a hook on DOMContentLoaded if needed
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', processDOM);
......@@ -44,38 +177,66 @@ class SIBOidc extends HTMLElement {
}
}
disconnectedCallback() {
this.manager = null;
this.user = null;
/** @function
* @name login
* Start login procedure
*/
async login() {
const { token } = this.setState('login');
await this.manager.signinRedirect({
state: token,
});
}
async getUser() {
// if user is already set, return it
if (this.user) {
return this.user;
/** @function
* @name loginCallback
* Finish the login procedure
*/
async loginCallback() {
const { token, previous_uri } = this.getState();
const user = await this.manager.signinRedirectCallback();
if (user.state !== token) {
throw new Error('CRSF token doesnt match');
}
this.user = user;
this.setState();
location.href = previous_uri;
}
// try to get user through OIDC Manager
let user = await this.manager.getUser();
/** @function
* @name logout
* Start a logout procedure
*/
async logout() {
const { token } = this.setState('logout');
await this.manager.signoutRedirect({
state: token,
});
}
if (user) {
this.user = user;
return user;
/** @function
* @name logoutCallback
* Finish the logout procedure
*/
async logoutCallback() {
const { token } = this.getState();
const signout = await this.manager.signoutRedirectCallback();
if (signout.state !== token) {
throw new Error('CRSF token doesnt match');
}
try {
user = await this.manager.signinRedirectCallback();
location.href = decodeURIComponent(user.state.url);
return user;
} catch (e) {
await this.manager.signinRedirect({
state: {
url: encodeURIComponent(location.href),
},
});
throw `Can't get user, redirect to OIDC authority ${
this.settings.authority
}`;
this.setState();
}
/** @function
* @name getUser
* Return User or undefined
* @return {User}
*/
getUser() {
if (this.user) {
return this.user;
}
return undefined;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment