From c538489ab25e7a863ed8a4c03b8b81fea657ed16 Mon Sep 17 00:00:00 2001 From: Jakub Andrzejewski Date: Wed, 3 Dec 2025 19:59:18 +0100 Subject: [PATCH] Docs/vue nuxt social auth (#40662) * docs: add vue social auth * docs: add nuxt social auth * docs: nuxt social auth typo * docs: vue-nuxt-social-auth cr fix * Apply suggestion from @saltcod --------- Co-authored-by: Ivan Vasilov Co-authored-by: Terry Sutton --- apps/ui-library/config/docs.ts | 2 +- .../docs/nuxtjs/password-based-auth.mdx | 2 +- .../content/docs/nuxtjs/social-auth.mdx | 61 +++++++++++++ .../content/docs/vue/social-auth.mdx | 57 +++++++++++++ apps/ui-library/public/llms.txt | 6 +- .../public/r/password-based-auth-vue.json | 12 ++- apps/ui-library/public/r/registry.json | 85 ++++++++++++++++++- .../public/r/social-auth-nuxtjs.json | 61 +++++++++++++ apps/ui-library/public/r/social-auth-vue.json | 22 +++++ .../vue/registry-item.json | 12 ++- .../nuxtjs/app/components/login-form.vue | 61 +++++++++++++ .../nuxtjs/app/components/logout-button.vue | 17 ++++ .../nuxtjs/app/pages/auth/error.vue | 30 +++++++ .../nuxtjs/app/pages/auth/login.vue | 11 +++ .../nuxtjs/app/pages/protected/index.vue | 36 ++++++++ .../social-auth/nuxtjs/registry-item.json | 45 ++++++++++ .../nuxtjs/server/middleware/auth.ts | 24 ++++++ .../nuxtjs/server/routes/auth/oauth.ts | 38 +++++++++ .../social-auth/vue/components/login-form.vue | 57 +++++++++++++ .../social-auth/vue/registry-item.json | 15 ++++ blocks/vue/registry/index.ts | 3 +- blocks/vue/registry/social-auth.ts | 5 ++ 22 files changed, 646 insertions(+), 16 deletions(-) create mode 100644 apps/ui-library/content/docs/nuxtjs/social-auth.mdx create mode 100644 apps/ui-library/content/docs/vue/social-auth.mdx create mode 100644 apps/ui-library/public/r/social-auth-nuxtjs.json create mode 100644 apps/ui-library/public/r/social-auth-vue.json create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/app/components/login-form.vue create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/app/components/logout-button.vue create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/error.vue create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/login.vue create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/app/pages/protected/index.vue create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/registry-item.json create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/server/middleware/auth.ts create mode 100644 blocks/vue/registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts create mode 100644 blocks/vue/registry/default/social-auth/vue/components/login-form.vue create mode 100644 blocks/vue/registry/default/social-auth/vue/registry-item.json create mode 100644 blocks/vue/registry/social-auth.ts diff --git a/apps/ui-library/config/docs.ts b/apps/ui-library/config/docs.ts index 5e005ea9937..df6701749d0 100644 --- a/apps/ui-library/config/docs.ts +++ b/apps/ui-library/config/docs.ts @@ -68,7 +68,7 @@ export const componentPages: SidebarNavGroup = { }, { title: 'Social Auth', - supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'], + supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'], href: '/docs/nextjs/social-auth', items: [], new: true, diff --git a/apps/ui-library/content/docs/nuxtjs/password-based-auth.mdx b/apps/ui-library/content/docs/nuxtjs/password-based-auth.mdx index 6323b654a8c..a49337cf2b7 100644 --- a/apps/ui-library/content/docs/nuxtjs/password-based-auth.mdx +++ b/apps/ui-library/content/docs/nuxtjs/password-based-auth.mdx @@ -70,7 +70,7 @@ NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY= ### Setting up routes and redirect URLs 1. Set the site URL in the [URL Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) settings in the Supabase Dashboard. -1. Set up the Next.js route that users will visit to reset or update their password. Go to the [URL Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) settings and add the `forgot-password` route to the list of Redirect URLs. It should look something like: `http://example.com/auth/forgot-password`. +1. Set up the Nuxt.js route that users will visit to reset or update their password. Go to the [URL Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) settings and add the `forgot-password` route to the list of Redirect URLs. It should look something like: `http://example.com/auth/forgot-password`. 1. Update the redirect paths in `login-form.vue` and `update-password-form.vue` components to point to the logged-in routes in your app. Our examples use `/protected`, but you can set this to whatever fits your app. diff --git a/apps/ui-library/content/docs/nuxtjs/social-auth.mdx b/apps/ui-library/content/docs/nuxtjs/social-auth.mdx new file mode 100644 index 00000000000..c1f12d6d57e --- /dev/null +++ b/apps/ui-library/content/docs/nuxtjs/social-auth.mdx @@ -0,0 +1,61 @@ +--- +title: Social Authentication +description: Social authentication block for Nuxt.js +--- + + + + + The block is using Github provider by default, but can be easily switched by changing a single + parameter. + + +## Installation + + + +## Folder structure + +This block assumes that you have already installed a Supabase client for Nuxt from the previous step. + + + +## Usage + +Once you install the block in your Nuxt.js project, you'll get all the necessary pages and components to set up a social authentication flow. + +### Getting started + +After installing the block, you'll have the following environment variables in your `.env.local` file: + +```env +NUXT_PUBLIC_SUPABASE_URL= +NUXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY= +``` + +- If you're using supabase.com, you can find these values in the [Connect modal](https://supabase.com/dashboard/project/_?showConnect=true&connectTab=frameworks&framework=nuxtjs&using=supabasejs) under App Frameworks or in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api). + +- If you're using a local instance of Supabase, you can find these values by running `supabase start` or `supabase status` (if you already have it running). + +### Setting up third party providers + +We support a wide variety of social providers that you can use to integrate with your application. The full list is available [here](https://supabase.com/docs/guides/auth/social-login). +This block uses the PKCE flow with GitHub as the provider. To switch providers, just update the `provider` field in the `supabase.auth.signInWithOAuth` call. Enable the provider you want to use under [Auth Providers](https://supabase.com/dashboard/project/_/auth/providers) in the Supabase Dashboard and add the necessary credentials. + +### Setting up routes and redirect URLs + +1. Set the site URL in the [URL Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) settings in the Supabase Dashboard. +1. Update the redirect paths in `login-form.vue` to point to your app’s logged-in routes. Our examples use `/protected`, but you can set this to whatever fits your app. +1. Visit `http://your-site-url/auth/login` to see this component in action. + +### Combining social auth with password-based auth + +If you want to combine this block with the password-based auth, you need to: + +- Copy the `handleSocialLogin` function into the password-based `login-form.vue` component and bind it to a "Login with ..." button. +- Copy the `@/server/routes/auth/oauth.ts` in your app under the same route. + +## Further reading + +- [Social login](https://supabase.com/docs/guides/auth/social-login) +- [Authentication error codes](https://supabase.com/docs/guides/auth/debugging/error-codes) diff --git a/apps/ui-library/content/docs/vue/social-auth.mdx b/apps/ui-library/content/docs/vue/social-auth.mdx new file mode 100644 index 00000000000..729dc532b5a --- /dev/null +++ b/apps/ui-library/content/docs/vue/social-auth.mdx @@ -0,0 +1,57 @@ +--- +title: Social Authentication +description: Social authentication block for Vue Single Page Applications +--- + + + + + The block is using GitHub provider by default, but can be easily switched by changing a single + parameter. + + +## Installation + + + +## Folder structure + +This block assumes that you have already installed a Supabase client for Vue from the previous step. + + + +## Usage + +Once you install the block in your Vue project, you'll get all the necessary pages and components to set up a social authentication flow. + +### Getting started + +After installing the block, you'll have the following environment variables in your `.env.local` file: + +```env +VITE_SUPABASE_URL= +VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY= +``` + +- If you're using supabase.com, you can find these values in the [Connect modal](https://supabase.com/dashboard/project/_?showConnect=true&connectTab=frameworks&framework=react&using=vite&with=supabasejs) under App Frameworks or in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api). + +- If you're using a local instance of Supabase, you can find these values by running `supabase start` or `supabase status` (if you already have it running). + +### Setting up third party providers + +We support a wide variety of social providers that you can use to integrate with your application. The full list is available [here](https://supabase.com/docs/guides/auth/social-login). +This block uses the implicit flow with GitHub as the provider. To switch providers, just update the `provider` field in the `supabase.auth.signInWithOAuth` call. Enable the provider you want to use under [Auth Providers](https://supabase.com/dashboard/project/_/auth/providers) in the Supabase Dashboard and add the necessary credentials. + +### Setting up routes and redirect URLs + +1. Set the site URL in the [URL Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) settings in the Supabase Dashboard. +1. Update the redirect paths in `login-form.vue` to point to your app’s logged-in routes. Our examples use `/protected`, but you can set this to whatever fits your app. + +### Combining social auth with password-based auth + +If you want to combine this block with the password-based auth, you need to copy the `handleSocialLogin` function into the password-based `login-form.vue` component and bind it to a button. + +## Further reading + +- [Social login](https://supabase.com/docs/guides/auth/social-login) +- [Authentication error codes](https://supabase.com/docs/guides/auth/debugging/error-codes) diff --git a/apps/ui-library/public/llms.txt b/apps/ui-library/public/llms.txt index aa18fe91ff8..179a12011d2 100644 --- a/apps/ui-library/public/llms.txt +++ b/apps/ui-library/public/llms.txt @@ -1,5 +1,5 @@ # Supabase UI Library -Last updated: 2025-11-06T16:46:13.786Z +Last updated: 2025-11-20T11:14:14.146Z ## Overview Library of components for your project. The components integrate with Supabase and are shadcn compatible. @@ -35,6 +35,8 @@ Library of components for your project. The components integrate with Supabase a - Supabase client for Nuxt.js - [Password-based Authentication](https://supabase.com/ui/docs/nuxtjs/password-based-auth) - Password-based authentication block for Nuxt.js +- [Social Authentication](https://supabase.com/ui/docs/nuxtjs/social-auth) + - Social authentication block for Nuxt.js - [Platform Kit](https://supabase.com/ui/docs/platform/platform-kit) - The easiest way to build platforms on top of Supabase - [Supabase Client Libraries](https://supabase.com/ui/docs/react-router/client) @@ -89,3 +91,5 @@ Library of components for your project. The components integrate with Supabase a - Supabase client for Vue Single Page Applications - [Password-based Authentication](https://supabase.com/ui/docs/vue/password-based-auth) - Password-based authentication block for Vue Single Page Applications +- [Social Authentication](https://supabase.com/ui/docs/vue/social-auth) + - Social authentication block for Vue Single Page Applications diff --git a/apps/ui-library/public/r/password-based-auth-vue.json b/apps/ui-library/public/r/password-based-auth-vue.json index 4b5b1d4b10e..a3edccc7173 100644 --- a/apps/ui-library/public/r/password-based-auth-vue.json +++ b/apps/ui-library/public/r/password-based-auth-vue.json @@ -17,22 +17,26 @@ { "path": "registry/default/password-based-auth/vue/components/login-form.vue", "content": "\n\n\n", - "type": "registry:component" + "type": "registry:component", + "target": "components/login-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/sign-up-form.vue", "content": "\n\n\n", - "type": "registry:component" + "type": "registry:component", + "target": "components/sign-up-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/forgot-password-form.vue", "content": "\n\n\n", - "type": "registry:component" + "type": "registry:component", + "target": "components/forgot-password-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/update-password-form.vue", "content": "\n\n\n", - "type": "registry:component" + "type": "registry:component", + "target": "components/update-password-form.vue" } ] } \ No newline at end of file diff --git a/apps/ui-library/public/r/registry.json b/apps/ui-library/public/r/registry.json index ddef884a812..595c4484a22 100644 --- a/apps/ui-library/public/r/registry.json +++ b/apps/ui-library/public/r/registry.json @@ -1853,25 +1853,102 @@ "files": [ { "path": "registry/default/password-based-auth/vue/components/login-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/login-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/sign-up-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/sign-up-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/forgot-password-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/forgot-password-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/update-password-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/update-password-form.vue" } ], "dependencies": [ "@supabase/supabase-js@latest" ] }, + { + "name": "social-auth-vue", + "type": "registry:block", + "title": "Social Auth flow for Vue and Supabase", + "description": "Social Auth flow for Vue and Supabase", + "registryDependencies": [ + "button", + "card" + ], + "files": [ + { + "path": "registry/default/social-auth/vue/components/login-form.vue", + "type": "registry:component", + "target": "components/login-form.vue" + } + ], + "dependencies": [ + "@supabase/supabase-js@latest" + ] + }, + { + "name": "social-auth-nuxtjs", + "type": "registry:block", + "title": "Social Auth flow for Nuxt and Supabase", + "description": "Social Auth flow for Nuxt and Supabase", + "registryDependencies": [ + "button", + "card", + "input", + "label" + ], + "files": [ + { + "path": "registry/default/social-auth/nuxtjs/app/components/login-form.vue", + "type": "registry:file", + "target": "app/components/login-form.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/components/logout-button.vue", + "type": "registry:file", + "target": "app/components/logout-button.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/login.vue", + "type": "registry:file", + "target": "app/pages/auth/login.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/error.vue", + "type": "registry:file", + "target": "app/pages/auth/error.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/protected/index.vue", + "type": "registry:file", + "target": "app/pages/protected/index.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/middleware/auth.ts", + "type": "registry:file", + "target": "server/middleware/auth.ts" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts", + "type": "registry:file", + "target": "server/routes/auth/oauth.ts" + } + ], + "dependencies": [ + "@supabase/ssr@latest", + "@supabase/supabase-js@latest" + ] + }, { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ai-editor-rules", diff --git a/apps/ui-library/public/r/social-auth-nuxtjs.json b/apps/ui-library/public/r/social-auth-nuxtjs.json new file mode 100644 index 00000000000..64639fe3b99 --- /dev/null +++ b/apps/ui-library/public/r/social-auth-nuxtjs.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "social-auth-nuxtjs", + "type": "registry:block", + "title": "Social Auth flow for Nuxt and Supabase", + "description": "Social Auth flow for Nuxt and Supabase", + "dependencies": [ + "@supabase/ssr@latest", + "@supabase/supabase-js@latest" + ], + "registryDependencies": [ + "button", + "card", + "input", + "label" + ], + "files": [ + { + "path": "registry/default/social-auth/nuxtjs/app/components/login-form.vue", + "content": "\n\n\n", + "type": "registry:file", + "target": "app/components/login-form.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/components/logout-button.vue", + "content": "\n\n\n", + "type": "registry:file", + "target": "app/components/logout-button.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/login.vue", + "content": "\n\n", + "type": "registry:file", + "target": "app/pages/auth/login.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/error.vue", + "content": "\n\n", + "type": "registry:file", + "target": "app/pages/auth/error.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/protected/index.vue", + "content": "\n\n\n", + "type": "registry:file", + "target": "app/pages/protected/index.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/middleware/auth.ts", + "content": "import { defineEventHandler, sendRedirect } from 'h3'\nimport { createSupabaseServerClient } from '@/registry/default/clients/nuxtjs/server/supabase/client'\n\nexport default defineEventHandler(async (event) => {\n const supabase = createSupabaseServerClient(event)\n\n // Get user claims\n const { data } = await supabase.auth.getClaims()\n const user = data?.claims\n\n const pathname = event.node.req.url || '/'\n\n // Redirect if no user and not already on login/auth route\n if (\n !user &&\n !pathname.startsWith('/login') &&\n !pathname.startsWith('/auth')\n ) {\n return sendRedirect(event, '/auth/login')\n }\n\n // Return event as-is (you could return any object if needed)\n return { user }\n})\n", + "type": "registry:file", + "target": "server/middleware/auth.ts" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts", + "content": "import { createSupabaseServerClient } from '@/registry/default/clients/nuxtjs/server/supabase/client'\nimport { defineEventHandler, getQuery, sendRedirect, getRequestURL } from \"h3\"\n\nexport default defineEventHandler(async (event) => {\n const url = getRequestURL(event) // URL object of the current request\n const query = getQuery(event)\n\n const code = query.code as string | undefined\n let next = (query.next as string | undefined) ?? \"/\"\n\n if (!next.startsWith(\"/\")) {\n next = \"/\"\n }\n\n if (code) {\n const supabase = createSupabaseServerClient(event)\n const { error } = await supabase.auth.exchangeCodeForSession(code)\n\n if (!error) {\n // Determine origin\n const forwardedHost = event.node.req.headers[\"x-forwarded-host\"] as string | undefined\n const isLocalEnv = process.env.NODE_ENV === \"development\"\n const origin = `${url.protocol}//${url.host}`\n\n if (isLocalEnv) {\n return sendRedirect(event, `${origin}${next}`)\n } else if (forwardedHost) {\n return sendRedirect(event, `https://${forwardedHost}${next}`)\n } else {\n return sendRedirect(event, `${origin}${next}`)\n }\n }\n }\n\n // fallback to error page\n const origin = `${url.protocol}//${url.host}`\n return sendRedirect(event, `${origin}/auth/error`)\n})\n", + "type": "registry:file", + "target": "server/routes/auth/oauth.ts" + } + ] +} \ No newline at end of file diff --git a/apps/ui-library/public/r/social-auth-vue.json b/apps/ui-library/public/r/social-auth-vue.json new file mode 100644 index 00000000000..870daed4a91 --- /dev/null +++ b/apps/ui-library/public/r/social-auth-vue.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "social-auth-vue", + "type": "registry:block", + "title": "Social Auth flow for Vue and Supabase", + "description": "Social Auth flow for Vue and Supabase", + "dependencies": [ + "@supabase/supabase-js@latest" + ], + "registryDependencies": [ + "button", + "card" + ], + "files": [ + { + "path": "registry/default/social-auth/vue/components/login-form.vue", + "content": "\n\n\n", + "type": "registry:component", + "target": "components/login-form.vue" + } + ] +} \ No newline at end of file diff --git a/blocks/vue/registry/default/password-based-auth/vue/registry-item.json b/blocks/vue/registry/default/password-based-auth/vue/registry-item.json index 8966e22af38..bc62839ba3b 100644 --- a/blocks/vue/registry/default/password-based-auth/vue/registry-item.json +++ b/blocks/vue/registry/default/password-based-auth/vue/registry-item.json @@ -7,19 +7,23 @@ "files": [ { "path": "registry/default/password-based-auth/vue/components/login-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/login-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/sign-up-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/sign-up-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/forgot-password-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/forgot-password-form.vue" }, { "path": "registry/default/password-based-auth/vue/components/update-password-form.vue", - "type": "registry:component" + "type": "registry:component", + "target": "components/update-password-form.vue" } ], "dependencies": ["@supabase/supabase-js@latest"] diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/app/components/login-form.vue b/blocks/vue/registry/default/social-auth/nuxtjs/app/components/login-form.vue new file mode 100644 index 00000000000..ef5ebeb466c --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/app/components/login-form.vue @@ -0,0 +1,61 @@ + + + diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/app/components/logout-button.vue b/blocks/vue/registry/default/social-auth/nuxtjs/app/components/logout-button.vue new file mode 100644 index 00000000000..066aa006754 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/app/components/logout-button.vue @@ -0,0 +1,17 @@ + + + diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/error.vue b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/error.vue new file mode 100644 index 00000000000..966dea5d7c4 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/error.vue @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/login.vue b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/login.vue new file mode 100644 index 00000000000..76bdef58a23 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/auth/login.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/protected/index.vue b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/protected/index.vue new file mode 100644 index 00000000000..8722f8a6872 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/app/pages/protected/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/registry-item.json b/blocks/vue/registry/default/social-auth/nuxtjs/registry-item.json new file mode 100644 index 00000000000..b252d0c7f6a --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/registry-item.json @@ -0,0 +1,45 @@ +{ + "name": "social-auth-nuxtjs", + "type": "registry:block", + "title": "Social Auth flow for Nuxt and Supabase", + "description": "Social Auth flow for Nuxt and Supabase", + "registryDependencies": ["button", "card", "input", "label"], + "files": [ + { + "path": "registry/default/social-auth/nuxtjs/app/components/login-form.vue", + "type": "registry:file", + "target": "app/components/login-form.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/components/logout-button.vue", + "type": "registry:file", + "target": "app/components/logout-button.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/login.vue", + "type": "registry:file", + "target": "app/pages/auth/login.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/auth/error.vue", + "type": "registry:file", + "target": "app/pages/auth/error.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/app/pages/protected/index.vue", + "type": "registry:file", + "target": "app/pages/protected/index.vue" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/middleware/auth.ts", + "type": "registry:file", + "target": "server/middleware/auth.ts" + }, + { + "path": "registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts", + "type": "registry:file", + "target": "server/routes/auth/oauth.ts" + } + ], + "dependencies": ["@supabase/ssr@latest", "@supabase/supabase-js@latest"] +} diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/server/middleware/auth.ts b/blocks/vue/registry/default/social-auth/nuxtjs/server/middleware/auth.ts new file mode 100644 index 00000000000..e87ca43ea0e --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/server/middleware/auth.ts @@ -0,0 +1,24 @@ +import { defineEventHandler, sendRedirect } from 'h3' +import { createSupabaseServerClient } from '@/registry/default/clients/nuxtjs/server/supabase/client' + +export default defineEventHandler(async (event) => { + const supabase = createSupabaseServerClient(event) + + // Get user claims + const { data } = await supabase.auth.getClaims() + const user = data?.claims + + const pathname = event.node.req.url || '/' + + // Redirect if no user and not already on login/auth route + if ( + !user && + !pathname.startsWith('/login') && + !pathname.startsWith('/auth') + ) { + return sendRedirect(event, '/auth/login') + } + + // Return event as-is (you could return any object if needed) + return { user } +}) diff --git a/blocks/vue/registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts b/blocks/vue/registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts new file mode 100644 index 00000000000..16994eb83d5 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/nuxtjs/server/routes/auth/oauth.ts @@ -0,0 +1,38 @@ +import { createSupabaseServerClient } from '@/registry/default/clients/nuxtjs/server/supabase/client' +import { defineEventHandler, getQuery, sendRedirect, getRequestURL } from "h3" + +export default defineEventHandler(async (event) => { + const url = getRequestURL(event) // URL object of the current request + const query = getQuery(event) + + const code = query.code as string | undefined + let next = (query.next as string | undefined) ?? "/" + + if (!next.startsWith("/")) { + next = "/" + } + + if (code) { + const supabase = createSupabaseServerClient(event) + const { error } = await supabase.auth.exchangeCodeForSession(code) + + if (!error) { + // Determine origin + const forwardedHost = event.node.req.headers["x-forwarded-host"] as string | undefined + const isLocalEnv = process.env.NODE_ENV === "development" + const origin = `${url.protocol}//${url.host}` + + if (isLocalEnv) { + return sendRedirect(event, `${origin}${next}`) + } else if (forwardedHost) { + return sendRedirect(event, `https://${forwardedHost}${next}`) + } else { + return sendRedirect(event, `${origin}${next}`) + } + } + } + + // fallback to error page + const origin = `${url.protocol}//${url.host}` + return sendRedirect(event, `${origin}/auth/error`) +}) diff --git a/blocks/vue/registry/default/social-auth/vue/components/login-form.vue b/blocks/vue/registry/default/social-auth/vue/components/login-form.vue new file mode 100644 index 00000000000..6744a764eb0 --- /dev/null +++ b/blocks/vue/registry/default/social-auth/vue/components/login-form.vue @@ -0,0 +1,57 @@ + + + diff --git a/blocks/vue/registry/default/social-auth/vue/registry-item.json b/blocks/vue/registry/default/social-auth/vue/registry-item.json new file mode 100644 index 00000000000..2838756ba6b --- /dev/null +++ b/blocks/vue/registry/default/social-auth/vue/registry-item.json @@ -0,0 +1,15 @@ +{ + "name": "social-auth-vue", + "type": "registry:block", + "title": "Social Auth flow for Vue and Supabase", + "description": "Social Auth flow for Vue and Supabase", + "registryDependencies": ["button", "card"], + "files": [ + { + "path": "registry/default/social-auth/vue/components/login-form.vue", + "type": "registry:component", + "target": "components/login-form.vue" + } + ], + "dependencies": ["@supabase/supabase-js@latest"] +} diff --git a/blocks/vue/registry/index.ts b/blocks/vue/registry/index.ts index b432fb397b9..0ce964f3b34 100644 --- a/blocks/vue/registry/index.ts +++ b/blocks/vue/registry/index.ts @@ -1,6 +1,7 @@ import { clients } from './clients' import { passwordBasedAuth } from './password-based-auth' +import { socialAuth } from './social-auth' -const blocks = [...clients, ...passwordBasedAuth] +const blocks = [...clients, ...passwordBasedAuth, ...socialAuth] export { blocks } diff --git a/blocks/vue/registry/social-auth.ts b/blocks/vue/registry/social-auth.ts new file mode 100644 index 00000000000..8d7a0cabff6 --- /dev/null +++ b/blocks/vue/registry/social-auth.ts @@ -0,0 +1,5 @@ +import { type Registry } from 'shadcn/schema' +import vue from './default/social-auth/vue/registry-item.json' with { type: 'json' } +import nuxt from './default/social-auth/nuxtjs/registry-item.json' with { type: 'json' } + +export const socialAuth = [vue, nuxt] as Registry['items']