@floatingpixels/supabase-nuxt 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -158,20 +158,22 @@ Once the authorization flow is triggered using the `auth` wrapper of the `useSup
158
158
 
159
159
  ### E-Mail Authentication
160
160
 
161
- When using e-mail authentication, a confirmation e-mail is sent to new users, and an e-mail containing a magic link is sent to existing users. For those links to work with your application, you need to adjust the e-mail templates in your Supabase settings under `Aithentication -> Email Templates`. The generated links must contain a `token_hash` and `type` URL parameter, and point to the confirmation URL of your app, which is `/supabase/confirm` by default. In addition you can set the URL parameter `next` to determine the route users will be forwarded to in your app when authorization is successful. If `next` is omitted, it will route to `/`. An example template looks like this:
161
+ When using e-mail authentication, a confirmation e-mail is sent to new users, and an e-mail containing a magic link is sent to existing users. For those links to work with your application, you need to adjust the e-mail templates in your Supabase settings under `Authentication -> Email Templates`. The generated links must contain a `token_hash` and `type` URL parameter, and point to the confirmation URL of your app, which is `/supabase/auth/confirm` by default. In addition you can set the URL parameter `next` to determine the route users will be forwarded to in your app when authorization is successful. If `next` is omitted, it will route to `/`. An example template looks like this:
162
162
 
163
163
  ```html
164
164
  <h2>Confirm your signup</h2>
165
165
 
166
166
  <p>Follow this link to confirm your user:</p>
167
167
  <p>
168
- <a href="{{ .SiteURL }}/supabase/confirm?token_hash={{ .TokenHash }}&type=email&next=/path-to-your-page"
168
+ <a href="{{ .SiteURL }}/supabase/auth/confirm?token_hash={{ .TokenHash }}&type=email&next=/path-to-your-page"
169
169
  >Confirm your email</a
170
170
  >
171
171
  </p>
172
172
  ```
173
173
 
174
- The confirmation route on your server is provided by this module, so you don't need to implement it yourself. It's available at `/supabase/confirm`. It will automatically confirm the user and redirect to the `next` route. If you want to customize the confirmation route, you can do so by creating a server route to handle the request, and point to it in your Supabase e-mail template. Your custom route will receive the `token_hash` and `type` URL parameters, and the `next` URL parameter if provided. You can use the `useSupabaseClient` composable to confirm the user and redirect to the `next` route:
174
+ The confirmation route on your server is provided by this module, so you don't need to implement it yourself. It's available at `/supabase/auth/confirm`. It will automatically confirm the user and redirect to the `next` route.
175
+
176
+ If you want to customize the confirmation route, you can do so by creating a server route to handle the request, and point to it in your Supabase e-mail template. Your custom route will receive the `token_hash` and `type` URL parameters, and the `next` URL parameter if provided. You can use the `useSupabaseClient` composable to confirm the user and redirect to the `next` route:
175
177
 
176
178
  > ⚠️ You can use the provided confirm route at `/supabase/confirm`, the implementation of a custom route is optional!
177
179
 
@@ -202,14 +204,14 @@ export default defineEventHandler(async event => {
202
204
 
203
205
  ### OAuth Authentication
204
206
 
205
- When using OAuth authentication, you need to configure the OAuth provider in your Supabase settings under `Authentication -> Providers`. You can then use the `signInWithOAuth` method of the `auth` wrapper of the `useSupabaseClient` composable to initiate the authorization flow. This module provides a default callback under `/supabase/callback` that you can provide to the authentication function:
207
+ When using OAuth authentication, you need to configure the OAuth provider in your Supabase settings under `Authentication -> Providers`. You can then use the `signInWithOAuth` method of the `auth` wrapper of the `useSupabaseClient` composable to initiate the authorization flow. This module provides a default callback under `/supabase/auth/callback` that you can provide to the authentication function:
206
208
 
207
209
  ```ts [pages/login.vue]
208
210
  const signInWithOAuth = async () => {
209
211
  const { error } = await supabase.auth.signInWithOAuth({
210
212
  provider: 'github',
211
213
  options: {
212
- redirectTo: 'http://<your-site-url>/supabase/callback',
214
+ redirectTo: 'http://<your-site-url>/supabase/auth/callback',
213
215
  },
214
216
  })
215
217
  if (error) console.log(error)
@@ -218,7 +220,7 @@ const signInWithOAuth = async () => {
218
220
 
219
221
  You can customize the callback by creating your own server route, and point to it when calling `signInWithOAuth`. The callback route will receive a code, that needs to be exchanged for a session. Here is an example:
220
222
 
221
- > ⚠️ You can use the provided callback route at `/supabase/callback`, the implementation of a custom callback is optional!
223
+ > ⚠️ You can use the provided callback route at `/supabase/auth/callback`, the implementation of a custom callback is optional!
222
224
 
223
225
  ```ts [server/api/callback.ts]
224
226
  import { supabaseServerClient } from '#supabase/server'
@@ -247,6 +249,29 @@ export default defineEventHandler(async event => {
247
249
 
248
250
  If `redirect` is set to `true` in the module options, users will be automatically routed to the login page when they are not authenticated. If you want to allow access to "public" pages, you just need to add them in the `exclude` `redirect` option, and they will not redirect unauthenticated users.
249
251
 
252
+ ### Error Handling
253
+
254
+ When an authentication error occurs, an exception is thrown. You can create an error page in the root of your app, to show an appropriate error message, clear the error and send the user to an appropriate route to continue. Here is an example for `error.vue`:
255
+
256
+ ```vue
257
+ <script setup lang="ts">
258
+ import type { H3Error } from 'h3'
259
+ const { error } = defineProps<{
260
+ error: H3Error
261
+ }>()
262
+
263
+ const handleError = () => clearError({ redirect: '/' })
264
+ </script>
265
+
266
+ <template>
267
+ <div>
268
+ <h2>{{ error.statusCode }}</h2>
269
+ <p>{{ error.message }}</p>
270
+ <button @click="handleError">Clear errors</button>
271
+ </div>
272
+ </template>
273
+ ```
274
+
250
275
  ## Composables
251
276
 
252
277
  ### useSupabaseClient
package/dist/module.d.mts CHANGED
@@ -1,5 +1,20 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { SupabaseClientOptions } from '@supabase/supabase-js';
3
+ import { CookieOptions } from 'nuxt/app';
2
4
 
5
+ declare module '@nuxt/schema' {
6
+ interface PublicRuntimeConfig {
7
+ supabase: {
8
+ url: string
9
+ key: string
10
+ redirect: boolean
11
+ redirectOptions: RedirectOptions
12
+ cookieName: string
13
+ cookieOptions: CookieOptions
14
+ clientOptions: SupabaseClientOptions<string>
15
+ }
16
+ }
17
+ }
3
18
  interface ModuleOptions {
4
19
  /**
5
20
  * Supabase API URL
package/dist/module.d.ts CHANGED
@@ -1,5 +1,20 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { SupabaseClientOptions } from '@supabase/supabase-js';
3
+ import { CookieOptions } from 'nuxt/app';
2
4
 
5
+ declare module '@nuxt/schema' {
6
+ interface PublicRuntimeConfig {
7
+ supabase: {
8
+ url: string
9
+ key: string
10
+ redirect: boolean
11
+ redirectOptions: RedirectOptions
12
+ cookieName: string
13
+ cookieOptions: CookieOptions
14
+ clientOptions: SupabaseClientOptions<string>
15
+ }
16
+ }
17
+ }
3
18
  interface ModuleOptions {
4
19
  /**
5
20
  * Supabase API URL
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">3.0.0"
6
6
  },
7
- "version": "0.1.7"
7
+ "version": "0.1.9"
8
8
  }
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addPlugin, addServerHandler, addTemplate, extendViteConfig } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addServerHandler, addPlugin, addTemplate, extendViteConfig } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
3
 
4
4
  const module = defineNuxtModule({
@@ -35,7 +35,6 @@ const module = defineNuxtModule({
35
35
  },
36
36
  setup(options, nuxt) {
37
37
  const { resolve } = createResolver(import.meta.url);
38
- console.log("Module setup function");
39
38
  if (!options.url) {
40
39
  console.warn("Missing `SUPABASE_URL` in `.env`");
41
40
  }
@@ -53,21 +52,23 @@ const module = defineNuxtModule({
53
52
  nuxt.options.runtimeConfig.supabase = defu(nuxt.options.runtimeConfig.supabase, {
54
53
  serviceKey: options.serviceKey
55
54
  });
55
+ addServerHandler({
56
+ route: "/supabase/auth/confirm",
57
+ handler: resolve("./runtime/server/auth/confirm"),
58
+ method: "get"
59
+ });
60
+ addServerHandler({
61
+ route: "/supabase/auth/callback",
62
+ handler: resolve("./runtime/server/auth/callback"),
63
+ method: "get"
64
+ });
56
65
  addPlugin(resolve("./runtime/plugins/supabase.server"));
57
66
  addPlugin(resolve("./runtime/plugins/supabase.client"));
58
67
  nuxt.hook("imports:dirs", (dirs) => {
59
68
  dirs.push(resolve("./runtime/composables"));
60
69
  });
61
- addServerHandler({
62
- route: "/supabase/confirm",
63
- handler: resolve("./runtime/server/auth/confirm")
64
- });
65
- addServerHandler({
66
- route: "/supabase/callback",
67
- handler: resolve("./runtime/server/auth/callback")
68
- });
69
70
  if (options.redirect) {
70
- addPlugin(resolve("./runtime/plugins/auth-redirect"));
71
+ addPlugin(resolve("./runtime/plugins/middleware-auth-redirect"));
71
72
  }
72
73
  nuxt.hook("nitro:config", (nitroConfig) => {
73
74
  nitroConfig.alias = nitroConfig.alias || {};
@@ -90,18 +91,17 @@ const module = defineNuxtModule({
90
91
  options2.references.push({ path: resolve(nuxt.options.buildDir, "types/supabase.d.ts") });
91
92
  });
92
93
  extendViteConfig((config) => {
93
- config.optimizeDeps = config.optimizeDeps || {};
94
- config.optimizeDeps.include = config.optimizeDeps.include || [];
95
- config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
96
- config.optimizeDeps.include.push(
97
- "@supabase/functions-js",
98
- "@supabase/gotrue-js",
99
- "@supabase/postgrest-js",
100
- "@supabase/realtime-js",
101
- "@supabase/storage-js",
102
- "@supabase/supabase-js",
103
- "@supabase/ssr"
104
- );
94
+ config.optimizeDeps = defu(typeof config.optimizeDeps === "object" ? config.optimizeDeps : {}, {
95
+ include: [
96
+ "@supabase/functions-js",
97
+ "@supabase/gotrue-js",
98
+ "@supabase/postgrest-js",
99
+ "@supabase/realtime-js",
100
+ "@supabase/storage-js",
101
+ "@supabase/supabase-js",
102
+ "@supabase/ssr"
103
+ ]
104
+ });
105
105
  });
106
106
  }
107
107
  });
@@ -1,23 +1,14 @@
1
1
  import { useSupabaseUser } from "../composables/useSupabaseUser.mjs";
2
- import {
3
- defineNuxtPlugin,
4
- addRouteMiddleware,
5
- defineNuxtRouteMiddleware,
6
- useRuntimeConfig,
7
- navigateTo,
8
- abortNavigation
9
- } from "#imports";
2
+ import { defineNuxtPlugin, addRouteMiddleware, defineNuxtRouteMiddleware, useRuntimeConfig, navigateTo } from "#imports";
10
3
  export default defineNuxtPlugin({
11
- name: "auth-redirect",
4
+ name: "middleware-auth-redirect",
12
5
  setup() {
13
6
  addRouteMiddleware(
14
- "global-auth",
7
+ "02-global-auth-redirect",
15
8
  defineNuxtRouteMiddleware(async (to) => {
16
- if (to.path === "/supabase/confirm")
17
- abortNavigation();
18
9
  const config = useRuntimeConfig().public.supabase;
19
10
  const { login, exclude } = config.redirectOptions;
20
- const isExcluded = [...exclude, login, "/supabase/confirm"]?.some((path) => {
11
+ const isExcluded = [...exclude || [], login ? login : "/login"]?.some((path) => {
21
12
  const regex = new RegExp(`^${path.replace(/\*/g, ".*")}$`);
22
13
  return regex.test(to.path);
23
14
  });
@@ -25,8 +16,7 @@ export default defineNuxtPlugin({
25
16
  return;
26
17
  const user = await useSupabaseUser();
27
18
  if (!user) {
28
- if (to.path !== "/login")
29
- return navigateTo("/login");
19
+ return navigateTo("/login");
30
20
  }
31
21
  }),
32
22
  { global: true }
@@ -1,2 +1,10 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<{
2
+ supabase: {
3
+ client: import("@supabase/supabase-js").SupabaseClient<any, "public", any>;
4
+ };
5
+ }> & import("nuxt/app").ObjectPlugin<{
6
+ supabase: {
7
+ client: import("@supabase/supabase-js").SupabaseClient<any, "public", any>;
8
+ };
9
+ }>;
2
10
  export default _default;
@@ -1,4 +1,4 @@
1
- import { defineNuxtPlugin, useRuntimeConfig, useCookie } from "#imports";
1
+ import { defineNuxtPlugin, useRuntimeConfig, useCookie } from "nuxt/app";
2
2
  import { createBrowserClient } from "@supabase/ssr";
3
3
  export default defineNuxtPlugin({
4
4
  name: "supabase",
@@ -12,7 +12,7 @@ export default defineNuxtPlugin({
12
12
  return useCookie(name).value;
13
13
  },
14
14
  set(name, value) {
15
- useCookie(name, cookieOptions).value = value;
15
+ useCookie(name, { ...cookieOptions, readonly: false }).value = value;
16
16
  },
17
17
  remove(key2, options) {
18
18
  useCookie(key2, { ...options, expires: 0 }).value = "";
@@ -28,8 +28,5 @@ export default defineNuxtPlugin({
28
28
  }
29
29
  }
30
30
  };
31
- },
32
- env: {
33
- islands: false
34
31
  }
35
32
  });
@@ -1,2 +1,10 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<{
2
+ supabase: {
3
+ client: import("@supabase/supabase-js").SupabaseClient<any, "public", any>;
4
+ };
5
+ }> & import("nuxt/app").ObjectPlugin<{
6
+ supabase: {
7
+ client: import("@supabase/supabase-js").SupabaseClient<any, "public", any>;
8
+ };
9
+ }>;
2
10
  export default _default;
@@ -1,4 +1,4 @@
1
- import { defineNuxtPlugin, useRuntimeConfig, useRequestEvent } from "#imports";
1
+ import { defineNuxtPlugin, useRuntimeConfig, useRequestEvent } from "nuxt/app";
2
2
  import { createServerClient } from "@supabase/ssr";
3
3
  import { getCookie, setCookie } from "h3";
4
4
  export default defineNuxtPlugin({
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
2
  export default _default;
@@ -1,5 +1,4 @@
1
- import { defineEventHandler } from "#imports";
2
- import { createError, getQuery, sendRedirect } from "h3";
1
+ import { defineEventHandler, createError, getQuery, sendRedirect } from "h3";
3
2
  import { supabaseServerClient } from "#supabase/server";
4
3
  export default defineEventHandler(async (event) => {
5
4
  const query = getQuery(event);
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
2
  export default _default;
@@ -1,5 +1,4 @@
1
- import { defineEventHandler } from "#imports";
2
- import { createError, getQuery, sendRedirect } from "h3";
1
+ import { createError, getQuery, sendRedirect, defineEventHandler } from "h3";
3
2
  import { supabaseServerClient } from "#supabase/server";
4
3
  export default defineEventHandler(async (event) => {
5
4
  const query = getQuery(event);
@@ -14,5 +13,6 @@ export default defineEventHandler(async (event) => {
14
13
  if (error) {
15
14
  throw createError({ statusMessage: error.message });
16
15
  }
16
+ console.log("EMail OTP verified");
17
17
  await sendRedirect(event, next, 302);
18
18
  });
@@ -4,7 +4,7 @@ import { useRuntimeConfig } from "#imports";
4
4
  export const supabaseServerClient = async (event) => {
5
5
  const {
6
6
  supabase: { url, key, cookieOptions }
7
- } = useRuntimeConfig(event).public;
7
+ } = useRuntimeConfig().public;
8
8
  let supabaseClient = event.context._supabaseClient;
9
9
  if (!supabaseClient) {
10
10
  supabaseClient = createServerClient(url, key, {
@@ -13,7 +13,7 @@ export const supabaseServerClient = async (event) => {
13
13
  return getCookie(event, name);
14
14
  },
15
15
  set(name, value) {
16
- setCookie(event, name, value);
16
+ setCookie(event, name, value, cookieOptions);
17
17
  },
18
18
  remove(key2, options) {
19
19
  setCookie(event, key2, "", { ...options, expires: 0 });
@@ -7,7 +7,7 @@ export const supabaseServiceRole = async (event) => {
7
7
  public: {
8
8
  supabase: { url, cookieOptions }
9
9
  }
10
- } = useRuntimeConfig(event);
10
+ } = useRuntimeConfig();
11
11
  if (!serviceKey) {
12
12
  throw new Error("Missing `SUPABASE_SERVICE_KEY` in `.env`");
13
13
  }
@@ -1,3 +1,18 @@
1
+ import { SupabaseClientOptions } from '@supabase/supabase-js'
2
+ import { CookieOptions } from 'nuxt/app'
3
+ declare module '@nuxt/schema' {
4
+ interface PublicRuntimeConfig {
5
+ supabase: {
6
+ url: string
7
+ key: string
8
+ redirect: boolean
9
+ redirectOptions: RedirectOptions
10
+ cookieName: string
11
+ cookieOptions: CookieOptions
12
+ clientOptions: SupabaseClientOptions<string>
13
+ }
14
+ }
15
+ }
1
16
  export interface ModuleOptions {
2
17
  /**
3
18
  * Supabase API URL
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floatingpixels/supabase-nuxt",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Supabase module for Nuxt",
5
5
  "repository": "floatingpixels/supabase-nuxt",
6
6
  "license": "MIT",
@@ -50,21 +50,23 @@
50
50
  "defu": "^6.1.4"
51
51
  },
52
52
  "devDependencies": {
53
- "@nuxt/devtools": "latest",
53
+ "@nuxt/devtools": "^1.0.8",
54
54
  "@nuxt/eslint-config": "^0.2.0",
55
55
  "@nuxt/module-builder": "^0.5.5",
56
56
  "@nuxt/schema": "^3.9.3",
57
- "@nuxt/test-utils": "^3.10.0",
58
- "@types/node": "^20.11.5",
59
- "@vue/test-utils": "^2.4.3",
57
+ "@nuxt/test-utils": "^3.11.0",
58
+ "@types/node": "^20.11.10",
59
+ "@vue/test-utils": "^2.4.4",
60
60
  "changelogen": "^0.5.5",
61
61
  "eslint": "^8.56.0",
62
62
  "eslint-config-prettier": "^9.1.0",
63
- "happy-dom": "^13.3.1",
63
+ "happy-dom": "^13.3.4",
64
64
  "nuxt": "^3.9.3",
65
65
  "playwright-core": "^1.41.1",
66
66
  "prettier": "^3.2.4",
67
67
  "release-it": "^17.0.3",
68
- "vitest": "^1.2.1"
68
+ "typescript": "^5.3.3",
69
+ "vitest": "^1.2.2",
70
+ "vue-tsc": "^1.8.27"
69
71
  }
70
72
  }