11import { deleteApp , type FirebaseApp , initializeApp } from 'firebase/app'
22import { getAuth , signInWithCustomToken , type User } from 'firebase/auth'
33import { type App as AdminApp } from 'firebase-admin/app'
4- import { getAuth as getAdminAuth } from 'firebase-admin/auth'
4+ import { DecodedIdToken , getAuth as getAdminAuth } from 'firebase-admin/auth'
55import { LRUCache } from 'lru-cache'
66import { log } from '../logging'
7- import { UserSymbol } from '../constants'
8- import { defineNuxtPlugin , useAppConfig } from '#app'
7+ import { DECODED_ID_TOKEN_SYMBOL , UserSymbol } from '../constants'
8+ import { defineNuxtPlugin , useAppConfig , useRequestEvent } from '#app'
99
1010// TODO: allow customizing
1111// TODO: find sensible defaults. Should they change depending on the platform?
@@ -27,12 +27,19 @@ const appCache = new LRUCache<string, FirebaseApp>({
2727 */
2828export default defineNuxtPlugin ( async ( nuxtApp ) => {
2929 const appConfig = useAppConfig ( )
30+ const event = useRequestEvent ( )
3031
31- const user = nuxtApp [
32+ const decodedToken = nuxtApp [
3233 // we cannot use a symbol to index
33- UserSymbol as unknown as string
34- ] as User | undefined | null
35- const uid = user ?. uid
34+ DECODED_ID_TOKEN_SYMBOL as unknown as string
35+ ] as DecodedIdToken | null | undefined
36+
37+ const uid = decodedToken ?. uid
38+
39+ // // expose the user to code
40+ // event.context.user = user
41+ // // for SSR
42+ // nuxtApp.payload.vuefireUser = user?.toJSON()
3643
3744 let firebaseApp : FirebaseApp | undefined
3845
@@ -42,29 +49,53 @@ export default defineNuxtPlugin(async (nuxtApp) => {
4249 if ( ! firebaseApp ) {
4350 const randomId = Math . random ( ) . toString ( 36 ) . slice ( 2 )
4451 // TODO: do we need a randomId?
45- const appName = `auth:${ user . uid } :${ randomId } `
52+ const appName = `auth:${ uid } :${ randomId } `
4653
47- // log('log ', '👤 creating new app', appName)
54+ log ( 'debug ' , '👤 creating new app' , appName )
4855
4956 appCache . set ( uid , initializeApp ( appConfig . firebaseConfig , appName ) )
5057 firebaseApp = appCache . get ( uid ) !
51- const firebaseAdminApp = nuxtApp . $firebaseAdminApp as AdminApp
52- const adminAuth = getAdminAuth ( firebaseAdminApp )
5358 // console.time('token')
54- const customToken = await adminAuth . createCustomToken ( user . uid )
59+ } else {
60+ log ( 'debug' , '👤 reusing authenticated app' , uid )
61+ }
62+ // TODO: this should only happen if user enabled auth in config
63+ const firebaseAdminApp = nuxtApp . $firebaseAdminApp as AdminApp
64+ const adminAuth = getAdminAuth ( firebaseAdminApp )
65+ const auth = getAuth ( firebaseApp )
66+
67+ // reauthenticate if the user is not the same (e.g. invalidated)
68+ if ( auth . currentUser ?. uid !== uid ) {
69+ const customToken = await adminAuth
70+ . createCustomToken ( uid )
71+ . catch ( ( err ) => {
72+ log ( 'error' , 'Error creating custom token' , err )
73+ return null
74+ } )
5575 // console.timeLog('token', `got token for ${user.uid}`)
56- const credentials = await signInWithCustomToken (
57- getAuth ( firebaseApp ) ,
58- customToken
59- )
60- // console.timeLog ('token', `signed in with token for ${user.uid}` )
61- // console.timeEnd(' token' )
62- // TODO: token expiration (1h)
76+ if ( customToken ) {
77+ const auth = getAuth ( firebaseApp )
78+ await signInWithCustomToken ( auth , customToken )
79+ // console.timeLog('token', `signed in with token for ${user.uid}` )
80+ // console.timeEnd ('token')
81+ // TODO: token expiration (1h )
82+ }
6383 }
84+ nuxtApp [
85+ // we cannot use a symbol to index
86+ UserSymbol as unknown as string
87+ ] = auth . currentUser
88+ // expose the user to code
89+ event . context . user = auth . currentUser
90+ // for SSR
91+ nuxtApp . payload . vuefireUser = auth . currentUser ?. toJSON ( )
6492 } else {
93+ if ( ! appCache . has ( '' ) ) {
94+ appCache . set ( '' , initializeApp ( appConfig . firebaseConfig ) )
95+ }
96+ firebaseApp = appCache . get ( '' ) !
6597 // anonymous session, just create a new app
66- // log('log', '🥸 anonymous session')
67- firebaseApp = initializeApp ( appConfig . firebaseConfig )
98+ log ( 'debug' , '🥸 anonymous session' )
6899 }
69100
70101 return {
@@ -73,3 +104,15 @@ export default defineNuxtPlugin(async (nuxtApp) => {
73104 } ,
74105 }
75106} )
107+
108+ // TODO: should the type extensions be added in a different way to the module?
109+ declare module 'h3' {
110+ interface H3EventContext {
111+ /**
112+ * Firebase Admin User Record. `null` if the user is not logged in or their token is no longer valid and requires a
113+ * refresh.
114+ * @experimental This API is experimental and may change in future releases.
115+ */
116+ user : User | null
117+ }
118+ }
0 commit comments