@firebase-oss/ui-shadcn 0.0.1

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.
Files changed (34) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/bin/cli.mjs +63 -0
  4. package/dist/bin/cli.mjs.map +1 -0
  5. package/dist/registry/apple-sign-in-button.json +39 -0
  6. package/dist/registry/country-selector.json +23 -0
  7. package/dist/registry/email-link-auth-form.json +27 -0
  8. package/dist/registry/email-link-auth-screen.json +26 -0
  9. package/dist/registry/facebook-sign-in-button.json +32 -0
  10. package/dist/registry/forgot-password-auth-form.json +26 -0
  11. package/dist/registry/forgot-password-auth-screen.json +24 -0
  12. package/dist/registry/github-sign-in-button.json +39 -0
  13. package/dist/registry/google-sign-in-button.json +44 -0
  14. package/dist/registry/microsoft-sign-in-button.json +39 -0
  15. package/dist/registry/multi-factor-auth-assertion-form.json +25 -0
  16. package/dist/registry/multi-factor-auth-assertion-screen.json +24 -0
  17. package/dist/registry/multi-factor-auth-enrollment-form.json +25 -0
  18. package/dist/registry/multi-factor-auth-enrollment-screen.json +24 -0
  19. package/dist/registry/oauth-button.json +23 -0
  20. package/dist/registry/oauth-screen.json +26 -0
  21. package/dist/registry/phone-auth-form.json +27 -0
  22. package/dist/registry/phone-auth-screen.json +27 -0
  23. package/dist/registry/policies.json +20 -0
  24. package/dist/registry/redirect-error.json +20 -0
  25. package/dist/registry/sign-in-auth-form.json +26 -0
  26. package/dist/registry/sign-in-auth-screen.json +26 -0
  27. package/dist/registry/sign-up-auth-form.json +26 -0
  28. package/dist/registry/sign-up-auth-screen.json +26 -0
  29. package/dist/registry/sms-multi-factor-assertion-form.json +26 -0
  30. package/dist/registry/sms-multi-factor-enrollment-form.json +27 -0
  31. package/dist/registry/totp-multi-factor-assertion-form.json +25 -0
  32. package/dist/registry/totp-multi-factor-enrollment-form.json +26 -0
  33. package/dist/registry/twitter-sign-in-button.json +32 -0
  34. package/package.json +56 -0
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "google-sign-in-button",
4
+ "type": "registry:block",
5
+ "title": "Google Sign In Button",
6
+ "description": "A button component for Google OAuth authentication.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "https://firebaseopensource.com/r/oauth-button.json"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "src/components/google-sign-in-button.tsx",
16
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { GoogleAuthProvider } from \"firebase/auth\";\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type GoogleSignInButtonProps, GoogleLogo } from \"@firebase-oss/ui-react\";\n\nimport { OAuthButton } from \"@/components/oauth-button\";\n\nexport type { GoogleSignInButtonProps };\n\nexport function GoogleSignInButton({ provider, ...props }: GoogleSignInButtonProps) {\n const ui = useUI();\n\n return (\n <OAuthButton {...props} provider={provider || new GoogleAuthProvider()}>\n <GoogleLogo />\n <span>{getTranslation(ui, \"labels\", \"signInWithGoogle\")}</span>\n </OAuthButton>\n );\n}\n",
17
+ "type": "registry:component"
18
+ }
19
+ ],
20
+ "css": {
21
+ "@layer components": {
22
+ "button[data-provider='google.com'][data-themed='true']": {
23
+ "--google-primary": "#131314",
24
+ "--primary": "var(--google-primary)",
25
+ "--primary-foreground": "var(--color-white)"
26
+ },
27
+ "button[data-provider='google.com'][data-themed='neutral']": {
28
+ "--google-primary": "#F2F2F2",
29
+ "--primary": "var(--google-primary)",
30
+ "--primary-foreground": "var(--color-black)"
31
+ }
32
+ },
33
+ "@variant dark": {
34
+ "button[data-provider='google.com'][data-themed='true']": {
35
+ "--google-primary": "#FFFFFF",
36
+ "--primary": "var(--google-primary)",
37
+ "--primary-foreground": "var(--color-black)"
38
+ }
39
+ }
40
+ },
41
+ "meta": {
42
+ "version": "0.0.1"
43
+ }
44
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "microsoft-sign-in-button",
4
+ "type": "registry:block",
5
+ "title": "Microsoft Sign In Button",
6
+ "description": "A button component for Microsoft OAuth authentication.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "https://firebaseopensource.com/r/oauth-button.json"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "src/components/microsoft-sign-in-button.tsx",
16
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { OAuthProvider } from \"firebase/auth\";\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type MicrosoftSignInButtonProps, MicrosoftLogo } from \"@firebase-oss/ui-react\";\n\nimport { OAuthButton } from \"@/components/oauth-button\";\n\nexport type { MicrosoftSignInButtonProps };\n\nexport function MicrosoftSignInButton({ provider, ...props }: MicrosoftSignInButtonProps) {\n const ui = useUI();\n\n return (\n <OAuthButton {...props} provider={provider || new OAuthProvider(\"microsoft.com\")}>\n <MicrosoftLogo />\n <span>{getTranslation(ui, \"labels\", \"signInWithMicrosoft\")}</span>\n </OAuthButton>\n );\n}\n",
17
+ "type": "registry:component"
18
+ }
19
+ ],
20
+ "css": {
21
+ "@layer components": {
22
+ "button[data-provider='microsoft.com'][data-themed='true']": {
23
+ "--microsoft-primary": "#2F2F2F",
24
+ "--primary": "var(--microsoft-primary)",
25
+ "--primary-foreground": "var(--color-white)"
26
+ }
27
+ },
28
+ "@variant dark": {
29
+ "button[data-provider='microsoft.com'][data-themed='true']": {
30
+ "--microsoft-primary": "var(--color-white)",
31
+ "--primary": "var(--microsoft-primary)",
32
+ "--primary-foreground": "var(--color-black)"
33
+ }
34
+ }
35
+ },
36
+ "meta": {
37
+ "version": "0.0.1"
38
+ }
39
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "multi-factor-auth-assertion-form",
4
+ "type": "registry:block",
5
+ "title": "Multi-Factor Auth Assertion Form",
6
+ "description": "A form allowing users to complete multi-factor authentication during sign-in with TOTP or SMS options.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "button",
12
+ "https://firebaseopensource.com/r/sms-multi-factor-assertion-form.json",
13
+ "https://firebaseopensource.com/r/totp-multi-factor-assertion-form.json"
14
+ ],
15
+ "files": [
16
+ {
17
+ "path": "src/components/multi-factor-auth-assertion-form.tsx",
18
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI } from \"@firebase-oss/ui-react\";\nimport {\n PhoneMultiFactorGenerator,\n TotpMultiFactorGenerator,\n type MultiFactorInfo,\n type UserCredential,\n} from \"firebase/auth\";\nimport { useState, type ComponentProps } from \"react\";\nimport { useMultiFactorAssertionCleanup } from \"@firebase-oss/ui-react\";\n\nimport { SmsMultiFactorAssertionForm } from \"@/components/sms-multi-factor-assertion-form\";\nimport { TotpMultiFactorAssertionForm } from \"@/components/totp-multi-factor-assertion-form\";\nimport { Button } from \"@/components/ui/button\";\n\nexport type MultiFactorAuthAssertionFormProps = {\n onSuccess?: (credential: UserCredential) => void;\n};\n\nexport function MultiFactorAuthAssertionForm({ onSuccess }: MultiFactorAuthAssertionFormProps) {\n const ui = useUI();\n const resolver = ui.multiFactorResolver;\n const mfaAssertionFactorPrompt = getTranslation(ui, \"prompts\", \"mfaAssertionFactorPrompt\");\n\n useMultiFactorAssertionCleanup();\n\n if (!resolver) {\n throw new Error(\"MultiFactorAuthAssertionForm requires a multi-factor resolver\");\n }\n\n // If only a single hint is provided, select it by default to improve UX.\n const [hint, setHint] = useState<MultiFactorInfo | undefined>(\n resolver.hints.length === 1 ? resolver.hints[0] : undefined\n );\n\n if (hint) {\n if (hint.factorId === PhoneMultiFactorGenerator.FACTOR_ID) {\n return <SmsMultiFactorAssertionForm hint={hint} onSuccess={onSuccess} />;\n }\n\n if (hint.factorId === TotpMultiFactorGenerator.FACTOR_ID) {\n return <TotpMultiFactorAssertionForm hint={hint} onSuccess={onSuccess} />;\n }\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <p className=\"text-sm text-muted-foreground\">{mfaAssertionFactorPrompt}</p>\n {resolver.hints.map((hint) => {\n if (hint.factorId === TotpMultiFactorGenerator.FACTOR_ID) {\n return <TotpButton key={hint.factorId} onClick={() => setHint(hint)} />;\n }\n\n if (hint.factorId === PhoneMultiFactorGenerator.FACTOR_ID) {\n return <SmsButton key={hint.factorId} onClick={() => setHint(hint)} />;\n }\n\n return null;\n })}\n </div>\n );\n}\n\nfunction TotpButton(props: ComponentProps<typeof Button>) {\n const ui = useUI();\n const labelText = getTranslation(ui, \"labels\", \"mfaTotpVerification\");\n return <Button {...props}>{labelText}</Button>;\n}\n\nfunction SmsButton(props: ComponentProps<typeof Button>) {\n const ui = useUI();\n const labelText = getTranslation(ui, \"labels\", \"mfaSmsVerification\");\n return <Button {...props}>{labelText}</Button>;\n}\n",
19
+ "type": "registry:component"
20
+ }
21
+ ],
22
+ "meta": {
23
+ "version": "0.0.1"
24
+ }
25
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "multi-factor-auth-assertion-screen",
4
+ "type": "registry:block",
5
+ "title": "Multi-Factor Auth Assertion Screen",
6
+ "description": "A screen allowing users to complete multi-factor authentication during sign-in with TOTP or SMS options.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "card",
12
+ "https://firebaseopensource.com/r/multi-factor-auth-assertion-form.json"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "src/components/multi-factor-auth-assertion-screen.tsx",
17
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type MultiFactorAuthAssertionScreenProps } from \"@firebase-oss/ui-react\";\n\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { MultiFactorAuthAssertionForm } from \"@/components/multi-factor-auth-assertion-form\";\n\nexport type MultiFactorAuthEnrollmentScreenProps = MultiFactorAuthAssertionScreenProps;\n\nexport function MultiFactorAuthAssertionScreen(props: MultiFactorAuthEnrollmentScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"multiFactorAssertion\");\n const subtitleText = getTranslation(ui, \"prompts\", \"mfaAssertionPrompt\");\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <MultiFactorAuthAssertionForm {...props} />\n </CardContent>\n </Card>\n </div>\n );\n}\n",
18
+ "type": "registry:component"
19
+ }
20
+ ],
21
+ "meta": {
22
+ "version": "0.0.1"
23
+ }
24
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "multi-factor-auth-enrollment-form",
4
+ "type": "registry:block",
5
+ "title": "Multi-Factor Auth Enrollment Form",
6
+ "description": "A form allowing users to select and configure multi-factor authentication methods.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "button",
12
+ "https://firebaseopensource.com/r/sms-multi-factor-enrollment-form.json",
13
+ "https://firebaseopensource.com/r/totp-multi-factor-enrollment-form.json"
14
+ ],
15
+ "files": [
16
+ {
17
+ "path": "src/components/multi-factor-auth-enrollment-form.tsx",
18
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { type ComponentProps, useState } from \"react\";\nimport { FactorId } from \"firebase/auth\";\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI } from \"@firebase-oss/ui-react\";\n\nimport { SmsMultiFactorEnrollmentForm } from \"@/components/sms-multi-factor-enrollment-form\";\nimport { TotpMultiFactorEnrollmentForm } from \"@/components/totp-multi-factor-enrollment-form\";\nimport { Button } from \"@/components/ui/button\";\n\ntype Hint = (typeof FactorId)[keyof typeof FactorId];\n\nexport type MultiFactorAuthEnrollmentFormProps = {\n onEnrollment?: () => void;\n hints?: Hint[];\n};\n\nconst DEFAULT_HINTS = [FactorId.TOTP, FactorId.PHONE] as const;\n\nexport function MultiFactorAuthEnrollmentForm(props: MultiFactorAuthEnrollmentFormProps) {\n const hints = props.hints ?? DEFAULT_HINTS;\n\n if (hints.length === 0) {\n throw new Error(\"MultiFactorAuthEnrollmentForm must have at least one hint\");\n }\n\n // If only a single hint is provided, select it by default to improve UX.\n const [hint, setHint] = useState<Hint | undefined>(hints.length === 1 ? hints[0] : undefined);\n\n if (hint) {\n if (hint === FactorId.TOTP) {\n return <TotpMultiFactorEnrollmentForm onSuccess={props.onEnrollment} />;\n }\n\n if (hint === FactorId.PHONE) {\n return <SmsMultiFactorEnrollmentForm onSuccess={props.onEnrollment} />;\n }\n\n throw new Error(`Unknown multi-factor enrollment type: ${hint}`);\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n {hints.map((hint) => {\n if (hint === FactorId.TOTP) {\n return <TotpButton key={hint} onClick={() => setHint(hint)} />;\n }\n\n if (hint === FactorId.PHONE) {\n return <SmsButton key={hint} onClick={() => setHint(hint)} />;\n }\n\n return null;\n })}\n </div>\n );\n}\n\nfunction TotpButton(props: ComponentProps<typeof Button>) {\n const ui = useUI();\n const labelText = getTranslation(ui, \"labels\", \"mfaTotpVerification\");\n return (\n <Button {...props} variant=\"outline\">\n {labelText}\n </Button>\n );\n}\n\nfunction SmsButton(props: ComponentProps<typeof Button>) {\n const ui = useUI();\n const labelText = getTranslation(ui, \"labels\", \"mfaSmsVerification\");\n return (\n <Button {...props} variant=\"outline\">\n {labelText}\n </Button>\n );\n}\n",
19
+ "type": "registry:component"
20
+ }
21
+ ],
22
+ "meta": {
23
+ "version": "0.0.1"
24
+ }
25
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "multi-factor-auth-enrollment-screen",
4
+ "type": "registry:block",
5
+ "title": "Multi-Factor Auth Enrollment Screen",
6
+ "description": "A screen allowing users to set up multi-factor authentication with TOTP or SMS options.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "card",
12
+ "https://firebaseopensource.com/r/multi-factor-auth-enrollment-form.json"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "src/components/multi-factor-auth-enrollment-screen.tsx",
17
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type MultiFactorAuthEnrollmentFormProps } from \"@firebase-oss/ui-react\";\n\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { MultiFactorAuthEnrollmentForm } from \"@/components/multi-factor-auth-enrollment-form\";\n\nexport type MultiFactorAuthEnrollmentScreenProps = MultiFactorAuthEnrollmentFormProps;\n\nexport function MultiFactorAuthEnrollmentScreen(props: MultiFactorAuthEnrollmentScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"multiFactorEnrollment\");\n const subtitleText = getTranslation(ui, \"prompts\", \"mfaEnrollmentPrompt\");\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <MultiFactorAuthEnrollmentForm {...props} />\n </CardContent>\n </Card>\n </div>\n );\n}\n",
18
+ "type": "registry:component"
19
+ }
20
+ ],
21
+ "meta": {
22
+ "version": "0.0.1"
23
+ }
24
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "oauth-button",
4
+ "type": "registry:block",
5
+ "title": "OAuth Button",
6
+ "description": "A button component for OAuth authentication providers.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "button"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "src/components/oauth-button.tsx",
16
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { useUI, type OAuthButtonProps, useSignInWithProvider } from \"@firebase-oss/ui-react\";\nimport { Button } from \"@/components/ui/button\";\n\nexport type { OAuthButtonProps };\n\nexport function OAuthButton({ provider, children, themed, onSignIn }: OAuthButtonProps) {\n const ui = useUI();\n\n const { error, callback } = useSignInWithProvider(provider, onSignIn);\n\n return (\n <div>\n <Button\n type=\"button\"\n disabled={ui.state !== \"idle\"}\n onClick={callback}\n data-provider={provider.providerId}\n data-themed={themed}\n className=\"w-full\"\n variant={themed ? \"default\" : \"outline\"}\n >\n {children}\n </Button>\n {error && <div className=\"text-destructive text-left text-xs\">{error}</div>}\n </div>\n );\n}\n",
17
+ "type": "registry:component"
18
+ }
19
+ ],
20
+ "meta": {
21
+ "version": "0.0.1"
22
+ }
23
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "oauth-screen",
4
+ "type": "registry:block",
5
+ "title": "OAuth Screen",
6
+ "description": "A screen allowing users to sign in with OAuth providers.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "card",
12
+ "https://firebaseopensource.com/r/policies.json",
13
+ "https://firebaseopensource.com/r/multi-factor-auth-assertion-screen.json",
14
+ "https://firebaseopensource.com/r/redirect-error.json"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/oauth-screen.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { type User } from \"firebase/auth\";\nimport { type PropsWithChildren } from \"react\";\nimport { useUI, useOnUserAuthenticated } from \"@firebase-oss/ui-react\";\nimport { Card, CardContent, CardHeader, CardDescription, CardTitle } from \"@/components/ui/card\";\nimport { Policies } from \"@/components/policies\";\nimport { MultiFactorAuthAssertionScreen } from \"@/components/multi-factor-auth-assertion-screen\";\nimport { RedirectError } from \"@/components/redirect-error\";\n\nexport type OAuthScreenProps = PropsWithChildren<{\n onSignIn?: (user: User) => void;\n}>;\n\nexport function OAuthScreen({ children, onSignIn }: OAuthScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"signIn\");\n const subtitleText = getTranslation(ui, \"prompts\", \"signInToAccount\");\n\n useOnUserAuthenticated(onSignIn);\n\n if (ui.multiFactorResolver) {\n return <MultiFactorAuthAssertionScreen />;\n }\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">{children}</div>\n <div className=\"mt-4 space-y-4\">\n <RedirectError />\n <Policies />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "phone-auth-form",
4
+ "type": "registry:block",
5
+ "title": "Phone Auth Form",
6
+ "description": "A form allowing users to authenticate using their phone number with SMS verification.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "form",
12
+ "input",
13
+ "button",
14
+ "https://firebaseopensource.com/r/country-selector.json",
15
+ "https://firebaseopensource.com/r/policies.json"
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "src/components/phone-auth-form.tsx",
20
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport {\n type PhoneAuthFormProps,\n usePhoneAuthNumberFormSchema,\n usePhoneAuthVerifyFormSchema,\n usePhoneNumberFormAction,\n useRecaptchaVerifier,\n useUI,\n useVerifyPhoneNumberFormAction,\n} from \"@firebase-oss/ui-react\";\nimport { useState } from \"react\";\nimport type { UserCredential } from \"firebase/auth\";\nimport { useRef } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\nimport {\n FirebaseUIError,\n formatPhoneNumber,\n getTranslation,\n type PhoneAuthNumberFormSchema,\n type PhoneAuthVerifyFormSchema,\n} from \"@firebase-oss/ui-core\";\n\nimport { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { Policies } from \"@/components/policies\";\nimport { CountrySelector, type CountrySelectorRef } from \"@/components/country-selector\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@/components/ui/input-otp\";\n\ntype VerifyPhoneNumberFormProps = {\n verificationId: string;\n onSuccess: (credential: UserCredential) => void;\n};\n\nfunction VerifyPhoneNumberForm(props: VerifyPhoneNumberFormProps) {\n const ui = useUI();\n const schema = usePhoneAuthVerifyFormSchema();\n const action = useVerifyPhoneNumberFormAction();\n\n const form = useForm<PhoneAuthVerifyFormSchema>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n verificationId: props.verificationId,\n verificationCode: \"\",\n },\n });\n\n async function onSubmit(values: PhoneAuthVerifyFormSchema) {\n try {\n const credential = await action(values);\n props.onSuccess(credential);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-4\">\n <FormField\n control={form.control}\n name=\"verificationCode\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"verificationCode\")}</FormLabel>\n <FormDescription>{getTranslation(ui, \"prompts\", \"smsVerificationPrompt\")}</FormDescription>\n <FormControl>\n <InputOTP maxLength={6} {...field}>\n <InputOTPGroup>\n <InputOTPSlot index={0} />\n <InputOTPSlot index={1} />\n <InputOTPSlot index={2} />\n <InputOTPSlot index={3} />\n <InputOTPSlot index={4} />\n <InputOTPSlot index={5} />\n </InputOTPGroup>\n </InputOTP>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"verifyCode\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n </form>\n </Form>\n );\n}\n\ntype PhoneNumberFormProps = {\n onSubmit: (verificationId: string) => void;\n};\n\nfunction PhoneNumberForm(props: PhoneNumberFormProps) {\n const ui = useUI();\n const recaptchaContainerRef = useRef<HTMLDivElement>(null);\n const recaptchaVerifier = useRecaptchaVerifier(recaptchaContainerRef);\n const countrySelector = useRef<CountrySelectorRef>(null);\n const action = usePhoneNumberFormAction();\n const schema = usePhoneAuthNumberFormSchema();\n\n const form = useForm<PhoneAuthNumberFormSchema>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n phoneNumber: \"\",\n },\n });\n\n async function onSubmit(values: PhoneAuthNumberFormSchema) {\n try {\n const formatted = formatPhoneNumber(values.phoneNumber, countrySelector.current!.getCountry());\n const verificationId = await action({ phoneNumber: formatted, recaptchaVerifier: recaptchaVerifier! });\n props.onSubmit(verificationId);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-4\">\n <FormField\n control={form.control}\n name=\"phoneNumber\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"phoneNumber\")}</FormLabel>\n <FormControl>\n <div className=\"flex items-center gap-2\">\n <CountrySelector ref={countrySelector} />\n <Input {...field} type=\"tel\" />\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <div ref={recaptchaContainerRef} />\n <Policies />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"sendCode\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n </form>\n </Form>\n );\n}\n\nexport type { PhoneAuthFormProps };\n\nexport function PhoneAuthForm(props: PhoneAuthFormProps) {\n const [verificationId, setVerificationId] = useState<string | null>(null);\n\n if (!verificationId) {\n return <PhoneNumberForm onSubmit={setVerificationId} />;\n }\n\n return (\n <VerifyPhoneNumberForm\n verificationId={verificationId}\n onSuccess={(credential) => {\n props.onSignIn?.(credential);\n }}\n />\n );\n}\n",
21
+ "type": "registry:component"
22
+ }
23
+ ],
24
+ "meta": {
25
+ "version": "0.0.1"
26
+ }
27
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "phone-auth-screen",
4
+ "type": "registry:block",
5
+ "title": "Phone Auth Screen",
6
+ "description": "A screen allowing users to authenticate using their phone number with SMS verification.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "card",
12
+ "separator",
13
+ "https://firebaseopensource.com/r/phone-auth-form.json",
14
+ "https://firebaseopensource.com/r/multi-factor-auth-assertion-form.json",
15
+ "https://firebaseopensource.com/r/redirect-error.json"
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "src/components/phone-auth-screen.tsx",
20
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport type { PropsWithChildren } from \"react\";\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, useOnUserAuthenticated } from \"@firebase-oss/ui-react\";\nimport { Card, CardContent, CardHeader, CardDescription, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { PhoneAuthForm } from \"@/components/phone-auth-form\";\nimport { MultiFactorAuthAssertionScreen } from \"@/components/multi-factor-auth-assertion-screen\";\nimport { RedirectError } from \"@/components/redirect-error\";\nimport type { User } from \"firebase/auth\";\n\nexport type PhoneAuthScreenProps = PropsWithChildren<{\n onSignIn?: (user: User) => void;\n}>;\n\nexport function PhoneAuthScreen({ children, onSignIn }: PhoneAuthScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"signIn\");\n const subtitleText = getTranslation(ui, \"prompts\", \"signInToAccount\");\n\n useOnUserAuthenticated(onSignIn);\n\n if (ui.multiFactorResolver) {\n return <MultiFactorAuthAssertionScreen />;\n }\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <PhoneAuthForm />\n {children ? (\n <>\n <Separator className=\"my-4\" />\n <div className=\"space-y-2\">\n {children}\n <RedirectError />\n </div>\n </>\n ) : null}\n </CardContent>\n </Card>\n </div>\n );\n}\n",
21
+ "type": "registry:component"
22
+ }
23
+ ],
24
+ "meta": {
25
+ "version": "0.0.1"
26
+ }
27
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "policies",
4
+ "type": "registry:block",
5
+ "title": "Policies",
6
+ "description": "A component allowing users to navigate to the terms of service and privacy policy.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "src/components/policies.tsx",
13
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { cn } from \"@/lib/utils\";\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, PolicyContext } from \"@firebase-oss/ui-react\";\nimport { cloneElement, useContext } from \"react\";\n\nexport function Policies() {\n const ui = useUI();\n const policies = useContext(PolicyContext);\n\n if (!policies) {\n return null;\n }\n\n const { termsOfServiceUrl, privacyPolicyUrl, onNavigate } = policies;\n const termsAndPrivacyText = getTranslation(ui, \"messages\", \"termsAndPrivacy\");\n const parts = termsAndPrivacyText.split(/(\\{tos\\}|\\{privacy\\})/);\n\n const className = cn(\"hover:underline font-semibold\");\n const Handler = onNavigate ? (\n <button className={className} />\n ) : (\n <a target=\"_blank\" rel=\"noopener noreferrer\" className={className} />\n );\n\n return (\n <div className=\"text-text-muted text-center text-xs\">\n {parts.map((part: string, index: number) => {\n if (part === \"{tos}\") {\n return cloneElement(Handler, {\n key: index,\n onClick: onNavigate ? () => onNavigate(termsOfServiceUrl) : undefined,\n href: onNavigate ? undefined : termsOfServiceUrl,\n children: getTranslation(ui, \"labels\", \"termsOfService\"),\n });\n }\n\n if (part === \"{privacy}\") {\n return cloneElement(Handler, {\n key: index,\n onClick: onNavigate ? () => onNavigate(privacyPolicyUrl) : undefined,\n href: onNavigate ? undefined : privacyPolicyUrl,\n children: getTranslation(ui, \"labels\", \"privacyPolicy\"),\n });\n }\n\n return <span key={index}>{part}</span>;\n })}\n </div>\n );\n}\n",
14
+ "type": "registry:component"
15
+ }
16
+ ],
17
+ "meta": {
18
+ "version": "0.0.1"
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "redirect-error",
4
+ "type": "registry:block",
5
+ "title": "Redirect Error",
6
+ "description": "A component that displays redirect errors from Firebase UI authentication flow.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "src/components/redirect-error.tsx",
13
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { useRedirectError } from \"@firebase-oss/ui-react\";\n\nexport function RedirectError() {\n const error = useRedirectError();\n\n if (!error) {\n return null;\n }\n\n return <div className=\"text-sm text-destructive\">{error}</div>;\n}\n",
14
+ "type": "registry:component"
15
+ }
16
+ ],
17
+ "meta": {
18
+ "version": "0.0.1"
19
+ }
20
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sign-in-auth-form",
4
+ "type": "registry:block",
5
+ "title": "Sign In Auth Form",
6
+ "description": "A form allowing users to sign in with email and password.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "input",
12
+ "button",
13
+ "form",
14
+ "https://firebaseopensource.com/r/policies.json"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/sign-in-auth-form.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport type { SignInAuthFormSchema } from \"@firebase-oss/ui-core\";\nimport {\n useSignInAuthFormAction,\n useSignInAuthFormSchema,\n useUI,\n type SignInAuthFormProps,\n} from \"@firebase-oss/ui-react\";\nimport { useForm } from \"react-hook-form\";\nimport { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\nimport { FirebaseUIError, getTranslation } from \"@firebase-oss/ui-core\";\n\nimport { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { Policies } from \"./policies\";\n\nexport type { SignInAuthFormProps };\n\nexport function SignInAuthForm(props: SignInAuthFormProps) {\n const ui = useUI();\n const schema = useSignInAuthFormSchema();\n const action = useSignInAuthFormAction();\n\n const form = useForm<SignInAuthFormSchema>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n email: \"\",\n password: \"\",\n },\n });\n\n async function onSubmit(values: SignInAuthFormSchema) {\n try {\n const credential = await action(values);\n props.onSignIn?.(credential);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-y-4\">\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"emailAddress\")}</FormLabel>\n <FormControl>\n <Input {...field} type=\"email\" />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"password\"\n render={({ field }) => (\n <FormItem>\n <FormLabel className=\"flex items-center gap-2\">\n <span className=\"grow\">{getTranslation(ui, \"labels\", \"password\")}</span>\n {props.onForgotPasswordClick ? (\n <Button type=\"button\" variant=\"link\" onClick={props.onForgotPasswordClick} size=\"sm\">\n <span className=\"text-xs\">{getTranslation(ui, \"labels\", \"forgotPassword\")}</span>\n </Button>\n ) : null}\n </FormLabel>\n <FormControl>\n <Input {...field} type=\"password\" />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Policies />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"signIn\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n {props.onSignUpClick ? (\n <>\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={props.onSignUpClick}>\n <span className=\"text-xs\">\n {getTranslation(ui, \"prompts\", \"noAccount\")} {getTranslation(ui, \"labels\", \"signUp\")}\n </span>\n </Button>\n </>\n ) : null}\n </form>\n </Form>\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sign-in-auth-screen",
4
+ "type": "registry:block",
5
+ "title": "Sign In Auth Screen",
6
+ "description": "A screen allowing users to sign in with email and password.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "separator",
12
+ "card",
13
+ "https://firebaseopensource.com/r/sign-in-auth-form.json",
14
+ "https://firebaseopensource.com/r/multi-factor-auth-assertion-screen.json"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/sign-in-auth-screen.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type SignInAuthScreenProps, useOnUserAuthenticated } from \"@firebase-oss/ui-react\";\n\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { SignInAuthForm } from \"@/components/sign-in-auth-form\";\nimport { MultiFactorAuthAssertionScreen } from \"@/components/multi-factor-auth-assertion-screen\";\n\nexport type { SignInAuthScreenProps };\n\nexport function SignInAuthScreen({ children, onSignIn, ...props }: SignInAuthScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"signIn\");\n const subtitleText = getTranslation(ui, \"prompts\", \"signInToAccount\");\n\n useOnUserAuthenticated(onSignIn);\n\n if (ui.multiFactorResolver) {\n return <MultiFactorAuthAssertionScreen />;\n }\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <SignInAuthForm {...props} />\n {children ? (\n <>\n <Separator className=\"my-4\" />\n <div className=\"space-y-2\">{children}</div>\n </>\n ) : null}\n </CardContent>\n </Card>\n </div>\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sign-up-auth-form",
4
+ "type": "registry:block",
5
+ "title": "Sign Up Auth Form",
6
+ "description": "A form allowing users to sign up with email and password.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "input",
12
+ "button",
13
+ "form",
14
+ "https://firebaseopensource.com/r/policies.json"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/sign-up-auth-form.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport type { SignUpAuthFormSchema } from \"@firebase-oss/ui-core\";\nimport {\n useSignUpAuthFormAction,\n useSignUpAuthFormSchema,\n useUI,\n type SignUpAuthFormProps,\n useRequireDisplayName,\n} from \"@firebase-oss/ui-react\";\nimport { useForm } from \"react-hook-form\";\nimport { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\nimport { FirebaseUIError, getTranslation } from \"@firebase-oss/ui-core\";\n\nimport { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { Policies } from \"./policies\";\n\nexport type { SignUpAuthFormProps };\n\nexport function SignUpAuthForm(props: SignUpAuthFormProps) {\n const ui = useUI();\n const schema = useSignUpAuthFormSchema();\n const action = useSignUpAuthFormAction();\n const requireDisplayName = useRequireDisplayName();\n\n const form = useForm<SignUpAuthFormSchema>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n email: \"\",\n password: \"\",\n displayName: requireDisplayName ? \"\" : undefined,\n },\n });\n\n async function onSubmit(values: SignUpAuthFormSchema) {\n try {\n const credential = await action(values);\n props.onSignUp?.(credential);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-y-4\">\n {requireDisplayName ? (\n <FormField\n control={form.control}\n name=\"displayName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"displayName\")}</FormLabel>\n <FormControl>\n <Input {...field} />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n ) : null}\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"emailAddress\")}</FormLabel>\n <FormControl>\n <Input {...field} type=\"email\" />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"password\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"password\")}</FormLabel>\n <FormControl>\n <Input {...field} type=\"password\" />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Policies />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"createAccount\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n {props.onSignInClick ? (\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={props.onSignInClick}>\n <span className=\"text-xs\">\n {getTranslation(ui, \"prompts\", \"haveAccount\")} {getTranslation(ui, \"labels\", \"signIn\")}\n </span>\n </Button>\n ) : null}\n </form>\n </Form>\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sign-up-auth-screen",
4
+ "type": "registry:block",
5
+ "title": "Sign Up Auth Screen",
6
+ "description": "A screen allowing users to sign up with email and password.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "separator",
12
+ "card",
13
+ "https://firebaseopensource.com/r/sign-up-auth-form.json",
14
+ "https://firebaseopensource.com/r/multi-factor-auth-assertion-screen.json"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/sign-up-auth-screen.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { getTranslation } from \"@firebase-oss/ui-core\";\nimport { useUI, type SignUpAuthScreenProps, useOnUserAuthenticated } from \"@firebase-oss/ui-react\";\n\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { SignUpAuthForm } from \"@/components/sign-up-auth-form\";\nimport { MultiFactorAuthAssertionScreen } from \"@/components/multi-factor-auth-assertion-screen\";\n\nexport type { SignUpAuthScreenProps };\n\nexport function SignUpAuthScreen({ children, onSignUp, ...props }: SignUpAuthScreenProps) {\n const ui = useUI();\n\n const titleText = getTranslation(ui, \"labels\", \"signUp\");\n const subtitleText = getTranslation(ui, \"prompts\", \"enterDetailsToCreate\");\n\n useOnUserAuthenticated(onSignUp);\n\n if (ui.multiFactorResolver) {\n return <MultiFactorAuthAssertionScreen />;\n }\n\n return (\n <div className=\"max-w-sm mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle>{titleText}</CardTitle>\n <CardDescription>{subtitleText}</CardDescription>\n </CardHeader>\n <CardContent>\n <SignUpAuthForm {...props} />\n {children ? (\n <>\n <Separator className=\"my-4\" />\n <div className=\"space-y-2\">{children}</div>\n </>\n ) : null}\n </CardContent>\n </Card>\n </div>\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sms-multi-factor-assertion-form",
4
+ "type": "registry:block",
5
+ "title": "SMS Multi-Factor Assertion Form",
6
+ "description": "A form allowing users to complete SMS-based multi-factor authentication during sign-in.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "form",
12
+ "input",
13
+ "button",
14
+ "input-otp"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "src/components/sms-multi-factor-assertion-form.tsx",
19
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport { type UserCredential, type MultiFactorInfo } from \"firebase/auth\";\n\nimport { FirebaseUIError, getTranslation } from \"@firebase-oss/ui-core\";\nimport {\n useMultiFactorPhoneAuthVerifyFormSchema,\n useRecaptchaVerifier,\n useUI,\n useSmsMultiFactorAssertionPhoneFormAction,\n useSmsMultiFactorAssertionVerifyFormAction,\n} from \"@firebase-oss/ui-react\";\nimport { useForm } from \"react-hook-form\";\nimport { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\n\nimport { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from \"@/components/ui/form\";\nimport { Button } from \"@/components/ui/button\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@/components/ui/input-otp\";\n\ntype PhoneMultiFactorInfo = MultiFactorInfo & {\n phoneNumber?: string;\n};\n\ntype SmsMultiFactorAssertionPhoneFormProps = {\n hint: MultiFactorInfo;\n onSubmit: (verificationId: string) => void;\n};\n\nfunction SmsMultiFactorAssertionPhoneForm(props: SmsMultiFactorAssertionPhoneFormProps) {\n const ui = useUI();\n const recaptchaContainerRef = useRef<HTMLDivElement>(null);\n const recaptchaVerifier = useRecaptchaVerifier(recaptchaContainerRef);\n const action = useSmsMultiFactorAssertionPhoneFormAction();\n const [error, setError] = useState<string | null>(null);\n\n const onSubmit = async () => {\n try {\n setError(null);\n const verificationId = await action({ hint: props.hint, recaptchaVerifier: recaptchaVerifier! });\n props.onSubmit(verificationId);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n setError(message);\n }\n };\n\n return (\n <div className=\"space-y-4\">\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"phoneNumber\")}</FormLabel>\n <FormDescription>\n {getTranslation(ui, \"messages\", \"mfaSmsAssertionPrompt\", {\n phoneNumber: (props.hint as PhoneMultiFactorInfo).phoneNumber || \"\",\n })}\n </FormDescription>\n </FormItem>\n <div className=\"fui-recaptcha-container\" ref={recaptchaContainerRef} />\n <Button onClick={onSubmit} disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"sendCode\")}\n </Button>\n {error && <div className=\"text-sm text-red-600\">{error}</div>}\n </div>\n );\n}\n\ntype SmsMultiFactorAssertionVerifyFormProps = {\n verificationId: string;\n onSuccess: (credential: UserCredential) => void;\n};\n\nfunction SmsMultiFactorAssertionVerifyForm(props: SmsMultiFactorAssertionVerifyFormProps) {\n const ui = useUI();\n const schema = useMultiFactorPhoneAuthVerifyFormSchema();\n const action = useSmsMultiFactorAssertionVerifyFormAction();\n\n const form = useForm<{ verificationId: string; verificationCode: string }>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n verificationId: props.verificationId,\n verificationCode: \"\",\n },\n });\n\n const onSubmit = async (values: { verificationId: string; verificationCode: string }) => {\n try {\n const credential = await action({\n verificationId: values.verificationId,\n verificationCode: values.verificationCode,\n });\n props.onSuccess(credential);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n };\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-y-4\">\n <FormField\n control={form.control}\n name=\"verificationCode\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"verificationCode\")}</FormLabel>\n <FormDescription>{getTranslation(ui, \"prompts\", \"smsVerificationPrompt\")}</FormDescription>\n <FormControl>\n <InputOTP maxLength={6} {...field}>\n <InputOTPGroup>\n <InputOTPSlot index={0} />\n <InputOTPSlot index={1} />\n <InputOTPSlot index={2} />\n <InputOTPSlot index={3} />\n <InputOTPSlot index={4} />\n <InputOTPSlot index={5} />\n </InputOTPGroup>\n </InputOTP>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"verifyCode\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n </form>\n </Form>\n );\n}\n\nexport type SmsMultiFactorAssertionFormProps = {\n hint: MultiFactorInfo;\n onSuccess?: (credential: UserCredential) => void;\n};\n\nexport function SmsMultiFactorAssertionForm(props: SmsMultiFactorAssertionFormProps) {\n const [verification, setVerification] = useState<{\n verificationId: string;\n } | null>(null);\n\n if (!verification) {\n return (\n <SmsMultiFactorAssertionPhoneForm\n hint={props.hint}\n onSubmit={(verificationId) => setVerification({ verificationId })}\n />\n );\n }\n\n return (\n <SmsMultiFactorAssertionVerifyForm\n verificationId={verification.verificationId}\n onSuccess={(credential) => {\n props.onSuccess?.(credential);\n }}\n />\n );\n}\n",
20
+ "type": "registry:component"
21
+ }
22
+ ],
23
+ "meta": {
24
+ "version": "0.0.1"
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sms-multi-factor-enrollment-form",
4
+ "type": "registry:block",
5
+ "title": "SMS Multi-Factor Enrollment Form",
6
+ "description": "A form allowing users to enroll SMS-based multi-factor authentication.",
7
+ "dependencies": [
8
+ "@firebase-oss/ui-react"
9
+ ],
10
+ "registryDependencies": [
11
+ "form",
12
+ "input",
13
+ "button",
14
+ "input-otp",
15
+ "https://firebaseopensource.com/r/country-selector.json"
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "src/components/sms-multi-factor-enrollment-form.tsx",
20
+ "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport { multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator } from \"firebase/auth\";\nimport {\n enrollWithMultiFactorAssertion,\n FirebaseUIError,\n formatPhoneNumber,\n getTranslation,\n verifyPhoneNumber,\n} from \"@firebase-oss/ui-core\";\nimport { CountrySelector, type CountrySelectorRef } from \"@/components/country-selector\";\nimport {\n useMultiFactorPhoneAuthNumberFormSchema,\n useMultiFactorPhoneAuthVerifyFormSchema,\n useRecaptchaVerifier,\n useUI,\n} from \"@firebase-oss/ui-react\";\nimport { useForm } from \"react-hook-form\";\nimport { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\n\nimport { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@/components/ui/input-otp\";\n\ntype MultiFactorEnrollmentPhoneNumberFormProps = {\n onSubmit: (verificationId: string, displayName?: string) => void;\n};\n\nfunction MultiFactorEnrollmentPhoneNumberForm(props: MultiFactorEnrollmentPhoneNumberFormProps) {\n const ui = useUI();\n const recaptchaContainerRef = useRef<HTMLDivElement>(null);\n const recaptchaVerifier = useRecaptchaVerifier(recaptchaContainerRef);\n const countrySelector = useRef<CountrySelectorRef>(null);\n const schema = useMultiFactorPhoneAuthNumberFormSchema();\n\n const form = useForm<{ displayName: string; phoneNumber: string }>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n displayName: \"\",\n phoneNumber: \"\",\n },\n });\n\n const onSubmit = async (values: { displayName: string; phoneNumber: string }) => {\n try {\n const formatted = formatPhoneNumber(values.phoneNumber, countrySelector.current!.getCountry());\n const mfaUser = multiFactor(ui.auth.currentUser!);\n const confirmationResult = await verifyPhoneNumber(ui, formatted, recaptchaVerifier!, mfaUser);\n props.onSubmit(confirmationResult, values.displayName);\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n };\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex flex-col gap-y-4\">\n <FormField\n control={form.control}\n name=\"displayName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"displayName\")}</FormLabel>\n <FormControl>\n <Input {...field} type=\"text\" />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"phoneNumber\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"phoneNumber\")}</FormLabel>\n <FormControl>\n <div className=\"flex items-center gap-2\">\n <CountrySelector ref={countrySelector} />\n <Input {...field} type=\"tel\" className=\"flex-grow\" />\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <div className=\"fui-recaptcha-container\" ref={recaptchaContainerRef} />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"sendCode\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n </form>\n </Form>\n );\n}\n\ntype MultiFactorEnrollmentVerifyPhoneNumberFormProps = {\n verificationId: string;\n displayName?: string;\n onSuccess: () => void;\n};\n\nexport function MultiFactorEnrollmentVerifyPhoneNumberForm(props: MultiFactorEnrollmentVerifyPhoneNumberFormProps) {\n const ui = useUI();\n const schema = useMultiFactorPhoneAuthVerifyFormSchema();\n\n const form = useForm<{ verificationId: string; verificationCode: string }>({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n verificationId: props.verificationId,\n verificationCode: \"\",\n },\n });\n\n const onSubmit = async (values: { verificationId: string; verificationCode: string }) => {\n try {\n const credential = PhoneAuthProvider.credential(values.verificationId, values.verificationCode);\n const assertion = PhoneMultiFactorGenerator.assertion(credential);\n await enrollWithMultiFactorAssertion(ui, assertion, props.displayName);\n props.onSuccess();\n } catch (error) {\n const message = error instanceof FirebaseUIError ? error.message : String(error);\n form.setError(\"root\", { message });\n }\n };\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"verificationCode\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{getTranslation(ui, \"labels\", \"verificationCode\")}</FormLabel>\n <FormDescription>{getTranslation(ui, \"prompts\", \"smsVerificationPrompt\")}</FormDescription>\n <FormControl>\n <InputOTP maxLength={6} {...field}>\n <InputOTPGroup>\n <InputOTPSlot index={0} />\n <InputOTPSlot index={1} />\n <InputOTPSlot index={2} />\n <InputOTPSlot index={3} />\n <InputOTPSlot index={4} />\n <InputOTPSlot index={5} />\n </InputOTPGroup>\n </InputOTP>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\" disabled={ui.state !== \"idle\"}>\n {getTranslation(ui, \"labels\", \"verifyCode\")}\n </Button>\n {form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}\n </form>\n </Form>\n );\n}\n\nexport type SmsMultiFactorEnrollmentFormProps = {\n onSuccess?: () => void;\n};\n\nexport function SmsMultiFactorEnrollmentForm(props: SmsMultiFactorEnrollmentFormProps) {\n const ui = useUI();\n\n const [verification, setVerification] = useState<{\n verificationId: string;\n displayName?: string;\n } | null>(null);\n\n if (!ui.auth.currentUser) {\n throw new Error(\"User must be authenticated to enroll with multi-factor authentication\");\n }\n\n if (!verification) {\n return (\n <MultiFactorEnrollmentPhoneNumberForm\n onSubmit={(verificationId, displayName) => setVerification({ verificationId, displayName })}\n />\n );\n }\n\n return (\n <MultiFactorEnrollmentVerifyPhoneNumberForm\n {...verification}\n onSuccess={() => {\n props.onSuccess?.();\n }}\n />\n );\n}\n",
21
+ "type": "registry:component"
22
+ }
23
+ ],
24
+ "meta": {
25
+ "version": "0.0.1"
26
+ }
27
+ }