@floatingpixels/supabase-nuxt 0.1.5 → 0.1.7

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
@@ -1,94 +1,393 @@
1
- <!--
2
- Get your module up and running quickly.
1
+ # Supabase Nuxt
3
2
 
4
- Find and replace all on all files (CMD+SHIFT+F):
5
- - Name: My Module
6
- - Package name: my-module
7
- - Description: My new Nuxt module
8
- -->
3
+ ![NPM Version](https://img.shields.io/npm/v/%40floatingpixels%2Fsupabase-nuxt?color=28CF8D)
4
+ ![NPM Downloads](https://img.shields.io/npm/dt/%40floatingpixels%2Fsupabase-nuxt)
9
5
 
10
- # My Module
6
+ ## Features
11
7
 
12
- [![npm version][npm-version-src]][npm-version-href]
13
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
14
- [![License][license-src]][license-href]
15
- [![Nuxt][nuxt-src]][nuxt-href]
8
+ [@floatingpixels/supabase-nuxt](https://github.com/nuxt-modules/supabase) is a Nuxt module for first class integration with Supabase. It makes it easy to use Supabase authentication, database and realtime features in your Nuxt 3 application. Especially when using server-side rendering SSR, using Supabase can be tricky, this module takes care of the intricacies and lets you simply use the power of Supabase!
16
9
 
17
- My new Nuxt module for doing amazing things.
10
+ Checkout the [Nuxt 3](https://v3.nuxtjs.org) documentation and [Supabase](https://supabase.com) to learn more.
18
11
 
19
- - [✨ &nbsp;Release Notes](/CHANGELOG.md)
20
- <!-- - [🏀 Online playground](https://stackblitz.com/github/your-org/my-module?file=playground%2Fapp.vue) -->
21
- <!-- - [📖 &nbsp;Documentation](https://example.com) -->
12
+ ## Installation
22
13
 
23
- ## Features
14
+ Add `@floatingpixels/supabase-nuxt` dev dependency to your project:
24
15
 
25
- <!-- Highlight some of the features your module provide here -->
26
- - ⛰ &nbsp;Foo
27
- - 🚠 &nbsp;Bar
28
- - 🌲 &nbsp;Baz
16
+ ```bash
17
+ pnpm add -D @floatingpixels/supabase-nuxt
29
18
 
30
- ## Quick Setup
19
+ yarn add --dev @floatingpixels/supabase-nuxt
31
20
 
32
- 1. Add `my-module` dependency to your project
21
+ npm install @floatingpixels/supabase-nuxt --save-dev
33
22
 
34
- ```bash
35
- # Using pnpm
36
- pnpm add -D my-module
23
+ bun add -D @floatingpixels/supabase-nuxt
24
+ ```
25
+
26
+ Add `@floatingpixels/supabase-nuxt` to the `modules` section of `nuxt.config.ts`:
27
+
28
+ ```ts
29
+ export default defineNuxtConfig({
30
+ modules: ['@floatingpixels/supabase-nuxt'],
31
+ })
32
+ ```
37
33
 
38
- # Using yarn
39
- yarn add --dev my-module
34
+ Add `SUPABASE_URL` and `SUPABASE_KEY` to the `.env`:
40
35
 
41
- # Using npm
42
- npm install --save-dev my-module
36
+ ```zsh
37
+ SUPABASE_URL="https://example.supabase.co"
38
+ SUPABASE_KEY="<your_key>"
43
39
  ```
44
40
 
45
- 2. Add `my-module` to the `modules` section of `nuxt.config.ts`
41
+ Alternatively, you can prefix the env variables with `NUXT_PUBLIC_` in order to use runtimeConfig.
42
+
43
+ ## Options
44
+
45
+ You can configure the supabase module by using the `supabase` key in `nuxt.config`:
46
46
 
47
- ```js
47
+ ```ts [nuxt.config.ts]
48
48
  export default defineNuxtConfig({
49
- modules: [
50
- 'my-module'
51
- ]
49
+ // ...
50
+ supabase: {
51
+ // Options
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### `url`
57
+
58
+ Default: `process.env.SUPABASE_URL` (ex: `https://example.supabase.co`)
59
+
60
+ The unique Supabase URL which is supplied when you create a new project in your project dashboard.
61
+
62
+ ### `key`
63
+
64
+ Default: `process.env.SUPABASE_KEY`
65
+
66
+ Supabase 'anon key', used to bypass the Supabase API gateway and interact with your Supabase database making use of user JWT to apply RLS Policies.
67
+
68
+ ### `serviceKey`
69
+
70
+ Default: `process.env.SUPABASE_SERVICE_KEY`
71
+
72
+ Supabase 'service role key', has super admin rights and can bypass your Row Level Security.
73
+
74
+ ### `redirect`
75
+
76
+ Default: `false`
77
+
78
+ Redirect automatically to the configured login page if a non authenticated user is navigating to a page. When set to `true` a global middleware is used to check for a logged-in supabase use on all non-excluded routes.
79
+
80
+ ### `redirectOptions`
81
+
82
+ Default:
83
+
84
+ ```ts [nuxt.config.ts]
85
+ redirectOptions: {
86
+ login: '/login',
87
+ exclude: [],
88
+ }
89
+ ```
90
+
91
+ - `login`: User will be redirected to this route if not authenticated or after logout.
92
+ - `exclude`: Routes to exclude from the redirect. `['/foo', '/bar/*']` will exclude the `foo` page and all pages in your `bar` folder.
93
+
94
+ ### cookieOptions
95
+
96
+ ```ts
97
+ cookieOptions: {
98
+ maxAge: 60 * 60 * 8,
99
+ sameSite: 'lax',
100
+ secure: true
101
+ }
102
+ ```
103
+
104
+ Options for cookies used for authentication and session management, refer to [cookieOptions](https://nuxt.com/docs/api/composables/use-cookie#options) for available settings. Please note that the lifetime set here does not determine the Supabase session lifetime.
105
+
106
+ ### `clientOptions`
107
+
108
+ Default:
109
+
110
+ ```ts
111
+ clientOptions: {
112
+ auth: {
113
+ flowType: 'pkce',
114
+ detectSessionInUrl: true,
115
+ persistSession: true,
116
+ autoRefreshToken: true
117
+ },
118
+ }
119
+ ```
120
+
121
+ A documentation of Supabase client options is [available here](https://supabase.com/docs/reference/javascript/initializing#parameters).
122
+
123
+ ## Authentication
124
+
125
+ The module makes it easy to use [Supabase Auth](https://supabase.com/docs/guides/auth) in your application. In most use-cases for Supabase you'll want users to be authenticated, so you can leverage row-level security (RLS) in the database. Supabase Auth is designed to work perfectly with [RLS](https://supabase.com/docs/guides/auth/row-level-security).
126
+
127
+ All you need to do is to create a login page, when using the default module settings that's `login.vue` in the `pages` folder. On the log-in page you initiate log-in method(s) you choose from the [available authorization methods](https://supabase.com/docs/reference/javascript/auth-signinwithpassword) provided by Supabase, below is a simple example for e-mail authentication:
128
+
129
+ ```vue
130
+ <script setup lang="ts">
131
+ const supabase = useSupabaseClient()
132
+ const email = ref('')
133
+
134
+ const signInWithOtp = async () => {
135
+ const { error } = await supabase.auth.signInWithOtp({
136
+ email: email.value,
137
+ options: {
138
+ emailRedirectTo: 'http://localhost:3000/confirm',
139
+ },
140
+ })
141
+ if (error) console.log(error)
142
+ }
143
+ </script>
144
+ <template>
145
+ <div>
146
+ <button @click="signInWithOtp">Sign In with E-Mail</button>
147
+ <input
148
+ v-model="email"
149
+ type="email"
150
+ />
151
+ </div>
152
+ </template>
153
+ ```
154
+
155
+ > ⚠️ Ensure to activate and configure the authentication providers you want to use in the Supabase Dashboard under `Authentication -> Providers`.
156
+
157
+ Once the authorization flow is triggered using the `auth` wrapper of the `useSupabaseClient` composable, the session management is handled automatically. For the authentication flow PKCE is used, which requires an exchange between your server and the Supabase authentication server for some authentication methods. Please make sure you update your Supabase settings accordingly.
158
+
159
+ ### E-Mail Authentication
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:
162
+
163
+ ```html
164
+ <h2>Confirm your signup</h2>
165
+
166
+ <p>Follow this link to confirm your user:</p>
167
+ <p>
168
+ <a href="{{ .SiteURL }}/supabase/confirm?token_hash={{ .TokenHash }}&type=email&next=/path-to-your-page"
169
+ >Confirm your email</a
170
+ >
171
+ </p>
172
+ ```
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:
175
+
176
+ > ⚠️ You can use the provided confirm route at `/supabase/confirm`, the implementation of a custom route is optional!
177
+
178
+ ```ts [server/api/confirm.ts]
179
+ import { EmailOtpType } from '@supabase/supabase-js'
180
+ import { supabaseServerClient } from '#supabase/server'
181
+
182
+ export default defineEventHandler(async event => {
183
+ const query = getQuery(event)
184
+ const token_hash = query.token_hash as string
185
+ const type = query.type as EmailOtpType | null
186
+ const next = (query.next as string) ?? '/'
187
+
188
+ if (!token_hash || !type) {
189
+ throw createError({ statusMessage: 'Invalid token' })
190
+ }
191
+
192
+ const supabase = await supabaseServerClient(event)
193
+ const { error } = await supabase.auth.verifyOtp({ type, token_hash })
194
+
195
+ if (error) {
196
+ throw createError({ statusMessage: error.message })
197
+ }
198
+
199
+ await sendRedirect(event, next, 302)
52
200
  })
53
201
  ```
54
202
 
55
- That's it! You can now use My Module in your Nuxt app ✨
203
+ ### OAuth Authentication
56
204
 
57
- ## Development
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:
58
206
 
59
- ```bash
60
- # Install dependencies
61
- npm install
207
+ ```ts [pages/login.vue]
208
+ const signInWithOAuth = async () => {
209
+ const { error } = await supabase.auth.signInWithOAuth({
210
+ provider: 'github',
211
+ options: {
212
+ redirectTo: 'http://<your-site-url>/supabase/callback',
213
+ },
214
+ })
215
+ if (error) console.log(error)
216
+ }
217
+ ```
218
+
219
+ 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:
62
220
 
63
- # Generate type stubs
64
- npm run dev:prepare
221
+ > ⚠️ You can use the provided callback route at `/supabase/callback`, the implementation of a custom callback is optional!
65
222
 
66
- # Develop with the playground
67
- npm run dev
223
+ ```ts [server/api/callback.ts]
224
+ import { supabaseServerClient } from '#supabase/server'
68
225
 
69
- # Build the playground
70
- npm run dev:build
226
+ export default defineEventHandler(async event => {
227
+ const query = getQuery(event)
228
+ const code = query.code as string
229
+ const next = (query.next as string) ?? '/'
71
230
 
72
- # Run ESLint
73
- npm run lint
231
+ if (!code) {
232
+ throw createError({ statusMessage: 'No code provided' })
233
+ }
74
234
 
75
- # Run Vitest
76
- npm run test
77
- npm run test:watch
235
+ const supabase = await supabaseServerClient(event)
236
+ const { error } = await supabase.auth.exchangeCodeForSession(code)
78
237
 
79
- # Release new version
80
- npm run release
238
+ if (error) {
239
+ throw createError({ statusMessage: error.message })
240
+ }
241
+
242
+ await sendRedirect(event, next, 302)
243
+ })
81
244
  ```
82
245
 
83
- <!-- Badges -->
84
- [npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
85
- [npm-version-href]: https://npmjs.com/package/my-module
246
+ ### Redirection for non-authorized users
247
+
248
+ 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
+
250
+ ## Composables
251
+
252
+ ### useSupabaseClient
253
+
254
+ This composable can be used to make requests to the Supabase API. It's autoimported and ready to use in your components. It's using [supabase-js](https://github.com/supabase/supabase-js/) under the hood, it gives access to the [Supabase client](https://supabase.com/docs/reference/javascript/initializing) and all of its features.
255
+
256
+ #### Database Request
257
+
258
+ Please check [Supabase](https://supabase.com/docs/reference/javascript/select) documentation on how to fully use the Supabase client.
259
+
260
+ Here is an example of fetching from the database using the Supabase client's `select` method with Nuxt 3 [useAsyncData](https://nuxt.com/docs/getting-started/data-fetching#useasyncdata).
261
+
262
+ ```vue
263
+ <script setup lang="ts">
264
+ const client = useSupabaseClient()
265
+
266
+ const { data: restaurant } = await useAsyncData('restaurant', async () => {
267
+ const { data } = await client.from('restaurants').select('name, location').eq('name', 'My Restaurant Name').single()
268
+
269
+ return data
270
+ })
271
+ </script>
272
+ ```
273
+
274
+ #### Realtime
275
+
276
+ Based on [Supabase Realtime](https://github.com/supabase/realtime), listen to changes in your PostgreSQL Database and broadcasts them over WebSockets.
277
+
278
+ To enable it, make sure you have turned on the [Realtime API](https://supabase.com/docs/guides/api#realtime-api) for your table.
279
+
280
+ Then, listen to changes directly in your vue page / component:
281
+
282
+ ```vue
283
+ <script setup lang="ts">
284
+ import type { RealtimeChannel } from '@supabase/supabase-js'
285
+
286
+ const client = useSupabaseClient()
287
+
288
+ let realtimeChannel: RealtimeChannel
289
+
290
+ // Fetch collaborators and get the refresh method provided by useAsyncData
291
+ const { data: collaborators, refresh: refreshCollaborators } = await useAsyncData('collaborators', async () => {
292
+ const { data } = await client.from('collaborators').select('name')
293
+ return data
294
+ })
295
+
296
+ // Once page is mounted, listen to changes on the `collaborators` table and refresh collaborators when receiving event
297
+ onMounted(() => {
298
+ // Real time listener for new workouts
299
+ realtimeChannel = client
300
+ .channel('public:collaborators')
301
+ .on('postgres_changes', { event: '*', schema: 'public', table: 'collaborators' }, () => refreshCollaborators())
302
+
303
+ realtimeChannel.subscribe()
304
+ })
305
+
306
+ // Don't forget to unsubscribe when user left the page
307
+ onUnmounted(() => {
308
+ client.removeChannel(realtimeChannel)
309
+ })
310
+ </script>
311
+ ```
86
312
 
87
- [npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
88
- [npm-downloads-href]: https://npmjs.com/package/my-module
313
+ #### Type Support
89
314
 
90
- [license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
91
- [license-href]: https://npmjs.com/package/my-module
315
+ You can pass Database typings to the client. Check Supabase [documentation](https://supabase.com/docs/reference/javascript/release-notes#typescript-support) for further information.
92
316
 
93
- [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
94
- [nuxt-href]: https://nuxt.com
317
+ ```vue
318
+ <script setup lang="ts">
319
+ import type { Database } from '~/types'
320
+ const client = useSupabaseClient<Database>()
321
+ </script>
322
+ ```
323
+
324
+ #### Authentication Client
325
+
326
+ The useSupabaseClient composable is providing all methods to manage authorization under `useSupabaseClient().auth`. For more details please see the [supabase-js auth documentation](https://supabase.com/docs/reference/javascript/auth-api). Here is an example for signing in and out:
327
+
328
+ > ⚠️ If you want a full explanation on how to handle the authentication process, please read this [section](#authentication).
329
+
330
+ ```ts
331
+ <script setup lang="ts">
332
+ const supabase = useSupabaseClient()
333
+
334
+ const signInWithOAuth = async () => {
335
+ const { error } = await supabase.auth.signInWithOAuth({
336
+ provider: 'github',
337
+ options: {
338
+ redirectTo: 'http://localhost:3000/confirm',
339
+ },
340
+ })
341
+ if (error) console.log(error)
342
+ }
343
+
344
+ const signOut = async () => {
345
+ const { error } = await supabase.auth.signOut()
346
+ if (error) console.log(error)
347
+ }
348
+ </script>
349
+ ```
350
+
351
+ ## Server Side Services
352
+
353
+ ### supabaseServerClient
354
+
355
+ Make requests to the Supabase API on server side with the supabaseServerClient service. It provides the same functionality as the `useSupabaseClient` composable, but it is designed to be used on [server routes](https://nuxt.com/docs/guide/directory-structure/server#server-routes).
356
+
357
+ On your server route import the `supabaseServerClient` from `#supabase/server`. Please note that `supabaseServerClient` is returning a promise.
358
+
359
+ ```ts [server/api/libraries.ts]
360
+ import { supabaseServerClient } from '#supabase/server'
361
+
362
+ export default eventHandler(async event => {
363
+ const client = await supabaseServerClient(event)
364
+
365
+ const { data } = await client.from('libraries').select('*')
366
+
367
+ return { libraries: data }
368
+ })
369
+ ```
370
+
371
+ ### supabaseServiceRole
372
+
373
+ Make requests with super admin rights to the Supabase API with the `supabaseServiceRole` service. This function is designed to work only in [server routes](https://nuxt.com/docs/guide/directory-structure/server#server-routes), there is no vue composable equivalent.
374
+
375
+ It provides similar functionality as the `supabaseServerClient` but it provides a client with super admin rights that can bypass your [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security).
376
+
377
+ The client is initialized with the `SUPABASE_SERVICE_KEY` you must have in your `.env` file. Checkout the doc if you want to know more about [Supabase keys](https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts#jwts-in-supabase).
378
+
379
+ > ⚠️ The service key gives admin access to your database, be careful to not expose it in your client side code or in your git repository.
380
+
381
+ In your server route use the `supabaseServiceRole` from `#supabase/server`.
382
+
383
+ ```ts [server/api/bypass-rls.ts]
384
+ import { supabaseServiceRole } from '#supabase/server'
385
+
386
+ export default eventHandler(async event => {
387
+ const client = supabaseServiceRole(event)
388
+
389
+ const { data } = await client.from('rls-protected-table').select()
390
+
391
+ return { sensitiveData: data }
392
+ })
393
+ ```
package/dist/module.d.mts CHANGED
@@ -36,16 +36,41 @@ interface ModuleOptions {
36
36
  redirect?: boolean
37
37
 
38
38
  /**
39
- * Redirection options, set routes for login and callback redirect
39
+ * Redirection options, set routes for login and specify pages to exclude from redirection
40
40
  * @default
41
41
  * {
42
42
  login: '/login',
43
- callback: '/confirm',
44
43
  exclude: [],
45
44
  }
46
45
  * @type RedirectOptions
47
46
  */
48
47
  redirectOptions?: RedirectOptions
48
+
49
+ /**
50
+ * Cookie options
51
+ * @default {
52
+ maxAge: 60 * 60 * 8,
53
+ sameSite: 'lax',
54
+ secure: true,
55
+ }
56
+ * @type CookieOptions
57
+ * @docs https://nuxt.com/docs/api/composables/use-cookie#options
58
+ */
59
+ cookieOptions?: CookieOptions
60
+
61
+ /**
62
+ * Supabase Client options
63
+ * @default {
64
+ auth: {
65
+ flowType: 'pkce',
66
+ detectSessionInUrl: true,
67
+ persistSession: true,
68
+ },
69
+ }
70
+ * @type object
71
+ * @docs https://supabase.com/docs/reference/javascript/initializing#parameters
72
+ */
73
+ clientOptions?: SupabaseClientOptions<string>
49
74
  }
50
75
 
51
76
  interface RedirectOptions {
@@ -56,7 +81,7 @@ interface RedirectOptions {
56
81
  */
57
82
  login?: string
58
83
  /**
59
- * Callback route
84
+ * Routes to exclude from redirection
60
85
  * @default []
61
86
  * @type string[]
62
87
  */
package/dist/module.d.ts CHANGED
@@ -36,16 +36,41 @@ interface ModuleOptions {
36
36
  redirect?: boolean
37
37
 
38
38
  /**
39
- * Redirection options, set routes for login and callback redirect
39
+ * Redirection options, set routes for login and specify pages to exclude from redirection
40
40
  * @default
41
41
  * {
42
42
  login: '/login',
43
- callback: '/confirm',
44
43
  exclude: [],
45
44
  }
46
45
  * @type RedirectOptions
47
46
  */
48
47
  redirectOptions?: RedirectOptions
48
+
49
+ /**
50
+ * Cookie options
51
+ * @default {
52
+ maxAge: 60 * 60 * 8,
53
+ sameSite: 'lax',
54
+ secure: true,
55
+ }
56
+ * @type CookieOptions
57
+ * @docs https://nuxt.com/docs/api/composables/use-cookie#options
58
+ */
59
+ cookieOptions?: CookieOptions
60
+
61
+ /**
62
+ * Supabase Client options
63
+ * @default {
64
+ auth: {
65
+ flowType: 'pkce',
66
+ detectSessionInUrl: true,
67
+ persistSession: true,
68
+ },
69
+ }
70
+ * @type object
71
+ * @docs https://supabase.com/docs/reference/javascript/initializing#parameters
72
+ */
73
+ clientOptions?: SupabaseClientOptions<string>
49
74
  }
50
75
 
51
76
  interface RedirectOptions {
@@ -56,7 +81,7 @@ interface RedirectOptions {
56
81
  */
57
82
  login?: string
58
83
  /**
59
- * Callback route
84
+ * Routes to exclude from redirection
60
85
  * @default []
61
86
  * @type string[]
62
87
  */
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">3.0.0"
6
6
  },
7
- "version": "0.1.5"
7
+ "version": "0.1.7"
8
8
  }
package/dist/module.mjs CHANGED
@@ -18,6 +18,19 @@ const module = defineNuxtModule({
18
18
  redirectOptions: {
19
19
  login: "/login",
20
20
  exclude: []
21
+ },
22
+ cookieOptions: {
23
+ maxAge: 60 * 60 * 8,
24
+ sameSite: "lax",
25
+ secure: true
26
+ },
27
+ clientOptions: {
28
+ auth: {
29
+ flowType: "pkce",
30
+ detectSessionInUrl: true,
31
+ persistSession: true,
32
+ autoRefreshToken: true
33
+ }
21
34
  }
22
35
  },
23
36
  setup(options, nuxt) {
@@ -33,7 +46,9 @@ const module = defineNuxtModule({
33
46
  url: options.url,
34
47
  key: options.key,
35
48
  redirect: options.redirect,
36
- redirectOptions: options.redirectOptions
49
+ redirectOptions: options.redirectOptions,
50
+ cookieOptions: options.cookieOptions,
51
+ clientOptions: options.clientOptions
37
52
  });
38
53
  nuxt.options.runtimeConfig.supabase = defu(nuxt.options.runtimeConfig.supabase, {
39
54
  serviceKey: options.serviceKey
@@ -45,7 +60,11 @@ const module = defineNuxtModule({
45
60
  });
46
61
  addServerHandler({
47
62
  route: "/supabase/confirm",
48
- handler: resolve("./runtime/server/api/confirm")
63
+ handler: resolve("./runtime/server/auth/confirm")
64
+ });
65
+ addServerHandler({
66
+ route: "/supabase/callback",
67
+ handler: resolve("./runtime/server/auth/callback")
49
68
  });
50
69
  if (options.redirect) {
51
70
  addPlugin(resolve("./runtime/plugins/auth-redirect"));
@@ -74,7 +93,15 @@ const module = defineNuxtModule({
74
93
  config.optimizeDeps = config.optimizeDeps || {};
75
94
  config.optimizeDeps.include = config.optimizeDeps.include || [];
76
95
  config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
77
- config.optimizeDeps.include.push("@supabase/supabase-js", "@supabase/ssr");
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
+ );
78
105
  });
79
106
  }
80
107
  });
@@ -1,12 +1,26 @@
1
- import { defineNuxtPlugin, useRuntimeConfig } from "#imports";
1
+ import { defineNuxtPlugin, useRuntimeConfig, useCookie } from "#imports";
2
2
  import { createBrowserClient } from "@supabase/ssr";
3
3
  export default defineNuxtPlugin({
4
4
  name: "supabase",
5
5
  enforce: "pre",
6
6
  async setup() {
7
7
  const config = useRuntimeConfig().public.supabase;
8
- const { url, key } = config;
9
- const supabaseBrowserClient = createBrowserClient(url, key);
8
+ const { url, key, cookieOptions } = config;
9
+ const supabaseBrowserClient = createBrowserClient(url, key, {
10
+ cookies: {
11
+ get(name) {
12
+ return useCookie(name).value;
13
+ },
14
+ set(name, value) {
15
+ useCookie(name, cookieOptions).value = value;
16
+ },
17
+ remove(key2, options) {
18
+ useCookie(key2, { ...options, expires: 0 }).value = "";
19
+ }
20
+ },
21
+ cookieOptions,
22
+ isSingleton: true
23
+ });
10
24
  return {
11
25
  provide: {
12
26
  supabase: {
@@ -1,18 +1,25 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig, useRequestEvent } from "#imports";
2
2
  import { createServerClient } from "@supabase/ssr";
3
- import { getCookie } from "h3";
3
+ import { getCookie, setCookie } from "h3";
4
4
  export default defineNuxtPlugin({
5
5
  name: "supabase",
6
6
  enforce: "pre",
7
7
  async setup() {
8
- const { url, key } = useRuntimeConfig().public.supabase;
8
+ const { url, key, cookieOptions } = useRuntimeConfig().public.supabase;
9
9
  const event = useRequestEvent();
10
10
  const supabaseServerClient = createServerClient(url, key, {
11
11
  cookies: {
12
12
  get(name) {
13
13
  return getCookie(event, name);
14
+ },
15
+ set(name, value) {
16
+ setCookie(event, name, value, cookieOptions);
17
+ },
18
+ remove(key2, options) {
19
+ setCookie(event, key2, "", { ...options, expires: 0 });
14
20
  }
15
- }
21
+ },
22
+ cookieOptions
16
23
  });
17
24
  return {
18
25
  provide: {
@@ -0,0 +1,17 @@
1
+ import { defineEventHandler } from "#imports";
2
+ import { createError, getQuery, sendRedirect } from "h3";
3
+ import { supabaseServerClient } from "#supabase/server";
4
+ export default defineEventHandler(async (event) => {
5
+ const query = getQuery(event);
6
+ const code = query.code;
7
+ const next = query.next ?? "/";
8
+ if (!code) {
9
+ throw createError({ statusMessage: "No code provided" });
10
+ }
11
+ const supabase = await supabaseServerClient(event);
12
+ const { error } = await supabase.auth.exchangeCodeForSession(code);
13
+ if (error) {
14
+ throw createError({ statusMessage: error.message });
15
+ }
16
+ await sendRedirect(event, next, 302);
17
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -1,3 +1,3 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
- import type { H3Event } from 'h3';
2
+ import { H3Event } from 'h3';
3
3
  export declare const supabaseServerClient: <T>(event: H3Event) => Promise<SupabaseClient<T, "public" extends keyof T ? keyof T & "public" : string & keyof T, T["public" extends keyof T ? keyof T & "public" : string & keyof T] extends import("@supabase/supabase-js/dist/module/lib/types").GenericSchema ? T["public" extends keyof T ? keyof T & "public" : string & keyof T] : any>>;
@@ -3,7 +3,7 @@ import { getCookie, setCookie } from "h3";
3
3
  import { useRuntimeConfig } from "#imports";
4
4
  export const supabaseServerClient = async (event) => {
5
5
  const {
6
- supabase: { url, key }
6
+ supabase: { url, key, cookieOptions }
7
7
  } = useRuntimeConfig(event).public;
8
8
  let supabaseClient = event.context._supabaseClient;
9
9
  if (!supabaseClient) {
@@ -18,7 +18,8 @@ export const supabaseServerClient = async (event) => {
18
18
  remove(key2, options) {
19
19
  setCookie(event, key2, "", { ...options, expires: 0 });
20
20
  }
21
- }
21
+ },
22
+ cookieOptions
22
23
  });
23
24
  event.context._supabaseClient = supabaseClient;
24
25
  }
@@ -1,3 +1,3 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
- import type { H3Event } from 'h3';
2
+ import { H3Event } from 'h3';
3
3
  export declare const supabaseServiceRole: <T>(event: H3Event) => Promise<SupabaseClient<T, "public" extends keyof T ? keyof T & "public" : string & keyof T, T["public" extends keyof T ? keyof T & "public" : string & keyof T] extends import("@supabase/supabase-js/dist/module/lib/types").GenericSchema ? T["public" extends keyof T ? keyof T & "public" : string & keyof T] : any>>;
@@ -1,11 +1,11 @@
1
1
  import { createServerClient } from "@supabase/ssr";
2
- import { getCookie } from "h3";
2
+ import { getCookie, setCookie } from "h3";
3
3
  import { useRuntimeConfig } from "#imports";
4
4
  export const supabaseServiceRole = async (event) => {
5
5
  const {
6
6
  supabase: { serviceKey },
7
7
  public: {
8
- supabase: { url }
8
+ supabase: { url, cookieOptions }
9
9
  }
10
10
  } = useRuntimeConfig(event);
11
11
  if (!serviceKey) {
@@ -17,8 +17,15 @@ export const supabaseServiceRole = async (event) => {
17
17
  cookies: {
18
18
  get(name) {
19
19
  return getCookie(event, name);
20
+ },
21
+ set(name, value) {
22
+ setCookie(event, name, value);
23
+ },
24
+ remove(key, options) {
25
+ setCookie(event, key, "", { ...options, expires: 0 });
20
26
  }
21
- }
27
+ },
28
+ cookieOptions
22
29
  });
23
30
  event.context._supabaseServiceRole = supabaseClient;
24
31
  }
@@ -34,16 +34,41 @@ export interface ModuleOptions {
34
34
  redirect?: boolean
35
35
 
36
36
  /**
37
- * Redirection options, set routes for login and callback redirect
37
+ * Redirection options, set routes for login and specify pages to exclude from redirection
38
38
  * @default
39
39
  * {
40
40
  login: '/login',
41
- callback: '/confirm',
42
41
  exclude: [],
43
42
  }
44
43
  * @type RedirectOptions
45
44
  */
46
45
  redirectOptions?: RedirectOptions
46
+
47
+ /**
48
+ * Cookie options
49
+ * @default {
50
+ maxAge: 60 * 60 * 8,
51
+ sameSite: 'lax',
52
+ secure: true,
53
+ }
54
+ * @type CookieOptions
55
+ * @docs https://nuxt.com/docs/api/composables/use-cookie#options
56
+ */
57
+ cookieOptions?: CookieOptions
58
+
59
+ /**
60
+ * Supabase Client options
61
+ * @default {
62
+ auth: {
63
+ flowType: 'pkce',
64
+ detectSessionInUrl: true,
65
+ persistSession: true,
66
+ },
67
+ }
68
+ * @type object
69
+ * @docs https://supabase.com/docs/reference/javascript/initializing#parameters
70
+ */
71
+ clientOptions?: SupabaseClientOptions<string>
47
72
  }
48
73
 
49
74
  export interface RedirectOptions {
@@ -54,7 +79,7 @@ export interface RedirectOptions {
54
79
  */
55
80
  login?: string
56
81
  /**
57
- * Callback route
82
+ * Routes to exclude from redirection
58
83
  * @default []
59
84
  * @type string[]
60
85
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floatingpixels/supabase-nuxt",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Supabase module for Nuxt",
5
5
  "repository": "floatingpixels/supabase-nuxt",
6
6
  "license": "MIT",
File without changes