diff --git a/README.md b/README.md index 67aef1f959..5007ddf2ab 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ You can already use the current state, and extend it with your needs. - [x] Apple - [x] Generic OIDC - [x] Generic OAuth - - [ ] Generic JWT + - [x] Generic JWT - [ ] LDAP - [ ] SAML SP - Multifactor Registration an Login @@ -73,7 +73,7 @@ You can already use the current state, and extend it with your needs. - [x] TOTP - [x] OTP: Email Code - [x] OTP: SMS Code -- [ ] Password Change/Reset +- [x] Password Change/Reset - [x] Domain Discovery - [x] Branding - OIDC Standard diff --git a/apps/login/locales/de.json b/apps/login/locales/de.json index 8b3d4b311e..9da622340b 100644 --- a/apps/login/locales/de.json +++ b/apps/login/locales/de.json @@ -8,6 +8,17 @@ "addAnother": "Ein weiteres Konto hinzufügen", "noResults": "Keine Konten gefunden" }, + "logout": { + "title": "Logout", + "description": "Wählen Sie den Account aus, das Sie entfernen möchten", + "noResults": "Keine Konten gefunden", + "clear": "Session beenden", + "verifiedAt": "Zuletzt aktiv: {time}", + "success": { + "title": "Logout erfolgreich", + "description": "Sie haben sich erfolgreich abgemeldet." + } + }, "loginname": { "title": "Willkommen zurück!", "description": "Geben Sie Ihre Anmeldedaten ein.", diff --git a/apps/login/locales/en.json b/apps/login/locales/en.json index daaaeba108..37a1b62289 100644 --- a/apps/login/locales/en.json +++ b/apps/login/locales/en.json @@ -8,6 +8,17 @@ "addAnother": "Add another account", "noResults": "No accounts found" }, + "logout": { + "title": "Logout", + "description": "Click an account to end the session", + "noResults": "No accounts found", + "clear": "End Session", + "verifiedAt": "Last active: {time}", + "success": { + "title": "Logout successful", + "description": "You have successfully logged out." + } + }, "loginname": { "title": "Welcome back!", "description": "Enter your login data.", diff --git a/apps/login/locales/es.json b/apps/login/locales/es.json index b7dd57b4c0..8969618c67 100644 --- a/apps/login/locales/es.json +++ b/apps/login/locales/es.json @@ -8,6 +8,17 @@ "addAnother": "Agregar otra cuenta", "noResults": "No se encontraron cuentas" }, + "logout": { + "title": "Cerrar sesión", + "description": "Selecciona la cuenta que deseas eliminar", + "noResults": "No se encontraron cuentas", + "clear": "Eliminar sesión", + "verifiedAt": "Última actividad: {time}", + "success": { + "title": "Cierre de sesión exitoso", + "description": "Has cerrado sesión correctamente." + } + }, "loginname": { "title": "¡Bienvenido de nuevo!", "description": "Introduce tus datos de acceso.", diff --git a/apps/login/locales/it.json b/apps/login/locales/it.json index f476da3402..83fc5f3bfc 100644 --- a/apps/login/locales/it.json +++ b/apps/login/locales/it.json @@ -8,6 +8,17 @@ "addAnother": "Aggiungi un altro account", "noResults": "Nessun account trovato" }, + "logout": { + "title": "Esci", + "description": "Seleziona l'account che desideri uscire", + "noResults": "Nessun account trovato", + "clear": "Elimina sessione", + "verifiedAt": "Ultima attività: {time}", + "success": { + "title": "Uscita riuscita", + "description": "Hai effettuato l'uscita con successo." + } + }, "loginname": { "title": "Bentornato!", "description": "Inserisci i tuoi dati di accesso.", diff --git a/apps/login/locales/pl.json b/apps/login/locales/pl.json index 4dd607f3cb..ad9f5d9a65 100644 --- a/apps/login/locales/pl.json +++ b/apps/login/locales/pl.json @@ -8,6 +8,17 @@ "addAnother": "Dodaj kolejne konto", "noResults": "Nie znaleziono kont" }, + "logout": { + "title": "Wyloguj się", + "description": "Wybierz konto, które chcesz usunąć", + "noResults": "Nie znaleziono kont", + "clear": "Usuń sesję", + "verifiedAt": "Ostatnia aktywność: {time}", + "success": { + "title": "Wylogowanie udane", + "description": "Pomyślnie się wylogowałeś." + } + }, "loginname": { "title": "Witamy ponownie!", "description": "Wprowadź dane logowania.", diff --git a/apps/login/locales/ru.json b/apps/login/locales/ru.json index e8bbac212b..73b0810e93 100644 --- a/apps/login/locales/ru.json +++ b/apps/login/locales/ru.json @@ -8,6 +8,17 @@ "addAnother": "Добавить другой аккаунт", "noResults": "Аккаунты не найдены" }, + "logout": { + "title": "Выход", + "description": "Выберите аккаунт, который хотите удалить", + "noResults": "Аккаунты не найдены", + "clear": "Удалить сессию", + "verifiedAt": "Последняя активность: {time}", + "success": { + "title": "Выход выполнен успешно", + "description": "Вы успешно вышли из системы." + } + }, "loginname": { "title": "С возвращением!", "description": "Введите свои данные для входа.", diff --git a/apps/login/locales/zh.json b/apps/login/locales/zh.json index 7bc4ecf68a..bba15c62dd 100644 --- a/apps/login/locales/zh.json +++ b/apps/login/locales/zh.json @@ -8,6 +8,17 @@ "addAnother": "添加另一个账户", "noResults": "未找到账户" }, + "logout": { + "title": "注销", + "description": "选择您想注销的账户", + "noResults": "未找到账户", + "clear": "注销会话", + "verifiedAt": "最后活动时间:{time}", + "success": { + "title": "注销成功", + "description": "您已成功注销。" + } + }, "loginname": { "title": "欢迎回来!", "description": "请输入您的登录信息。", diff --git a/apps/login/src/app/(login)/logout/page.tsx b/apps/login/src/app/(login)/logout/page.tsx new file mode 100644 index 0000000000..3540845b12 --- /dev/null +++ b/apps/login/src/app/(login)/logout/page.tsx @@ -0,0 +1,84 @@ +import { DynamicTheme } from "@/components/dynamic-theme"; +import { SessionsClearList } from "@/components/sessions-clear-list"; +import { getAllSessionCookieIds } from "@/lib/cookies"; +import { getServiceUrlFromHeaders } from "@/lib/service-url"; +import { + getBrandingSettings, + getDefaultOrg, + listSessions, +} from "@/lib/zitadel"; +import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb"; +import { getLocale, getTranslations } from "next-intl/server"; +import { headers } from "next/headers"; + +async function loadSessions({ serviceUrl }: { serviceUrl: string }) { + const ids: (string | undefined)[] = await getAllSessionCookieIds(); + + if (ids && ids.length) { + const response = await listSessions({ + serviceUrl, + ids: ids.filter((id) => !!id) as string[], + }); + return response?.sessions ?? []; + } else { + console.info("No session cookie found."); + return []; + } +} + +export default async function Page(props: { + searchParams: Promise>; +}) { + const searchParams = await props.searchParams; + const locale = getLocale(); + const t = await getTranslations({ locale, namespace: "logout" }); + + const organization = searchParams?.organization; + const postLogoutRedirectUri = searchParams?.post_logout_redirect_uri; + const logoutHint = searchParams?.logout_hint; + const UILocales = searchParams?.ui_locales; // TODO implement with new translation service + + const _headers = await headers(); + const { serviceUrl } = getServiceUrlFromHeaders(_headers); + + let defaultOrganization; + if (!organization) { + const org: Organization | null = await getDefaultOrg({ + serviceUrl, + }); + if (org) { + defaultOrganization = org.id; + } + } + + let sessions = await loadSessions({ serviceUrl }); + + const branding = await getBrandingSettings({ + serviceUrl, + organization: organization ?? defaultOrganization, + }); + + const params = new URLSearchParams(); + + if (organization) { + params.append("organization", organization); + } + + return ( + +
+

{t("title")}

+

{t("description")}

+ +
+ +
+
+
+ ); +} diff --git a/apps/login/src/app/(login)/logout/success/page.tsx b/apps/login/src/app/(login)/logout/success/page.tsx new file mode 100644 index 0000000000..da98092852 --- /dev/null +++ b/apps/login/src/app/(login)/logout/success/page.tsx @@ -0,0 +1,41 @@ +import { DynamicTheme } from "@/components/dynamic-theme"; +import { getServiceUrlFromHeaders } from "@/lib/service-url"; +import { getBrandingSettings, getDefaultOrg } from "@/lib/zitadel"; +import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb"; +import { getLocale, getTranslations } from "next-intl/server"; +import { headers } from "next/headers"; + +export default async function Page(props: { searchParams: Promise }) { + const searchParams = await props.searchParams; + const locale = getLocale(); + const t = await getTranslations({ locale, namespace: "logout" }); + + const _headers = await headers(); + const { serviceUrl } = getServiceUrlFromHeaders(_headers); + + const { login_hint, organization } = searchParams; + + let defaultOrganization; + if (!organization) { + const org: Organization | null = await getDefaultOrg({ + serviceUrl, + }); + if (org) { + defaultOrganization = org.id; + } + } + + const branding = await getBrandingSettings({ + serviceUrl, + organization, + }); + + return ( + +
+

{t("success.title")}

+

{t("success.description")}

+
+
+ ); +} diff --git a/apps/login/src/app/(login)/verify/success/page.tsx b/apps/login/src/app/(login)/verify/success/page.tsx index 678687a7f6..1668e2e3fd 100644 --- a/apps/login/src/app/(login)/verify/success/page.tsx +++ b/apps/login/src/app/(login)/verify/success/page.tsx @@ -1,35 +1,16 @@ import { DynamicTheme } from "@/components/dynamic-theme"; import { UserAvatar } from "@/components/user-avatar"; -import { getSessionCookieById } from "@/lib/cookies"; import { getServiceUrlFromHeaders } from "@/lib/service-url"; import { loadMostRecentSession } from "@/lib/session"; import { getBrandingSettings, getLoginSettings, - getSession, getUserByID, } from "@/lib/zitadel"; import { HumanUser, User } from "@zitadel/proto/zitadel/user/v2/user_pb"; import { getLocale, getTranslations } from "next-intl/server"; import { headers } from "next/headers"; -async function loadSessionById( - serviceUrl: string, - sessionId: string, - organization?: string, -) { - const recent = await getSessionCookieById({ sessionId, organization }); - return getSession({ - serviceUrl, - sessionId: recent.id, - sessionToken: recent.token, - }).then((response) => { - if (response?.session) { - return response.session; - } - }); -} - export default async function Page(props: { searchParams: Promise }) { const searchParams = await props.searchParams; const locale = getLocale(); diff --git a/apps/login/src/components/language-switcher.tsx b/apps/login/src/components/language-switcher.tsx index 8d68248c4a..67b54e58e3 100644 --- a/apps/login/src/components/language-switcher.tsx +++ b/apps/login/src/components/language-switcher.tsx @@ -43,7 +43,7 @@ export function LanguageSwitcher() { > {selected.name}