Possibility to configure an OpenID Connect provider on the instance level WIP (#128).
This commit is contained in:
parent
514cc1d159
commit
e646ebfd69
@ -159,6 +159,7 @@ async function initConverse (
|
|||||||
|
|
||||||
// no viewer mode if authenticated.
|
// no viewer mode if authenticated.
|
||||||
params.livechat_enable_viewer_mode = autoViewerMode && !isAuthenticated && !isRemoteWithNicknameSet
|
params.livechat_enable_viewer_mode = autoViewerMode && !isAuthenticated && !isRemoteWithNicknameSet
|
||||||
|
params.livechat_external_auth_oidc_button_label = initConverseParams.externalAuthOIDC?.buttonLabel
|
||||||
|
|
||||||
if (chatIncludeMode === 'peertube-video') {
|
if (chatIncludeMode === 'peertube-video') {
|
||||||
params.livechat_mini_muc_head = true // we must replace the muc-head by the custom buttons toolbar.
|
params.livechat_mini_muc_head = true // we must replace the muc-head by the custom buttons toolbar.
|
||||||
|
@ -93,6 +93,17 @@ body[livechat-viewer-mode="on"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.livechat-external-login-modal {
|
||||||
|
.livechat-external-login-modal-external-auth-oidc {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background-color: var(--chatroom-head-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Transparent mode
|
// Transparent mode
|
||||||
body.livechat-transparent {
|
body.livechat-transparent {
|
||||||
// --peertube-main-background: rgba(0 0 0 / 0%) !important;
|
// --peertube-main-background: rgba(0 0 0 / 0%) !important;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { api } from '@converse/headless/core'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
|
||||||
@ -7,7 +8,22 @@ export const tplExternalLoginModal = (el, o) => {
|
|||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
const i18nRemotePeertubeUrl = __(LOC_login_remote_peertube_url)
|
const i18nRemotePeertubeUrl = __(LOC_login_remote_peertube_url)
|
||||||
const i18nRemotePeertubeOpen = __('OK')
|
const i18nRemotePeertubeOpen = __('OK')
|
||||||
|
const externalAuthOIDCButtonLabel = api.settings.get('livechat_external_auth_oidc_button_label')
|
||||||
return html`<div class="modal-body livechat-external-login-modal">
|
return html`<div class="modal-body livechat-external-login-modal">
|
||||||
|
${!externalAuthOIDCButtonLabel
|
||||||
|
? ''
|
||||||
|
: html`
|
||||||
|
<div class="livechat-external-login-modal-external-auth-oidc">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
@click=${() => console.log('ok, go')}
|
||||||
|
>
|
||||||
|
${externalAuthOIDCButtonLabel}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
`
|
||||||
|
}
|
||||||
<form class="converse-form chatroom-form" @submit=${(ev) => el.openRemotePeertube(ev)}>
|
<form class="converse-form chatroom-form" @submit=${(ev) => el.openRemotePeertube(ev)}>
|
||||||
<label>
|
<label>
|
||||||
${i18nRemotePeertube}
|
${i18nRemotePeertube}
|
||||||
@ -51,6 +67,6 @@ export const tplExternalLoginModal = (el, o) => {
|
|||||||
}</button>
|
}</button>
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
</fieldset>
|
</form>
|
||||||
</form></div>`
|
</div>`
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,12 @@ export const livechatSpecificsPlugin = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update other settings
|
// update other settings
|
||||||
for (const k of ['hide_muc_participants', 'livechat_enable_viewer_mode', 'livechat_mini_muc_head']) {
|
for (const k of [
|
||||||
|
'hide_muc_participants',
|
||||||
|
'livechat_enable_viewer_mode',
|
||||||
|
'livechat_external_auth_oidc_button_label',
|
||||||
|
'livechat_mini_muc_head'
|
||||||
|
]) {
|
||||||
_converse.api.settings.set(k, params[k])
|
_converse.api.settings.set(k, params[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ export const livechatViewerModePlugin = {
|
|||||||
_converse.api.settings.extend({
|
_converse.api.settings.extend({
|
||||||
livechat_enable_viewer_mode: false,
|
livechat_enable_viewer_mode: false,
|
||||||
livechat_peertube_video_original_url: undefined,
|
livechat_peertube_video_original_url: undefined,
|
||||||
livechat_peertube_video_uuid: undefined
|
livechat_peertube_video_uuid: undefined,
|
||||||
|
livechat_external_auth_oidc_button_label: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const originalGetDefaultMUCNickname = _converse.getDefaultMUCNickname
|
const originalGetDefaultMUCNickname = _converse.getDefaultMUCNickname
|
||||||
|
@ -79,7 +79,11 @@ async function getConverseJSParams (
|
|||||||
|
|
||||||
const oidc = ExternalAuthOIDC.singleton()
|
const oidc = ExternalAuthOIDC.singleton()
|
||||||
// TODO:
|
// TODO:
|
||||||
const externalAuthOIDC = await oidc.isOk() ? undefined : undefined
|
const externalAuthOIDC = await oidc.isOk()
|
||||||
|
? {
|
||||||
|
buttonLabel: oidc.getButtonLabel() ?? '???'
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peertubeVideoOriginalUrl: roomInfos.video?.url,
|
peertubeVideoOriginalUrl: roomInfos.video?.url,
|
||||||
|
@ -16,13 +16,20 @@ export async function diagExternalAuthCustomOIDC (test: string, _options: Regist
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = await oidc.check()
|
result.messages.push('Discovery URL: ' + (oidc.getDiscoveryUrl() ?? 'undefined'))
|
||||||
if (errors.length) {
|
|
||||||
|
const oidcErrors = await oidc.check()
|
||||||
|
if (oidcErrors.length) {
|
||||||
result.messages.push({
|
result.messages.push({
|
||||||
level: 'error',
|
level: 'error',
|
||||||
message: 'The ExternalAuthOIDC singleton got some errors:'
|
message: 'The ExternalAuthOIDC singleton got some errors:'
|
||||||
})
|
})
|
||||||
result.messages.push(...errors)
|
for (const oidcError of oidcErrors) {
|
||||||
|
result.messages.push({
|
||||||
|
level: 'error',
|
||||||
|
message: oidcError
|
||||||
|
})
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -33,6 +40,18 @@ export async function diagExternalAuthCustomOIDC (test: string, _options: Regist
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oidc = ExternalAuthOIDC.singleton()
|
||||||
|
const issuer = await oidc.loadIssuer()
|
||||||
|
if (issuer) {
|
||||||
|
result.messages.push('Discovery URL loaded: ' + JSON.stringify(issuer.metadata))
|
||||||
|
} else {
|
||||||
|
result.messages.push({
|
||||||
|
level: 'error',
|
||||||
|
message: 'Failed to load the Discovery URL.'
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
result.ok = true
|
result.ok = true
|
||||||
result.messages.push('Configuration OK.')
|
result.messages.push('Configuration OK.')
|
||||||
return result
|
return result
|
||||||
|
@ -14,6 +14,7 @@ class ExternalAuthOIDC {
|
|||||||
private readonly clientId: string | undefined
|
private readonly clientId: string | undefined
|
||||||
private readonly clientSecret: string | undefined
|
private readonly clientSecret: string | undefined
|
||||||
private ok: boolean | undefined
|
private ok: boolean | undefined
|
||||||
|
private issuer: Issuer | undefined | null
|
||||||
protected readonly logger: {
|
protected readonly logger: {
|
||||||
debug: (s: string) => void
|
debug: (s: string) => void
|
||||||
info: (s: string) => void
|
info: (s: string) => void
|
||||||
@ -54,6 +55,22 @@ class ExternalAuthOIDC {
|
|||||||
return !this.enabled
|
return !this.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the button
|
||||||
|
* @returns Button label
|
||||||
|
*/
|
||||||
|
getButtonLabel (): string | undefined {
|
||||||
|
return this.buttonLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the discovery URL
|
||||||
|
* @returns discoveryURL
|
||||||
|
*/
|
||||||
|
getDiscoveryUrl (): string | undefined {
|
||||||
|
return this.discoveryUrl
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the OIDC provider is correctly configured.
|
* Indicates if the OIDC provider is correctly configured.
|
||||||
* @param force If true, all checks will be forced again.
|
* @param force If true, all checks will be forced again.
|
||||||
@ -78,43 +95,52 @@ class ExternalAuthOIDC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
if (this.buttonLabel === undefined) {
|
if ((this.buttonLabel ?? '') === '') {
|
||||||
errors.push('Missing button label')
|
errors.push('Missing button label')
|
||||||
}
|
}
|
||||||
if (this.discoveryUrl === undefined) {
|
if ((this.discoveryUrl ?? '') === '') {
|
||||||
errors.push('Missing discovery url')
|
errors.push('Missing discovery url')
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const uri = new URL(this.discoveryUrl)
|
const uri = new URL(this.discoveryUrl ?? 'wrong url')
|
||||||
this.logger.debug('OIDC Discovery url is valid: ' + uri.toString())
|
this.logger.debug('OIDC Discovery url is valid: ' + uri.toString())
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors.push('Invalid discovery url')
|
errors.push('Invalid discovery url')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.clientId === undefined) {
|
if ((this.clientId ?? '') === '') {
|
||||||
errors.push('Missing client id')
|
errors.push('Missing client id')
|
||||||
}
|
}
|
||||||
if (this.clientSecret === undefined) {
|
if ((this.clientSecret ?? '') === '') {
|
||||||
errors.push('Missing client secret')
|
errors.push('Missing client secret')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length === 0) {
|
|
||||||
// Now we can try to use the discover service
|
|
||||||
try {
|
|
||||||
const issuer = await Issuer.discover(this.discoveryUrl as string)
|
|
||||||
this.logger.debug(`Discovered issuer, metadata are: ${JSON.stringify(issuer.metadata)}`)
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(err as string)
|
|
||||||
errors.push(`Discovery URL non working: ${err as string}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
this.logger.error('OIDC is not ok: ' + JSON.stringify(errors))
|
this.logger.error('OIDC is not ok: ' + JSON.stringify(errors))
|
||||||
}
|
}
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the issuer is loaded.
|
||||||
|
* @returns the issuer if enabled
|
||||||
|
*/
|
||||||
|
async loadIssuer (): Promise<Issuer | null> {
|
||||||
|
// this.issuer === null means we already tried, but it failed.
|
||||||
|
if (this.issuer !== undefined) { return this.issuer }
|
||||||
|
|
||||||
|
if (!await this.isOk()) { return null }
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.issuer = await Issuer.discover(this.discoveryUrl as string)
|
||||||
|
this.logger.debug(`Discovered issuer, metadata are: ${JSON.stringify(this.issuer.metadata)}`)
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error(err as string)
|
||||||
|
this.issuer = null
|
||||||
|
}
|
||||||
|
return this.issuer
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* frees the singleton
|
* frees the singleton
|
||||||
*/
|
*/
|
||||||
@ -143,6 +169,7 @@ class ExternalAuthOIDC {
|
|||||||
settings['external-auth-custom-oidc-client-id'] as string | undefined,
|
settings['external-auth-custom-oidc-client-id'] as string | undefined,
|
||||||
settings['external-auth-custom-oidc-client-secret'] as string | undefined
|
settings['external-auth-custom-oidc-client-secret'] as string | undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
return singleton
|
return singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user