Skip to content

Commit be573b1

Browse files
Add method to link anonymous to Google account (#5)
1 parent f886e44 commit be573b1

8 files changed

Lines changed: 117 additions & 5 deletions

File tree

demo/project.godot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Firebase="*res://addons/GodotFirebaseAndroid/Firebase.gd"
2626

2727
window/size/viewport_width=720
2828
window/size/viewport_height=1280
29+
window/stretch/mode="canvas_items"
2930
window/handheld/orientation=1
3031

3132
[editor_plugins]

demo/scenes/authentication.gd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ func _notification(what: int) -> void:
1313
func _ready() -> void:
1414
Firebase.auth.auth_success.connect(print_output.bind("auth_success"))
1515
Firebase.auth.auth_failure.connect(print_output.bind("auth_failure"))
16+
Firebase.auth.link_with_google_success.connect(print_output.bind("link_with_google_success"))
17+
Firebase.auth.link_with_google_failure.connect(print_output.bind("link_with_google_failure"))
1618
Firebase.auth.sign_out_success.connect(print_output.bind("sign_out_success"))
1719
Firebase.auth.email_verification_sent.connect(print_output.bind("email_verification_sent"))
1820
Firebase.auth.password_reset_sent.connect(print_output.bind("password_reset_sent"))
@@ -38,6 +40,10 @@ func _on_google_sign_in_pressed() -> void:
3840
Firebase.auth.sign_in_with_google()
3941

4042

43+
func _on_link_anonymous_with_google_pressed() -> void:
44+
Firebase.auth.link_anonymous_with_google()
45+
46+
4147
func _on_get_user_data_pressed() -> void:
4248
print_output(Firebase.auth.get_current_user_data(), "Current User Data")
4349

demo/scenes/authentication.tscn

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ theme_override_styles/pressed = SubResource("StyleBoxFlat_0xm2m")
135135
theme_override_styles/normal = SubResource("StyleBoxFlat_ig7tw")
136136
text = "Sign In With Google"
137137

138+
[node name="link_anonymous_with_google" type="Button" parent="MarginContainer/VBoxContainer"]
139+
custom_minimum_size = Vector2(120, 60)
140+
layout_mode = 2
141+
theme_override_font_sizes/font_size = 32
142+
theme_override_styles/focus = SubResource("StyleBoxFlat_7dm0k")
143+
theme_override_styles/hover = SubResource("StyleBoxFlat_ig7tw")
144+
theme_override_styles/pressed = SubResource("StyleBoxFlat_0xm2m")
145+
theme_override_styles/normal = SubResource("StyleBoxFlat_ig7tw")
146+
text = "Link Anonymous To Google"
147+
138148
[node name="get_user_data" type="Button" parent="MarginContainer/VBoxContainer"]
139149
custom_minimum_size = Vector2(120, 60)
140150
layout_mode = 2
@@ -186,6 +196,7 @@ theme_override_font_sizes/normal_font_size = 32
186196
[connection signal="pressed" from="MarginContainer/VBoxContainer/email_verification" to="." method="_on_email_verification_pressed"]
187197
[connection signal="pressed" from="MarginContainer/VBoxContainer/password_reset" to="." method="_on_password_reset_pressed"]
188198
[connection signal="pressed" from="MarginContainer/VBoxContainer/google_sign_in" to="." method="_on_google_sign_in_pressed"]
199+
[connection signal="pressed" from="MarginContainer/VBoxContainer/link_anonymous_with_google" to="." method="_on_link_anonymous_with_google_pressed"]
189200
[connection signal="pressed" from="MarginContainer/VBoxContainer/get_user_data" to="." method="_on_get_user_data_pressed"]
190201
[connection signal="pressed" from="MarginContainer/VBoxContainer/is_signed_in" to="." method="_on_is_signed_in_pressed"]
191202
[connection signal="pressed" from="MarginContainer/VBoxContainer/sign_out" to="." method="_on_sign_out_pressed"]

docs/authentication.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ The Firebase Authentication module in **GodotFirebaseAndroid** supports anonymou
1616
- `auth_failure(error_message: String)`
1717
Emitted when an authentication operation fails.
1818

19+
- `link_with_google_success(current_user_data: Dictionary)`
20+
Emitted when an anonymous user is successfully linked to a Google account.
21+
22+
- `link_with_google_failure(error_message: String)`
23+
Emitted when linking an anonymous user to a Google account fails.
24+
1925
- `sign_out_success(success: bool)`
2026
Emitted after a sign-out operation. `true` indicates success.
2127

@@ -104,12 +110,31 @@ Firebase.auth.sign_in_with_google()
104110
```
105111
---
106112

113+
{: .text-green-100 }
114+
### link_anonymous_with_google()
115+
116+
Links the currently signed-in anonymous user to a Google account, converting it to a permanent account. The anonymous user's UID and data are preserved. Must be called while an anonymous user is signed in.
117+
118+
**Emits:** `link_with_google_success` or `link_with_google_failure`.
119+
120+
```gdscript
121+
Firebase.auth.link_anonymous_with_google()
122+
```
123+
124+
---
125+
107126
{: .text-green-100 }
108127
### get_current_user_data() -> Dictionary
109128

110-
If no user is signed in, returns an dictionary with error.
129+
If no user is signed in, returns a dictionary with error.
111130

112-
**Returns** a dictionary containing the currently signed-in user's data.
131+
**Returns** a dictionary containing the currently signed-in user's data:
132+
- `name` — Display name
133+
- `email` — Email address
134+
- `photoUrl` — Profile photo URL
135+
- `emailVerified` — Whether the email is verified
136+
- `isAnonymous` — Whether the user is anonymous
137+
- `uid` — User ID
113138

114139
```gdscript
115140
Firebase.auth.get_current_user_data()

firebase/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ android {
1818
}
1919

2020
defaultConfig {
21-
minSdk = 21
21+
minSdk = 24
2222

2323
manifestPlaceholders["godotPluginName"] = pluginName
2424
manifestPlaceholders["godotPluginPackageName"] = pluginPackageName

firebase/export_scripts_template/modules/Auth.gd

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ extends Node
22

33
signal auth_success(current_user_data: Dictionary)
44
signal auth_failure(error_message: String)
5+
signal link_with_google_success(current_user_data: Dictionary)
6+
signal link_with_google_failure(error_message: String)
57
signal sign_out_success(success: bool)
68
signal password_reset_sent(success: bool)
79
signal email_verification_sent(success: bool)
@@ -14,6 +16,8 @@ func _connect_signals():
1416
return
1517
_plugin_singleton.connect("auth_success", auth_success.emit)
1618
_plugin_singleton.connect("auth_failure", auth_failure.emit)
19+
_plugin_singleton.connect("link_with_google_success", link_with_google_success.emit)
20+
_plugin_singleton.connect("link_with_google_failure", link_with_google_failure.emit)
1721
_plugin_singleton.connect("sign_out_success", sign_out_success.emit)
1822
_plugin_singleton.connect("password_reset_sent", password_reset_sent.emit)
1923
_plugin_singleton.connect("email_verification_sent", email_verification_sent.emit)
@@ -43,6 +47,10 @@ func sign_in_with_google() -> void:
4347
if _plugin_singleton:
4448
_plugin_singleton.signInWithGoogle()
4549

50+
func link_anonymous_with_google() -> void:
51+
if _plugin_singleton:
52+
_plugin_singleton.linkAnonymousWithGoogle()
53+
4654
func get_current_user_data() -> Dictionary:
4755
var user_data: Dictionary
4856
if _plugin_singleton:

firebase/src/main/java/org/godotengine/plugin/firebase/Authentication.kt

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ class Authentication(private val plugin: FirebasePlugin) {
2424
private lateinit var activity: android.app.Activity
2525
private val auth: FirebaseAuth = Firebase.auth
2626
private lateinit var googleSignInClient: GoogleSignInClient
27+
private var isLinkingAnonymous = false
2728

2829
fun authSignals(): MutableSet<SignalInfo> {
2930
val signals: MutableSet<SignalInfo> = mutableSetOf()
3031
signals.add(SignalInfo("auth_success", Dictionary::class.java))
3132
signals.add(SignalInfo("auth_failure", String::class.java))
33+
signals.add(SignalInfo("link_with_google_success", Dictionary::class.java))
34+
signals.add(SignalInfo("link_with_google_failure", String::class.java))
3235
signals.add(SignalInfo("sign_out_success", Boolean::class.javaObjectType))
3336
signals.add(SignalInfo("password_reset_sent", Boolean::class.javaObjectType))
3437
signals.add(SignalInfo("email_verification_sent", Boolean::class.javaObjectType))
@@ -61,15 +64,32 @@ class Authentication(private val plugin: FirebasePlugin) {
6164
try {
6265
val account = task.getResult(ApiException::class.java)!!
6366
Log.d(TAG, "authWithGoogle:" + account.id)
64-
authWithGoogle(account.idToken!!)
67+
if (isLinkingAnonymous) {
68+
isLinkingAnonymous = false
69+
linkWithGoogle(account.idToken!!)
70+
} else {
71+
authWithGoogle(account.idToken!!)
72+
}
6573
} catch (e: ApiException) {
74+
val wasLinking = isLinkingAnonymous
75+
isLinkingAnonymous = false
6676
Log.w(TAG, "Google sign in failed", e)
67-
plugin.emitGodotSignal("auth_failure", e.message ?: "Unknown error")
77+
if (wasLinking) {
78+
plugin.emitGodotSignal("link_with_google_failure", e.message ?: "Unknown error")
79+
} else {
80+
plugin.emitGodotSignal("auth_failure", e.message ?: "Unknown error")
81+
}
6882
}
6983
}
7084
}
7185

7286
fun signInAnonymously() {
87+
val currentUser = auth.currentUser
88+
if (currentUser != null) {
89+
Log.d(TAG, "User already signed in (uid=${currentUser.uid}, isAnonymous=${currentUser.isAnonymous}). Skipping anonymous sign-in.")
90+
plugin.emitGodotSignal("auth_failure", "User is already signed in.")
91+
return
92+
}
7393
auth.signInAnonymously()
7494
.addOnSuccessListener {
7595
val uid = it.user?.uid
@@ -146,6 +166,23 @@ class Authentication(private val plugin: FirebasePlugin) {
146166
}
147167
}
148168

169+
fun linkAnonymousWithGoogle() {
170+
val currentUser = auth.currentUser
171+
if (currentUser == null) {
172+
Log.e(TAG, "No user signed in.")
173+
plugin.emitGodotSignal("link_with_google_failure", "No user signed in.")
174+
return
175+
}
176+
if (!currentUser.isAnonymous) {
177+
Log.d(TAG, "Current user is not anonymous (uid=${currentUser.uid}). Cannot link.")
178+
plugin.emitGodotSignal("link_with_google_failure", "Current user is not anonymous.")
179+
return
180+
}
181+
Log.d(TAG, "Linking anonymous user (uid=${currentUser.uid}) with Google.")
182+
isLinkingAnonymous = true
183+
signInWithGoogle()
184+
}
185+
149186
private fun authWithGoogle(idToken: String) {
150187
val credential = GoogleAuthProvider.getCredential(idToken, null)
151188
auth.signInWithCredential(credential)
@@ -160,6 +197,26 @@ class Authentication(private val plugin: FirebasePlugin) {
160197
}
161198
}
162199

200+
private fun linkWithGoogle(idToken: String) {
201+
val currentUser = auth.currentUser
202+
if (currentUser == null) {
203+
Log.e(TAG, "No user signed in during linkWithGoogle.")
204+
plugin.emitGodotSignal("link_with_google_failure", "No user signed in.")
205+
return
206+
}
207+
val credential = GoogleAuthProvider.getCredential(idToken, null)
208+
currentUser.linkWithCredential(credential)
209+
.addOnSuccessListener { authResult ->
210+
val uid = authResult.user?.uid
211+
Log.d(TAG, "linkWithCredential:success -> $uid")
212+
plugin.emitGodotSignal("link_with_google_success", getCurrentUser())
213+
}
214+
.addOnFailureListener { e ->
215+
Log.w(TAG, "linkWithCredential:failure", e)
216+
plugin.emitGodotSignal("link_with_google_failure", e.message ?: "Unknown error")
217+
}
218+
}
219+
163220
fun getCurrentUser(): Dictionary {
164221
val user = auth.currentUser
165222
val userData = Dictionary()
@@ -168,6 +225,7 @@ class Authentication(private val plugin: FirebasePlugin) {
168225
userData["email"] = user.email
169226
userData["photoUrl"] = user.photoUrl?.toString()
170227
userData["emailVerified"] = user.isEmailVerified
228+
userData["isAnonymous"] = user.isAnonymous
171229
userData["uid"] = user.uid
172230
} else {
173231
userData["error"] = "No user signed in"

firebase/src/main/java/org/godotengine/plugin/firebase/FirebasePlugin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class FirebasePlugin(godot: Godot) : GodotPlugin(godot) {
6464
@UsedByGodot
6565
fun signInWithGoogle() = auth.signInWithGoogle()
6666

67+
@UsedByGodot
68+
fun linkAnonymousWithGoogle() = auth.linkAnonymousWithGoogle()
69+
6770
@UsedByGodot
6871
fun getCurrentUser() = auth.getCurrentUser()
6972

0 commit comments

Comments
 (0)