Skip to content

Commit df3c235

Browse files
committed
refactor(nuxt): domain-based arch
1 parent 1c0d6b1 commit df3c235

File tree

8 files changed

+87
-63
lines changed

8 files changed

+87
-63
lines changed

packages/nuxt/src/module.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { NuxtModule } from '@nuxt/schema'
1111
// cannot import from firebase-admin because the build fails, maybe a nuxt bug?
1212
import type { FirebaseOptions } from '@firebase/app-types'
1313
import type { AppOptions, ServiceAccount } from 'firebase-admin'
14-
import type { NuxtVueFireAppCheckOptions } from './app-check'
14+
import type { NuxtVueFireAppCheckOptions } from './runtime/app-check'
1515

1616
export interface VueFireNuxtModuleOptions {
1717
/**
@@ -85,8 +85,6 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
8585
}
8686

8787
const { resolve } = createResolver(import.meta.url)
88-
// TODO: refactor folder structure to be domain based (e.g. auth, firestore, etc.)
89-
const authDir = fileURLToPath(new URL('./auth', import.meta.url))
9088
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
9189
const templatesDir = fileURLToPath(
9290
new URL('../templates', import.meta.url)
@@ -98,7 +96,6 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
9896

9997
// nuxt.options.build.transpile.push(templatesDir)
10098
nuxt.options.build.transpile.push(runtimeDir)
101-
nuxt.options.build.transpile.push(authDir)
10299

103100
// FIXME: this is a workaround because of the resolve issue with firebase
104101
// without this, we use different firebase packages within vuefire and nuxt-vuefire
@@ -120,15 +117,17 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
120117
if (nuxt.options.ssr) {
121118
addServerHandler({
122119
route: '/api/_vuefire/auth',
123-
handler: resolve(authDir, 'session'),
120+
handler: resolve(runtimeDir, './auth/api.session'),
124121
})
125122
}
126123

124+
// this allows us to the order of the plugins
127125
nuxt.hook('modules:done', () => {
126+
addPlugin(resolve(runtimeDir, 'auth/plugin.client'))
128127
// must be added after the admin module to use the admin app
129-
addPlugin(resolve(runtimeDir, 'plugins/auth'))
128+
addPlugin(resolve(runtimeDir, 'auth/plugin.server'))
130129

131-
addPlugin(resolve(runtimeDir, 'plugins/admin.server'))
130+
addPlugin(resolve(runtimeDir, 'admin/plugin.server'))
132131

133132
// plugin are added in reverse order
134133
addPluginTemplate({
@@ -139,7 +138,7 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
139138
ssr: nuxt.options.ssr,
140139
},
141140
})
142-
addPlugin(resolve(runtimeDir, 'plugins/app'))
141+
addPlugin(resolve(runtimeDir, 'app/plugin'))
143142
})
144143
},
145144
})
@@ -149,7 +148,7 @@ export type {
149148
NuxtVueFireAppCheckOptions,
150149
NuxtVueFireAppCheckOptionsReCaptchaV3,
151150
NuxtVueFireAppCheckOptionsReCaptchaEnterprise,
152-
} from './app-check'
151+
} from './runtime/app-check'
153152

154153
declare module '@nuxt/schema' {
155154
export interface AppConfig {

packages/nuxt/src/runtime/plugins/admin.server.ts renamed to packages/nuxt/src/runtime/admin/plugin.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default defineNuxtPlugin((nuxtApp) => {
88

99
const { firebaseConfig, firebaseAdmin, vuefireOptions } = appConfig
1010

11-
// the admin sdk is not always needed
11+
// the admin sdk is not always needed, skip if not provided
1212
if (!firebaseAdmin?.config) {
1313
return
1414
}

packages/nuxt/src/auth/session.ts renamed to packages/nuxt/src/runtime/auth/api.session.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
import { readBody, setCookie, assertMethod, defineEventHandler, H3Response, setHeader } from 'h3'
1+
import {
2+
readBody,
3+
setCookie,
4+
assertMethod,
5+
defineEventHandler,
6+
H3Response,
7+
setHeader,
8+
} from 'h3'
29
import { useRuntimeConfig } from '#app'
310

11+
/**
12+
* Setups an API endpoint to be used by the client to mint a cookie based auth session.
13+
*/
414
export default defineEventHandler(async (event) => {
515
assertMethod(event, 'POST')
616
const { token } = await readBody(event)
@@ -13,14 +23,14 @@ export default defineEventHandler(async (event) => {
1323
secure: true,
1424
httpOnly: true,
1525
path: '/',
16-
sameSite: 'lax'
26+
sameSite: 'lax',
1727
})
1828
// empty content status
1929
} else {
2030
// delete the cookie
2131
setCookie(event, AUTH_COOKIE_NAME, '', {
2232
maxAge: -1,
23-
path: '/'
33+
path: '/',
2434
})
2535
}
2636

@@ -29,5 +39,6 @@ export default defineEventHandler(async (event) => {
2939
return ''
3040
})
3141

42+
// TODO: customizable defaults
3243
export const AUTH_COOKIE_MAX_AGE = 60 * 60 * 24 * 5 * 1_000
3344
export const AUTH_COOKIE_NAME = '_vuefire_auth'
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { FirebaseApp } from '@firebase/app-types'
2+
import { getAuth, onIdTokenChanged } from 'firebase/auth'
3+
import { VueFireAuth } from 'vuefire'
4+
import { defineNuxtPlugin } from '#app'
5+
6+
/**
7+
* Setups VueFireAuth and automatically mints a cookie based auth session. On the server, it reads the cookie to
8+
* generate the proper auth state.
9+
*/
10+
export default defineNuxtPlugin((nuxtApp) => {
11+
const firebaseApp = nuxtApp.$firebaseApp as FirebaseApp
12+
13+
// TODO: provide the server user?
14+
VueFireAuth()(firebaseApp, nuxtApp.vueApp)
15+
const auth = getAuth(firebaseApp)
16+
// send a post request to the server when auth state changes to mint a cookie
17+
onIdTokenChanged(auth, async (user) => {
18+
const jwtToken = await user?.getIdToken()
19+
// console.log('📚 updating server cookie with', jwtToken)
20+
// TODO: error handling: should we call showError() in dev only?
21+
await $fetch('/api/_vuefire/auth', {
22+
method: 'POST',
23+
// if the token is undefined, the server will delete the cookie
24+
body: { token: jwtToken },
25+
})
26+
})
27+
})
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { FirebaseApp } from '@firebase/app-types'
2+
import type { App as AdminApp } from 'firebase-admin/app'
3+
import { getAuth as getAdminAuth } from 'firebase-admin/auth'
4+
import {
5+
getAuth,
6+
onIdTokenChanged,
7+
signInWithCredential,
8+
AuthCredential,
9+
} from 'firebase/auth'
10+
import { getCurrentUser, VueFireAuth } from 'vuefire'
11+
import { getCookie } from 'h3'
12+
import { AUTH_COOKIE_NAME } from './api.session'
13+
import { defineNuxtPlugin, useRequestEvent } from '#app'
14+
15+
/**
16+
* Setups the auth state based on the cookie.
17+
*/
18+
export default defineNuxtPlugin(async (nuxtApp) => {
19+
const firebaseApp = nuxtApp.$firebaseApp as FirebaseApp
20+
21+
const event = useRequestEvent()
22+
const token = getCookie(event, AUTH_COOKIE_NAME)
23+
24+
if (token) {
25+
const adminApp = nuxtApp.$adminApp as AdminApp
26+
const auth = getAdminAuth(adminApp)
27+
28+
const decodedToken = await auth.verifyIdToken(token)
29+
const user = await auth.getUser(decodedToken.uid)
30+
31+
// signInWithCredential(getAuth(firebaseApp)))
32+
33+
console.log('🔥 setting user', user)
34+
35+
// provide user
36+
}
37+
})

packages/nuxt/src/runtime/plugins/auth.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)