Skip to content

Commit 1665386

Browse files
committed
merge oidc branch
Signed-off-by: Zoey <zoey@z0ey.de>
1 parent 9953b9c commit 1665386

File tree

14 files changed

+233
-447
lines changed

14 files changed

+233
-447
lines changed

backend/internal/token.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ module.exports = {
8787
getTokenFromOAuthClaim: (data) => {
8888
let Token = new TokenModel();
8989

90-
data.scope = 'user';
90+
data.scope = 'user';
9191
data.expiry = '1d';
9292

9393
return userModel
@@ -109,13 +109,12 @@ module.exports = {
109109

110110
let iss = 'api',
111111
attrs = { id: user.id },
112-
scope = [ data.scope ],
112+
scope = [data.scope],
113113
expiresIn = data.expiry;
114114

115-
return Token.create({ iss, attrs, scope, expiresIn })
116-
.then((signed) => {
117-
return { token: signed.token, expires: expiry.toISOString() };
118-
});
115+
return Token.create({ iss, attrs, scope, expiresIn }).then((signed) => {
116+
return { token: signed.token, expires: expiry.toISOString() };
117+
});
119118
});
120119
},
121120

backend/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"moment": "2.30.1",
2424
"mysql2": "3.14.0",
2525
"node-rsa": "1.1.1",
26-
"openid-client": "5.4.0",
26+
"openid-client": "5.7.1",
2727
"objection": "3.1.5",
2828
"path": "0.12.7",
2929
"pg": "8.14.1",

backend/routes/oidc.js

+31-29
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
const crypto = require('crypto');
2-
const error = require('../lib/error');
3-
const express = require('express');
4-
const jwtdecode = require('../lib/express/jwt-decode');
5-
const logger = require('../logger').oidc;
6-
const oidc = require('openid-client');
7-
const settingModel = require('../models/setting');
1+
const crypto = require('crypto');
2+
const error = require('../lib/error');
3+
const express = require('express');
4+
const jwtdecode = require('../lib/express/jwt-decode');
5+
const logger = require('../logger').oidc;
6+
const oidc = require('openid-client');
7+
const settingModel = require('../models/setting');
88
const internalToken = require('../internal/token');
99

1010
let router = express.Router({
1111
caseSensitive: true,
12-
strict: true,
13-
mergeParams: true
12+
strict: true,
13+
mergeParams: true,
1414
});
1515

1616
router
@@ -29,14 +29,13 @@ router
2929
logger.info('Initializing OAuth flow');
3030
settingModel
3131
.query()
32-
.where({id: 'oidc-config'})
32+
.where({ id: 'oidc-config' })
3333
.first()
3434
.then((row) => getInitParams(req, row))
3535
.then((params) => redirectToAuthorizationURL(res, params))
3636
.catch((err) => redirectWithError(res, err));
3737
});
3838

39-
4039
router
4140
.route('/callback')
4241
.options((req, res) => {
@@ -53,7 +52,7 @@ router
5352
logger.info('Processing callback');
5453
settingModel
5554
.query()
56-
.where({id: 'oidc-config'})
55+
.where({ id: 'oidc-config' })
5756
.first()
5857
.then((settings) => validateCallback(req, settings))
5958
.then((token) => redirectWithJwtToken(res, token))
@@ -74,9 +73,9 @@ let getClient = async (row) => {
7473
}
7574

7675
return new issuer.Client({
77-
client_id: row.meta.clientID,
78-
client_secret: row.meta.clientSecret,
79-
redirect_uris: [row.meta.redirectURL],
76+
client_id: row.meta.clientID,
77+
client_secret: row.meta.clientSecret,
78+
redirect_uris: [row.meta.redirectURL],
8079
response_types: ['code'],
8180
});
8281
};
@@ -90,10 +89,10 @@ let getClient = async (row) => {
9089
* */
9190
let getInitParams = async (req, row) => {
9291
let client = await getClient(row),
93-
state = crypto.randomUUID(),
94-
nonce = crypto.randomUUID(),
95-
url = client.authorizationUrl({
96-
scope: 'openid email profile',
92+
state = crypto.randomUUID(),
93+
nonce = crypto.randomUUID(),
94+
url = client.authorizationUrl({
95+
scope: 'openid email profile',
9796
resource: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
9897
state,
9998
nonce,
@@ -109,14 +108,17 @@ let getInitParams = async (req, row) => {
109108
* @return { {String}, {String} } state and nonce
110109
* */
111110
let parseStateFromCookie = (req) => {
111+
if (!req.headers || !req.headers.cookie) {
112+
return { state: undefined, nonce: undefined };
113+
}
112114
let state, nonce;
113115
let cookies = req.headers.cookie.split(';');
114116
for (let cookie of cookies) {
115-
if (cookie.split('=')[0].trim() === 'npm_oidc') {
117+
if (cookie.split('=')[0].trim() === 'npmplus_oidc') {
116118
let raw = cookie.split('=')[1],
117119
val = raw.split('--');
118-
state = val[0].trim();
119-
nonce = val[1].trim();
120+
state = val[0].trim();
121+
nonce = val[1].trim();
120122
break;
121123
}
122124
}
@@ -132,15 +134,15 @@ let parseStateFromCookie = (req) => {
132134
* @return {Promise} a promise resolving to a jwt token
133135
* */
134136
let validateCallback = async (req, settings) => {
135-
let client = await getClient(settings);
137+
let client = await getClient(settings);
136138
let { state, nonce } = parseStateFromCookie(req);
137139

138-
const params = client.callbackParams(req);
140+
const params = client.callbackParams(req);
139141
const tokenSet = await client.callback(settings.meta.redirectURL, params, { state, nonce });
140-
let claims = tokenSet.claims();
142+
let claims = tokenSet.claims();
141143

142144
if (!claims.email) {
143-
throw new error.AuthError('The Identity Provider didn\'t send the \'email\' claim');
145+
throw new error.AuthError("The Identity Provider didn't send the 'email' claim");
144146
} else {
145147
logger.info('Successful authentication for email ' + claims.email);
146148
}
@@ -150,18 +152,18 @@ let validateCallback = async (req, settings) => {
150152

151153
let redirectToAuthorizationURL = (res, params) => {
152154
logger.info('Authorization URL: ' + params.url);
153-
res.cookie('npm_oidc', params.state + '--' + params.nonce);
155+
res.cookie('npmplus_oidc', params.state + '--' + params.nonce);
154156
res.redirect(params.url);
155157
};
156158

157159
let redirectWithJwtToken = (res, token) => {
158-
res.cookie('npm_oidc', token.token + '---' + token.expires);
160+
res.cookie('npmplus_oidc', token.token + '---' + token.expires);
159161
res.redirect('/login');
160162
};
161163

162164
let redirectWithError = (res, error) => {
163165
logger.error('Callback error: ' + error.message);
164-
res.cookie('npm_oidc_error', error.message);
166+
res.cookie('npmplus_oidc_error', error.message);
165167
res.redirect('/login');
166168
};
167169

backend/routes/settings.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ router
8383
};
8484

8585
// Remove these temporary cookies used during oidc authentication
86-
res.clearCookie('npm_oidc');
87-
res.clearCookie('npm_oidc_error');
86+
res.clearCookie('npmplus_oidc');
87+
res.clearCookie('npmplus_oidc_error');
8888
}
8989
res.status(200).send(row);
9090
})

backend/routes/tokens.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ router
3131
})
3232
.then((data) => {
3333
// clear this temporary cookie following a successful oidc authentication
34-
res.clearCookie('npm_oidc');
34+
res.clearCookie('npmplus_oidc');
3535
res.status(200).send(data);
3636
})
3737
.catch(next);

backend/setup.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ const setupDefaultSettings = () => {
123123
.insert({
124124
id: 'oidc-config',
125125
name: 'Open ID Connect',
126-
description: 'Sign in to Nginx Proxy Manager with an external Identity Provider',
126+
description: 'Sign in to NPMplus with an external Identity Provider',
127127
value: 'metadata',
128128
meta: {},
129129
})

backend/yarn.lock

+40-8
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,11 @@ jackspeak@^3.1.2:
14901490
optionalDependencies:
14911491
"@pkgjs/parseargs" "^0.11.0"
14921492

1493+
jose@^4.10.0:
1494+
version "4.15.9"
1495+
resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100"
1496+
integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==
1497+
14931498
js-yaml@^4.1.0:
14941499
version "4.1.0"
14951500
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -1691,6 +1696,13 @@ lru-cache@^10.2.0:
16911696
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
16921697
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
16931698

1699+
lru-cache@^6.0.0:
1700+
version "6.0.0"
1701+
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
1702+
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
1703+
dependencies:
1704+
yallist "^4.0.0"
1705+
16941706
lru-cache@^7.14.1:
16951707
version "7.18.3"
16961708
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
@@ -1738,17 +1750,17 @@ mime-db@1.52.0:
17381750
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
17391751
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
17401752

1741-
"mime-db@>= 1.43.0 < 2", mime-db@^1.53.0:
1753+
"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0:
17421754
version "1.54.0"
17431755
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5"
17441756
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
17451757

17461758
mime-types@^3.0.0:
1747-
version "3.0.0"
1748-
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.0.tgz#148453a900475522d095a445355c074cca4f5217"
1749-
integrity sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==
1759+
version "3.0.1"
1760+
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce"
1761+
integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==
17501762
dependencies:
1751-
mime-db "^1.53.0"
1763+
mime-db "^1.54.0"
17521764

17531765
mime-types@~2.1.24, mime-types@~2.1.34:
17541766
version "2.1.35"
@@ -1943,6 +1955,11 @@ object-assign@^4.1.1:
19431955
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
19441956
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
19451957

1958+
object-hash@^2.0.1:
1959+
version "2.2.0"
1960+
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
1961+
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
1962+
19461963
object-inspect@^1.13.3:
19471964
version "1.13.4"
19481965
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
@@ -1957,6 +1974,11 @@ objection@3.1.5:
19571974
ajv-formats "^2.1.1"
19581975
db-errors "^0.2.3"
19591976

1977+
oidc-token-hash@^5.0.1:
1978+
version "5.1.0"
1979+
resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz#74bda0c35dd9f71ea9ce0db72ce8dabf5f90ef79"
1980+
integrity sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==
1981+
19601982
on-finished@2.4.1, on-finished@^2.4.1:
19611983
version "2.4.1"
19621984
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
@@ -1976,6 +1998,16 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
19761998
dependencies:
19771999
wrappy "1"
19782000

2001+
openid-client@5.4.0:
2002+
version "5.4.0"
2003+
resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.0.tgz#77f1cda14e2911446f16ea3f455fc7c405103eac"
2004+
integrity sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==
2005+
dependencies:
2006+
jose "^4.10.0"
2007+
lru-cache "^6.0.0"
2008+
object-hash "^2.0.1"
2009+
oidc-token-hash "^5.0.1"
2010+
19792011
optionator@^0.9.3:
19802012
version "0.9.4"
19812013
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
@@ -2792,9 +2824,9 @@ type-check@^0.4.0, type-check@~0.4.0:
27922824
prelude-ls "^1.2.1"
27932825

27942826
type-is@^2.0.0:
2795-
version "2.0.0"
2796-
resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.0.tgz#7d249c2e2af716665cc149575dadb8b3858653af"
2797-
integrity sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==
2827+
version "2.0.1"
2828+
resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97"
2829+
integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==
27982830
dependencies:
27992831
content-type "^1.0.5"
28002832
media-typer "^1.1.0"

frontend/js/i18n/de-lang.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
"form-title": "Hinzufügen {provider, select, letsencrypt{Certbot} other{Custom}} Zertifikat",
8383
"help-content": "TLS-Zertifikate (früher bekannt als SSL-Zertifikate) sind eine Art Verschlüsselungsschlüssel, mit dem Ihre Website für die:den Endbenutzer:in verschlüsselt werden kann.\nNPMplus verwendet standardmäßig einen Dienst namens Let's Encrypt, um kostenlos TLS-Zertifikate auszustellen.\nWenn Sie persönliche Informationen, Passwörter oder sensible Daten hinter NPM haben, ist es wahrscheinlich eine gute Idee, ein Zertifikat zu verwenden.\nNPMplus unterstützt auch die DNS-Authentifizierung, wenn Sie Ihre Website nicht mit dem Internet verbinden oder wenn Sie nur ein Platzhalterzertifikat benötigen.",
8484
"help-title": "TLS-Zertifikate",
85-
"in-use" : "In Verwendung",
85+
"in-use": "In Verwendung",
8686
"inactive": "Inaktiv",
8787
"other-certificate": "Zertifikat",
8888
"other-certificate-key": "Zertifikat Schlüssel",
@@ -116,7 +116,7 @@
116116
"title": "Dead Hosts"
117117
},
118118
"footer": {
119-
"text": " - MIT-Lizenz von <a href=\"{url1}\" target=\"_blank\">jc21.com</a> NPM, <a href=\"{url2}\" target=\"_blank\">ZoeyVid</a> NPMplus und Mitwirkende - Thema <a href=\"{url3}\" target=\"_blank\">Tabler v0.0.31</a>",
119+
"text": " - MIT-Lizenz von <a href=\"{url1}\" target=\"_blank\">jc21.com</a> NPMplus, <a href=\"{url2}\" target=\"_blank\">ZoeyVid</a> NPMplus und Mitwirkende - Thema <a href=\"{url3}\" target=\"_blank\">Tabler v0.0.31</a>",
120120
"repo": "Repository auf GitHub",
121121
"toggle-dark": "Dark Modus ein-/ausschalten"
122122
},
@@ -187,6 +187,11 @@
187187
"default-site-description": "Was angezeigt werden soll, wenn Nginx einen unbekannten Host antrifft",
188188
"default-site-html": "Individuelle Seite",
189189
"default-site-redirect": "Weiterleitung",
190+
"oidc-config": "Open ID Conncect Einstellungen",
191+
"oidc-config-description": "Bei NPMplus mit einem externen Identitäts Anbieter anmelden (IdP)",
192+
"oidc-not-configured": "Nicht konfiguriert",
193+
"oidc-config-hint-1": "Setze die Konfiguration für einen IdP welcher Open ID Connect Discovery unerstützt.",
194+
"oidc-config-hint-2": "Die 'Weiterleitungs URL' muss auf '[NPMplus basis URL]/api/oidc/callback' eingestellt sein, der IdP muss die 'email' senden and ein:e Nutzer:in mit dieser E-Mail-Adresse muss in NPMplus existieren.",
190195
"title": "Einstellungen"
191196
},
192197
"str": {

frontend/js/i18n/en-lang.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"empty": "There are no TLS Certificates",
8181
"force-renew": "Renew Now",
8282
"form-title": "Add {provider, select, letsencrypt{Certbot} other{Custom}} Certificate",
83-
"help-content": "TLS certificates (previously known as SSL Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPMplus uses by default a service called Let's Encrypt to issue TLS certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPM, it's probably a good idea to use a certificate.\nNPMplus also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.",
83+
"help-content": "TLS certificates (previously known as SSL Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPMplus uses by default a service called Let's Encrypt to issue TLS certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPMplus, it's probably a good idea to use a certificate.\nNPMplus also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.",
8484
"help-title": "TLS Certificates",
8585
"in-use" : "In use",
8686
"inactive": "Inactive",
@@ -187,6 +187,11 @@
187187
"default-site-description": "What to show when Nginx is hit with an unknown Host",
188188
"default-site-html": "Custom Page",
189189
"default-site-redirect": "Redirect",
190+
"oidc-config": "Configurazione di Open ID Connect",
191+
"oidc-config-description": "Accedere a NPMplus con un Identity Provider (IdP)",
192+
"oidc-not-configured": "Not configured",
193+
"oidc-config-hint-1": "Provide configuration for an IdP that supports Open ID Connect Discovery.",
194+
"oidc-config-hint-2": "The 'Redirect URL' must be set to '[NPMplus base URL]/api/oidc/callback', the IdP must send the 'email' claim and a user with matching email address must exist in NPMplus.",
190195
"title": "Settings"
191196
},
192197
"str": {

frontend/js/i18n/it-lang.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"empty": "Non ci sono certificati TLS",
8181
"force-renew": "Rinnova ora",
8282
"form-title": "Aggiungi un certificato {provider, select, letsencrypt{Certbot} other{Custom}}",
83-
"help-content": "I certificati TLS (noti precedentemente come certificati SSL) sono una forma di chiave di crittografia che consente la crittografia del tuo sito per l'utente finale.\nNPMplus utilizza di default un servizio chiamato Let's Encrypt per rilasciare gratuitamente certificati TLS.\nSe NPM protegge informazioni personali, password o dati sensibili, probabilmente è una buona idea utilizzare un certificato.\nNPMplus supporta anche l'autenticazione DNS nel caso in cui il tuo sito non sia accessibile da Internet o se desideri semplicemente un certificato wildcard.",
83+
"help-content": "I certificati TLS (noti precedentemente come certificati SSL) sono una forma di chiave di crittografia che consente la crittografia del tuo sito per l'utente finale.\nNPMplus utilizza di default un servizio chiamato Let's Encrypt per rilasciare gratuitamente certificati TLS.\nSe NPMplus protegge informazioni personali, password o dati sensibili, probabilmente è una buona idea utilizzare un certificato.\nNPMplus supporta anche l'autenticazione DNS nel caso in cui il tuo sito non sia accessibile da Internet o se desideri semplicemente un certificato wildcard.",
8484
"help-title": "Certificati TLS",
8585
"in-use" : "In uso",
8686
"inactive": "Inattivo",
@@ -187,6 +187,11 @@
187187
"default-site-description": "Cosa mostrare quando Nginx viene raggiunto da un host sconosciuto",
188188
"default-site-html": "Pagina personalizzata",
189189
"default-site-redirect": "Reindirizzamento",
190+
"oidc-config": "Open ID Conncect Configuration",
191+
"oidc-config-description": "Accedere a NPMplus con un Identity Provider esterno (IdP)",
192+
"oidc-not-configured": "Non configurato",
193+
"oidc-config-hint-1": "Fornire la configurazione di un IdP che supporta Open ID Connect Discovery.",
194+
"oidc-config-hint-2": "L'URL di reindirizzamento' deve essere impostato su '[URL di base NPMplus]/api/oidc/callback', l'IdP deve inviare la richiesta 'email' e deve esistere in NPMplus un utente con un indirizzo email corrispondente.",
190195
"title": "Impostazioni"
191196
},
192197
"str": {

0 commit comments

Comments
 (0)