mirror of
https://github.com/lukin/keywind.git
synced 2025-01-10 18:06:23 +00:00
ui(totp): configure and login otp
- added configure and login otp pages
This commit is contained in:
parent
54e9c33abb
commit
4cff8b30c8
11 changed files with 246 additions and 16 deletions
|
@ -1,6 +1,6 @@
|
||||||
<#macro kw component="span" rest...>
|
<#macro kw component="span" rest...>
|
||||||
<${component}
|
<${component}
|
||||||
class="absolute left-0 ml-3 text-lg"
|
class="ml-3 text-lg text-primary-600"
|
||||||
<#list rest as attrName, attrValue>
|
<#list rest as attrName, attrValue>
|
||||||
${attrName}="${attrValue}"
|
${attrName}="${attrValue}"
|
||||||
</#list>
|
</#list>
|
||||||
|
|
10
theme/keywind/login/components/button/secondary.ftl
Normal file
10
theme/keywind/login/components/button/secondary.ftl
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<#macro kw component="button" rest...>
|
||||||
|
<${component}
|
||||||
|
class="bg-transparent flex justify-center px-4 py-3 relative rounded-lg font-bold text-gray-600 w-full focus:outline-none hover:text-black"
|
||||||
|
<#list rest as attrName, attrValue>
|
||||||
|
${attrName}="${attrValue}"
|
||||||
|
</#list>
|
||||||
|
>
|
||||||
|
<#nested>
|
||||||
|
</${component}>
|
||||||
|
</#macro>
|
5
theme/keywind/login/components/label/totp.ftl
Normal file
5
theme/keywind/login/components/label/totp.ftl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<#macro kw>
|
||||||
|
<#compress>
|
||||||
|
${msg("authenticatorCode")} *
|
||||||
|
</#compress>
|
||||||
|
</#macro>
|
5
theme/keywind/login/components/label/userdevice.ftl
Normal file
5
theme/keywind/login/components/label/userdevice.ftl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<#macro kw>
|
||||||
|
<#compress>
|
||||||
|
${msg("loginTotpDeviceName")} <#if totp.otpCredentials?size gte 1>*</#if>
|
||||||
|
</#compress>
|
||||||
|
</#macro>
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
<#macro kw>
|
<#macro kw>
|
||||||
<#nested "show-username">
|
<#nested "show-username">
|
||||||
<div class="mb-4">
|
<div class="mb-4 flex items-center justify-center">
|
||||||
<div class="font-bold mb-2 text-center">${auth.attemptedUsername}</div>
|
<div class="font-bold text-center">${auth.attemptedUsername}</div>
|
||||||
<@buttonPrimary.kw component="a" href="${url.loginRestartFlowUrl}">
|
<@buttonIcon.kw
|
||||||
<@buttonIcon.kw>
|
component="a"
|
||||||
|
href="${url.loginRestartFlowUrl}"
|
||||||
|
title="${msg('restartLoginTooltip')}"
|
||||||
|
>
|
||||||
<@iconExternalLink.kw />
|
<@iconExternalLink.kw />
|
||||||
</@buttonIcon.kw>
|
</@buttonIcon.kw>
|
||||||
${msg("restartLoginTooltip")}
|
|
||||||
</@buttonPrimary.kw>
|
|
||||||
</div>
|
</div>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
18
theme/keywind/login/components/radio/primary.ftl
Normal file
18
theme/keywind/login/components/radio/primary.ftl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<#macro kw tabIndex id checked=false rest...>
|
||||||
|
<input
|
||||||
|
<#if checked>checked</#if>
|
||||||
|
class="focus:ring-primary-500 h-4 w-4 text-primary-600 border-gray-300"
|
||||||
|
type="radio"
|
||||||
|
id="${id}"
|
||||||
|
<#list rest as attrName, attrValue>
|
||||||
|
${attrName}="${attrValue}"
|
||||||
|
</#list>
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="ml-1 text-sm font-medium mr-4"
|
||||||
|
for="${id}"
|
||||||
|
tabindex="${tabIndex}"
|
||||||
|
>
|
||||||
|
<#nested>
|
||||||
|
</label>
|
||||||
|
</#macro>
|
129
theme/keywind/login/login-config-totp.ftl
Normal file
129
theme/keywind/login/login-config-totp.ftl
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<#import "components/button/primary.ftl" as buttonPrimary>
|
||||||
|
<#import "components/button/secondary.ftl" as buttonSecondary>
|
||||||
|
<#import "components/input/primary.ftl" as inputPrimary>
|
||||||
|
<#import "components/label/totp.ftl" as labelTotp>
|
||||||
|
<#import "components/label/userdevice.ftl" as labelUserDevice>
|
||||||
|
<#import "components/link/primary.ftl" as linkPrimary>
|
||||||
|
|
||||||
|
<@layout.registrationLayout
|
||||||
|
displayRequiredFields=false
|
||||||
|
displayMessage=!messagesPerField.existsError('totp','userLabel')
|
||||||
|
;
|
||||||
|
section
|
||||||
|
>
|
||||||
|
<#if section="header">
|
||||||
|
${msg("loginTotpTitle")}
|
||||||
|
<#elseif section="form">
|
||||||
|
<ol class="list-decimal pl-4 space-y-2">
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep1")}</p>
|
||||||
|
|
||||||
|
<ul class="ml-6 list-disc space-y-1">
|
||||||
|
<#list totp.policy.supportedApplications as app>
|
||||||
|
<li>${app}</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<#if mode?? && mode = "manual">
|
||||||
|
<li class="pt-2">
|
||||||
|
<p>${msg("loginTotpManualStep2")}</p>
|
||||||
|
<p class="font-bold text-xl">${totp.totpSecretEncoded}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<@linkPrimary.kw href=totp.qrUrl>
|
||||||
|
${msg("loginTotpScanBarcode")}
|
||||||
|
</@linkPrimary.kw>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpManualStep3")}</p>
|
||||||
|
|
||||||
|
<ul class="ml-6 list-disc space-y-2">
|
||||||
|
<li>${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}</li>
|
||||||
|
<li>${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}</li>
|
||||||
|
<li>${msg("loginTotpDigits")}: ${totp.policy.digits}</li>
|
||||||
|
<#if totp.policy.type = "totp">
|
||||||
|
<li>${msg("loginTotpInterval")}: ${totp.policy.period}</li>
|
||||||
|
<#elseif totp.policy.type = "hotp">
|
||||||
|
<li>${msg("loginTotpCounter")}: ${totp.policy.initialCounter}</li>
|
||||||
|
</#if>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<#else>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep2")}</p>
|
||||||
|
|
||||||
|
<img class="text-center mx-auto" src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
|
||||||
|
<p>
|
||||||
|
<@linkPrimary.kw href=totp.manualUrl>
|
||||||
|
${msg("loginTotpUnableToScan")}
|
||||||
|
</@linkPrimary.kw>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</#if>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep3")}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep3DeviceName")}</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
|
||||||
|
<div>
|
||||||
|
<@inputPrimary.kw
|
||||||
|
autocomplete="off"
|
||||||
|
autofocus=true
|
||||||
|
invalid=["totp"]
|
||||||
|
name="totp"
|
||||||
|
type="text"
|
||||||
|
id="totp"
|
||||||
|
>
|
||||||
|
<@labelTotp.kw />
|
||||||
|
</@inputPrimary.kw>
|
||||||
|
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
||||||
|
<#if mode??><input type="hidden" id="mode" name="mode" value="${mode}"/></#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<@inputPrimary.kw
|
||||||
|
autocomplete="off"
|
||||||
|
autofocus=true
|
||||||
|
invalid=["userLabel"]
|
||||||
|
name="userLabel"
|
||||||
|
type="text"
|
||||||
|
id="totp"
|
||||||
|
>
|
||||||
|
<@labelUserDevice.kw />
|
||||||
|
</@inputPrimary.kw>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<#if isAppInitiatedAction??>
|
||||||
|
<div class="py-2 flex flex-col space-y-1">
|
||||||
|
<@buttonPrimary.kw
|
||||||
|
type="submit"
|
||||||
|
value=msg("doSubmit")
|
||||||
|
>
|
||||||
|
${msg("doSubmit")}
|
||||||
|
</@buttonPrimary.kw>
|
||||||
|
|
||||||
|
<@buttonSecondary.kw
|
||||||
|
type="submit"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
${msg("doCancel")}
|
||||||
|
</@buttonSecondary.kw>
|
||||||
|
</div>
|
||||||
|
<#else>
|
||||||
|
<div class="py-2">
|
||||||
|
<@buttonPrimary.kw
|
||||||
|
type="submit"
|
||||||
|
value=msg("doSubmit")
|
||||||
|
>
|
||||||
|
${msg("doSubmit")}
|
||||||
|
</@buttonPrimary.kw>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
63
theme/keywind/login/login-otp.ftl
Normal file
63
theme/keywind/login/login-otp.ftl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<#import "components/button/primary.ftl" as buttonPrimary>
|
||||||
|
<#import "components/input/primary.ftl" as inputPrimary>
|
||||||
|
<#import "components/radio/primary.ftl" as radioPrimary>
|
||||||
|
<#import "components/label/totp.ftl" as labelTotp>
|
||||||
|
<#import "components/link/secondary.ftl" as linkSecondary>
|
||||||
|
|
||||||
|
<@layout.registrationLayout
|
||||||
|
displayMessage=!messagesPerField.existsError('totp')
|
||||||
|
;
|
||||||
|
section
|
||||||
|
>
|
||||||
|
<#if section="header">
|
||||||
|
${msg("doLogIn")}
|
||||||
|
<#elseif section="form">
|
||||||
|
<form
|
||||||
|
action="${url.loginAction}"
|
||||||
|
method="post"
|
||||||
|
class="m-0 space-y-4"
|
||||||
|
>
|
||||||
|
<#if otpLogin.userOtpCredentials?size gt 1>
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="flex flex-wrap items-center">
|
||||||
|
<#list otpLogin.userOtpCredentials as otpCredential>
|
||||||
|
<@radioPrimary.kw
|
||||||
|
tabIndex="${otpCredential?index}"
|
||||||
|
id="kc-otp-credential-${otpCredential?index}"
|
||||||
|
name="selectedCredentialId"
|
||||||
|
value="${otpCredential.id}"
|
||||||
|
checked=(otpCredential.id == otpLogin.selectedCredentialId)
|
||||||
|
>
|
||||||
|
${otpCredential.userLabel}
|
||||||
|
</@radioPrimary.kw>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<div class="py-2">
|
||||||
|
<@inputPrimary.kw
|
||||||
|
autocomplete="off"
|
||||||
|
autofocus=true
|
||||||
|
invalid=["otp"]
|
||||||
|
name="otp"
|
||||||
|
type="text"
|
||||||
|
id="otp"
|
||||||
|
>
|
||||||
|
<@labelTotp.kw />
|
||||||
|
</@inputPrimary.kw>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="py-2 flex flex-col items-center justify-center space-y-2">
|
||||||
|
<@buttonPrimary.kw
|
||||||
|
name="submitAction"
|
||||||
|
type="submit"
|
||||||
|
value=msg("doLogIn")
|
||||||
|
>
|
||||||
|
${msg("doLogIn")}
|
||||||
|
</@buttonPrimary.kw>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</#if>
|
||||||
|
</@layout.registrationLayout>
|
2
theme/keywind/login/resources/dist/index.css
vendored
2
theme/keywind/login/resources/dist/index.css
vendored
File diff suppressed because one or more lines are too long
7
theme/keywind/login/resources/dist/index.js
vendored
7
theme/keywind/login/resources/dist/index.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue