diff --git a/client/src/actions/login.js b/client/src/actions/login.js
index da0ccaa1..7cd14692 100644
--- a/client/src/actions/login.js
+++ b/client/src/actions/login.js
@@ -54,6 +54,13 @@ authenticateWithOidc.failure = (error, terms) => ({
},
});
+authenticateWithOidc.debug = (logs) => ({
+ type: ActionTypes.WITH_OIDC_AUTHENTICATE__DEBUG,
+ payload: {
+ logs,
+ },
+});
+
const clearAuthenticateError = () => ({
type: ActionTypes.AUTHENTICATE_ERROR_CLEAR,
payload: {},
diff --git a/client/src/api/access-tokens.js b/client/src/api/access-tokens.js
index 5b423558..426c0501 100755
--- a/client/src/api/access-tokens.js
+++ b/client/src/api/access-tokens.js
@@ -13,6 +13,8 @@ const createAccessToken = (data, headers) =>
const exchangeForAccessTokenWithOidc = (data, headers) =>
http.post('/access-tokens/exchange-with-oidc?withHttpOnlyToken=true', data, headers);
+const debugOidc = (data, headers) => http.post('/access-tokens/debug-oidc', data, headers);
+
// TODO: rename?
const acceptTerms = (data, headers) => http.post('/access-tokens/accept-terms', data, headers);
@@ -24,6 +26,7 @@ const deleteCurrentAccessToken = (headers) => http.delete('/access-tokens/me', u
export default {
createAccessToken,
exchangeForAccessTokenWithOidc,
+ debugOidc,
acceptTerms,
revokePendingToken,
deleteCurrentAccessToken,
diff --git a/client/src/components/common/Login/Content.jsx b/client/src/components/common/Login/Content.jsx
index 29d35963..03904133 100644
--- a/client/src/components/common/Login/Content.jsx
+++ b/client/src/components/common/Login/Content.jsx
@@ -8,7 +8,8 @@ import React, { useCallback, useEffect, useMemo } from 'react';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation, Trans } from 'react-i18next';
-import { Button, Divider, Form, Grid, Header, Message } from 'semantic-ui-react';
+import TextareaAutosize from 'react-textarea-autosize';
+import { Button, Divider, Form, Grid, Header, Message, TextArea } from 'semantic-ui-react';
import { useDidUpdate, usePrevious, useToggle } from '../../../lib/hooks';
import { Input } from '../../../lib/custom-ui';
@@ -23,7 +24,7 @@ import logo from '../../../assets/images/logo.png';
import styles from './Content.module.scss';
-const createMessage = (error) => {
+const createMessage = (error, isDebug) => {
if (!error) {
return error;
}
@@ -82,7 +83,7 @@ const createMessage = (error) => {
default:
return {
type: 'warning',
- content: 'common.unknownError',
+ content: isDebug ? error.message : 'common.unknownError',
};
}
};
@@ -95,6 +96,7 @@ const Content = React.memo(() => {
isSubmitting,
isSubmittingWithOidc,
error,
+ debugLogs,
step,
} = useSelector(selectors.selectAuthenticateForm);
@@ -124,7 +126,11 @@ const Content = React.memo(() => {
return initialData;
});
- const message = useMemo(() => createMessage(error), [error]);
+ const withOidc = !!bootstrap.oidc;
+ const isOidcEnforced = withOidc && bootstrap.oidc.isEnforced;
+ const isOidcDebug = withOidc && bootstrap.oidc.debug;
+
+ const message = useMemo(() => createMessage(error, isOidcDebug), [error, isOidcDebug]);
const [focusPasswordFieldState, focusPasswordField] = useToggle();
const [emailOrUsernameFieldRef, handleEmailOrUsernameFieldRef] = useNestedRef('inputRef');
@@ -157,14 +163,11 @@ const Content = React.memo(() => {
dispatch(entryActions.clearAuthenticateError());
}, [dispatch]);
- const withOidc = !!bootstrap.oidc;
- const isOidcEnforced = withOidc && bootstrap.oidc.isEnforced;
-
useEffect(() => {
if (!isOidcEnforced) {
emailOrUsernameFieldRef.current.focus();
}
- }, [emailOrUsernameFieldRef, isOidcEnforced]);
+ }, [isOidcEnforced, emailOrUsernameFieldRef]);
useDidUpdate(() => {
if (wasSubmitting && !isSubmitting && error) {
@@ -269,16 +272,27 @@ const Content = React.memo(() => {
>
)}
{withOidc && (
-
+ <>
+
+ {debugLogs && (
+
+ )}
+ >
)}
diff --git a/client/src/components/common/Login/Content.module.scss b/client/src/components/common/Login/Content.module.scss
index e1bbd88f..d0f7ad61 100644
--- a/client/src/components/common/Login/Content.module.scss
+++ b/client/src/components/common/Login/Content.module.scss
@@ -19,6 +19,16 @@
width: 100%;
}
+ .debugLog {
+ border: 1px solid rgba(9, 30, 66, 0.13);
+ border-radius: 3px;
+ color: #333;
+ line-height: 1.4;
+ margin-top: 16px;
+ padding: 8px 12px;
+ width: 100%;
+ }
+
.divider {
font-weight: normal;
}
diff --git a/client/src/constants/ActionTypes.js b/client/src/constants/ActionTypes.js
index f26e5fe8..a947e279 100644
--- a/client/src/constants/ActionTypes.js
+++ b/client/src/constants/ActionTypes.js
@@ -29,6 +29,7 @@ export default {
WITH_OIDC_AUTHENTICATE: 'WITH_OIDC_AUTHENTICATE',
WITH_OIDC_AUTHENTICATE__SUCCESS: 'WITH_OIDC_AUTHENTICATE__SUCCESS',
WITH_OIDC_AUTHENTICATE__FAILURE: 'WITH_OIDC_AUTHENTICATE__FAILURE',
+ WITH_OIDC_AUTHENTICATE__DEBUG: 'WITH_OIDC_AUTHENTICATE__DEBUG',
AUTHENTICATE_ERROR_CLEAR: 'AUTHENTICATE_ERROR_CLEAR',
TERMS_ACCEPT: 'TERMS_ACCEPT',
TERMS_ACCEPT__SUCCESS: 'TERMS_ACCEPT__SUCCESS',
diff --git a/client/src/locales/ar-YE/login.js b/client/src/locales/ar-YE/login.js
index e7464f4d..033916fb 100644
--- a/client/src/locales/ar-YE/login.js
+++ b/client/src/locales/ar-YE/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'إلغاء وإغلاق',
continue: 'متابعة',
+ debugSso: 'تصحيح أخطاء تسجيل الدخول الموحد',
goBack: 'العودة',
goHome: 'الذهاب للرئيسية',
logIn: 'تسجيل الدخول',
diff --git a/client/src/locales/bg-BG/login.js b/client/src/locales/bg-BG/login.js
index 5a95581d..4e4ee012 100644
--- a/client/src/locales/bg-BG/login.js
+++ b/client/src/locales/bg-BG/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Отказ и затваряне',
continue: 'Продължи',
+ debugSso: 'Дебъгване на SSO',
goBack: 'Назад',
goHome: 'Към началото',
logIn: 'Вход',
diff --git a/client/src/locales/ca-ES/login.js b/client/src/locales/ca-ES/login.js
index 0c58e6f7..aebf22d0 100644
--- a/client/src/locales/ca-ES/login.js
+++ b/client/src/locales/ca-ES/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Cancel·lar i tancar',
continue: 'Continuar',
+ debugSso: 'Depurar SSO',
goBack: 'Tornar',
goHome: "Anar a l'inici",
logIn: 'Iniciar sessió',
diff --git a/client/src/locales/cs-CZ/login.js b/client/src/locales/cs-CZ/login.js
index b737bc33..11acd2c5 100644
--- a/client/src/locales/cs-CZ/login.js
+++ b/client/src/locales/cs-CZ/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Zrušit a zavřít',
continue: 'Pokračovat',
+ debugSso: 'Ladit SSO',
goBack: 'Zpět',
goHome: 'Domů',
logIn: 'Přihlásit se',
diff --git a/client/src/locales/da-DK/login.js b/client/src/locales/da-DK/login.js
index 87aa00f5..544320fb 100644
--- a/client/src/locales/da-DK/login.js
+++ b/client/src/locales/da-DK/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Annuller og luk',
continue: 'Fortsæt',
+ debugSso: 'Fejlfind SSO',
goBack: 'Gå tilbage',
goHome: 'Gå hjem',
logIn: 'Log på',
diff --git a/client/src/locales/de-DE/login.js b/client/src/locales/de-DE/login.js
index 5d7d73b0..a9bd5fb5 100644
--- a/client/src/locales/de-DE/login.js
+++ b/client/src/locales/de-DE/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Abbrechen und schließen',
continue: 'Fortfahren',
+ debugSso: 'SSO debuggen',
goBack: 'Zurück gehen',
goHome: 'Zur Startseite',
logIn: 'Einloggen',
diff --git a/client/src/locales/el-GR/login.js b/client/src/locales/el-GR/login.js
index e18ccad9..0b6a618b 100644
--- a/client/src/locales/el-GR/login.js
+++ b/client/src/locales/el-GR/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Ακύρωση και κλείσιμο',
continue: 'Συνέχεια',
+ debugSso: 'Αποσφαλμάτωση SSO',
goBack: 'Επιστροφή',
goHome: 'Αρχική σελίδα',
logIn: 'Σύνδεση',
diff --git a/client/src/locales/en-GB/login.js b/client/src/locales/en-GB/login.js
index 81206524..d1511998 100644
--- a/client/src/locales/en-GB/login.js
+++ b/client/src/locales/en-GB/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'Cancel and close',
continue: 'Continue',
+ debugSso: 'Debug SSO',
goBack: 'Go back',
goHome: 'Go home',
logIn: 'Log in',
diff --git a/client/src/locales/en-US/login.js b/client/src/locales/en-US/login.js
index 81206524..d1511998 100644
--- a/client/src/locales/en-US/login.js
+++ b/client/src/locales/en-US/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'Cancel and close',
continue: 'Continue',
+ debugSso: 'Debug SSO',
goBack: 'Go back',
goHome: 'Go home',
logIn: 'Log in',
diff --git a/client/src/locales/es-ES/login.js b/client/src/locales/es-ES/login.js
index d07d65b1..ca4d7d0d 100644
--- a/client/src/locales/es-ES/login.js
+++ b/client/src/locales/es-ES/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Cancelar y cerrar',
continue: 'Continuar',
+ debugSso: 'Depurar SSO',
goBack: 'Volver',
goHome: 'Ir al inicio',
logIn: 'Iniciar sesión',
diff --git a/client/src/locales/et-EE/login.js b/client/src/locales/et-EE/login.js
index 7128832e..52be9e6f 100644
--- a/client/src/locales/et-EE/login.js
+++ b/client/src/locales/et-EE/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Tühista ja sulge',
continue: 'Jätka',
+ debugSso: 'Siluda SSO',
goBack: 'Tagasi',
goHome: 'Koju',
logIn: 'Logi sisse',
diff --git a/client/src/locales/fa-IR/login.js b/client/src/locales/fa-IR/login.js
index ce39d0eb..9edd7e13 100644
--- a/client/src/locales/fa-IR/login.js
+++ b/client/src/locales/fa-IR/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'لغو و بستن',
continue: 'ادامه',
+ debugSso: 'اشکالزدایی SSO',
goBack: 'بازگشت',
goHome: 'رفتن به خانه',
logIn: 'ورود',
diff --git a/client/src/locales/fi-FI/login.js b/client/src/locales/fi-FI/login.js
index 5c69248c..36f2ff95 100644
--- a/client/src/locales/fi-FI/login.js
+++ b/client/src/locales/fi-FI/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Peruuta ja sulje',
continue: 'Jatka',
+ debugSso: 'Korjaa SSO-virheitä',
goBack: 'Takaisin',
goHome: 'Kotiin',
logIn: 'Kirjaudu sisään',
diff --git a/client/src/locales/fr-FR/login.js b/client/src/locales/fr-FR/login.js
index a4410062..f9931820 100644
--- a/client/src/locales/fr-FR/login.js
+++ b/client/src/locales/fr-FR/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Annuler et fermer',
continue: 'Continuer',
+ debugSso: 'Déboguer le SSO',
goBack: 'Retour',
goHome: "Aller à l'accueil",
logIn: 'Se connecter',
diff --git a/client/src/locales/hu-HU/login.js b/client/src/locales/hu-HU/login.js
index da81a03d..9047833f 100644
--- a/client/src/locales/hu-HU/login.js
+++ b/client/src/locales/hu-HU/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Mégse és bezárás',
continue: 'Folytatás',
+ debugSso: 'SSO hibakeresése',
goBack: 'Vissza',
goHome: 'Kezdőlapra',
logIn: 'Belépés',
diff --git a/client/src/locales/id-ID/login.js b/client/src/locales/id-ID/login.js
index 032a75f5..12b5f663 100644
--- a/client/src/locales/id-ID/login.js
+++ b/client/src/locales/id-ID/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Batal dan tutup',
continue: 'Lanjutkan',
+ debugSso: 'Debug SSO',
goBack: 'Kembali',
goHome: 'Ke beranda',
logIn: 'Masuk',
diff --git a/client/src/locales/it-IT/login.js b/client/src/locales/it-IT/login.js
index 608a7b14..c263d41e 100644
--- a/client/src/locales/it-IT/login.js
+++ b/client/src/locales/it-IT/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Annulla e chiudi',
continue: 'Continua',
+ debugSso: 'Debug SSO',
goBack: 'Torna indietro',
goHome: 'Vai alla home',
logIn: 'Accedi',
diff --git a/client/src/locales/ja-JP/login.js b/client/src/locales/ja-JP/login.js
index efc4824a..2d0933b4 100644
--- a/client/src/locales/ja-JP/login.js
+++ b/client/src/locales/ja-JP/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'キャンセルして閉じる',
continue: '続行',
+ debugSso: 'SSOをデバッグ',
goBack: '戻る',
goHome: 'ホームへ',
logIn: 'ログイン',
diff --git a/client/src/locales/ko-KR/login.js b/client/src/locales/ko-KR/login.js
index 1e63164c..972b77fa 100644
--- a/client/src/locales/ko-KR/login.js
+++ b/client/src/locales/ko-KR/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: '취소 후 닫기',
continue: '계속',
+ debugSso: 'SSO 디버그',
goBack: '뒤로 가기',
goHome: '홈으로 가기',
logIn: '로그인',
diff --git a/client/src/locales/nl-NL/login.js b/client/src/locales/nl-NL/login.js
index e36c19b2..d92efb8d 100644
--- a/client/src/locales/nl-NL/login.js
+++ b/client/src/locales/nl-NL/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Annuleren en sluiten',
continue: 'Doorgaan',
+ debugSso: 'SSO debuggen',
goBack: 'Terug gaan',
goHome: 'Naar startpagina',
logIn: 'Inloggen',
diff --git a/client/src/locales/pl-PL/login.js b/client/src/locales/pl-PL/login.js
index 4b4d4bc1..b75809b1 100644
--- a/client/src/locales/pl-PL/login.js
+++ b/client/src/locales/pl-PL/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Anuluj i zamknij',
continue: 'Kontynuuj',
+ debugSso: 'Debuguj SSO',
goBack: 'Wróć',
goHome: 'Idź do domu',
logIn: 'Zaloguj',
diff --git a/client/src/locales/pt-BR/login.js b/client/src/locales/pt-BR/login.js
index effbc3b0..e5611560 100644
--- a/client/src/locales/pt-BR/login.js
+++ b/client/src/locales/pt-BR/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Cancelar e fechar',
continue: 'Continuar',
+ debugSso: 'Depurar SSO',
goBack: 'Voltar',
goHome: 'Ir para início',
logIn: 'Entrar',
diff --git a/client/src/locales/pt-PT/login.js b/client/src/locales/pt-PT/login.js
index 98c64ae1..7b5b47d6 100644
--- a/client/src/locales/pt-PT/login.js
+++ b/client/src/locales/pt-PT/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Cancelar e fechar',
continue: 'Continuar',
+ debugSso: 'Depurar SSO',
goBack: 'Voltar',
goHome: 'Ir para início',
logIn: 'Iniciar sessão',
diff --git a/client/src/locales/ro-RO/login.js b/client/src/locales/ro-RO/login.js
index c18721fc..de4d9a94 100644
--- a/client/src/locales/ro-RO/login.js
+++ b/client/src/locales/ro-RO/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Anulează și închide',
continue: 'Continuă',
+ debugSso: 'Depanează SSO',
goBack: 'Înapoi',
goHome: 'Acasă',
logIn: 'Autentificarea',
diff --git a/client/src/locales/ru-RU/login.js b/client/src/locales/ru-RU/login.js
index 0d1cb159..bfe1c6cb 100644
--- a/client/src/locales/ru-RU/login.js
+++ b/client/src/locales/ru-RU/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Отменить и закрыть',
continue: 'Продолжить',
+ debugSso: 'Отладить SSO',
goBack: 'Назад',
goHome: 'На главную',
logIn: 'Войти',
diff --git a/client/src/locales/sk-SK/login.js b/client/src/locales/sk-SK/login.js
index 7a91103d..788ba4c7 100644
--- a/client/src/locales/sk-SK/login.js
+++ b/client/src/locales/sk-SK/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Zrušiť a zavrieť',
continue: 'Pokračovať',
+ debugSso: 'Ladiť SSO',
goBack: 'Ísť späť',
goHome: 'Ísť domov',
logIn: 'Prihlásiť sa',
diff --git a/client/src/locales/sr-Cyrl-RS/login.js b/client/src/locales/sr-Cyrl-RS/login.js
index eb53fe01..ecffa99c 100644
--- a/client/src/locales/sr-Cyrl-RS/login.js
+++ b/client/src/locales/sr-Cyrl-RS/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Откажи и затвори',
continue: 'Настави',
+ debugSso: 'Дебагуј SSO',
goBack: 'Назад',
goHome: 'Иди кући',
logIn: 'Пријава',
diff --git a/client/src/locales/sr-Latn-RS/login.js b/client/src/locales/sr-Latn-RS/login.js
index 75e2ee86..aca277b7 100644
--- a/client/src/locales/sr-Latn-RS/login.js
+++ b/client/src/locales/sr-Latn-RS/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Otkaži i zatvori',
continue: 'Nastavi',
+ debugSso: 'Debaguj SSO',
goBack: 'Nazad',
goHome: 'Idi kući',
logIn: 'Prijava',
diff --git a/client/src/locales/sv-SE/login.js b/client/src/locales/sv-SE/login.js
index 58743087..dace8adc 100644
--- a/client/src/locales/sv-SE/login.js
+++ b/client/src/locales/sv-SE/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Avbryt och stäng',
continue: 'Fortsätt',
+ debugSso: 'Felsök SSO',
goBack: 'Gå tillbaka',
goHome: 'Gå hem',
logIn: 'Logga in',
diff --git a/client/src/locales/tr-TR/login.js b/client/src/locales/tr-TR/login.js
index 2c78dd41..96c5f3e4 100644
--- a/client/src/locales/tr-TR/login.js
+++ b/client/src/locales/tr-TR/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'İptal et ve kapat',
continue: 'Devam et',
+ debugSso: 'SSO hatalarını ayıkla',
goBack: 'Geri dön',
goHome: 'Ana sayfaya git',
logIn: 'Giriş yap',
diff --git a/client/src/locales/uk-UA/login.js b/client/src/locales/uk-UA/login.js
index 41ee019e..25e09af1 100644
--- a/client/src/locales/uk-UA/login.js
+++ b/client/src/locales/uk-UA/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Скасувати та закрити',
continue: 'Продовжити',
+ debugSso: 'Відлагодити SSO',
goBack: 'Назад',
goHome: 'На головну',
logIn: 'Увійти',
diff --git a/client/src/locales/uz-UZ/login.js b/client/src/locales/uz-UZ/login.js
index 29768cb3..9502c5b1 100644
--- a/client/src/locales/uz-UZ/login.js
+++ b/client/src/locales/uz-UZ/login.js
@@ -26,6 +26,7 @@ export default {
action: {
cancelAndClose: 'Bekor qilish va yopish',
continue: 'Davom etish',
+ debugSso: 'SSO ni tuzatish',
goBack: 'Orqaga',
goHome: 'Bosh sahifaga',
logIn: 'Kirish',
diff --git a/client/src/locales/vi-VN/login.js b/client/src/locales/vi-VN/login.js
index e4de4afc..af2b90c8 100644
--- a/client/src/locales/vi-VN/login.js
+++ b/client/src/locales/vi-VN/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: 'Hủy và đóng',
continue: 'Tiếp tục',
+ debugSso: 'Gỡ lỗi SSO',
goBack: 'Quay lại',
goHome: 'Về trang chủ',
logIn: 'Đăng nhập',
diff --git a/client/src/locales/zh-CN/login.js b/client/src/locales/zh-CN/login.js
index 8c51d33f..6920ad4e 100644
--- a/client/src/locales/zh-CN/login.js
+++ b/client/src/locales/zh-CN/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: '取消并关闭',
continue: '继续',
+ debugSso: '调试SSO',
goBack: '返回',
goHome: '回到首页',
logIn: '登录',
diff --git a/client/src/locales/zh-TW/login.js b/client/src/locales/zh-TW/login.js
index e8147489..b1ef51d6 100644
--- a/client/src/locales/zh-TW/login.js
+++ b/client/src/locales/zh-TW/login.js
@@ -25,6 +25,7 @@ export default {
action: {
cancelAndClose: '取消並關閉',
continue: '繼續',
+ debugSso: '偵錯SSO',
goBack: '返回',
goHome: '回到首頁',
logIn: '登入',
diff --git a/client/src/reducers/ui/authenticate-form.js b/client/src/reducers/ui/authenticate-form.js
index db475252..40f32020 100644
--- a/client/src/reducers/ui/authenticate-form.js
+++ b/client/src/reducers/ui/authenticate-form.js
@@ -16,6 +16,7 @@ const initialState = {
isSubmitting: false,
isSubmittingWithOidc: false,
error: null,
+ debugLogs: null,
pendingToken: null,
step: null,
termsForm: {
@@ -91,6 +92,12 @@ export default (state = initialState, { type, payload }) => {
isSubmittingWithOidc: false,
error: payload.error,
};
+ case ActionTypes.WITH_OIDC_AUTHENTICATE__DEBUG:
+ return {
+ ...state,
+ isSubmittingWithOidc: false,
+ debugLogs: payload.logs,
+ };
case ActionTypes.AUTHENTICATE_ERROR_CLEAR:
return {
...state,
diff --git a/client/src/sagas/login/services/login.js b/client/src/sagas/login/services/login.js
index 40634419..19864681 100644
--- a/client/src/sagas/login/services/login.js
+++ b/client/src/sagas/login/services/login.js
@@ -106,6 +106,20 @@ export function* authenticateWithOidcCallback() {
return;
}
+ const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
+
+ if (oidcBootstrap?.debug) {
+ const {
+ included: { logs },
+ } = yield call(api.debugOidc, {
+ code,
+ nonce,
+ });
+
+ yield put(actions.authenticateWithOidc.debug(logs));
+ return;
+ }
+
let accessToken;
try {
({ item: accessToken } = yield call(api.exchangeForAccessTokenWithOidc, {
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
index 213b2e8c..bf361c86 100644
--- a/docker-compose-dev.yml
+++ b/docker-compose-dev.yml
@@ -77,6 +77,7 @@ services:
# - OIDC_IGNORE_USERNAME=true
# - OIDC_IGNORE_ROLES=true
# - OIDC_ENFORCED=true
+ # - OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)
# These values override and disable configuration in the UI if set.
diff --git a/docker-compose.yml b/docker-compose.yml
index 0ebe7b8c..d50340aa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -95,6 +95,7 @@ services:
# - OIDC_IGNORE_USERNAME=true
# - OIDC_IGNORE_ROLES=true
# - OIDC_ENFORCED=true
+ # - OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)
# These values override and disable configuration in the UI if set.
diff --git a/server/.env.sample b/server/.env.sample
index 736b73ec..68c3ca45 100644
--- a/server/.env.sample
+++ b/server/.env.sample
@@ -68,6 +68,7 @@ SECRET_KEY=notsecretkey
# OIDC_IGNORE_USERNAME=true
# OIDC_IGNORE_ROLES=true
# OIDC_ENFORCED=true
+# OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)
# These values override and disable configuration in the UI if set.
diff --git a/server/api/controllers/access-tokens/debug-oidc.js b/server/api/controllers/access-tokens/debug-oidc.js
new file mode 100644
index 00000000..488eab16
--- /dev/null
+++ b/server/api/controllers/access-tokens/debug-oidc.js
@@ -0,0 +1,223 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+const Errors = {
+ NOT_ENOUGH_RIGHTS: {
+ notEnoughRights: 'Not enough rights',
+ },
+};
+
+module.exports = {
+ inputs: {
+ code: {
+ type: 'string',
+ maxLength: 2048,
+ required: true,
+ },
+ nonce: {
+ type: 'string',
+ maxLength: 1024,
+ required: true,
+ },
+ },
+
+ exits: {
+ notEnoughRights: {
+ responseType: 'forbidden',
+ },
+ },
+
+ async fn(inputs) {
+ if (!sails.config.custom.oidcDebug) {
+ throw Errors.NOT_ENOUGH_RIGHTS;
+ }
+
+ const logs = ['🔐 Starting OIDC debug flow...', ''];
+ const client = await sails.hooks.oidc.getClient();
+
+ if (!client) {
+ logs.push('❌ OIDC client is not initialized.');
+ logs.push('💡 Hint: Check your OIDC issuer and client configuration.');
+
+ return {
+ item: null,
+ included: {
+ logs,
+ },
+ };
+ }
+
+ let tokenSet;
+ try {
+ logs.push('🔄 Exchanging authorization code...');
+
+ if (sails.config.custom.oidcUseOauthCallback) {
+ tokenSet = await client.oauthCallback(
+ sails.config.custom.oidcRedirectUri,
+ {
+ iss: sails.config.custom.oidcIssuer,
+ code: inputs.code,
+ },
+ { nonce: inputs.nonce },
+ );
+ } else {
+ tokenSet = await client.callback(
+ sails.config.custom.oidcRedirectUri,
+ {
+ iss: sails.config.custom.oidcIssuer,
+ code: inputs.code,
+ },
+ { nonce: inputs.nonce },
+ );
+ }
+
+ logs.push('✅ Authorization code exchanged successfully.', '');
+ } catch (error) {
+ logs.push('❌ Failed to exchange authorization code.');
+ logs.push(`💬 Reason: ${error.message || error.toString()}`);
+ logs.push('💡 Hint: Check redirect URI, client secret, and nonce handling.');
+
+ return {
+ item: null,
+ included: {
+ logs,
+ },
+ };
+ }
+
+ if (sails.config.custom.oidcClaimsSource === 'id_token') {
+ logs.push('📥 Extracting claims from ID token...');
+
+ try {
+ claims = tokenSet.claims();
+ logs.push('✅ Claims extracted successfully.', '');
+ } catch (error) {
+ logs.push('❌ Failed to extract user claims.');
+ logs.push(`💬 Reason: ${error.message || error.toString()}`);
+
+ return {
+ item: null,
+ included: {
+ logs,
+ },
+ };
+ }
+ } else {
+ logs.push('📥 Fetching claims from userinfo endpoint...');
+
+ try {
+ claims = await client.userinfo(tokenSet);
+ logs.push('✅ Claims fetched successfully.', '');
+ } catch (error) {
+ logs.push('❌ Failed to fetch user claims.');
+
+ if (error instanceof SyntaxError && error.message.includes('Unexpected token e in JSON')) {
+ logs.push('💬 Reason: Userinfo response is signed or not JSON.');
+ logs.push(
+ '💡 Hint: Try configuring userinfo signed response algorithm or switch to ID token claims.',
+ );
+ } else {
+ logs.push(`💬 Reason: ${error.message || error.toString()}`);
+ }
+
+ return {
+ item: null,
+ included: {
+ logs,
+ },
+ };
+ }
+ }
+
+ logs.push('📦 Raw claims received:', JSON.stringify(claims, null, 2), '');
+ logs.push('🧩 Evaluating claim mappings...', '');
+
+ const mappings = {
+ email: {
+ attribute: sails.config.custom.oidcEmailAttribute,
+ value: _.get(claims, sails.config.custom.oidcEmailAttribute),
+ },
+ name: {
+ attribute: sails.config.custom.oidcNameAttribute,
+ value: _.get(claims, sails.config.custom.oidcNameAttribute),
+ },
+ username: sails.config.custom.oidcIgnoreUsername
+ ? undefined
+ : {
+ attribute: sails.config.custom.oidcUsernameAttribute,
+ value: _.get(claims, sails.config.custom.oidcUsernameAttribute),
+ },
+ roles: sails.config.custom.oidcIgnoreRoles
+ ? undefined
+ : {
+ attribute: sails.config.custom.oidcRolesAttribute,
+ value: _.get(claims, sails.config.custom.oidcRolesAttribute),
+ },
+ };
+
+ logs.push('📋 Mapping result:', JSON.stringify(mappings, null, 2), '');
+
+ if (!mappings.email.value) {
+ logs.push('❌ Email not resolved.');
+ logs.push('💡 Hint: Check email attribute mapping.', '');
+ }
+
+ if (!mappings.name.value) {
+ logs.push('❌ Name not resolved.');
+ logs.push('💡 Hint: Check name attribute mapping.', '');
+ }
+
+ if (!sails.config.custom.oidcIgnoreUsername) {
+ if (!mappings.username.value) {
+ logs.push('⚠️ Username not resolved.');
+ logs.push('💡 Hint: Check username attribute mapping.', '');
+ }
+ }
+
+ if (!sails.config.custom.oidcIgnoreRoles) {
+ if (!Array.isArray(mappings.roles.value) || mappings.roles.value.length === 0) {
+ logs.push('⚠️ Roles not resolved or empty.');
+ logs.push('💡 Hint: Check roles attribute mapping or IdP role configuration.', '');
+ } else {
+ logs.push('🎭 Resolving user role from OIDC roles...');
+
+ // Use a Set here to avoid quadratic time complexity
+ const claimsRolesSet = new Set(mappings.roles.value);
+
+ const foundRole = [User.Roles.ADMIN, User.Roles.PROJECT_OWNER, User.Roles.BOARD_USER].find(
+ (roleItem) => {
+ const configRoles = sails.config.custom[`oidc${_.upperFirst(roleItem)}Roles`];
+
+ if (configRoles.includes('*')) {
+ return true;
+ }
+
+ return configRoles.some((configRole) => claimsRolesSet.has(configRole));
+ },
+ );
+
+ if (foundRole) {
+ logs.push(`✅ Matched user role → ${_.lowerCase(foundRole)}`, '');
+ } else {
+ logs.push('⚠️ No user role matched configured OIDC roles.');
+ logs.push('💡 Hint: Check role matching settings.', '');
+ }
+ }
+ }
+
+ if (mappings.email.value && mappings.name.value) {
+ logs.push('🎉 OIDC debug completed successfully.');
+ } else {
+ logs.push('🛑 OIDC debug detected missing required attributes.');
+ }
+
+ return {
+ item: null,
+ included: {
+ logs,
+ },
+ };
+ },
+};
diff --git a/server/api/controllers/config/test-smtp.js b/server/api/controllers/config/test-smtp.js
index 8e626d5a..05757610 100644
--- a/server/api/controllers/config/test-smtp.js
+++ b/server/api/controllers/config/test-smtp.js
@@ -61,9 +61,8 @@ module.exports = {
return Errors.NOT_AVAILABLE;
}
- const logs = [];
+ const logs = ['📧 Sending test email...'];
try {
- logs.push('📧 Sending test email...');
/* eslint-disable no-underscore-dangle */
const info = await transporter.sendMail({
to: currentUser.email,
@@ -72,16 +71,16 @@ module.exports = {
html: this.req.i18n.__('This is a test html message!'),
});
/* eslint-enable no-underscore-dangle */
- logs.push('✅ Email sent successfully!', '');
+ logs.push('✅ Email sent successfully.', '');
logs.push(`📬 Message ID: ${info.messageId}`);
if (info.response) {
logs.push(`📤 Server response: ${info.response.trim()}`);
}
- logs.push('', '🎉 Your configuration is working correctly!');
+ logs.push('', '🎉 Your configuration is working correctly.');
} catch (error) {
- logs.push('❌ Failed to send email!', '');
+ logs.push('❌ Failed to send email.', '');
if (error.code) {
logs.push(`⚠️ Error code: ${error.code}`);
diff --git a/server/api/helpers/users/get-or-create-one-with-oidc.js b/server/api/helpers/users/get-or-create-one-with-oidc.js
index 69ea93e5..4d9298ce 100644
--- a/server/api/helpers/users/get-or-create-one-with-oidc.js
+++ b/server/api/helpers/users/get-or-create-one-with-oidc.js
@@ -106,6 +106,7 @@ module.exports = {
if (configRoles.includes('*')) {
return true;
}
+
return configRoles.some((configRole) => claimsRolesSet.has(configRole));
},
);
diff --git a/server/api/hooks/oidc/index.js b/server/api/hooks/oidc/index.js
index b0fde749..73a1dce2 100644
--- a/server/api/hooks/oidc/index.js
+++ b/server/api/hooks/oidc/index.js
@@ -89,11 +89,16 @@ module.exports = function defineOidcHook(sails) {
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
}
- return {
+ const bootstrap = {
authorizationUrl: instance.authorizationUrl(authorizationUrlParams),
endSessionUrl: instance.issuer.end_session_endpoint ? instance.endSessionUrl({}) : null,
isEnforced: sails.config.custom.oidcEnforced,
};
+ if (sails.config.custom.oidcDebug) {
+ bootstrap.debug = true;
+ }
+
+ return bootstrap;
},
isEnabled() {
diff --git a/server/config/custom.js b/server/config/custom.js
index 71424be7..6f8bfc59 100644
--- a/server/config/custom.js
+++ b/server/config/custom.js
@@ -94,6 +94,7 @@ module.exports.custom = {
oidcIgnoreUsername: process.env.OIDC_IGNORE_USERNAME === 'true',
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
oidcEnforced: process.env.OIDC_ENFORCED === 'true',
+ oidcDebug: process.env.OIDC_DEBUG === 'true',
// TODO: move client base url to environment variable?
oidcRedirectUri: `${
diff --git a/server/config/policies.js b/server/config/policies.js
index 8c6f38cb..8c2879b4 100644
--- a/server/config/policies.js
+++ b/server/config/policies.js
@@ -48,6 +48,7 @@ module.exports.policies = {
'terms/show': true,
'access-tokens/create': true,
'access-tokens/exchange-with-oidc': true,
+ 'access-tokens/debug-oidc': true,
'access-tokens/accept-terms': true,
'access-tokens/revoke-pending-token': true,
};
diff --git a/server/config/routes.js b/server/config/routes.js
index 0e9b1fd7..e65ec4b3 100644
--- a/server/config/routes.js
+++ b/server/config/routes.js
@@ -81,6 +81,7 @@ module.exports.routes = {
'POST /api/access-tokens': 'access-tokens/create',
'POST /api/access-tokens/exchange-with-oidc': 'access-tokens/exchange-with-oidc',
+ 'POST /api/access-tokens/debug-oidc': 'access-tokens/debug-oidc',
'POST /api/access-tokens/accept-terms': 'access-tokens/accept-terms',
'POST /api/access-tokens/revoke-pending-token': 'access-tokens/revoke-pending-token',
'DELETE /api/access-tokens/me': 'access-tokens/delete',