Skip to content

Commit 64a5e50

Browse files
committed
feat(admin): make options optional
1 parent b2fa5ef commit 64a5e50

File tree

5 files changed

+68
-48
lines changed

5 files changed

+68
-48
lines changed

packages/nuxt/playground/nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default defineNuxtConfig({
4747
},
4848

4949
admin: {
50-
config: {},
50+
// config: {},
5151
serviceAccount: resolve(
5252
fileURLToPath(new URL('./service-account.json', import.meta.url))
5353
),

packages/nuxt/src/module.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,18 @@ export interface VueFireNuxtModuleOptions {
3737
* Firebase Admin Options.
3838
*/
3939
admin?: {
40+
// TODO: rename to options
4041
/**
4142
* Firebase Admin Options passed to `firebase-admin`'s `initializeApp()`. Required if you are using the auth, or the
4243
* app-check module.
4344
*/
44-
config: Omit<AppOptions, 'credential'>
45+
config?: Omit<AppOptions, 'credential'>
4546

47+
// TODO: remove, use env variables instead
4648
/**
4749
* Firebase Admin Service Account passed to `firebase-admin`'s `initializeApp()`. Required if you are adding an adminConfig.
4850
*/
49-
serviceAccount: string | ServiceAccount
51+
serviceAccount?: string | ServiceAccount
5052
}
5153

5254
/**
@@ -104,18 +106,6 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
104106
nuxt.options.build.transpile.push('vuefire')
105107
nuxt.options.build.transpile.push('vuefire/server')
106108

107-
if (nuxt.options.ssr && options.admin) {
108-
// check the provided config is valid
109-
if (options.auth || options.appCheck) {
110-
if (!options.admin.config || !options.admin.serviceAccount) {
111-
throw new Error(
112-
'[VueFire]: Missing firebase "admin" config. Provide an "admin" option to the VueFire module options. This is necessary to use the auth or app-check module.'
113-
)
114-
}
115-
nuxt.options.appConfig.firebaseAdmin = markRaw(options.admin)
116-
}
117-
}
118-
119109
if (nuxt.options.ssr) {
120110
addServerHandler({
121111
route: '/api/_vuefire/auth',
@@ -168,12 +158,25 @@ const VueFire: NuxtModule<VueFireNuxtModuleOptions> =
168158
addPlugin(resolve(runtimeDir, 'app/plugin.server'))
169159

170160
// we start the admin app first so we can have access to the user uid everywhere
171-
if (options.admin) {
161+
// TODO: if options.admin
162+
if (options.admin || nuxt.options.ssr) {
172163
if (!nuxt.options.ssr) {
173164
console.warn(
174165
'[VueFire]: The "admin" option is only used during SSR. You should reenable ssr to use it.'
175166
)
176167
}
168+
// TODO: check env variables are present
169+
170+
// This one is set by servers, we set the GOOGLE_APPLICATION_CREDENTIALS env variable instead that has a lower priority and can be both a path or a JSON string
171+
// process.env.FIREBASE_CONFIG ||= JSON.stringify(options.config)
172+
if (typeof options.admin?.serviceAccount === 'string') {
173+
process.env.GOOGLE_APPLICATION_CREDENTIALS ||=
174+
options.admin.serviceAccount
175+
}
176+
// TODO: remove this runtime config if it's not needed as it could include sensitive data
177+
if (options.admin) {
178+
nuxt.options.appConfig.firebaseAdmin = markRaw(options.admin)
179+
}
177180
// this plugin adds the user so it's accessible directly in the app as well
178181
if (options.auth) {
179182
addPlugin(resolve(runtimeDir, 'admin/plugin-auth-user.server'))
@@ -268,10 +271,7 @@ declare module '@nuxt/schema' {
268271
* Firebase Admin options passed to VueFire module. Only available on the server.
269272
* @internal
270273
*/
271-
firebaseAdmin?: {
272-
config: Omit<AppOptions, 'credential'>
273-
serviceAccount: string | ServiceAccount
274-
}
274+
firebaseAdmin?: VueFireNuxtModuleOptions['admin']
275275
}
276276
}
277277

packages/nuxt/src/runtime/admin/plugin-auth-user.server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { App as AdminApp } from 'firebase-admin/app'
2-
import type { User } from 'firebase/auth'
32
import { getAuth as getAdminAuth, UserRecord } from 'firebase-admin/auth'
43
import { createServerUser } from 'vuefire/server'
54
import { getCookie } from 'h3'
@@ -9,7 +8,7 @@ import { AUTH_COOKIE_NAME } from '../auth/api.session'
98
import { defineNuxtPlugin, useRequestEvent } from '#app'
109

1110
/**
12-
* Check if there is a cookie and if it is valid, extracts the user from it.
11+
* Check if there is a cookie and if it is valid, extracts the user from it. This only requires the admin app.
1312
*/
1413
export default defineNuxtPlugin(async (nuxtApp) => {
1514
const event = useRequestEvent()
@@ -31,7 +30,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
3130
// the error is fine, the user is not logged in
3231
} else {
3332
// ignore the error and consider the user as not logged in
34-
console.error(err)
33+
console.error('[VueFire]:', err)
3534
}
3635
}
3736
}

packages/nuxt/src/runtime/admin/plugin.server.ts

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import {
33
cert,
44
getApp,
55
getApps,
6-
ServiceAccount,
6+
applicationDefault,
7+
// renamed because there seems to be a global Credential type in vscode
8+
Credential as FirebaseAdminCredential,
79
} from 'firebase-admin/app'
810
import { defineNuxtPlugin, useAppConfig } from '#app'
911

@@ -19,32 +21,50 @@ export default defineNuxtPlugin((nuxtApp) => {
1921

2022
// only initialize the admin sdk once
2123
if (!getApps().length) {
22-
const { FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY } =
23-
process.env
24-
// we need either a serviceAccount or the env variables
25-
let serviceAccountOrProject: string | ServiceAccount
24+
const {
25+
// these can be set by the user on other platforms
26+
FIREBASE_PROJECT_ID,
27+
FIREBASE_CLIENT_EMAIL,
28+
FIREBASE_PRIVATE_KEY,
29+
// set on firebase cloud functions
30+
FIREBASE_CONFIG,
31+
} = process.env
2632

27-
if (FIREBASE_CLIENT_EMAIL && FIREBASE_PRIVATE_KEY && FIREBASE_PROJECT_ID) {
33+
if (FIREBASE_CONFIG) {
34+
console.log('[VueFire]: using FIREBASE_CONFIG env variable')
35+
initializeApp()
36+
} else {
37+
let credential: FirebaseAdminCredential
2838
// This version should work in Firebase Functions and other providers while applicationDefault() only works on
29-
serviceAccountOrProject = {
30-
projectId: FIREBASE_PROJECT_ID,
31-
clientEmail: FIREBASE_CLIENT_EMAIL,
32-
// replace `\` and `n` character pairs w/ single `\n` character
33-
privateKey: FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
39+
if (FIREBASE_PRIVATE_KEY) {
40+
console.log('[VueFire]: using FIREBASE_PRIVATE_KEY env variable')
41+
credential = cert({
42+
projectId: FIREBASE_PROJECT_ID,
43+
clientEmail: FIREBASE_CLIENT_EMAIL,
44+
// replace `\` and `n` character pairs w/ single `\n` character
45+
privateKey: FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
46+
})
47+
} else if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
48+
console.log(
49+
'[VueFire]: using GOOGLE_APPLICATION_CREDENTIALS env variable'
50+
)
51+
// automatically picks up the service account file path from the env variable
52+
credential = applicationDefault()
53+
} else {
54+
// TODO: add link to docs
55+
console.warn(`\
56+
[VueFire]: You must provide an "admin.serviceAccount" path to your json so it's picked up during development. See https://firebase.google.com/docs/admin/setup#initialize-sdk for more information. Note that you can also set the GOOGLE_APPLICATION_CREDENTIALS env variable to a full resolved path or a JSON string.
57+
You can also set the FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY and FIREBASE_PROJECT_ID env variables in production if you are deploying to something else than Firebase Cloud Functions.
58+
`)
59+
throw new Error('admin-app/missing-credentials')
3460
}
35-
} else if (firebaseAdmin.serviceAccount) {
36-
serviceAccountOrProject = firebaseAdmin.serviceAccount
37-
} else {
38-
throw new Error(
39-
'[VueFire]: You must provide a "serviceAccount" (dev) or set the FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY and FIREBASE_PROJECT_ID env variables production.'
40-
)
41-
}
4261

43-
initializeApp({
44-
// TODO: is this really going to be used?
45-
...firebaseAdmin.config,
46-
credential: cert(serviceAccountOrProject),
47-
})
62+
initializeApp({
63+
// TODO: is this really going to be used?
64+
...firebaseAdmin.config,
65+
credential,
66+
})
67+
}
4868
}
4969

5070
const firebaseAdminApp = getApp()

packages/nuxt/src/runtime/app/plugin.server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { UserSymbol } from '../admin/plugin-auth-user.server'
55
import { defineNuxtPlugin, useAppConfig } from '#app'
66

77
// TODO: allow customizing
8-
// TODO: find sensible defaults
8+
// TODO: find sensible defaults. Should they change depending on the platform?
9+
// copied from https://github.com/FirebaseExtended/firebase-framework-tools/blob/e69f5bdd44695274ad88dbb4e21aac778ba60cc8/src/constants.ts
910
export const LRU_MAX_INSTANCES = 100
1011
export const LRU_TTL = 1_000 * 60 * 5
1112
const appCache = new LRU<string, FirebaseApp>({
@@ -21,7 +22,7 @@ const appCache = new LRU<string, FirebaseApp>({
2122
/**
2223
* Initializes the app and provides it to others.
2324
*/
24-
export default defineNuxtPlugin(async (nuxtApp) => {
25+
export default defineNuxtPlugin((nuxtApp) => {
2526
const appConfig = useAppConfig()
2627

2728
// @ts-expect-error: this is a private symbol

0 commit comments

Comments
 (0)