Skip to content

Commit 058a27b

Browse files
committed
support multiple OTP sources
1 parent d3803ef commit 058a27b

3 files changed

Lines changed: 43 additions & 9 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "id.helpwave.de",
3-
"version": "0.1.4",
3+
"version": "0.1.6",
44
"repository": {
55
"type": "git",
66
"url": "git://github.com/helpwave/id.helpwave.de.git"

src/index.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
@apply bg-background text-on-background;
88
}
99
}
10+
11+
*:focus-visible {
12+
@apply !outline-none;
13+
}

src/login/pages/LoginOtp.tsx

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from 'react'
2-
import { Button, Input, FormFieldLayout } from '@helpwave/hightide'
2+
import { Button, Input, FormFieldLayout, SelectUncontrolled, SelectOption } from '@helpwave/hightide'
33
import type { KcContext } from '../KcContext'
44
import { useI18n } from '../i18n'
55
import Template from 'keycloakify/login/Template'
@@ -13,6 +13,7 @@ type LoginOtpProps = {
1313
export default function LoginOtp({ kcContext }: LoginOtpProps) {
1414
const { i18n } = useI18n({ kcContext })
1515
const t = useTranslation()
16+
1617
const [otp, setOtp] = useState('')
1718

1819
const otpError = kcContext.messagesPerField?.existsError('otp')
@@ -21,6 +22,12 @@ export default function LoginOtp({ kcContext }: LoginOtpProps) {
2122

2223
const message = kcContext.message
2324

25+
const otpLogin = kcContext.otpLogin
26+
const credentials = otpLogin?.userOtpCredentials ?? []
27+
const hasMultipleSources = credentials.length > 1
28+
29+
const [selectedCredentialId, setSelectedCredentialId] = useState(credentials[0]?.id ?? '')
30+
2431
return (
2532
<Template
2633
kcContext={kcContext}
@@ -44,7 +51,7 @@ export default function LoginOtp({ kcContext }: LoginOtpProps) {
4451
message.type === 'error'
4552
? 'var(--hw-color-negative-900)'
4653
: 'var(--hw-color-positive-900)',
47-
marginBottom: '1rem'
54+
marginBottom: '1rem',
4855
}}
4956
>
5057
{message.summary}
@@ -57,11 +64,33 @@ export default function LoginOtp({ kcContext }: LoginOtpProps) {
5764
method="post"
5865
style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
5966
>
60-
<FormFieldLayout
61-
label={t('otp')}
62-
invalidDescription={otpError}
63-
required
64-
>
67+
{hasMultipleSources ? (
68+
<>
69+
<input type="hidden" name="selectedCredentialId" value={selectedCredentialId} />
70+
71+
<FormFieldLayout label={t('selectAuthenticatorTitle')} required>
72+
{({ id, ariaAttributes }) => (
73+
<SelectUncontrolled
74+
id={id}
75+
value={selectedCredentialId}
76+
onValueChange={(value) => setSelectedCredentialId(value)}
77+
onEditComplete={() => { }}
78+
{...ariaAttributes}
79+
>
80+
{credentials.map((c) => (
81+
<SelectOption key={c.id} value={c.id}>
82+
{c.userLabel}
83+
</SelectOption>
84+
))}
85+
</SelectUncontrolled>
86+
)}
87+
</FormFieldLayout>
88+
</>
89+
) : credentials.length === 1 ? (
90+
<input type="hidden" name="selectedCredentialId" value={credentials[0].id} />
91+
) : null}
92+
93+
<FormFieldLayout label={t('otp')} invalidDescription={otpError} required>
6594
{({ id, ariaAttributes }) => (
6695
<Input
6796
id={id}
@@ -84,4 +113,5 @@ export default function LoginOtp({ kcContext }: LoginOtpProps) {
84113
</PageLayout>
85114
</Template>
86115
)
87-
}
116+
}
117+

0 commit comments

Comments
 (0)