@libreapps/auth-firebase 1.1.1
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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +13 -0
- package/LICENSE.md +21 -0
- package/README.md +87 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/server/firebase-support.d.ts +28 -0
- package/dist/server/firebase-support.js +157 -0
- package/dist/server/firebase-support.js.map +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +5 -0
- package/dist/server/index.js.map +1 -0
- package/dist/service/firebase-auth-service.d.ts +36 -0
- package/dist/service/firebase-auth-service.js +182 -0
- package/dist/service/firebase-auth-service.js.map +1 -0
- package/dist/service/firebase-support.d.ts +39 -0
- package/dist/service/firebase-support.js +187 -0
- package/dist/service/firebase-support.js.map +1 -0
- package/dist/service/index.d.ts +6 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/wallet-support.d.ts +11 -0
- package/dist/service/wallet-support.js +85 -0
- package/dist/service/wallet-support.js.map +1 -0
- package/index.ts +30 -0
- package/package.json +51 -0
- package/server/firebase-support.ts +180 -0
- package/server/index.ts +11 -0
- package/service/firebase-auth-service.ts +233 -0
- package/service/firebase-support.ts +234 -0
- package/service/index.ts +20 -0
- package/service/wallet-support.ts +103 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Authentication Service Implementation
|
|
3
|
+
* Implements the AuthService interface using Firebase as the auth provider.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { makeAutoObservable, makeObservable, computed } from 'mobx'
|
|
7
|
+
|
|
8
|
+
import type { AuthService, AuthServiceConf, LibreAppsUserInfo, LibreAppsUserInfoValue } from '@libreapps/auth'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
auth as fbAuth,
|
|
12
|
+
signupWithEmailAndPassword,
|
|
13
|
+
loginWithCustomToken,
|
|
14
|
+
loginWithEmailAndPassword,
|
|
15
|
+
loginWithProvider,
|
|
16
|
+
logoutBackend,
|
|
17
|
+
isFirebaseConfigured
|
|
18
|
+
} from './firebase-support'
|
|
19
|
+
import { associateWalletAddressWithAccount, getAssociatedWalletAddress } from './wallet-support'
|
|
20
|
+
|
|
21
|
+
class LibreAppsUserInfoStore implements LibreAppsUserInfo {
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
makeAutoObservable(this)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_email: string = ''
|
|
28
|
+
_displayName: string | null = null
|
|
29
|
+
_walletAddress: string | null = null
|
|
30
|
+
|
|
31
|
+
get email(): string { return this._email}
|
|
32
|
+
get displayName(): string | null { return this._displayName}
|
|
33
|
+
get walletAddress(): string | null { return this._walletAddress}
|
|
34
|
+
|
|
35
|
+
clear():void {
|
|
36
|
+
this._email = ''
|
|
37
|
+
this._displayName = null
|
|
38
|
+
this._walletAddress = null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
set(v: LibreAppsUserInfoValue):void {
|
|
42
|
+
this._email = v.email
|
|
43
|
+
this._displayName = v.displayName
|
|
44
|
+
this._walletAddress = v.walletAddress
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get isValid(): boolean {
|
|
48
|
+
return (this._email.length > 0)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class FirebaseAuthService implements AuthService {
|
|
53
|
+
|
|
54
|
+
private _hzUser = new LibreAppsUserInfoStore()
|
|
55
|
+
|
|
56
|
+
constructor(conf: AuthServiceConf, user: LibreAppsUserInfoValue | null) {
|
|
57
|
+
makeObservable(this, {
|
|
58
|
+
loggedIn: computed,
|
|
59
|
+
user: computed
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
if (user) {
|
|
63
|
+
this._hzUser.set(user)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static isConfigured(): boolean {
|
|
68
|
+
return isFirebaseConfigured()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get user(): LibreAppsUserInfo | null {
|
|
72
|
+
return this._hzUser.isValid ? this._hzUser : null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get loggedIn(): boolean {
|
|
76
|
+
return this._hzUser.isValid
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
signupEmailAndPassword = async (
|
|
80
|
+
email: string,
|
|
81
|
+
password: string
|
|
82
|
+
): Promise<{success: boolean, userInfo: LibreAppsUserInfo | null, message?: string}> => {
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
this._hzUser.clear()
|
|
86
|
+
const res = await signupWithEmailAndPassword(email, password)
|
|
87
|
+
if (res.success && res.user) {
|
|
88
|
+
const walletAddress = res.user.email ? await getAssociatedWalletAddress(res.user.email) : undefined
|
|
89
|
+
this._hzUser.set({
|
|
90
|
+
email: res.user.email ?? '',
|
|
91
|
+
displayName : res.user.displayName ?? null,
|
|
92
|
+
walletAddress : walletAddress?.result ?? null
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
userInfo: this._hzUser,
|
|
98
|
+
message: res.message
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
userInfo: null,
|
|
104
|
+
message: res.message
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error('Error signing in with Firebase auth', e)
|
|
109
|
+
return {success: false, userInfo: null, message: 'Error signing in with Firebase auth'}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
loginEmailAndPassword = async (
|
|
114
|
+
email: string,
|
|
115
|
+
password: string
|
|
116
|
+
): Promise<{success: boolean, userInfo: LibreAppsUserInfo | null, message?: string}> => {
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
this._hzUser.clear()
|
|
120
|
+
const res = await loginWithEmailAndPassword(email, password)
|
|
121
|
+
if (res.success && res.user) {
|
|
122
|
+
const walletAddress = res.user.email ? await getAssociatedWalletAddress(res.user.email) : undefined
|
|
123
|
+
this._hzUser.set({
|
|
124
|
+
email: res.user.email ?? '',
|
|
125
|
+
displayName : res.user.displayName ?? null,
|
|
126
|
+
walletAddress : walletAddress?.result ?? null
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
userInfo: this._hzUser,
|
|
132
|
+
message: res.message
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
userInfo: null,
|
|
138
|
+
message: res.message
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
console.error('Error signing in with Firebase auth', e)
|
|
143
|
+
return {success: false, userInfo: null, message: 'Error signing in with Firebase auth'}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
loginWithProvider = async (
|
|
148
|
+
provider: 'google' | 'facebook' | 'github'
|
|
149
|
+
): Promise<{success: boolean, userInfo: LibreAppsUserInfo | null}> => {
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
this._hzUser.clear()
|
|
153
|
+
const res = await loginWithProvider(provider)
|
|
154
|
+
if (res.success && res.user) {
|
|
155
|
+
const walletAddress = res.user.email ? await getAssociatedWalletAddress(res.user.email) : undefined
|
|
156
|
+
this._hzUser.set({
|
|
157
|
+
email: res.user.email ?? '',
|
|
158
|
+
displayName : res.user.displayName ?? null,
|
|
159
|
+
walletAddress : walletAddress?.result ?? null
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
164
|
+
userInfo: this._hzUser
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
userInfo: null
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
console.error('Error signing in with Firebase auth', e)
|
|
174
|
+
return {success: false, userInfo: null}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
loginWithCustomToken = async (
|
|
179
|
+
token: string
|
|
180
|
+
): Promise<{success: boolean, userInfo: LibreAppsUserInfo | null}> => {
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
this._hzUser.clear()
|
|
184
|
+
const res = await loginWithCustomToken(token)
|
|
185
|
+
if (res.success && res.user) {
|
|
186
|
+
const walletAddress = res.user.email ? await getAssociatedWalletAddress(res.user.email) : undefined
|
|
187
|
+
this._hzUser.set({
|
|
188
|
+
email: res.user.email ?? '',
|
|
189
|
+
displayName : res.user.displayName ?? null,
|
|
190
|
+
walletAddress : walletAddress?.result ?? null
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
userInfo: this._hzUser
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
userInfo: null
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
console.error('Error signing in with Firebase auth', e)
|
|
205
|
+
return {success: false, userInfo: null}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
associateWallet = async (): Promise<void> => {
|
|
210
|
+
if (this._hzUser.isValid) {
|
|
211
|
+
const res = await associateWalletAddressWithAccount(this._hzUser.email)
|
|
212
|
+
if (!res.error) {
|
|
213
|
+
this._hzUser._walletAddress = res.result ?? null
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
logout = async (): Promise<{ success: boolean }> => {
|
|
219
|
+
if (fbAuth) {
|
|
220
|
+
await fbAuth.signOut()
|
|
221
|
+
}
|
|
222
|
+
this._hzUser.clear()
|
|
223
|
+
return await logoutBackend()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
setServerSideUser = (user: LibreAppsUserInfoValue | null) => {
|
|
227
|
+
if (user) {
|
|
228
|
+
this._hzUser.set(user)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export default FirebaseAuthService
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Client-side Authentication Support
|
|
3
|
+
* This module provides Firebase authentication methods for the client-side.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
FacebookAuthProvider,
|
|
8
|
+
GoogleAuthProvider,
|
|
9
|
+
GithubAuthProvider,
|
|
10
|
+
signInWithPopup,
|
|
11
|
+
createUserWithEmailAndPassword,
|
|
12
|
+
type User,
|
|
13
|
+
signInWithEmailAndPassword,
|
|
14
|
+
signInWithCustomToken,
|
|
15
|
+
type Auth,
|
|
16
|
+
} from 'firebase/auth'
|
|
17
|
+
|
|
18
|
+
import { initializeApp, getApps, FirebaseError, type FirebaseApp } from "firebase/app"
|
|
19
|
+
import { getAuth } from "firebase/auth"
|
|
20
|
+
import { getFirestore, type Firestore } from 'firebase/firestore'
|
|
21
|
+
|
|
22
|
+
import type { APIResponse } from '@libreapps/auth/types'
|
|
23
|
+
|
|
24
|
+
export const firebaseConfig = {
|
|
25
|
+
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
|
|
26
|
+
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
|
|
27
|
+
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
|
|
28
|
+
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
|
|
29
|
+
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
|
|
30
|
+
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if Firebase is configured
|
|
34
|
+
export const isFirebaseConfigured = () => {
|
|
35
|
+
return !!(firebaseConfig.apiKey && firebaseConfig.projectId)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Initialize Firebase only if configured
|
|
39
|
+
let firebaseApp: FirebaseApp | null = null
|
|
40
|
+
let auth: Auth | null = null
|
|
41
|
+
let db: Firestore | null = null
|
|
42
|
+
|
|
43
|
+
if (typeof window !== 'undefined' && isFirebaseConfigured()) {
|
|
44
|
+
firebaseApp = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0]
|
|
45
|
+
auth = getAuth(firebaseApp)
|
|
46
|
+
db = getFirestore(firebaseApp, 'accounts')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { auth, db }
|
|
50
|
+
|
|
51
|
+
export async function loginWithProvider(provider: string): Promise<{ success: boolean, user: User | null }> {
|
|
52
|
+
if (!auth) {
|
|
53
|
+
console.warn('Firebase auth not configured')
|
|
54
|
+
return { success: false, user: null }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const authProvider = (() => {
|
|
58
|
+
switch (provider) {
|
|
59
|
+
case 'google':
|
|
60
|
+
return new GoogleAuthProvider()
|
|
61
|
+
case 'facebook':
|
|
62
|
+
return new FacebookAuthProvider()
|
|
63
|
+
case 'github':
|
|
64
|
+
return new GithubAuthProvider()
|
|
65
|
+
default:
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
})()
|
|
69
|
+
|
|
70
|
+
if (!authProvider) {
|
|
71
|
+
return { success: false, user: null }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const userCreds = await signInWithPopup(auth, authProvider)
|
|
76
|
+
const idToken = await userCreds.user.getIdToken()
|
|
77
|
+
|
|
78
|
+
const response = await fetch('/api/auth/login', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({ idToken }),
|
|
82
|
+
})
|
|
83
|
+
const resBody = (await response.json()) as unknown as APIResponse<string>
|
|
84
|
+
|
|
85
|
+
if (response.ok && resBody.success) {
|
|
86
|
+
return { success: true, user: userCreds.user }
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return { success: false, user: null }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('Error signing in with provider', error)
|
|
94
|
+
return { success: false, user: null }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function signupWithEmailAndPassword(
|
|
99
|
+
email: string,
|
|
100
|
+
password: string
|
|
101
|
+
): Promise<{ success: boolean, user?: User, message?: string }> {
|
|
102
|
+
if (!auth) {
|
|
103
|
+
return { success: false, message: 'Firebase auth not configured' }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let user: User | undefined = undefined
|
|
107
|
+
try {
|
|
108
|
+
const userCredential = await createUserWithEmailAndPassword(auth, email, password)
|
|
109
|
+
user = userCredential.user
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error instanceof FirebaseError) {
|
|
113
|
+
console.error(error.code)
|
|
114
|
+
return {success: false, message: error.code as string}
|
|
115
|
+
}
|
|
116
|
+
return {success: false, message: error as string}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const idToken = await user.getIdToken()
|
|
121
|
+
|
|
122
|
+
const response = await fetch('/api/auth/login', {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
body: JSON.stringify({ idToken }),
|
|
126
|
+
})
|
|
127
|
+
const resBody = (await response.json()) as unknown as APIResponse<string>
|
|
128
|
+
|
|
129
|
+
if (response.ok && resBody.success) {
|
|
130
|
+
return { success: true, user }
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return { success: false }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error('Error signing in with Firebase auth', error)
|
|
138
|
+
return { success: false }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function loginWithEmailAndPassword(
|
|
143
|
+
email: string,
|
|
144
|
+
password: string
|
|
145
|
+
): Promise<{ success: boolean, user?: User, message?: string }> {
|
|
146
|
+
if (!auth) {
|
|
147
|
+
return { success: false, message: 'Firebase auth not configured' }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let user: User | undefined = undefined
|
|
151
|
+
try {
|
|
152
|
+
const userCredential = await signInWithEmailAndPassword(auth, email, password)
|
|
153
|
+
user = userCredential.user
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof FirebaseError) {
|
|
156
|
+
console.error(error.code)
|
|
157
|
+
return {success: false, message: error.code as string}
|
|
158
|
+
}
|
|
159
|
+
return {success: false, message: error as string}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const idToken = await user.getIdToken()
|
|
164
|
+
|
|
165
|
+
const response = await fetch('/api/auth/login', {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers: { 'Content-Type': 'application/json' },
|
|
168
|
+
body: JSON.stringify({ idToken }),
|
|
169
|
+
})
|
|
170
|
+
const resBody = (await response.json()) as unknown as APIResponse<string>
|
|
171
|
+
|
|
172
|
+
if (response.ok && resBody.success) {
|
|
173
|
+
return { success: true, user, message: "Login Successfully!" }
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return { success: false , message: "Login API Failed"}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
console.error('Error signing in with Firebase auth', error)
|
|
181
|
+
return { success: false, message: "Error signing in with Firebase auth" }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function loginWithCustomToken(
|
|
186
|
+
token: string,
|
|
187
|
+
): Promise<{ success: boolean, user?: User }> {
|
|
188
|
+
if (!auth) {
|
|
189
|
+
return { success: false }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let user: User | undefined = undefined
|
|
193
|
+
const userCredential = await signInWithCustomToken(auth, token)
|
|
194
|
+
user = userCredential.user
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const idToken = await user.getIdToken()
|
|
198
|
+
|
|
199
|
+
const response = await fetch('/api/auth/login', {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: { 'Content-Type': 'application/json' },
|
|
202
|
+
body: JSON.stringify({ idToken }),
|
|
203
|
+
})
|
|
204
|
+
const resBody = (await response.json()) as unknown as APIResponse<string>
|
|
205
|
+
|
|
206
|
+
if (response.ok && resBody.success) {
|
|
207
|
+
return { success: true, user }
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return { success: false }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.error('Error signing in with Firebase auth', error)
|
|
215
|
+
return { success: false }
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export async function logoutBackend(): Promise<{ success: boolean }> {
|
|
220
|
+
try {
|
|
221
|
+
const response = await fetch('/api/auth/logout', { headers: { 'Content-Type': 'application/json' } })
|
|
222
|
+
const resBody = (await response.json()) as unknown as APIResponse<string>
|
|
223
|
+
if (response.ok && resBody.success) {
|
|
224
|
+
return { success: true }
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return { success: false }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error('Error logging out on server', error)
|
|
232
|
+
return { success: false }
|
|
233
|
+
}
|
|
234
|
+
}
|
package/service/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Auth Service exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { default as FirebaseAuthService } from './firebase-auth-service'
|
|
6
|
+
export {
|
|
7
|
+
auth,
|
|
8
|
+
db,
|
|
9
|
+
isFirebaseConfigured,
|
|
10
|
+
firebaseConfig,
|
|
11
|
+
loginWithProvider,
|
|
12
|
+
signupWithEmailAndPassword,
|
|
13
|
+
loginWithEmailAndPassword,
|
|
14
|
+
loginWithCustomToken,
|
|
15
|
+
logoutBackend
|
|
16
|
+
} from './firebase-support'
|
|
17
|
+
export {
|
|
18
|
+
associateWalletAddressWithAccount,
|
|
19
|
+
getAssociatedWalletAddress
|
|
20
|
+
} from './wallet-support'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Support - Firebase Firestore integration for wallet addresses
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { collection, doc, getDoc, setDoc } from 'firebase/firestore'
|
|
6
|
+
|
|
7
|
+
import { auth, db } from './firebase-support'
|
|
8
|
+
|
|
9
|
+
const isObject = (obj: any): obj is Record<any, any> => typeof obj === 'object' && obj !== null
|
|
10
|
+
const isGlobalThisEthereum = (obj: any): obj is { ethereum: { request: <R = any>(payload: Record<any, any>) => Promise<R> } } => isObject(obj) && isObject(obj.ethereum) && typeof obj.ethereum.request === 'function'
|
|
11
|
+
const getEthereum = (obj: any) => isGlobalThisEthereum(obj) ? obj.ethereum : null
|
|
12
|
+
|
|
13
|
+
const ethereum = getEthereum(globalThis)
|
|
14
|
+
|
|
15
|
+
let signMessage = (opts: { siteName: string, address: string }) => `${opts.siteName} wants you to sign in with your Ethereum account:\n${opts.address}`
|
|
16
|
+
|
|
17
|
+
const USER_INFO_COLLECTION = 'HZ_USER_INFO'
|
|
18
|
+
|
|
19
|
+
async function connectWalletAddress(siteName?: string) {
|
|
20
|
+
if (!ethereum) {
|
|
21
|
+
throw new Error('No ethereum provider found')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!auth) {
|
|
25
|
+
throw new Error('Firebase auth not configured')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [account] = await ethereum.request<string[]>({ method: 'eth_requestAccounts' })
|
|
29
|
+
|
|
30
|
+
if (!account) {
|
|
31
|
+
throw new Error('No account found')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const signed = await ethereum.request<string>({
|
|
35
|
+
method: 'personal_sign',
|
|
36
|
+
params: [
|
|
37
|
+
signMessage({
|
|
38
|
+
siteName: siteName ?? auth.app.options.projectId ?? globalThis.location.hostname,
|
|
39
|
+
address: account,
|
|
40
|
+
}),
|
|
41
|
+
account,
|
|
42
|
+
auth.app.options.appId,
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
if (!signed) {
|
|
47
|
+
throw new Error('Not signed')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {account, signed}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function associateWalletAddressWithAccount(userEmail: string, siteName?: string) {
|
|
54
|
+
const {account} = await connectWalletAddress(siteName)
|
|
55
|
+
|
|
56
|
+
if (!db) {
|
|
57
|
+
return { result: null, error: new Error('Firebase Firestore not configured') }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let result = null
|
|
61
|
+
let error = null
|
|
62
|
+
const accountsRef = collection(db, USER_INFO_COLLECTION)
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
try {
|
|
66
|
+
await setDoc(doc(accountsRef, userEmail), { walletAddress: account })
|
|
67
|
+
result = account
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error(e)
|
|
70
|
+
error = e
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error(e)
|
|
74
|
+
error = e
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { result, error }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function getAssociatedWalletAddress(userEmail: string) : Promise<{error: any, result?: string}> {
|
|
81
|
+
if (!db) {
|
|
82
|
+
return { error: new Error('Firebase Firestore not configured'), result: undefined }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let result = undefined
|
|
86
|
+
let error = null
|
|
87
|
+
try {
|
|
88
|
+
try {
|
|
89
|
+
const docRef = await getDoc(doc(db, USER_INFO_COLLECTION, userEmail))
|
|
90
|
+
result = docRef.data() ? docRef.data()!.walletAddress as string : undefined
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
console.error(e)
|
|
94
|
+
error = e
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
console.error(e)
|
|
99
|
+
error = e
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { result, error }
|
|
103
|
+
}
|