From 0a492d192154b4bc91f02788c7be316850500b21 Mon Sep 17 00:00:00 2001 From: John Livingston Date: Mon, 22 Apr 2024 14:28:55 +0200 Subject: [PATCH] Adding some standard OpenID Connect providers (Google, Facebook) (WIP): * frontend --- conversejs/builtin.ts | 5 +- conversejs/custom/shared/styles/livechat.scss | 9 +- .../livechat-external-login-modal.js | 189 +++++++++++------- conversejs/lib/plugins/livechat-specific.ts | 2 +- .../lib/plugins/livechat-viewer-mode.ts | 3 +- server/lib/conversejs/params.ts | 20 +- server/lib/external-auth/oidc.ts | 3 +- shared/lib/types.ts | 10 +- 8 files changed, 148 insertions(+), 93 deletions(-) diff --git a/conversejs/builtin.ts b/conversejs/builtin.ts index 08874057..5ba1be28 100644 --- a/conversejs/builtin.ts +++ b/conversejs/builtin.ts @@ -109,7 +109,7 @@ async function initConverse ( let isRemoteWithNicknameSet: boolean = false // OIDC (OpenID Connect): - const tryOIDC = !!initConverseParams.externalAuthOIDC + const tryOIDC = (initConverseParams.externalAuthOIDC?.length ?? 0) > 0 const auth = await getLocalAuthentInfos(authenticationUrl, tryOIDC, peertubeAuthHeader) @@ -171,8 +171,7 @@ async function initConverse ( params.livechat_specific_external_authent = isAuthenticatedWithExternalAccount if (tryOIDC && !isAuthenticated) { - params.livechat_external_auth_oidc_button_label = initConverseParams.externalAuthOIDC?.buttonLabel - params.livechat_external_auth_oidc_url = initConverseParams.externalAuthOIDC?.url + params.livechat_external_auth_oidc_buttons = initConverseParams.externalAuthOIDC } if (tryOIDC) { // also needed when authenticated (for the signout button) diff --git a/conversejs/custom/shared/styles/livechat.scss b/conversejs/custom/shared/styles/livechat.scss index 9b1cc456..9c3132f1 100644 --- a/conversejs/custom/shared/styles/livechat.scss +++ b/conversejs/custom/shared/styles/livechat.scss @@ -94,8 +94,13 @@ body[livechat-viewer-mode="on"] { } .livechat-external-login-modal { - .livechat-external-login-modal-external-auth-oidc { - text-align: center; + .livechat-external-login-modal-external-auth-oidc-block { + justify-content: center; + align-items: center; + display: flex; + flex-flow: row wrap; + gap: 20px; + margin-bottom: 20px; width: 100%; } diff --git a/conversejs/custom/templates/livechat-external-login-modal.js b/conversejs/custom/templates/livechat-external-login-modal.js index c63bb8af..6f8730ad 100644 --- a/conversejs/custom/templates/livechat-external-login-modal.js +++ b/conversejs/custom/templates/livechat-external-login-modal.js @@ -2,88 +2,133 @@ import { _converse, api } from '@converse/headless/core' import { __ } from 'i18n' import { html } from 'lit' +function externalLoginClickHandler (ev, el, externalAuthOIDCUrl) { + ev.preventDefault() + + el.clearAlert() + + const popup = window.open( + externalAuthOIDCUrl, + 'livechat-external-auth', + 'popup' + ) + + window.externalAuthGetResult = (data) => { + window.externalAuthGetResult = undefined + + if (!data) { + // special case: when this modal is closed, used to close the popup + if (popup) { popup.close() } + return + } + + console.log('Received an external authentication result...', data) + if (!data.ok) { + // eslint-disable-next-line no-undef + el.external_auth_oidc_alert_message = __(LOC_login_external_auth_alert_message) + + (data.message ? ` (${data.message})` : '') + return + } + + console.info('Got external account information', data) + // Storing the token in sessionStorage. + window.sessionStorage.setItem('peertube-plugin-livechat-external-auth-oidc-token', data.token) + + const reconnectMode = api.settings.get('livechat_external_auth_reconnect_mode') + if (reconnectMode === 'button-close-open') { + // Here, we click on the close button, then on the open button. + // FIXME: there is maybe a better way to do this. + try { + // But first, close the modal. + document.getElementsByClassName('livechat-external-login-modal')[0] + .closest('.modal-dialog') + .querySelector('button.close') + .click() + + // As soon as disconnected, re-open: + _converse.api.listen.once('disconnected', () => { + document.getElementsByClassName('peertube-plugin-livechat-button-open')[0].click() + }) + + // And we close! + document.getElementsByClassName('peertube-plugin-livechat-button-close')[0].click() + } catch (err) { + // fallback... reloading window :/ + console.error(err) + window.location.reload() + } + } else { // reload and other use cases... + window.location.reload() + } + } + + return false +} + export const tplExternalLoginModal = (el, o) => { // eslint-disable-next-line no-undef const i18nRemotePeertube = __(LOC_login_remote_peertube) // eslint-disable-next-line no-undef const i18nRemotePeertubeUrl = __(LOC_login_remote_peertube_url) const i18nRemotePeertubeOpen = __('OK') - const externalAuthOIDCButtonLabel = api.settings.get('livechat_external_auth_oidc_button_label') - const externalAuthOIDCUrl = api.settings.get('livechat_external_auth_oidc_url') + const buttonsDefinitions = api.settings.get('livechat_external_auth_oidc_buttons') + + const externalButtonsBlocks = [] + if (Array.isArray(buttonsDefinitions) && buttonsDefinitions.length) { + // type=custom first, if present + // and sorting on label alphabetically + const customButtonsDefinitions = buttonsDefinitions.filter(b => b.type === 'custom') + .sort((a, b) => a.buttonLabel > b.buttonLabel ? 1 : (a.buttonLabel < b.buttonLabel ? -1 : 0)) + + const otherButtonsDefinition = buttonsDefinitions.filter(b => b.type !== 'custom') + .sort((a, b) => a.buttonLabel > b.buttonLabel ? 1 : (a.buttonLabel < b.buttonLabel ? -1 : 0)) + + for (const block of [customButtonsDefinitions, otherButtonsDefinition]) { + if (!block.length) { continue } + const externalButtons = [] + for (const buttonDef of block) { + if (typeof buttonDef !== 'object') { continue } + const type = buttonDef.type + const label = buttonDef.buttonLabel + const url = buttonDef.url + if (!type || !type || !url) { + continue + } + externalButtons.push({ + label, + url, + class: 'livechat-external-login-modal-external-auth-oidc-type-' + type + }) + } + externalButtonsBlocks.push(externalButtons) + } + } + return html`