diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 076658d1..ea1c369b 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -531,6 +531,7 @@
"user_has_no_passkeys_yet": "This user has no passkeys yet.",
"replay_protection": "Replay Protection",
"replay_protection_description": "If enabled the provided token can only be used once. If your provider uses the same token multiple times, you may need to disable this option.",
+ "i_have_a_longer_code": "I have a longer code",
"pkce_supported_client_title": "This client supports PKCE",
"pkce_supported_client_description": "This client supports Proof Key for Code Exchange (PKCE). PKCE is a security feature that helps protect against certain attacks during the OAuth 2.0 authorization process. It's recommended to enable it."
}
diff --git a/frontend/src/lib/components/ui/input-otp/index.ts b/frontend/src/lib/components/ui/input-otp/index.ts
new file mode 100644
index 00000000..e9ae273e
--- /dev/null
+++ b/frontend/src/lib/components/ui/input-otp/index.ts
@@ -0,0 +1,15 @@
+import Root from "./input-otp.svelte";
+import Group from "./input-otp-group.svelte";
+import Slot from "./input-otp-slot.svelte";
+import Separator from "./input-otp-separator.svelte";
+
+export {
+ Root,
+ Group,
+ Slot,
+ Separator,
+ Root as InputOTP,
+ Group as InputOTPGroup,
+ Slot as InputOTPSlot,
+ Separator as InputOTPSeparator,
+};
diff --git a/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte
new file mode 100644
index 00000000..084577f7
--- /dev/null
+++ b/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte
new file mode 100644
index 00000000..e6025bb0
--- /dev/null
+++ b/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte
@@ -0,0 +1,27 @@
+
+
+
+ {#if children}
+ {@render children?.()}
+ {:else}
+
+ {/if}
+
diff --git a/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte
new file mode 100644
index 00000000..8fe0a189
--- /dev/null
+++ b/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte
@@ -0,0 +1,33 @@
+
+
+
+ {cell.char}
+ {#if cell.hasFakeCaret}
+
+ {/if}
+
diff --git a/frontend/src/lib/components/ui/input-otp/input-otp.svelte b/frontend/src/lib/components/ui/input-otp/input-otp.svelte
new file mode 100644
index 00000000..f80a2f2d
--- /dev/null
+++ b/frontend/src/lib/components/ui/input-otp/input-otp.svelte
@@ -0,0 +1,27 @@
+
+
+
diff --git a/frontend/src/routes/login/alternative/code/+page.svelte b/frontend/src/routes/login/alternative/code/+page.svelte
index c7c4b40e..8799644d 100644
--- a/frontend/src/routes/login/alternative/code/+page.svelte
+++ b/frontend/src/routes/login/alternative/code/+page.svelte
@@ -2,6 +2,7 @@
import { afterNavigate, goto } from '$app/navigation';
import SignInWrapper from '$lib/components/login-wrapper.svelte';
import { Button } from '$lib/components/ui/button';
+ import * as InputOTP from '$lib/components/ui/input-otp/index.js';
import Input from '$lib/components/ui/input/input.svelte';
import { m } from '$lib/paraglide/messages';
import UserService from '$lib/services/user-service';
@@ -17,6 +18,10 @@
let error: string | undefined = $state();
let backHref = $state('/login/alternative');
+ let longCodeRequested = $state(code.length > 6);
+ let showLongCodeOption = $state(true);
+ let codeComplete = $derived(longCodeRequested ? code.length === 16 : code.length === 6);
+
const userService = new UserService();
// If the previous page is a Pocket ID page, go back there instead of the generic alternative login page
@@ -28,6 +33,7 @@
async function authenticate() {
if (!code?.trim()) return;
+ if (!codeComplete) return;
isLoading = true;
try {
const user = await userService.exchangeOneTimeAccessToken(code);
@@ -49,6 +55,10 @@
if (code) {
authenticate();
}
+
+ if (data.redirect.startsWith('/interaction')) {
+ showLongCodeOption = false;
+ }
});
@@ -68,18 +78,49 @@
{:else}
{m.enter_the_code_you_received_to_sign_in()}
{/if}
-