Commit eabebab5 authored by Alexandre's avatar Alexandre

Merge branch '14-solid-auth' into 'master'

Resolve "Replace sib-oidc with solid-auth"

Closes #14

See merge request !11
parents 447b35d0 fe9d824e
Pipeline #4502 passed with stage
in 58 seconds
...@@ -10,35 +10,54 @@ ...@@ -10,35 +10,54 @@
sib-test { sib-test {
display: block; display: block;
} }
pre{
white-space: normal;
word-wrap: break-word;
}
.bu-test{
border: 3px solid red;
}
.bu-test[data-src]{
border-color: green;
}
.bu-test:after{
content: '???';
}
.bu-test[data-src]:after{
color: green;
content: attr(data-src);
}
</style> </style>
</head> </head>
<body> <body>
<div bind-user class="bu-test">user-id: </div>
<h1>sib-oidc</h1> <h1>sib-oidc</h1>
<button id="reset">clear storage and set new URL</button> <button id="reset">clear storage and set new URL</button>
<button id="reload">reload</button> <button id="reload">reload</button>
<sib-test> <sib-test bind-user>
<button id="login">Se connecter</button> <button id="login">Se connecter</button>
<button id="logout">Se déconnecter</button> <button id="logout">Se déconnecter</button>
<button id="stat">Status (console)</button>
<div id="result"></div> <div id="result"></div>
</sib-test> </sib-test>
<sib-auth> <sib-auth>
<sib-auth-provider <sib-auth-provider
data-authority="https://test-paris.happy-dev.fr/openid/" data-authority="https://api.test-paris.happy-dev.fr/"
data-client_id="234528" data-client_id="234528"
data-id="paris" data-id="paris"
> >
</sib-auth-provider> </sib-auth-provider>
<sib-auth-provider <sib-auth-provider
data-authority="https://test-paris.happy-dev.fr/openid/" data-authority="https://api.test-paris.happy-dev.fr/"
data-client_id="234528" data-client_id="234528"
data-id="paris2" data-id="paris2"
> >
</sib-auth-provider> </sib-auth-provider>
</sib-auth> </sib-auth>
<script> <script>
reset.onclick = () => { reset.onclick = () => {
window.sessionStorage.clear(); window.sessionStorage.clear();
...@@ -49,11 +68,10 @@ ...@@ -49,11 +68,10 @@
</script> </script>
<script type="module"> <script type="module">
import { SIBBase } from 'https://unpkg.com/@startinblox/core@0.7'; import { SIBBase, store } from 'https://unpkg.com/@startinblox/core@0.7';
class TestComponent extends SIBBase { class TestComponent extends SIBBase {
async connectedCallback() { async connectedCallback() {
stat.onclick = () => console.log(this.getStatus());
login.onclick = () => this.triggerLogin(); login.onclick = () => this.triggerLogin();
logout.onclick = () => this.triggerLogout(); logout.onclick = () => this.triggerLogout();
...@@ -66,21 +84,22 @@ ...@@ -66,21 +84,22 @@
async triggerLogout() { async triggerLogout() {
await this.logout(); await this.logout();
} }
async getStatus() { async update() {
return this.getUser(); let user = await this.getUser();
} let idToken = await this.getUserIdToken();
if (!user) {
update() {
const user = this.getUser();
if (user) {
result.innerHTML = `Bonjour ${user.profile.name} !`;
} else {
result.innerHTML = `Vous n'êtes pas connecté !`; result.innerHTML = `Vous n'êtes pas connecté !`;
return;
} }
user = await store.get(user);
console.log(user);
result.innerHTML = `Bonjour ${user.username} !<br>ID Token: <pre>${idToken}</pre>`;
} }
empty(){}
populate(){}
} }
customElements.define('sib-test', TestComponent); customElements.define('sib-test', TestComponent);
</script> </script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -620,7 +620,8 @@ ...@@ -620,7 +620,8 @@
"core-js": { "core-js": {
"version": "2.6.2", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz",
"integrity": "sha512-NdBPF/RVwPW6jr0NCILuyN9RiqLo2b1mddWHkUL+VnvcB7dzlnBJ1bXYntjpTGOgkZiiLWj2JxmOr7eGE3qK6g==" "integrity": "sha512-NdBPF/RVwPW6jr0NCILuyN9RiqLo2b1mddWHkUL+VnvcB7dzlnBJ1bXYntjpTGOgkZiiLWj2JxmOr7eGE3qK6g==",
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
...@@ -1391,7 +1392,8 @@ ...@@ -1391,7 +1392,8 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
...@@ -1412,12 +1414,14 @@ ...@@ -1412,12 +1414,14 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
...@@ -1432,17 +1436,20 @@ ...@@ -1432,17 +1436,20 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
...@@ -1559,7 +1566,8 @@ ...@@ -1559,7 +1566,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
...@@ -1571,6 +1579,7 @@ ...@@ -1571,6 +1579,7 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
...@@ -1585,6 +1594,7 @@ ...@@ -1585,6 +1594,7 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
...@@ -1592,12 +1602,14 @@ ...@@ -1592,12 +1602,14 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
...@@ -1616,6 +1628,7 @@ ...@@ -1616,6 +1628,7 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
...@@ -1696,7 +1709,8 @@ ...@@ -1696,7 +1709,8 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
...@@ -1708,6 +1722,7 @@ ...@@ -1708,6 +1722,7 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
...@@ -1793,7 +1808,8 @@ ...@@ -1793,7 +1808,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
...@@ -1829,6 +1845,7 @@ ...@@ -1829,6 +1845,7 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
...@@ -1848,6 +1865,7 @@ ...@@ -1848,6 +1865,7 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
...@@ -1891,12 +1909,14 @@ ...@@ -1891,12 +1909,14 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
} }
} }
}, },
......
import 'https://unpkg.com/oidc-client@1.6'; import auth from 'https://dev.jspm.io/solid-auth-client';
Log.logger = console; // eslint-disable-line no-undef
Log.level = Log.INFO; // eslint-disable-line no-undef
const defaultSettings = {
redirect_uri: window.location.origin,
post_logout_redirect_uri: window.location.origin,
// authority: 'https://test-paris.happy-dev.fr/openid/',
// client_id: '833925',
response_type: 'id_token token',
scope: 'openid profile email',
automaticsilentrenew: 'true',
loaduserinfo: 'true',
};
class SIBAuthProvider extends HTMLElement { class SIBAuthProvider extends HTMLElement {
/** /**
...@@ -24,7 +10,6 @@ class SIBAuthProvider extends HTMLElement { ...@@ -24,7 +10,6 @@ class SIBAuthProvider extends HTMLElement {
constructor() { constructor() {
super(); super();
this.manager = null;
this.id = null; this.id = null;
} }
...@@ -34,106 +19,20 @@ class SIBAuthProvider extends HTMLElement { ...@@ -34,106 +19,20 @@ class SIBAuthProvider extends HTMLElement {
* with the params set in the component, render view * with the params set in the component, render view
*/ */
async connectedCallback() { async connectedCallback() {
const { authority, client_id, id } = this.dataset; // eslint-disable-line camelcase const { id } = this.dataset; // eslint-disable-line camelcase
this.id = id; this.id = id;
const settings = Object.assign({}, defaultSettings, { authority, client_id });
this.manager = new UserManager(settings); // eslint-disable-line no-undef
this.render(this.dataset); this.render(this.dataset);
} }
/** @function
* @name processState
* Try to get user, the if a state is set, call the appropriate method
* If failed, clear state
*/
async processState(parent) {
const user = await this.manager.getUser();
const state = parent.getState();
if (user) {
parent.setUser(user);
}
if (state && state.value) {
try {
switch (state.value) {
case 'login':
this.loginCallback(parent);
break;
case 'logout':
this.logoutCallback(parent);
break;
default:
parent.clearState();
}
} catch (e) {
parent.clearState();
}
}
}
/** @function
* @name disconnectedCallback
* Remove manager
*/
disconnectedCallback() {
this.manager = null;
}
/** @function /** @function
* @name login * @name login
* Start login procedure * Start login procedure
* @param {SIBAuth} parent - SIBAuth parent instance * @param {SIBAuth} parent - SIBAuth parent instance
*/ */
async login(parent) { async login() {
const { token } = parent.setState('login', this.id); auth.login(this.dataset.authority);
await this.manager.signinRedirect({
state: token,
});
}
/** @function
* @name loginCallback
* Finish the login procedure
* @param {SIBAuth} parent - SIBAuth parent instance
*/
async loginCallback(parent) {
const { token, previousUri } = parent.getState();
const user = await this.manager.signinRedirectCallback();
if (user.state !== token) {
throw new Error('CRSF token doesnt match');
}
parent.setUser(user);
parent.clearState();
window.location.href = previousUri;
} }
/** @function
* @name logout
* Start a logout procedure
* @param {SIBAuth} parent - SIBAuth parent instance
*/
async logout(parent) {
const { token } = parent.setState('logout', this.id);
await this.manager.signoutRedirect({
state: token,
});
}
/** @function
* @name logoutCallback
* Finish the logout procedure
* @param {SIBAuth} parent - SIBAuth parent instance
*/
async logoutCallback(parent) {
const { token } = parent.getState();
const signout = await this.manager.signoutRedirectCallback();
if (signout.state !== token) {
throw new Error('CRSF token doesnt match');
}
parent.setUser(null);
parent.clearState();
}
/** @function /** @function
* @name render * @name render
......
import 'https://unpkg.com/oidc-client@1.6'; import auth from 'https://dev.jspm.io/solid-auth-client';
import { SIBBase, Helpers } from 'https://unpkg.com/@startinblox/core@0.7'; import { Helpers, SIBBase } from 'https://unpkg.com/@startinblox/core@0.7';
const baseUrl = import.meta.url.replace(/\/[^/]*$/, ''); const baseUrl = import.meta.url.replace(/\/[^/]*$/, '');
Helpers.importCSS(`${baseUrl}/sib-auth.css`); Helpers.importCSS(`${baseUrl}/sib-auth.css`);
...@@ -15,7 +15,7 @@ class SIBAuth extends HTMLElement { ...@@ -15,7 +15,7 @@ class SIBAuth extends HTMLElement {
constructor() { constructor() {
super(); super();
this.state = null; this.provider = null;
this.user = null; this.user = null;
this.hide(); this.hide();
} }
...@@ -27,8 +27,8 @@ class SIBAuth extends HTMLElement { ...@@ -27,8 +27,8 @@ class SIBAuth extends HTMLElement {
*/ */
async connectedCallback() { async connectedCallback() {
this.install(); this.install();
this.processState(); // this.processState();
if (this.getUser()) { if (await this.getUserWebId()) {
this.dispatchUserInfo(); this.dispatchUserInfo();
} }
} }
...@@ -38,8 +38,7 @@ class SIBAuth extends HTMLElement { ...@@ -38,8 +38,7 @@ class SIBAuth extends HTMLElement {
* Clear state and user, uninstall * Clear state and user, uninstall
*/ */
disconnectedCallback() { disconnectedCallback() {
this.setUser(); auth.logout(); // .then(() => alert('Goodbye!'));
this.clearState();
this.uninstall(); this.uninstall();
} }
...@@ -51,7 +50,12 @@ class SIBAuth extends HTMLElement { ...@@ -51,7 +50,12 @@ class SIBAuth extends HTMLElement {
install() { install() {
SIBBase.prototype.login = () => this.login(); SIBBase.prototype.login = () => this.login();
SIBBase.prototype.logout = () => this.logout(); SIBBase.prototype.logout = () => this.logout();
SIBBase.prototype.getUser = () => this.getUser(); SIBBase.prototype.getUserIdToken = () => this.getUserIdToken();
SIBBase.prototype.getUser = async () => {
const id = await this.getUserWebId();
if (!id) return null;
return { '@id': id };
};
this.bindUserObserver = new MutationObserver((mutations) => { this.bindUserObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => { mutations.forEach((mutation) => {
if (mutation.type === 'attributes') { if (mutation.type === 'attributes') {
...@@ -85,6 +89,7 @@ class SIBAuth extends HTMLElement { ...@@ -85,6 +89,7 @@ class SIBAuth extends HTMLElement {
SIBBase.prototype.login = null; SIBBase.prototype.login = null;
SIBBase.prototype.logout = null; SIBBase.prototype.logout = null;
SIBBase.prototype.getUser = null; SIBBase.prototype.getUser = null;
SIBBase.prototype.getUserIdToken = null;
this.bindUserObserver.disconnect(); this.bindUserObserver.disconnect();
} }
...@@ -134,57 +139,6 @@ class SIBAuth extends HTMLElement { ...@@ -134,57 +139,6 @@ class SIBAuth extends HTMLElement {
} }
/** @function
* @name getState
* Search in localStorage for previous OIDC state
* @returns {State} - The state
*/
getState() {
if (!this.state) {
const state = localStorage.getItem('oidc_state');
if (state) {
this.state = JSON.parse(state);
} else {
this.state = {
provider: null,
value: null,
token: null,
previousUri: null,
};
}
}
return this.state;
}
/** @function
* @name setState
* Set state in localStorage
* @param {string} value - 'login' or 'logout', default null
* @param {string} provider - the id of the provider
* @returns {State} - The state
*/
setState(value = null, provider = null) {
const state = {
value,
provider,
token: (value === null) ? null : Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5),
previousUri: (value === null) ? null : window.location.href,
};
localStorage.setItem('oidc_state', JSON.stringify(state));
this.state = state;
return state;
}
/** @function
* @name clearState
* Reset current state
*/
clearState() {
this.setState();
}
/** @function /** @function
* @name processState * @name processState
* Try to get user, the if a state is set, call the appropriate provider * Try to get user, the if a state is set, call the appropriate provider
...@@ -199,7 +153,8 @@ class SIBAuth extends HTMLElement { ...@@ -199,7 +153,8 @@ class SIBAuth extends HTMLElement {
/** @function /** @function
* @name dispatchUserInfo * @name dispatchUserInfo
* Try to replace data-src by user iri on [bind-user] elements * @param {User} userWebId - User
* Try to replace data-src by userWebId iri on [bind-user] elements
*/ */
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
async dispatchUserInfo() { async dispatchUserInfo() {
...@@ -211,7 +166,7 @@ class SIBAuth extends HTMLElement { ...@@ -211,7 +166,7 @@ class SIBAuth extends HTMLElement {
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', processDOM); document.addEventListener('DOMContentLoaded', processDOM);
} else { } else {
await processDOM(); processDOM();
} }
} }
...@@ -219,43 +174,46 @@ class SIBAuth extends HTMLElement { ...@@ -219,43 +174,46 @@ class SIBAuth extends HTMLElement {
* @name applyUser * @name applyUser
* @param {Element} element * @param {Element} element
*/ */
applyUser(element) { async applyUser(element) {
const user = this.getUser(); const id = await this.getUserWebId();
if (user == null) return; if (id == null) return;
const id = user.profile.website;
if (element.getAttribute(id === 'data-src')) return; if (element.getAttribute(id === 'data-src')) return;
element.setAttribute('data-src', id); element.setAttribute('data-src', id);
} }
/** @function /** @function
* @name getUser * @name getUserWebId
* Return User or undefined * Return User or undefined
* @return {User} * @return {User}
*/ */
getUser() { // eslint-disable-next-line class-methods-use-this