Added scope customisation to the OIDC SSO configuration. Users can add or remove whichever scopes they like except 'openid'. They can revert to our core default values if they run into any issues
This commit is contained in:
parent
9af33e2fc0
commit
9a677fce72
|
@ -8,6 +8,7 @@
|
||||||
export let invalid = false
|
export let invalid = false
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let closable = false
|
export let closable = false
|
||||||
|
export let onClick
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
<span class="spectrum-Tags-itemLabel"><slot /></span>
|
<span class="spectrum-Tags-itemLabel"><slot /></span>
|
||||||
{#if closable}
|
{#if closable}
|
||||||
<ClearButton on:click />
|
<ClearButton on:click={onClick} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
Body,
|
Body,
|
||||||
Select,
|
Select,
|
||||||
Toggle,
|
Toggle,
|
||||||
|
Tag,
|
||||||
|
Tags,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -208,6 +210,7 @@
|
||||||
providers[res.type]._id = res._id
|
providers[res.type]._id = res._id
|
||||||
})
|
})
|
||||||
notifications.success(`Settings saved`)
|
notifications.success(`Settings saved`)
|
||||||
|
scopesFields[0].editing = false
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notifications.error("Failed to update auth settings")
|
notifications.error("Failed to update auth settings")
|
||||||
|
@ -215,6 +218,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultScopes = ["profile", "email", "offline_access"]
|
||||||
|
|
||||||
|
const refreshScopes = idx => {
|
||||||
|
providers.oidc.config.configs[idx]["scopes"] =
|
||||||
|
providers.oidc.config.configs[idx]["scopes"]
|
||||||
|
}
|
||||||
|
|
||||||
|
let scopesFields = [
|
||||||
|
{
|
||||||
|
editing: false,
|
||||||
|
inputText: null,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await organisation.init()
|
await organisation.init()
|
||||||
|
@ -276,7 +294,7 @@
|
||||||
if (!oidcDoc?._id) {
|
if (!oidcDoc?._id) {
|
||||||
providers.oidc = {
|
providers.oidc = {
|
||||||
type: ConfigTypes.OIDC,
|
type: ConfigTypes.OIDC,
|
||||||
config: { configs: [{ activated: true }] },
|
config: { configs: [{ activated: true, scopes: defaultScopes }] },
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
originalOidcDoc = cloneDeep(oidcDoc)
|
originalOidcDoc = cloneDeep(oidcDoc)
|
||||||
|
@ -397,10 +415,193 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
<span class="advanced-config">
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="XS">
|
||||||
|
<div class="advanced">Advanced</div>
|
||||||
|
</Heading>
|
||||||
|
<Body size="S">
|
||||||
|
Changes to your authentication scopes will only take effect when you
|
||||||
|
next log in. Please refer to your vendor documentation before
|
||||||
|
modification.
|
||||||
|
</Body>
|
||||||
|
|
||||||
|
<div class="auth-form" class:editing={scopesFields[0].editing}>
|
||||||
|
<Label size="L" tooltip={"Auth Scopes"}>{"Auth Scopes"}</Label>
|
||||||
|
{#if scopesFields[0].editing}
|
||||||
|
<span class="add-new">
|
||||||
|
<Input
|
||||||
|
error={scopesFields[0].error}
|
||||||
|
placeholder={"New Scope"}
|
||||||
|
bind:value={scopesFields[0].inputText}
|
||||||
|
on:keyup={e => {
|
||||||
|
if (!scopesFields[0].inputText) {
|
||||||
|
scopesFields[0].error = null
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
e.key === "Enter" ||
|
||||||
|
e.keyCode === 13 ||
|
||||||
|
e.code == "Space" ||
|
||||||
|
e.keyCode == 32
|
||||||
|
) {
|
||||||
|
let scopes = providers.oidc.config.configs[0]["scopes"]
|
||||||
|
? providers.oidc.config.configs[0]["scopes"]
|
||||||
|
: [...defaultScopes]
|
||||||
|
|
||||||
|
let update = scopesFields[0].inputText.trim()
|
||||||
|
|
||||||
|
if (/[\\"\s]/.test(update)) {
|
||||||
|
scopesFields[0].error =
|
||||||
|
"Auth scopes cannot contain spaces, double quotes or backslashes"
|
||||||
|
return
|
||||||
|
} else if (scopes.indexOf(update) > -1) {
|
||||||
|
scopesFields[0].error = "Auth scope already exists"
|
||||||
|
return
|
||||||
|
} else if (!update.length) {
|
||||||
|
scopesFields[0].inputText = null
|
||||||
|
scopesFields[0].error = null
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
scopesFields[0].error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopes.indexOf(update) == -1) {
|
||||||
|
scopes.push(update)
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = scopes
|
||||||
|
}
|
||||||
|
scopesFields[0].inputText = null
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="tag-wrap">
|
||||||
|
<Tags>
|
||||||
|
<Tag disabled={!scopesFields[0].editing} closable={false}>
|
||||||
|
openid
|
||||||
|
</Tag>
|
||||||
|
{#each providers.oidc.config.configs[0]["scopes"] || [...defaultScopes] as tag, idx}
|
||||||
|
<Tag
|
||||||
|
disabled={!scopesFields[0].editing}
|
||||||
|
closable={scopesFields[0].editing}
|
||||||
|
onClick={() => {
|
||||||
|
let idxScopes = providers.oidc.config.configs[0]["scopes"]
|
||||||
|
if (idxScopes.length == 1) {
|
||||||
|
idxScopes.pop()
|
||||||
|
} else {
|
||||||
|
idxScopes.splice(idx, 1)
|
||||||
|
refreshScopes(0)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if !scopesFields[0].editing}
|
||||||
|
<span
|
||||||
|
class="edit-icon"
|
||||||
|
on:click={() => {
|
||||||
|
if (!providers.oidc.config.configs[0]) {
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = [
|
||||||
|
...defaultScopes,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
scopesFields[0].editing = !scopesFields[0].editing
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tag>Edit</Tag>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</Tags>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if scopesFields[0].editing}
|
||||||
|
<div class="scope_actions">
|
||||||
|
<Button
|
||||||
|
quiet
|
||||||
|
secondary
|
||||||
|
size="S"
|
||||||
|
on:click={() => {
|
||||||
|
if (originalOidcDoc.config.configs[0].scopes) {
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = [
|
||||||
|
...originalOidcDoc.config.configs[0]["scopes"],
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
delete providers.oidc.config.configs[0]?.scopes
|
||||||
|
}
|
||||||
|
scopesFields[0].editing = false
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
secondary
|
||||||
|
size="S"
|
||||||
|
on:click={() => {
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = [
|
||||||
|
...defaultScopes,
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use Default
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.scope_actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.edit-icon :global(.spectrum-Tags-itemLabel) {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
.advanced-config :global(.spectrum-Tags-item) {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
grid-template-columns: 100px 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form.editing {
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-config :global(.spectrum-Tags-item:first-child) {
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
.advanced-config .auth-form.editing .tag-wrap {
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-sidenav-item-background-color-selected,
|
||||||
|
var(--spectrum-alias-highlight-hover)
|
||||||
|
);
|
||||||
|
padding: 0px 5px 5px 5px;
|
||||||
|
border-radius: var(
|
||||||
|
--spectrum-alias-border-radius-regular,
|
||||||
|
var(--spectrum-global-dimension-size-50)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.edit-icon,
|
||||||
|
.edit-icon :global(.icon) {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 100px 1fr;
|
grid-template-columns: 100px 1fr;
|
||||||
|
|
|
@ -227,9 +227,22 @@ export const oidcPreAuth = async (ctx: any, next: any) => {
|
||||||
|
|
||||||
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
|
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
|
||||||
|
|
||||||
|
const db = getGlobalDB()
|
||||||
|
const config = await core.db.getScopedConfig(db, {
|
||||||
|
type: Configs.OIDC,
|
||||||
|
group: ctx.query.group,
|
||||||
|
})
|
||||||
|
|
||||||
|
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
||||||
|
|
||||||
|
let authScopes =
|
||||||
|
chosenConfig.scopes?.length > 0
|
||||||
|
? chosenConfig.scopes
|
||||||
|
: ["profile", "email", "offline_access"]
|
||||||
|
|
||||||
return passport.authenticate(strategy, {
|
return passport.authenticate(strategy, {
|
||||||
// required 'openid' scope is added by oidc strategy factory
|
// required 'openid' scope is added by oidc strategy factory
|
||||||
scope: ["profile", "email", "offline_access"], //auth0 offline_access scope required for the refresh token behaviour.
|
scope: authScopes,
|
||||||
})(ctx, next)
|
})(ctx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ function oidcValidation() {
|
||||||
name: Joi.string().allow("", null),
|
name: Joi.string().allow("", null),
|
||||||
uuid: Joi.string().required(),
|
uuid: Joi.string().required(),
|
||||||
activated: Joi.boolean().required(),
|
activated: Joi.boolean().required(),
|
||||||
|
scopes: Joi.array().optional()
|
||||||
})
|
})
|
||||||
).required(true)
|
).required(true)
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
|
|
Loading…
Reference in New Issue