diff --git a/README.md b/README.md index 274c9eb..db8adbc 100644 --- a/README.md +++ b/README.md @@ -67,3 +67,9 @@ If you want to add new pages, there are some things to be aware of. If the browser language is supported, it will be loaded automatically. Otherwise, English is loaded by default. To add new languages, the corresponding file must be added under `static/login/resources/locales`. Then the language must be added to `SUPPORT_LOCALES` in `functions/i18n.ts`. + +### Credits + +Special thanks to the following projects, without which the development of keycloak-modern-login would not be possible: + +- [Keycloakify](https://github.com/keycloakify/keycloakify), from which the [`ftl_object_to_js_code_declaring_an_object`](src/static/login/baselayout.ftl) function and [Keycloak context type definitions](src/types/context.ts) are derived. \ No newline at end of file diff --git a/package.json b/package.json index ed285a0..1167101 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "webpack-cli": "^4.9.1" }, "dependencies": { - "moment": "^2.29.4", "rfc4648": "^1.5.2", "vue": "^3.2.26", "vue-i18n": "9" diff --git a/src/functions/i18n.ts b/src/functions/i18n.ts index 3c3de68..009de7a 100644 --- a/src/functions/i18n.ts +++ b/src/functions/i18n.ts @@ -1,39 +1,39 @@ -import { nextTick } from "vue" -import { createI18n } from "vue-i18n" +import { nextTick } from "vue"; +import { createI18n } from "vue-i18n"; import { KcContextBase } from "~/types/context"; -export const SUPPORT_LOCALES = ["en", "de"] +export const SUPPORT_LOCALES = ["en", "de"]; export function setupI18n(options = {}) { - let browserLocale = navigator.language.split("-")[0]; - let defaultLocale = "en"; - if (SUPPORT_LOCALES.includes(browserLocale)) { - defaultLocale = browserLocale; - } + const browserLocale = navigator.language.split("-")[0]; + let defaultLocale = "en"; + if (SUPPORT_LOCALES.includes(browserLocale)) { + defaultLocale = browserLocale; + } - const i18n = createI18n(options) - setI18nLanguage(i18n, defaultLocale) - return i18n + const i18n = createI18n(options); + setI18nLanguage(i18n, defaultLocale); + return i18n; +} + +export function setI18nLanguage(i18n, locale) { + loadLocaleMessages(i18n, locale); + if (i18n.mode === "legacy") { + i18n.global.locale = locale; + } else { + i18n.global.locale.value = locale; } - - export function setI18nLanguage(i18n, locale) { - loadLocaleMessages(i18n, locale); - if (i18n.mode === 'legacy') { - i18n.global.locale = locale - } else { - i18n.global.locale.value = locale - } - } - - export async function loadLocaleMessages(i18n, locale) { - // load locale messages with dynamic import - let context = (window as any).kcContext as KcContextBase.Common; - const messages = await import( - /* webpackIgnore: true */`${context.url.resourcesPath}/locales/${locale}.js` - ) - - // set locale and locale message - i18n.global.setLocaleMessage(locale, messages.content) - - return nextTick() - } \ No newline at end of file +} + +export async function loadLocaleMessages(i18n, locale) { + // load locale messages with dynamic import + const context = (window as any).kcContext as KcContextBase.Common; + const messages = await import( + /* webpackIgnore: true */ `${context.url.resourcesPath}/locales/${locale}.js` + ); + + // set locale and locale message + i18n.global.setLocaleMessage(locale, messages.content); + + return nextTick(); +} diff --git a/src/static/login/baselayout.ftl b/src/static/login/baselayout.ftl index 1c9c145..ef75886 100644 --- a/src/static/login/baselayout.ftl +++ b/src/static/login/baselayout.ftl @@ -1,3 +1,26 @@ +<#-- +MIT License + +Copyright (c) 2020 GitHub user u/garronej + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--> <#function are_same_path path searchedPath> <#if path?size != searchedPath?size> <#return false> @@ -69,9 +92,12 @@ key == "identityProviderBrokerCtx" && are_same_path(path, []) && ["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId) - ) || ( + ) || ( ["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) && are_same_path(path, ["realm"]) + ) || ( + ["password"]?seq_contains(key) && + are_same_path(path, ["login"]) ) > <#continue> diff --git a/src/static/login/resources/locales/de.js b/src/static/login/resources/locales/de.js index 603a57b..31d612f 100644 --- a/src/static/login/resources/locales/de.js +++ b/src/static/login/resources/locales/de.js @@ -1,37 +1,40 @@ export const content = { - "login": { - "title": "Mit Ihrem Konto anmelden", - "noAccount": "Sie haben noch kein Konto?", - "signupLink": "Jetzt registrieren.", - "username": "Benutzername", - "usernameOrEmail": "Benutzername oder E-Mail-Adresse", - "password": "Passwort", - "forgotPassword": "Passwort vergessen?", - "rememberMe": "Angemeldet bleiben", - "login": "Anmelden", - "rety": "Erneut versuchen", - "welcome": "Willkommen!" - }, - "2fa": { - "selectFactor": "Bitte wählen Sie einen zweiten Faktor aus:", - "hwSecKey": "Hardware Sicherheits-Schlüssel", - "hwSecKeyDesc": "Verwenden Sie ein WebAuthn kompatibles Gerät", - "otp": "Authenticator App", - "otpDesc": "Verwenden Sie einen Einmalcode aus ihrem Authenticator", - "recoveryCode": "Wiederherstellungsschlüssel", - "recoveryCodeDesc": "Verwenden Sie einen ihrer Wiederherstellungsschlüssel", - "tryAnotherWay": "Eine andere Option nutzen." - }, - "webauthn": { - "title": "Bitte verwenden Sie eines ihrer Geräte, um fortzufahren:", - "registered": "Registriert: {date}", - "authenticate": "Fortfahren", - "noDevice": "Kein Gerät zur Hand?", - "noSupport": "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung.", - "noSupportOtherMethod": "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung, oder wählen Sie eine andere Anmeldeoption.", - "error": "Bei der Anmeldung mit ihrem Gerät ist etwas schief gelaufen. Bitte versuchen Sie es erneut, oder wählen Sie eine andere Anmeldeoption." - }, - "redirect": { - "message": "Sie werden weitergeleitet..." - } -} \ No newline at end of file + login: { + title: "Mit Ihrem Konto anmelden", + noAccount: "Sie haben noch kein Konto?", + signupLink: "Jetzt registrieren.", + username: "Benutzername", + usernameOrEmail: "Benutzername oder E-Mail-Adresse", + password: "Passwort", + forgotPassword: "Passwort vergessen?", + rememberMe: "Angemeldet bleiben", + login: "Anmelden", + rety: "Erneut versuchen", + welcome: "Willkommen!", + }, + "2fa": { + selectFactor: "Bitte wählen Sie einen zweiten Faktor aus:", + hwSecKey: "Hardware Sicherheits-Schlüssel", + hwSecKeyDesc: "Verwenden Sie ein WebAuthn kompatibles Gerät", + otp: "Authenticator App", + otpDesc: "Verwenden Sie einen Einmalcode aus ihrem Authenticator", + recoveryCode: "Wiederherstellungsschlüssel", + recoveryCodeDesc: "Verwenden Sie einen ihrer Wiederherstellungsschlüssel", + tryAnotherWay: "Eine andere Option nutzen.", + }, + webauthn: { + title: "Bitte verwenden Sie eines ihrer Geräte, um fortzufahren:", + registered: "Registriert: {date}", + authenticate: "Fortfahren", + noDevice: "Kein Gerät zur Hand?", + noSupport: + "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung.", + noSupportOtherMethod: + "Es scheint, als würde ihr Browser WebAuthn nicht unterstützen. Bitte nutzen Sie einen anderen Browser für die Anmeldung, oder wählen Sie eine andere Anmeldeoption.", + error: + "Bei der Anmeldung mit ihrem Gerät ist etwas schief gelaufen. Bitte versuchen Sie es erneut, oder wählen Sie eine andere Anmeldeoption.", + }, + redirect: { + message: "Sie werden weitergeleitet...", + }, +}; diff --git a/src/static/login/resources/locales/en.js b/src/static/login/resources/locales/en.js index b9fb604..5eca53b 100644 --- a/src/static/login/resources/locales/en.js +++ b/src/static/login/resources/locales/en.js @@ -1,37 +1,40 @@ export const content = { - "login": { - "title": "Login to your account", - "noAccount": "Don't have an account yet?", - "signupLink": "Sign up.", - "username": "Username", - "usernameOrEmail": "Email or Username", - "password": "Password", - "forgotPassword": "Forgot Password?", - "rememberMe": "Remember me", - "login": "Login", - "rety": "Retry", - "welcome": "Welcome!" - }, - "2fa": { - "selectFactor": "Please select a second factor you would like to use:", - "hwSecKey": "Hardware Security Key", - "hwSecKeyDesc": "Authenticate using a WebAuthn capable device", - "otp": "Authenticator App", - "otpDesc": "Authenticate using a one time code from your authenticator app", - "recoveryCode": "Recovery Code", - "recoveryCodeDesc": "Authenticate using one of your recovery codes", - "tryAnotherWay": "Try another method." - }, - "webauthn": { - "title": "Please use one of your registered devices to continue:", - "registered": "Registered: {date}", - "authenticate": "Continue", - "noDevice": "Don't have your device at hand?", - "noSupport": "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser.", - "noSupportOtherMethod": "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser, or try a different login method using the button below.", - "error": "Something went wrong during authentication using your device. Please try again, or use a different login method." - }, - "redirect": { - "message": "Redirecting..." - } -} \ No newline at end of file + login: { + title: "Login to your account", + noAccount: "Don't have an account yet?", + signupLink: "Sign up.", + username: "Username", + usernameOrEmail: "Email or Username", + password: "Password", + forgotPassword: "Forgot Password?", + rememberMe: "Remember me", + login: "Login", + rety: "Retry", + welcome: "Welcome!", + }, + "2fa": { + selectFactor: "Please select a second factor you would like to use:", + hwSecKey: "Hardware Security Key", + hwSecKeyDesc: "Authenticate using a WebAuthn capable device", + otp: "Authenticator App", + otpDesc: "Authenticate using a one time code from your authenticator app", + recoveryCode: "Recovery Code", + recoveryCodeDesc: "Authenticate using one of your recovery codes", + tryAnotherWay: "Try another method.", + }, + webauthn: { + title: "Please use one of your registered devices to continue:", + registered: "Registered: {date}", + authenticate: "Continue", + noDevice: "Don't have your device at hand?", + noSupport: + "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser.", + noSupportOtherMethod: + "It seems that your browser doesn't support WebAuthn. Please try logging in with a different browser, or try a different login method using the button below.", + error: + "Something went wrong during authentication using your device. Please try again, or use a different login method.", + }, + redirect: { + message: "Redirecting...", + }, +}; diff --git a/src/types/context.ts b/src/types/context.ts index 348afbb..74191b8 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -1,4 +1,29 @@ /* eslint-disable */ + +/** + * MIT License + * + * Copyright (c) 2020 GitHub user u/garronej + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + /** Take theses type definition with a grain of salt. * Some values might be undefined on some pages. * (ex: url.loginAction is undefined on error.ftl) diff --git a/src/views/login/index.ts b/src/views/login/index.ts index f75dca7..a709dfe 100644 --- a/src/views/login/index.ts +++ b/src/views/login/index.ts @@ -4,6 +4,6 @@ import { setupI18n } from "~/functions/i18n"; if ((window as any).kcContext) { const app = createApp(index); - app.use(setupI18n()) + app.use(setupI18n()); app.mount("#app"); } diff --git a/src/views/login/index.vue b/src/views/login/index.vue index e6dc5e3..eda0df3 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -22,11 +22,14 @@ method="post">
- + - +