Skip to content

Commit ae90bed

Browse files
committed
feat(auth): update current profile info
1 parent 294c57c commit ae90bed

File tree

6 files changed

+118
-6
lines changed

6 files changed

+118
-6
lines changed

playground/src/firebase.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { initializeApp } from 'firebase/app'
2+
import { GoogleAuthProvider } from 'firebase/auth'
23

34
export function createFirebaseApp() {
45
return initializeApp({
@@ -12,3 +13,5 @@ export function createFirebaseApp() {
1213
measurementId: 'G-RL4BTWXKJ7',
1314
})
1415
}
16+
17+
export const googleAuthProvider = new GoogleAuthProvider()

playground/src/pages/authentication.vue

+57-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts" setup>
2+
import { googleAuthProvider } from '@/firebase'
23
import {
34
createUserWithEmailAndPassword,
45
EmailAuthProvider,
@@ -8,20 +9,31 @@ import {
89
signInWithPopup,
910
signInWithRedirect,
1011
signOut,
12+
GoogleAuthProvider,
13+
updateCurrentUser,
14+
updateProfile,
15+
AuthCredential,
16+
getRedirectResult,
1117
} from 'firebase/auth'
1218
import { ref } from 'vue'
13-
import { useCurrentUser, useFirebaseAuth } from 'vuefire'
19+
import {
20+
updateCurrentUserProfile,
21+
useCurrentUser,
22+
useFirebaseAuth,
23+
} from 'vuefire'
1424
1525
const auth = useFirebaseAuth()
1626
const user = useCurrentUser()
27+
let credential: AuthCredential | null = null
1728
1829
// new user
1930
const email = ref('')
2031
const password = ref('')
2132
function signUp() {
2233
// link to an existing anonymous account
2334
if (user.value?.isAnonymous) {
24-
const credential = EmailAuthProvider.credential(email.value, password.value)
35+
credential = EmailAuthProvider.credential(email.value, password.value)
36+
2537
return linkWithCredential(user.value, credential).then(() => {
2638
return signInWithEmailAndPassword(auth, email.value, password.value)
2739
})
@@ -30,13 +42,48 @@ function signUp() {
3042
// create a regular account
3143
return createUserWithEmailAndPassword(auth, email.value, password.value)
3244
}
45+
46+
function signinPopup() {
47+
return signInWithPopup(auth, googleAuthProvider).then((result) => {
48+
const googleCredential = GoogleAuthProvider.credentialFromResult(result)
49+
credential = googleCredential
50+
const token = googleCredential?.accessToken
51+
console.log('Got Google token', token)
52+
console.log('Got googleCredential', googleCredential)
53+
})
54+
}
55+
56+
async function changeUserImage() {
57+
if (user.value) {
58+
await updateCurrentUserProfile({
59+
photoURL: 'https://i.pravatar.cc/150?u=' + Date.now(),
60+
})
61+
62+
// updateCurrentUserEmail('[email protected]')
63+
}
64+
}
65+
66+
function signinRedirect() {
67+
signInWithRedirect(auth, googleAuthProvider)
68+
}
69+
70+
getRedirectResult(auth).then((creds) => {
71+
console.log('got creds', creds)
72+
if (creds) {
73+
// credential = creds.user.
74+
}
75+
})
3376
</script>
3477

3578
<template>
3679
<main>
3780
<h1>Auth playground</h1>
3881
<button @click="signOut(auth)">SignOut</button>
3982
<button @click="signInAnonymously(auth)">Anonymous signIn</button>
83+
<button @click="signinPopup()">Signin Google (popup)</button>
84+
<button @click="signinRedirect()">Signin Google (redirect)</button>
85+
<button @click="changeUserImage">Change User picture</button>
86+
4087
<form @submit.prevent="signUp()">
4188
<fieldset>
4289
<legend>New User</legend>
@@ -64,6 +111,14 @@ function signUp() {
64111
<button>Signin</button>
65112
</fieldset>
66113
</form>
114+
115+
<p v-if="user">
116+
Name: {{ user.displayName }} <br />
117+
<img v-if="user.photoURL" :src="user.photoURL" />
118+
</p>
119+
120+
<hr />
121+
67122
<p>Current User:</p>
68123
<pre>{{ user }}</pre>
69124
</main>

src/auth/index.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { getAuth, User } from 'firebase/auth'
33
import { App, ref, shallowRef } from 'vue-demi'
44
import { useFirebaseApp } from '../app'
55
import { getGlobalScope } from '../globals'
6+
import { _Nullable } from '../shared'
67
import { AuthUserInjectSymbol, setupOnAuthStateChanged } from './user'
78

8-
export { useCurrentUser, getCurrentUser } from './user'
9+
export {
10+
useCurrentUser,
11+
getCurrentUser,
12+
updateCurrentUserProfile,
13+
updateCurrentUserEmail,
14+
} from './user'
915

1016
/**
1117
* VueFire Auth Module to be added to the `VueFire` Vue plugin options.
@@ -36,7 +42,7 @@ modules: [VueFireAuth()]`)
3642

3743
return (firebaseApp: FirebaseApp, app: App) => {
3844
const user = getGlobalScope(firebaseApp, app).run(() =>
39-
shallowRef<User | null | undefined>()
45+
ref<_Nullable<User>>()
4046
)!
4147
// userMap.set(app, user)
4248
app.provide(AuthUserInjectSymbol, user)

src/auth/user.ts

+42-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {
1010
} from 'firebase/auth'
1111
import { inject, InjectionKey, Ref } from 'vue-demi'
1212
import { useFirebaseApp } from '../app'
13-
import type { _Nullable } from '../shared'
13+
import type { _MaybeRef, _Nullable } from '../shared'
1414

15-
export const AuthUserInjectSymbol: InjectionKey<Ref<User | null | undefined>> =
15+
export const AuthUserInjectSymbol: InjectionKey<Ref<_Nullable<User>>> =
1616
Symbol('user')
1717

1818
/**
@@ -24,6 +24,46 @@ export function useCurrentUser() {
2424
return inject(AuthUserInjectSymbol)!
2525
}
2626

27+
/**
28+
* Updates the current user profile and updates the current user state. This function internally calls `updateProfile()`
29+
* from 'firebase/auth' and then updates the current user state.
30+
*
31+
* @param user - the user to update
32+
* @param profile - the new profile information
33+
*/
34+
export function updateCurrentUserProfile(profile: {
35+
displayName?: _Nullable<string>
36+
photoURL?: _Nullable<string>
37+
}) {
38+
return getCurrentUser().then((user) => {
39+
if (user) {
40+
return updateProfile(user, profile).then(() => user.reload())
41+
}
42+
})
43+
}
44+
45+
export function updateCurrentUserEmail(
46+
newEmail: string,
47+
credential: AuthCredential
48+
) {
49+
return getCurrentUser()
50+
.then((user) => {
51+
if (user) {
52+
// TODO: Maybe this whole function should be dropped since it depends on reauthenticating first or we should let the user do it. Otherwise, we need a way to retrieve the credential token when logging in
53+
reauthenticateWithCredential(user, credential)
54+
}
55+
return user
56+
})
57+
.then((user) => {
58+
if (user) {
59+
return updateEmail(user, newEmail).then(() => {
60+
// @ts-expect-error: readonly property
61+
user.email = newEmail
62+
})
63+
}
64+
})
65+
}
66+
2767
// @internal
2868
type _UserState =
2969
// state 1 waiting for the initial load

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export {
6060
VueFireAuth,
6161
useFirebaseAuth,
6262
getCurrentUser,
63+
updateCurrentUserProfile,
6364
} from './auth'
6465

6566
// SSR

src/shared.ts

+7
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,10 @@ export interface _DataSourceOptions {
247247
*/
248248
wait?: boolean
249249
}
250+
251+
/**
252+
* Make all properties in T writable.
253+
*/
254+
export type _Mutable<T> = {
255+
-readonly [P in keyof T]: T[P]
256+
}

0 commit comments

Comments
 (0)