235 lines
10 KiB
JavaScript
235 lines
10 KiB
JavaScript
window.app = window.app || {};
|
|
window.app.oauth2 = window.app.oauth2 || {};
|
|
|
|
// note: data is the providerSettingsModal form store
|
|
window.app.oauth2.oidc = function(providerInfo, namePrefix, data) {
|
|
const uniqueId = "oidc_" + app.utils.randomString();
|
|
|
|
const userInfoOptions = [
|
|
{ label: "User info URL", value: true },
|
|
{ label: "ID Token", value: false },
|
|
];
|
|
|
|
const local = store({
|
|
useUserInfoUrl: false,
|
|
});
|
|
|
|
const watchers = [];
|
|
|
|
return t.div(
|
|
{
|
|
pbEvent: "oauth2OIDCOptions",
|
|
className: "oauth2-oidc-options",
|
|
// init defaults
|
|
onmount: (el) => {
|
|
if (typeof data.config.displayName == "undefined") {
|
|
data.config.displayName = "OIDC";
|
|
}
|
|
|
|
if (typeof data.config.pkce == "undefined") {
|
|
data.config.pkce = true;
|
|
}
|
|
|
|
if (data.config.userInfoURL || !data.config.extra) {
|
|
local.useUserInfoUrl = true;
|
|
}
|
|
|
|
// unset the id_token or info url fields based on the toggle state
|
|
watchers.push(
|
|
watch(() => local.useUserInfoUrl, (useURL, oldUseURL) => {
|
|
if (useURL) {
|
|
// note: null because {} will just result in JSON unmarshal merge with the existing data
|
|
data.config.extra = null;
|
|
} else {
|
|
data.config.userInfoURL = "";
|
|
// note: fallback to empty object to distinguish from the null state since all id_token fields are optional
|
|
data.config.extra = data.config.extra || {};
|
|
}
|
|
}),
|
|
);
|
|
},
|
|
onunmount: () => {
|
|
watchers.forEach((w) => w?.unwatch());
|
|
},
|
|
},
|
|
t.div(
|
|
{ className: "grid" },
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label({ htmlFor: uniqueId + ".displayName" }, "Display name"),
|
|
t.input({
|
|
id: uniqueId + ".displayName",
|
|
name: namePrefix + ".displayName",
|
|
type: "text",
|
|
required: true,
|
|
value: () => data.config.displayName || "",
|
|
oninput: (e) => data.config.displayName = e.target.value,
|
|
}),
|
|
),
|
|
),
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.p({ className: "txt-bold" }, "Endpoints"),
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label({ htmlFor: uniqueId + ".authURL" }, "Auth URL"),
|
|
t.input({
|
|
id: uniqueId + ".authURL",
|
|
name: namePrefix + ".authURL",
|
|
type: "url",
|
|
required: true,
|
|
value: () => data.config.authURL || "",
|
|
oninput: (e) => data.config.authURL = e.target.value,
|
|
}),
|
|
),
|
|
),
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label({ htmlFor: uniqueId + ".tokenURL" }, "Token URL"),
|
|
t.input({
|
|
id: uniqueId + ".tokenURL",
|
|
name: namePrefix + ".tokenURL",
|
|
type: "url",
|
|
required: true,
|
|
value: () => data.config.tokenURL || "",
|
|
oninput: (e) => data.config.tokenURL = e.target.value,
|
|
}),
|
|
),
|
|
),
|
|
// User info
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label({ htmlFor: uniqueId + ".userInfoSelect" }, "Fetch user info from"),
|
|
app.components.select({
|
|
id: uniqueId + ".userInfoSelect",
|
|
required: true,
|
|
options: userInfoOptions,
|
|
value: () => local.useUserInfoUrl,
|
|
onchange: (selectedOpts) => local.useUserInfoUrl = selectedOpts?.[0]?.value,
|
|
}),
|
|
),
|
|
t.div({ className: "oidc-userinfo-options m-t-10" }, () => {
|
|
if (local.useUserInfoUrl) {
|
|
return t.div(
|
|
{ className: "field" },
|
|
t.label({ htmlFor: uniqueId + ".userInfoURL" }, "User info URL"),
|
|
t.input({
|
|
id: uniqueId + ".userInfoURL",
|
|
name: namePrefix + ".userInfoURL",
|
|
type: "url",
|
|
required: true,
|
|
value: () => data.config.userInfoURL || "",
|
|
oninput: (e) => data.config.userInfoURL = e.target.value,
|
|
}),
|
|
);
|
|
}
|
|
|
|
return t.div(
|
|
{ className: "grid sm" },
|
|
t.div(
|
|
{ className: "col-12 txt-hint txt-sm" },
|
|
t.em(
|
|
null,
|
|
"Both fields are considered optional because the parsed ",
|
|
t.code(null, "id_token"),
|
|
" is a direct result of the TLS code->token exchange server response.",
|
|
),
|
|
),
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label(
|
|
{ htmlFor: uniqueId + ".extra.jwksURL" },
|
|
t.span({ className: "txt" }, "JWKS verification URL"),
|
|
t.i({
|
|
ariaHidden: true,
|
|
className: "ri-information-line link-hint",
|
|
ariaDescription: app.attrs.tooltip(
|
|
"URL to the public token verification keys.",
|
|
),
|
|
}),
|
|
),
|
|
t.input({
|
|
id: uniqueId + ".extra.jwksURL",
|
|
name: namePrefix + ".extra.jwksURL",
|
|
type: "url",
|
|
value: () => data.config.extra?.jwksURL || "",
|
|
oninput: (e) => {
|
|
data.config.extra = data.config.extra || {};
|
|
data.config.extra.jwksURL = e.target.value;
|
|
},
|
|
}),
|
|
),
|
|
),
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.label(
|
|
{ htmlFor: uniqueId + ".extra.issuers" },
|
|
t.span({ className: "txt" }, "Issuers"),
|
|
t.i({
|
|
ariaHidden: true,
|
|
className: "ri-information-line link-hint",
|
|
ariaDescription: app.attrs.tooltip(
|
|
"Comma separated list of accepted values for the iss token claim validation.",
|
|
),
|
|
}),
|
|
),
|
|
t.input({
|
|
id: uniqueId + ".extra.issuers",
|
|
name: namePrefix + ".extra.issuers",
|
|
type: "text",
|
|
value: () => app.utils.joinNonEmpty(data.config.extra?.issuers),
|
|
oninput: (e) => {
|
|
const newValue = app.utils.splitNonEmpty(e.target.value, ",");
|
|
const newStr = app.utils.joinNonEmpty(newValue);
|
|
const oldStr = app.utils.joinNonEmpty(data.config.extra?.issuers);
|
|
|
|
// has an actual change
|
|
if (oldStr != newStr) {
|
|
data.config.extra = data.config.extra || {};
|
|
data.config.extra.issuers = newValue;
|
|
}
|
|
},
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
t.div(
|
|
{ className: "col-12" },
|
|
t.div(
|
|
{ className: "field" },
|
|
t.input({
|
|
id: uniqueId + ".pkce",
|
|
name: namePrefix + ".pkce",
|
|
type: "checkbox",
|
|
checked: () => data.config.pkce || false,
|
|
onchange: (e) => data.config.pkce = e.target.checked,
|
|
}),
|
|
t.label(
|
|
{ htmlFor: uniqueId + ".pkce" },
|
|
t.span({ className: "txt", textContent: "Support PKCE" }),
|
|
t.i({
|
|
className: "ri-information-line link-hint",
|
|
ariaHidden: true,
|
|
ariaDescription: app.attrs.tooltip(
|
|
"Usually it should be safe to be always enabled as most providers will just ignore the extra query parameters if they don't support PKCE.",
|
|
),
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
};
|