@guettonenrique/passwordless-auth 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -2
- package/dist/functions/index.cjs +391 -0
- package/dist/functions/index.cjs.map +1 -0
- package/dist/functions/index.d.cts +70 -0
- package/dist/functions/index.d.ts +70 -0
- package/dist/functions/index.js +348 -0
- package/dist/functions/index.js.map +1 -0
- package/dist/react-native/index.cjs +617 -0
- package/dist/react-native/index.cjs.map +1 -0
- package/dist/react-native/index.d.cts +64 -0
- package/dist/react-native/index.d.ts +64 -0
- package/dist/react-native/index.js +606 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/shared/index.cjs +151 -0
- package/dist/shared/index.cjs.map +1 -0
- package/dist/shared/index.d.cts +50 -0
- package/dist/shared/index.d.ts +50 -0
- package/dist/shared/index.js +111 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/types-BUi8ZPam.d.cts +110 -0
- package/dist/types-BUi8ZPam.d.ts +110 -0
- package/package.json +91 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react-native/index.ts","../../src/react-native/api.ts","../../src/react-native/OtpInput.tsx","../../src/shared/constants.ts","../../src/shared/utils.ts","../../src/react-native/EmailOtpModal.tsx"],"sourcesContent":["// React Native entry.\n// Import from '@guettonenrique/passwordless-auth/react-native' in your app.\n\nexport { requestEmailOtp, verifyEmailOtp, signInWithEmailOtp } from './api';\nexport { OtpInput, type OtpInputProps } from './OtpInput';\nexport { EmailOtpModal, type EmailOtpModalProps } from './EmailOtpModal';\n","import { getFunctions, httpsCallable } from '@react-native-firebase/functions';\nimport { getAuth, signInWithCustomToken } from '@react-native-firebase/auth';\nimport type {\n RequestOtpPayload,\n RequestOtpResponse,\n VerifyOtpPayload,\n VerifyOtpResponse,\n} from '../shared';\n\n/**\n * Dispatches a 6-digit OTP code to the provided email.\n */\nexport async function requestEmailOtp(email: string): Promise<RequestOtpResponse> {\n const functions = getFunctions();\n const callable = httpsCallable<RequestOtpPayload, RequestOtpResponse>(\n functions,\n 'requestEmailOtp'\n );\n const result = await callable({ email: email.trim().toLowerCase() });\n return result.data;\n}\n\n/**\n * Verifies the code and returns the Firebase custom auth token.\n */\nexport async function verifyEmailOtp(email: string, code: string): Promise<string> {\n const functions = getFunctions();\n const callable = httpsCallable<VerifyOtpPayload, VerifyOtpResponse>(\n functions,\n 'verifyEmailOtp'\n );\n const result = await callable({\n email: email.trim().toLowerCase(),\n code: code.trim(),\n });\n return result.data.customToken;\n}\n\n/**\n * Verifies the code and signs in with the minted custom token.\n * After this resolves, the standard Firebase Auth session is active\n * (token persistence, refresh, onAuthStateChanged).\n */\nexport async function signInWithEmailOtp(email: string, code: string): Promise<void> {\n const customToken = await verifyEmailOtp(email, code);\n await signInWithCustomToken(getAuth(), customToken);\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport {\n Platform,\n Text,\n TextInput,\n TouchableOpacity,\n View,\n type TextInputInstance,\n} from 'react-native';\nimport { CODE_LENGTH } from '../shared';\n\n// expo-haptics is an optional peer: degrade silently when absent.\nlet Haptics: {\n impactAsync?: (style: string) => Promise<void>;\n notificationAsync?: (type: string) => Promise<void>;\n} | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n Haptics = require('expo-haptics');\n} catch {\n Haptics = null;\n}\n\nconst impact = (style: 'light' | 'success') => {\n try {\n if (style === 'light') {\n Haptics?.impactAsync?.('light').catch(() => null);\n } else {\n Haptics?.notificationAsync?.('success').catch(() => null);\n }\n } catch {\n // haptics are best-effort\n }\n};\n\nexport interface OtpInputProps {\n /** Number of digits. Default 6 (matches the backend). */\n length?: number;\n value: string;\n onChange: (value: string) => void;\n /** Fired when the last digit arrives. */\n onComplete?: (code: string) => void;\n disabled?: boolean;\n /** Red error styling for the whole row. */\n error?: boolean;\n autoFocus?: boolean;\n}\n\n/**\n * Six-slot code input backed by ONE hidden native TextInput.\n *\n * A single input is what makes paste and native one-tap auto-fill work\n * reliably: `textContentType=\"oneTimeCode\"` (iOS) and\n * `autoComplete=\"one-time-code\"` (Android) let the OS inject the code\n * from the email in one tap.\n */\nexport function OtpInput({\n length = CODE_LENGTH,\n value,\n onChange,\n onComplete,\n disabled = false,\n error = false,\n autoFocus = true,\n}: OtpInputProps) {\n const inputRef = useRef<TextInputInstance>(null);\n const [isFocused, setIsFocused] = useState(false);\n const [cursorVisible, setCursorVisible] = useState(true);\n\n // Blinking cursor for the active slot\n useEffect(() => {\n if (!isFocused) return;\n const interval = setInterval(() => {\n setCursorVisible((prev) => !prev);\n }, 550);\n return () => clearInterval(interval);\n }, [isFocused]);\n\n const handleChangeText = (text: string) => {\n const cleaned = text.replace(/[^0-9]/g, '').slice(0, length);\n onChange(cleaned);\n\n if (cleaned.length > value.length) impact('light');\n\n if (cleaned.length === length) {\n impact('success');\n onComplete?.(cleaned);\n }\n };\n\n const slots = Array.from({ length }, (_, i) => i);\n\n return (\n <TouchableOpacity\n style={{ width: '100%' }}\n activeOpacity={1}\n onPress={() => {\n if (!disabled) inputRef.current?.focus();\n }}>\n <View\n style={{\n flexDirection: 'row',\n width: '100%',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginVertical: 12,\n }}>\n {slots.map((index) => {\n const char = value[index] ?? '';\n const isSlotActive = isFocused && index === value.length && !disabled;\n const isFilled = char !== '';\n\n let borderColor = '#e5e7eb';\n let backgroundColor = '#f8fafc';\n if (error) {\n borderColor = '#f43f5e';\n backgroundColor = '#fff1f2';\n } else if (isSlotActive) {\n borderColor = '#3b82f6';\n backgroundColor = '#ffffff';\n } else if (isFilled) {\n borderColor = '#94a3b8';\n backgroundColor = '#ffffff';\n }\n\n return (\n <View\n key={index}\n style={{\n flex: 1,\n maxWidth: 48,\n height: 56,\n borderRadius: 8,\n alignItems: 'center',\n justifyContent: 'center',\n marginHorizontal: 4,\n borderWidth: isSlotActive ? 2 : 1,\n borderColor,\n backgroundColor,\n }}>\n {char ? (\n <Text\n style={{\n fontSize: 24,\n fontWeight: '700',\n color: error ? '#e11d48' : '#1e293b',\n }}>\n {char}\n </Text>\n ) : isSlotActive && cursorVisible ? (\n <View\n style={{\n width: 2,\n height: 24,\n backgroundColor: '#3b82f6',\n borderRadius: 2,\n }}\n />\n ) : null}\n </View>\n );\n })}\n </View>\n\n <TextInput\n ref={inputRef}\n value={value}\n onChangeText={handleChangeText}\n keyboardType=\"number-pad\"\n textContentType=\"oneTimeCode\"\n autoComplete={Platform.OS === 'android' ? 'one-time-code' : undefined}\n maxLength={length}\n editable={!disabled}\n autoFocus={autoFocus}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n caretHidden\n style={{\n position: 'absolute',\n top: 0,\n bottom: 0,\n left: 0,\n right: 0,\n opacity: 0,\n }}\n accessibilityLabel={`${length}-digit verification code`}\n />\n </TouchableOpacity>\n );\n}\n","/**\n * Single source of truth for the OTP contract.\n *\n * Backend factories and mobile components both read these as their defaults,\n * so the UI cooldown and the backend rate limit cannot drift apart silently.\n * A project may override them via factory options — if it does, override the\n * SAME value on both sides (e.g. a custom cooldownMs in the backend factory\n * AND as the `cooldownMs` prop on the mobile modal).\n */\n\n/** Number of digits in an OTP code. */\nexport const CODE_LENGTH = 6;\n\n/** Minimum time between two code requests for the same email. */\nexport const RESEND_COOLDOWN_MS = 60 * 1000; // 60 seconds\n\n/** Lifetime of an issued code. */\nexport const OTP_EXPIRY_MS = 10 * 60 * 1000; // 10 minutes\n\n/** Failed verifications allowed per code before it is invalidated. */\nexport const MAX_ATTEMPTS = 5;\n\n/** Firestore collection that stores pending OTPs (doc id = email). */\nexport const DEFAULT_COLLECTION = 'auth_otps';\n\n/** Default schedule for the cleanup function that reaps expired docs. */\nexport const DEFAULT_CLEANUP_SCHEDULE = 'every 15 minutes';\n\n/** Writes per batch in the cleanup drain loop (Firestore limit is 500). */\nexport const CLEANUP_BATCH_SIZE = 400;\n","import { CODE_LENGTH } from './constants';\n\n/** Deliberately simple RFC-ish check — mirrors the client-side pre-check. */\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Trim + lowercase an email so doc keys and hash inputs always match. */\nexport function normalizeEmail(raw: string): string {\n return raw.trim().toLowerCase();\n}\n\nexport function isValidEmail(raw: string): boolean {\n return EMAIL_REGEX.test(normalizeEmail(raw));\n}\n\nexport function isOtpCode(value: string, length: number = CODE_LENGTH): boolean {\n return new RegExp(`^\\\\d{${length}}$`).test(value);\n}\n\n/**\n * Formats the resend-cooldown error message exactly as the backend emits it.\n * Exported so mobile copy (if ever shown manually) cannot drift from it.\n */\nexport function cooldownMessage(remainingMs: number): string {\n const seconds = Math.ceil(remainingMs / 1000);\n return `Please wait ${seconds} second${seconds === 1 ? '' : 's'} before requesting a new code.`;\n}\n\n/** Formats the remaining-attempts message for a wrong-code rejection. */\nexport function attemptsMessage(remaining: number): string {\n return `Invalid verification code. You have ${remaining} attempt${remaining === 1 ? '' : 's'} remaining.`;\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport {\n ActivityIndicator,\n Modal,\n Text,\n TextInput,\n TouchableOpacity,\n View,\n type TextInputInstance,\n type ViewStyle,\n} from 'react-native';\nimport { KeyboardAwareScrollView } from 'react-native-keyboard-controller';\nimport { AlertCircle, ArrowLeft, Mail, X } from 'lucide-react-native';\nimport { CODE_LENGTH, RESEND_COOLDOWN_MS, isValidEmail } from '../shared';\nimport { OtpInput } from './OtpInput';\nimport { requestEmailOtp, signInWithEmailOtp } from './api';\n\nexport interface EmailOtpModalProps {\n visible: boolean;\n onClose: () => void;\n /** Pre-fill the email step (e.g. from the email already typed on the auth screen). */\n initialEmail?: string;\n /** Brand name shown in the header. Default: 'Sign in with email code'. */\n appName?: string;\n /**\n * Resend cooldown in seconds shown in the UI. Keep in sync with the\n * backend's `cooldownMs` option. Default 60.\n */\n cooldownSeconds?: number;\n}\n\n/**\n * Two-step email → code modal. Handles the full OTP flow:\n * email validation, send, 60s resend cooldown, auto-submit on the last\n * digit, backend error surfacing, and sign-in via custom token.\n *\n * On success the modal closes and the app's global `onAuthStateChanged`\n * listener takes over — no navigation code needed.\n */\nexport function EmailOtpModal({\n visible,\n onClose,\n initialEmail = '',\n appName,\n cooldownSeconds = Math.round(RESEND_COOLDOWN_MS / 1000),\n}: EmailOtpModalProps) {\n const [step, setStep] = useState<'email' | 'otp'>('email');\n const [email, setEmail] = useState(initialEmail);\n const [otp, setOtp] = useState('');\n const [loading, setLoading] = useState(false);\n const [resending, setResending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [cooldown, setCooldown] = useState(0);\n\n const [prevVisible, setPrevVisible] = useState(false);\n const isVerifyingRef = useRef(false);\n const emailRef = useRef<TextInputInstance>(null);\n\n // Reset state each time the modal opens\n if (visible !== prevVisible) {\n setPrevVisible(visible);\n if (visible) {\n setStep('email');\n setEmail(initialEmail);\n setOtp('');\n setError(null);\n setLoading(false);\n setResending(false);\n setCooldown(0);\n isVerifyingRef.current = false;\n }\n }\n\n // Resend cooldown timer\n useEffect(() => {\n if (cooldown <= 0) return;\n const timer = setInterval(() => {\n setCooldown((prev) => (prev > 0 ? prev - 1 : 0));\n }, 1000);\n return () => clearInterval(timer);\n }, [cooldown]);\n\n // Strip Firebase's \"[functions/xyz] \" prefix from backend error messages\n const cleanErrorMessage = (err: unknown, fallback: string): string => {\n const raw =\n err instanceof Error ? err.message : typeof err === 'string' ? err : '';\n if (raw.length > 0) {\n return raw.replace(/^\\[.*?\\]\\s*/, '').trim();\n }\n return fallback;\n };\n\n const handleSendCode = async () => {\n const trimmedEmail = email.trim().toLowerCase();\n if (!trimmedEmail) {\n setError('Please enter your email address.');\n return;\n }\n if (!isValidEmail(trimmedEmail)) {\n setError('Please enter a valid email address.');\n return;\n }\n\n setLoading(true);\n setError(null);\n emailRef.current?.blur();\n\n try {\n await requestEmailOtp(trimmedEmail);\n setStep('otp');\n setCooldown(cooldownSeconds);\n setOtp('');\n } catch (err) {\n console.error('[passwordless-auth] requestEmailOtp failed:', err);\n setError(cleanErrorMessage(err, 'Failed to send verification code. Please try again.'));\n } finally {\n setLoading(false);\n }\n };\n\n const handleResendCode = async () => {\n if (cooldown > 0 || resending || loading) return;\n\n setResending(true);\n setError(null);\n\n try {\n await requestEmailOtp(email.trim().toLowerCase());\n setCooldown(cooldownSeconds);\n setOtp('');\n } catch (err) {\n console.error('[passwordless-auth] resend failed:', err);\n setError(cleanErrorMessage(err, 'Failed to resend verification code.'));\n } finally {\n setResending(false);\n }\n };\n\n const handleClose = () => {\n setError(null);\n setLoading(false);\n setResending(false);\n onClose();\n };\n\n const handleVerifyOtp = async (codeToVerify?: string) => {\n const code = (codeToVerify ?? otp).trim();\n if (code.length < CODE_LENGTH || isVerifyingRef.current) return;\n\n isVerifyingRef.current = true;\n setLoading(true);\n setError(null);\n\n try {\n await signInWithEmailOtp(email.trim().toLowerCase(), code);\n handleClose();\n } catch (err) {\n console.error('[passwordless-auth] verifyEmailOtp failed:', err);\n setError(\n cleanErrorMessage(err, 'Invalid or expired verification code. Please try again.')\n );\n setOtp('');\n } finally {\n setLoading(false);\n isVerifyingRef.current = false;\n }\n };\n\n const handleBackToEmail = () => {\n if (loading) return;\n setStep('email');\n setOtp('');\n setError(null);\n };\n\n const buttonBase: ViewStyle = {\n flex: 1,\n paddingVertical: 14,\n borderRadius: 12,\n alignItems: 'center',\n flexDirection: 'row',\n justifyContent: 'center',\n gap: 8,\n };\n\n return (\n <Modal visible={visible} transparent animationType=\"fade\" onRequestClose={handleClose}>\n <KeyboardAwareScrollView\n contentContainerStyle={{ flexGrow: 1 }}\n keyboardShouldPersistTaps=\"handled\"\n bottomOffset={24}\n showsVerticalScrollIndicator={false}>\n <TouchableOpacity\n style={{\n flex: 1,\n backgroundColor: 'rgba(0,0,0,0.5)',\n justifyContent: 'center',\n alignItems: 'center',\n padding: 16,\n }}\n activeOpacity={1}\n onPress={handleClose}>\n <TouchableOpacity\n activeOpacity={1}\n style={{\n backgroundColor: '#fff',\n width: '100%',\n maxWidth: 420,\n borderRadius: 16,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOpacity: 0.2,\n shadowRadius: 12,\n elevation: 10,\n }}>\n {/* Header */}\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingHorizontal: 24,\n paddingVertical: 16,\n borderBottomWidth: 1,\n borderBottomColor: '#f3f4f6',\n }}>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n {step === 'otp' && (\n <TouchableOpacity onPress={handleBackToEmail} disabled={loading} activeOpacity={0.7}>\n <ArrowLeft size={20} color=\"#64748b\" />\n </TouchableOpacity>\n )}\n <View>\n <Text style={{ fontSize: 12, color: '#94a3b8', marginBottom: 2 }}>\n {step === 'email' ? 'Instant Sign In' : 'Verification'}\n </Text>\n <Text style={{ fontSize: 18, fontWeight: '700', color: '#1e293b' }}>\n {step === 'email'\n ? appName\n ? `${appName} — email code`\n : 'Sign in with email code'\n : 'Enter verification code'}\n </Text>\n </View>\n </View>\n <TouchableOpacity onPress={handleClose} activeOpacity={0.7}>\n <X size={20} color=\"#94a3b8\" />\n </TouchableOpacity>\n </View>\n\n {/* Content */}\n <View style={{ padding: 24 }}>\n {error ? (\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n padding: 12,\n marginBottom: 16,\n borderRadius: 12,\n backgroundColor: '#fef2f2',\n borderWidth: 1,\n borderColor: '#fecaca',\n }}>\n <AlertCircle size={16} color=\"#dc2626\" />\n <Text style={{ fontSize: 12, color: '#dc2626', flex: 1, fontWeight: '500' }}>\n {error}\n </Text>\n </View>\n ) : null}\n\n {step === 'email' ? (\n /* ---------- Step 1: Email ---------- */\n <View>\n <Text style={{ fontSize: 13, color: '#64748b', marginBottom: 16, lineHeight: 18 }}>\n We'll send a 6-digit verification code to your email. No password needed.\n </Text>\n\n <View style={{ marginBottom: 24 }}>\n <Text style={{ fontSize: 12, fontWeight: '600', color: '#334155', marginBottom: 8 }}>\n Email\n </Text>\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 16,\n backgroundColor: '#f8fafc',\n borderRadius: 12,\n borderWidth: 1,\n borderColor: '#e5e7eb',\n height: 56,\n }}>\n <TextInput\n ref={emailRef}\n style={{ flex: 1, fontSize: 14, fontWeight: '600', color: '#1e293b' }}\n placeholder=\"name@email.com\"\n placeholderTextColor=\"#94a3b8\"\n value={email}\n onChangeText={(val) => {\n setEmail(val);\n if (error) setError(null);\n }}\n keyboardType=\"email-address\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n returnKeyType=\"send\"\n onSubmitEditing={handleSendCode}\n autoFocus\n editable={!loading}\n />\n <Mail size={20} color=\"#cbd5e1\" />\n </View>\n </View>\n\n <View style={{ flexDirection: 'row', gap: 12 }}>\n <TouchableOpacity\n onPress={handleClose}\n style={{ ...buttonBase, borderWidth: 1, borderColor: '#e5e7eb', backgroundColor: 'transparent' }}\n activeOpacity={0.7}>\n <Text style={{ color: '#64748b', fontWeight: '600', fontSize: 12, textTransform: 'uppercase', letterSpacing: 1 }}>\n Cancel\n </Text>\n </TouchableOpacity>\n\n <TouchableOpacity\n onPress={handleSendCode}\n disabled={loading}\n style={{ ...buttonBase, backgroundColor: loading ? '#93c5fd' : '#3b82f6' }}\n activeOpacity={0.7}>\n {loading ? <ActivityIndicator size=\"small\" color=\"#fff\" /> : null}\n <Text style={{ color: '#fff', fontWeight: '600', fontSize: 12, textTransform: 'uppercase', letterSpacing: 1 }}>\n {loading ? 'Sending…' : 'Send Code'}\n </Text>\n </TouchableOpacity>\n </View>\n </View>\n ) : (\n /* ---------- Step 2: Code ---------- */\n <View>\n <Text style={{ fontSize: 13, color: '#64748b', marginBottom: 16, lineHeight: 18 }}>\n We sent a 6-digit code to{' '}\n <Text style={{ fontWeight: '600', color: '#1e293b' }}>{email.trim()}</Text>\n {' '}\n . Enter it below to sign in.\n </Text>\n\n <View style={{ alignItems: 'center', marginBottom: 16 }}>\n <OtpInput\n value={otp}\n onChange={(val) => {\n setOtp(val);\n if (error) setError(null);\n }}\n onComplete={(code) => handleVerifyOtp(code)}\n disabled={loading}\n error={!!error}\n autoFocus\n />\n </View>\n\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 24,\n paddingHorizontal: 4,\n }}>\n <TouchableOpacity onPress={handleBackToEmail} disabled={loading} activeOpacity={0.7}>\n <Text style={{ fontSize: 12, fontWeight: '600', color: '#64748b' }}>\n Change email\n </Text>\n </TouchableOpacity>\n\n {cooldown > 0 ? (\n <Text style={{ fontSize: 12, color: '#94a3b8', fontWeight: '500' }}>\n Resend in {cooldown}s\n </Text>\n ) : (\n <TouchableOpacity\n onPress={handleResendCode}\n disabled={resending || loading}\n style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}\n activeOpacity={0.7}>\n {resending ? <ActivityIndicator size=\"small\" color=\"#2563eb\" /> : null}\n <Text style={{ fontSize: 12, fontWeight: '600', color: '#2563eb' }}>\n Resend Code\n </Text>\n </TouchableOpacity>\n )}\n </View>\n\n <View style={{ flexDirection: 'row', gap: 12 }}>\n <TouchableOpacity\n onPress={handleClose}\n style={{ ...buttonBase, borderWidth: 1, borderColor: '#e5e7eb', backgroundColor: 'transparent' }}\n activeOpacity={0.7}>\n <Text style={{ color: '#64748b', fontWeight: '600', fontSize: 12, textTransform: 'uppercase', letterSpacing: 1 }}>\n Cancel\n </Text>\n </TouchableOpacity>\n\n <TouchableOpacity\n onPress={() => handleVerifyOtp()}\n disabled={loading || otp.length < CODE_LENGTH}\n style={{\n ...buttonBase,\n backgroundColor: loading || otp.length < CODE_LENGTH ? '#93c5fd' : '#3b82f6',\n }}\n activeOpacity={0.7}>\n {loading ? <ActivityIndicator size=\"small\" color=\"#fff\" /> : null}\n <Text style={{ color: '#fff', fontWeight: '600', fontSize: 12, textTransform: 'uppercase', letterSpacing: 1 }}>\n {loading ? 'Verifying…' : 'Verify & Sign In'}\n </Text>\n </TouchableOpacity>\n </View>\n </View>\n )}\n </View>\n </TouchableOpacity>\n </TouchableOpacity>\n </KeyboardAwareScrollView>\n </Modal>\n );\n}\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { b as RequestOtpResponse } from '../types-BUi8ZPam.cjs';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Dispatches a 6-digit OTP code to the provided email.
|
|
6
|
+
*/
|
|
7
|
+
declare function requestEmailOtp(email: string): Promise<RequestOtpResponse>;
|
|
8
|
+
/**
|
|
9
|
+
* Verifies the code and returns the Firebase custom auth token.
|
|
10
|
+
*/
|
|
11
|
+
declare function verifyEmailOtp(email: string, code: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Verifies the code and signs in with the minted custom token.
|
|
14
|
+
* After this resolves, the standard Firebase Auth session is active
|
|
15
|
+
* (token persistence, refresh, onAuthStateChanged).
|
|
16
|
+
*/
|
|
17
|
+
declare function signInWithEmailOtp(email: string, code: string): Promise<void>;
|
|
18
|
+
|
|
19
|
+
interface OtpInputProps {
|
|
20
|
+
/** Number of digits. Default 6 (matches the backend). */
|
|
21
|
+
length?: number;
|
|
22
|
+
value: string;
|
|
23
|
+
onChange: (value: string) => void;
|
|
24
|
+
/** Fired when the last digit arrives. */
|
|
25
|
+
onComplete?: (code: string) => void;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** Red error styling for the whole row. */
|
|
28
|
+
error?: boolean;
|
|
29
|
+
autoFocus?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Six-slot code input backed by ONE hidden native TextInput.
|
|
33
|
+
*
|
|
34
|
+
* A single input is what makes paste and native one-tap auto-fill work
|
|
35
|
+
* reliably: `textContentType="oneTimeCode"` (iOS) and
|
|
36
|
+
* `autoComplete="one-time-code"` (Android) let the OS inject the code
|
|
37
|
+
* from the email in one tap.
|
|
38
|
+
*/
|
|
39
|
+
declare function OtpInput({ length, value, onChange, onComplete, disabled, error, autoFocus, }: OtpInputProps): React.JSX.Element;
|
|
40
|
+
|
|
41
|
+
interface EmailOtpModalProps {
|
|
42
|
+
visible: boolean;
|
|
43
|
+
onClose: () => void;
|
|
44
|
+
/** Pre-fill the email step (e.g. from the email already typed on the auth screen). */
|
|
45
|
+
initialEmail?: string;
|
|
46
|
+
/** Brand name shown in the header. Default: 'Sign in with email code'. */
|
|
47
|
+
appName?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Resend cooldown in seconds shown in the UI. Keep in sync with the
|
|
50
|
+
* backend's `cooldownMs` option. Default 60.
|
|
51
|
+
*/
|
|
52
|
+
cooldownSeconds?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Two-step email → code modal. Handles the full OTP flow:
|
|
56
|
+
* email validation, send, 60s resend cooldown, auto-submit on the last
|
|
57
|
+
* digit, backend error surfacing, and sign-in via custom token.
|
|
58
|
+
*
|
|
59
|
+
* On success the modal closes and the app's global `onAuthStateChanged`
|
|
60
|
+
* listener takes over — no navigation code needed.
|
|
61
|
+
*/
|
|
62
|
+
declare function EmailOtpModal({ visible, onClose, initialEmail, appName, cooldownSeconds, }: EmailOtpModalProps): React.JSX.Element;
|
|
63
|
+
|
|
64
|
+
export { EmailOtpModal, type EmailOtpModalProps, OtpInput, type OtpInputProps, requestEmailOtp, signInWithEmailOtp, verifyEmailOtp };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { b as RequestOtpResponse } from '../types-BUi8ZPam.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Dispatches a 6-digit OTP code to the provided email.
|
|
6
|
+
*/
|
|
7
|
+
declare function requestEmailOtp(email: string): Promise<RequestOtpResponse>;
|
|
8
|
+
/**
|
|
9
|
+
* Verifies the code and returns the Firebase custom auth token.
|
|
10
|
+
*/
|
|
11
|
+
declare function verifyEmailOtp(email: string, code: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Verifies the code and signs in with the minted custom token.
|
|
14
|
+
* After this resolves, the standard Firebase Auth session is active
|
|
15
|
+
* (token persistence, refresh, onAuthStateChanged).
|
|
16
|
+
*/
|
|
17
|
+
declare function signInWithEmailOtp(email: string, code: string): Promise<void>;
|
|
18
|
+
|
|
19
|
+
interface OtpInputProps {
|
|
20
|
+
/** Number of digits. Default 6 (matches the backend). */
|
|
21
|
+
length?: number;
|
|
22
|
+
value: string;
|
|
23
|
+
onChange: (value: string) => void;
|
|
24
|
+
/** Fired when the last digit arrives. */
|
|
25
|
+
onComplete?: (code: string) => void;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** Red error styling for the whole row. */
|
|
28
|
+
error?: boolean;
|
|
29
|
+
autoFocus?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Six-slot code input backed by ONE hidden native TextInput.
|
|
33
|
+
*
|
|
34
|
+
* A single input is what makes paste and native one-tap auto-fill work
|
|
35
|
+
* reliably: `textContentType="oneTimeCode"` (iOS) and
|
|
36
|
+
* `autoComplete="one-time-code"` (Android) let the OS inject the code
|
|
37
|
+
* from the email in one tap.
|
|
38
|
+
*/
|
|
39
|
+
declare function OtpInput({ length, value, onChange, onComplete, disabled, error, autoFocus, }: OtpInputProps): React.JSX.Element;
|
|
40
|
+
|
|
41
|
+
interface EmailOtpModalProps {
|
|
42
|
+
visible: boolean;
|
|
43
|
+
onClose: () => void;
|
|
44
|
+
/** Pre-fill the email step (e.g. from the email already typed on the auth screen). */
|
|
45
|
+
initialEmail?: string;
|
|
46
|
+
/** Brand name shown in the header. Default: 'Sign in with email code'. */
|
|
47
|
+
appName?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Resend cooldown in seconds shown in the UI. Keep in sync with the
|
|
50
|
+
* backend's `cooldownMs` option. Default 60.
|
|
51
|
+
*/
|
|
52
|
+
cooldownSeconds?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Two-step email → code modal. Handles the full OTP flow:
|
|
56
|
+
* email validation, send, 60s resend cooldown, auto-submit on the last
|
|
57
|
+
* digit, backend error surfacing, and sign-in via custom token.
|
|
58
|
+
*
|
|
59
|
+
* On success the modal closes and the app's global `onAuthStateChanged`
|
|
60
|
+
* listener takes over — no navigation code needed.
|
|
61
|
+
*/
|
|
62
|
+
declare function EmailOtpModal({ visible, onClose, initialEmail, appName, cooldownSeconds, }: EmailOtpModalProps): React.JSX.Element;
|
|
63
|
+
|
|
64
|
+
export { EmailOtpModal, type EmailOtpModalProps, OtpInput, type OtpInputProps, requestEmailOtp, signInWithEmailOtp, verifyEmailOtp };
|