Integrating facebook as an auth provider within firebase (already have a working gmail implementation). The below code works fine in Unity, the FB SDK prompts for token, enter it manually, token is passed to firebase, login can be seen in the console, the required scene is then loaded.
When this is built onto an android device the full behavior doesnt happen, the correct Facebook token is received either from fresh login or existing login, we enter the SignInWithFacebookOnFirebase function to log this account into firebase and nothing else happens, sits on the debug of ("your token is " + aToken);
Im pretty sure it has something to do with the async behaviour and maybe not awaiting the task but im not sure what, any suggestions would be great !
using System.Collections.Generic;
using UnityEngine;
using Facebook.Unity;
using TMPro;
using UnityEngine.SceneManagement;
using Firebase.Auth;
using Firebase;
public class FacebookAuth : MonoBehaviour
{
private FirebaseAuth auth;
public TMP_Text debug;
void Awake ()
{
if (FB.IsInitialized) {
FB.ActivateApp();
} else {
//Handle FB.Init
FB.Init( () => {
FB.ActivateApp();
});
}
CheckFirebaseDependencies();
}
private void CheckFirebaseDependencies()
{
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
if (task.IsCompleted)
{
if (task.Result == DependencyStatus.Available)
auth = FirebaseAuth.DefaultInstance;
else
debug.text =("Could not resolve all Firebase dependencies: " + task.Result.ToString());
}
else
{
debug.text =("Dependency check was not completed. Error : " + task.Exception.Message);
}
});
}
void OnApplicationPause (bool pauseStatus)
{
// Check the pauseStatus to see if we are in the foreground
// or background
if (!pauseStatus) {
//app resume
if (FB.IsInitialized) {
FB.ActivateApp();
} else {
//Handle FB.Init
FB.Init( () => {
FB.ActivateApp();
});
}
}
}
private void InitCallBack()
{
if(!FB.IsInitialized)
{
FB.ActivateApp();
}
else
{
// debug.text=("Failed to initialize");
}
}
private void OnHideUnity(bool isgameshown)
{
if(!isgameshown)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
public void Facebook_Login()
{
var permission = new List<string>() { "public_profile", "email" };
if (!FB.IsLoggedIn)
{
FB.LogInWithReadPermissions(permission, AuthCallBack);
}
else
{
var aToken = AccessToken.CurrentAccessToken.TokenString;
debug.text=("already logged in - starting game" + aToken);
//THIS IS THE PROBLEM ON ANDROID - ITS NOT HAPPENING/async issue?
SignInWithFacebookOnFirebase(aToken);
SceneManager.LoadScene(1);
}
}
public void LogOut()
{
FB.LogOut();
debug.text=("Logged out of facebook");
}
private void SignInWithFacebookOnFirebase(string idToken)
{
Firebase.Auth.Credential credential = Firebase.Auth.FacebookAuthProvider.GetCredential(idToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.Exception != null) {
Debug.LogWarning("SignInWithCredentialAsync encountered an error: " + task.Exception);
FirebaseException firebaseEx = task.Exception.GetBaseException() as FirebaseException;
AuthError errorCode = (AuthError)firebaseEx.ErrorCode;
string message = "Login Failed!";
switch (errorCode)
{
case AuthError.AccountExistsWithDifferentCredentials:
message = "Your account is already linked to an email address";
break;
//we can add other conditions here if required to catch exceptions
}
debug.text=(message);
}
else{
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
debug.text=("Logged into facebook");
}
});
}
private void AuthCallBack(ILoginResult result)
{
if(FB.IsLoggedIn)
{
var aToken = result.AccessToken.TokenString;
debug.text=("your token is " + aToken);
//THIS IS THE PROBLEM ON ANDROID - ITS NOT HAPPENING/async issue?
SignInWithFacebookOnFirebase(aToken);
debug.text=("weve signed into firebase");
SceneManager.LoadScene(1);
}
else
{
debug.text=("User Cancelled login");
}
}
}
EDIT - So the problem is nothing to do with async, its the fact that the firebase credential is being persisted on the mobile device. I uncommented all of my already working google auth code below and we log into firebase with our facebook creds fine! So i need some method of clearing our this token when A) the user logs out and B) the user closes the app (Cleanly or uncleanly) Any help would be great !
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Firebase;
using Firebase.Auth;
using Google;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GoogleAuth : MonoBehaviour
{ /*
public TMP_Text infoText;
public string webClientId = "<your client id here>";
private FirebaseAuth auth;
private GoogleSignInConfiguration configuration;
private void Awake()
{
configuration = new GoogleSignInConfiguration { WebClientId = webClientId, RequestEmail = true, RequestIdToken = true };
CheckFirebaseDependencies();
}
private void CheckFirebaseDependencies()
{
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
if (task.IsCompleted)
{
if (task.Result == DependencyStatus.Available)
auth = FirebaseAuth.DefaultInstance;
else
AddToInformation("Could not resolve all Firebase dependencies: " + task.Result.ToString());
}
else
{
AddToInformation("Dependency check was not completed. Error : " + task.Exception.Message);
}
});
}
public void SignInWithGoogle() { OnSignIn(); }
public void SignOutFromGoogle() { OnSignOut(); }
private void OnSignIn()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = false;
GoogleSignIn.Configuration.RequestIdToken = true;
AddToInformation("Calling SignIn");
GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
}
private void OnSignOut()
{
AddToInformation("Calling SignOut");
GoogleSignIn.DefaultInstance.SignOut();
}
public void OnDisconnect()
{
GoogleSignIn.DefaultInstance.Disconnect();
infoText.text=("signed out");
}
internal void OnAuthenticationFinished(Task<GoogleSignInUser> task)
{
if (task.IsFaulted)
{
using (IEnumerator<Exception> enumerator = task.Exception.InnerExceptions.GetEnumerator())
{
if (enumerator.MoveNext())
{
GoogleSignIn.SignInException error = (GoogleSignIn.SignInException)enumerator.Current;
AddToInformation("Got Error: " + error.Status + " " + error.Message);
}
else
{
AddToInformation("Got Unexpected Exception?!?" + task.Exception);
}
}
}
else if (task.IsCanceled)
{
AddToInformation("Cancelled");
}
else
{
AddToInformation("Welcome: " + task.Result.DisplayName + "!");
AddToInformation("Email = " + task.Result.Email);
//AddToInformation("Google ID Token = " + task.Result.IdToken);
AddToInformation("Email = " + task.Result.Email);
SignInWithGoogleOnFirebase(task.Result.IdToken);
}
}
private void SignInWithGoogleOnFirebase(string idToken)
{
Credential credential = GoogleAuthProvider.GetCredential(idToken, null);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
AggregateException ex = task.Exception;
if (ex != null)
{
if (ex.InnerExceptions[0] is FirebaseException inner && (inner.ErrorCode != 0))
AddToInformation("\nError code = " + inner.ErrorCode + " Message = " + inner.Message);
}
else
{
AddToInformation("Sign In Successful.");
SceneManager.LoadScene(1);
}
});
}
public void OnSignInSilently()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = false;
GoogleSignIn.Configuration.RequestIdToken = true;
AddToInformation("Calling SignIn Silently");
GoogleSignIn.DefaultInstance.SignInSilently().ContinueWith(OnAuthenticationFinished);
}
public void OnGamesSignIn()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = true;
GoogleSignIn.Configuration.RequestIdToken = false;
AddToInformation("Calling Games SignIn");
GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
}
private void AddToInformation(string str) { infoText.text += "\n" + str; } */
}
this is solved, it was because i was checking that the firebase dependences existed in both these scripts and it was throwing an exception. I found this using the logcat logs \adb logcat -s Unity ActivityManager PackageManager dalvikvm DEBUG
Related
I am usign Firebase for the logins with gmail and facebook i am sharing the code below its working in the Game mode in unity but after building the apk after that we click on the fb assigned button on android device the app stops workign As i have fallowed the document give in firebase and facebook developer example doc
https://firebase.google.com/docs/auth/unity/facebook-login
https://developers.facebook.com/docs/unity/examples
using Facebook.Unity;
using Firebase.Auth;
void Awake()
{
if (!FB.IsInitialized)
{
// Initialize the Facebook SDK
FB.Init(InitCallback, OnHideUnity);
}
else
{
// Already initialized, signal an app activation App Event
FB.ActivateApp();
}
}
private void InitCallback()
{
if (FB.IsInitialized)
{
// Signal an app activation App Event
FB.ActivateApp();
// Continue with Facebook SDK
// ...
}
else
{
Debug.Log("Failed to Initialize the Facebook SDK");
}
}
private void OnHideUnity(bool isGameShown)
{
if (!isGameShown)
{
// Pause the game - we will need to hide
Time.timeScale = 0;
}
else
{
// Resume the game - we're getting focus again
Time.timeScale = 1;
}
}
public void loginfb()
{
var perms = new List<string>() { "public_profile", "email" };
FB.LogInWithReadPermissions(perms, AuthCallback);
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
// AccessToken class will have session details
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
// Print current access token's User ID
Debug.Log(aToken.UserId);
// Print current access token's granted permissions
foreach (string perm in aToken.Permissions)
{
Debug.Log(perm);
}
}
else
{
Debug.Log("User cancelled login");
}
}
Firebase.Auth.FirebaseAuth auth;
public void loginviaFirebaseFacebook(String accessToken)
{
auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
Firebase.Auth.Credential credential =
Firebase.Auth.FacebookAuthProvider.GetCredential(accessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithCredentialAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
I'm here to make a game with anonymous login with firebase auth, and a database with firebase real-time database, everything works smoothly except for firebase auth. The problem is I don't know why? the unity won't connect to firebase auth.
Installation
I already install the package like always database and auth
also for the firebase is like this
Script
here my login script :
//Firebase variables
[Header("Firebase")]
public DependencyStatus dependencyStatus;
public FirebaseAuth auth;
public FirebaseUser User;
public DatabaseReference DBreference;
public static FirebaseManager instance;
void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
instance = this;
}
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
dependencyStatus = task.Result;
if (dependencyStatus == DependencyStatus.Available)
{
InitializeFirebase();
}
else
{
Debug.LogError(
"Could not resolve all Firebase dependencies: " + dependency status);
}
});
}
public void InitializeFirebase()
{
auth = FirebaseAuth.DefaultInstance;
DBreference = FirebaseDatabase.DefaultInstance.RootReference;
auth.StateChanged += AuthStateChanged;
AuthStateChanged(this, null);
}
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
//This checks if the user (your local user) is the same as the one from the auth
if (auth.CurrentUser != User)
{
bool signedIn = User != auth.CurrentUser && auth.CurrentUser != null;
User = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + User.UserId + " " + User.DisplayName);
}
else
{
StartCoroutine(SignAnonymously());
}
}
}
//it does not directly log the user out but invalidates the auth
void OnDestroy()
{
auth.StateChanged -= AuthStateChanged;
auth = null;
}
public IEnumerator SignAnonymously()
{
var loginTask = auth.SignInAnonymouslyAsync();
yield return new WaitUntil(predicate: () => loginTask.IsCompleted);
if (loginTask.Exception != null)
{
//If there are errors handle them
Debug.LogWarning(message: $"Failed to register task with {loginTask.Exception}");
}
else
{
User = loginTask.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})", User.DisplayName, User.UserId);
}
}
What I've tried
Check the google-service.json ( already same or updated )
already implement this https://firebase.googleblog.com/2020/08/firebase-compatibility-with-unity-20201.html due to a compatibility issue.
already changed continuewith to ContinueWithOnMainThread(task..
note: I'm using unity ver 2020.3.1f1 LTS
am I doing it wrong?
Try to replace your Coroutine method SignAnonymously with this async method:
public async void SignAnonymously()
{
await auth.SignInAnonymouslyAsync().ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("SignInAnonymouslyAsync was canceled.");
return;
}
if (task.IsFaulted) {
Debug.LogError("SignInAnonymouslyAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
I think that the problem could be the lacking of ContinueWithat the end of your SignInAnonymouslyAsync, maybe you can use it on your Coroutine but I've never done it that way, tell me if it works ^^
I am developing an app in unity. I use the firebase google login method. basically google login is working and the user was listed in google firebase users log after login. the problem is, it is throwing an error. because of this can't fetch data from the firestore. even without firestore code, the app is showing the error
Got Error: DeveloperError Exception of type
'Google.GoogleSignIn+SignInException' was thrown
what may be the problem.
below is my code
public class GoogleSignInDemo : MonoBehaviour
{
public Text infoText;
private string webClientId = "xxxxxxaaaaaaabbbb.apps.googleusercontent.com";
private FirebaseAuth auth;
private GoogleSignInConfiguration configuration;
private void Awake()
{
configuration = new GoogleSignInConfiguration { WebClientId = webClientId, RequestEmail = true, RequestIdToken = true };
CheckFirebaseDependencies();
}
private void CheckFirebaseDependencies()
{
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
if (task.IsCompleted)
{
if (task.Result == DependencyStatus.Available)
auth = FirebaseAuth.DefaultInstance;
else
AddToInformation("Could not resolve all Firebase dependencies: " + task.Result.ToString());
}
else
{
AddToInformation("Dependency check was not completed. Error : " + task.Exception.Message);
}
});
}
public void SignInWithGoogle() { OnSignIn(); }
public void SignOutFromGoogle() { OnSignOut(); }
private void OnSignIn()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = false;
GoogleSignIn.Configuration.RequestIdToken = true;
AddToInformation("Calling SignIn");
GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
}
private void OnSignOut()
{
AddToInformation("Calling SignOut");
GoogleSignIn.DefaultInstance.SignOut();
}
public void OnDisconnect()
{
AddToInformation("Calling Disconnect");
GoogleSignIn.DefaultInstance.Disconnect();
}
internal void OnAuthenticationFinished(Task<GoogleSignInUser> task)
{
if (task.IsFaulted)
{
using (IEnumerator<Exception> enumerator = task.Exception.InnerExceptions.GetEnumerator())
{
if (enumerator.MoveNext())
{
GoogleSignIn.SignInException error = (GoogleSignIn.SignInException)enumerator.Current;
AddToInformation("Got Error: " + error.Status + " " + error.Message);
}
else
{
AddToInformation("Got Unexpected Exception?!?" + task.Exception);
}
}
}
else if (task.IsCanceled)
{
AddToInformation("Canceled");
}
else
{
AddToInformation("Welcome: " + task.Result.DisplayName + "!");
AddToInformation("Email = " + task.Result.Email);
AddToInformation("Google ID Token = " + task.Result.IdToken);
AddToInformation("Email = " + task.Result.Email);
SignInWithGoogleOnFirebase(task.Result.IdToken);
SceneManager.LoadScene(1); //Savad - Load Welcome screen when Google Login
}
}
private void SignInWithGoogleOnFirebase(string idToken)
{
Credential credential = GoogleAuthProvider.GetCredential(idToken, null);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
AggregateException ex = task.Exception;
//==============Here is the problem
if (ex != null)
{
if (ex.InnerExceptions[0] is FirebaseException inner && (inner.ErrorCode != 0))
AddToInformation("\nError code = " + inner.ErrorCode + " Message = " + inner.Message);
//=======================================
}
else
{
AddToInformation("Sign In Successful.");
}
});
}
public void OnSignInSilently()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = false;
GoogleSignIn.Configuration.RequestIdToken = true;
AddToInformation("Calling SignIn Silently");
GoogleSignIn.DefaultInstance.SignInSilently().ContinueWith(OnAuthenticationFinished);
}
public void OnGamesSignIn()
{
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = true;
GoogleSignIn.Configuration.RequestIdToken = false;
AddToInformation("Calling Games SignIn");
GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
}
private void AddToInformation(string str) { infoText.text += "\n" + str; }
public void SwitchToPhoneSignup()
{
SceneManager.LoadScene(2);
}
public void SwitchToOtp()
{
SceneManager.LoadScene(2);
}
public void SwitchToEmailSignUP()
{
SceneManager.LoadScene(2);
}
}
Here is a working example of Google SignIn code w/ Firebase Authentication and GoogleSignIn libraries:
private void SignInWithGoogle(bool linkWithCurrentAnonUser)
{
GoogleSignIn.Configuration = new GoogleSignInConfiguration
{
RequestIdToken = true,
// Copy this value from the google-service.json file.
// oauth_client with type == 3
WebClientId = "[YOUR API CLIENT ID HERE].apps.googleusercontent.com"
};
Task<GoogleSignInUser> signIn = GoogleSignIn.DefaultInstance.SignIn();
TaskCompletionSource<FirebaseUser> signInCompleted = new TaskCompletionSource<FirebaseUser>();
signIn.ContinueWith(task =>
{
if (task.IsCanceled)
{
signInCompleted.SetCanceled();
}
else if (task.IsFaulted)
{
signInCompleted.SetException(task.Exception);
}
else
{
Credential credential = Firebase.Auth.GoogleAuthProvider.GetCredential(((Task<GoogleSignInUser>)task).Result.IdToken, null);
if (linkWithCurrentAnonUser)
{
mAuth.CurrentUser.LinkWithCredentialAsync(credential).ContinueWith(HandleLoginResult);
}
else
{
SignInWithCredential(credential);
}
}
});
}
The parameter is for signing in with intentions of linking the new google account with an anonymous user that is currently logged on. You can ignore those parts of the method if desired. Please note all of this is called after proper initialization of the Firebase Auth libraries.
Source: https://github.com/googlesamples/google-signin-unity
The readme page contains a step-by-step instructions for getting this setup for your environment. After following those and using the code above, you should have this working on both android and iOS.
Here is the SignInWithCredential method used in the code above:
private void SignInWithCredential(Credential credential)
{
if (mAuth != null)
{
mAuth.SignInWithCredentialAsync(credential).ContinueWith(HandleLoginResult);
}
}
`mAuth` is a reference to FirebaseAuth:
mAuth = Firebase.Auth.FirebaseAuth.DefaultInstance;
For someone asking for the HandleLoginResult from #DIGI Byte, here is the code, feel free to remove try/catch block for debug purpose:
private void HandleLoginResult(Task<FirebaseUser> task)
{
try
{
if (task.IsCanceled)
{
UnityEngine.Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
UnityEngine.Debug.LogError("SignInWithCredentialAsync encountered an error: " + task.Exception.InnerException.Message);
return;
}
else
{
FirebaseUser newUser = task.Result;
UnityEngine.Debug.Log($"User signed in successfully: {newUser.DisplayName} ({newUser.UserId})");
}
}
catch (Exception e)
{
if (e != null)
{
UnityEngine.Debug.Log(e.InnerException.Message);
}
}
}
You have Error like: got error developer error exception of type 'google.googlesignin+signin exception' was thrown?
APK and AAB file have different SHA1 and SHA256. In Firebase, it's best to add SHA for both applications.
You can check the SHA from the AAB file in Google Play Console -> Your application -> Configuration -> Application integrity -> Application signing.
I added these SHA keys to Firebase and the problem was gone.
I have a quite strange or somewhat understandable(?) problem.
My firebase project support 4 types of authentications: Google, Apple, Facebook and anonymous.
I created 2 accounts. One has Google linked with facebook, the other one has apple only.
Then I link same facebook account that is already linked with google at apple account.
Firebase console shows no change but everytime I login into apple account, firebase returns google's UID.
I kinda understand that there are all linked into one account but I don't get why the apple's UID isn't removed and google's UID doesn't have apple provider from firebase console.
Another case:
Create 3 accounts: Google, Apple and Facebook separately.
Link all together into one. every time whichever I login into, firebase returns facebook's UID. (this is something that I completely unable to understand why.)
If it's normal, should I manage them manually on console or my project if I want to see it as one account on firebase console?
Edit:
firebase console
Unity FirebaseAuth.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Firebase.Auth;
using System.Text;
using System.Security.Cryptography;
using UnityEngine.SocialPlatforms;
#if !UNITY_IOS
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using GooglePlayGames.OurUtils;
#else
using AppleAuth;
using AppleAuth.Native;
using AppleAuth.Enums;
using AppleAuth.Extensions;
using AppleAuth.Interfaces;
#endif
public class FirebaseAuth : MonoBehaviour
{
public enum eMOBILEPLATFORM_STATUS
{
CONNECTED,
DISCONNECT,
ERROR,
LOGIN_WAIT,
};
private static FirebaseAuth _instance;
protected eMOBILEPLATFORM_STATUS _accountStatus = eMOBILEPLATFORM_STATUS.DISCONNECT;
#if UNITY_IOS
private IAppleAuthManager _appleAuthManager;
#endif
public eMOBILEPLATFORM_STATUS Status { get { return _accountStatus; } }
public FirebaseAuth auth;
private string _accessToken = string.Empty;
public static FirebaseAuth Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType(typeof(FirebaseAuth)) as FirebaseAuth;
if (_instance == null)
{
Debug.LogError("Missing GlobalObject, Use for Firebase");
return null;
}
}
return _instance;
}
}
void Update()
{
#if UNITY_IOS
if (_appleAuthManager != null) _appleAuthManager.Update();
#endif
}
public void Init()
{
#if !UNITY_IOS
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration
.Builder()
.RequestServerAuthCode(false)
.RequestIdToken()
.Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.DebugLogEnabled = true;
PlayGamesPlatform.Activate();
#else
if (AppleAuthManager.IsCurrentPlatformSupported)
{
var deserializer = new PayloadDeserializer();
_appleAuthManager = new AppleAuthManager(deserializer);
}
#endif
Debug.Log("Firebase Init");
auth = FirebaseAuth.DefaultInstance;
_accessToken = string.Empty;
}
#if !UNITY_IOS
public void TryGoogleLogin(bool newLogin = true)
{
if (!Social.localUser.authenticated)
{
Social.localUser.Authenticate(success =>
{
if (success)
{
Debug.Log("login success. " + Social.localUser.userName);
if (newLogin)
{
StartCoroutine(TryGoogleFirebaseLogin(newLogin));
}
else
{
LinkGoogleFirebase();
}
}
else
{
Debug.Log("login failed");
}
});
}
else
{
if (newLogin)
{
StartCoroutine(TryGoogleFirebaseLogin(newLogin));
}
else
{
LinkGoogleFirebase();
}
}
}
public void TryGoogleLogout()
{
if (Social.localUser.authenticated)
{
PlayGamesPlatform.Instance.SignOut();
TryFirebaseLogout();
Debug.Log("logout - done");
}
}
public IEnumerator TryGoogleFirebaseLogin(bool newLogin = true)
{
if (newLogin)
{
auth.Dispose();
auth = FirebaseAuth.DefaultInstance;
}
while (string.IsNullOrEmpty(((PlayGamesLocalUser)Social.localUser).GetIdToken()))
yield return null;
string idToken = ((PlayGamesLocalUser)Social.localUser).GetIdToken();
_accessToken = idToken;
Credential credential = GoogleAuthProvider.GetCredential(idToken, null);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("google-CredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("google-CredentialAsync encountered and error: " + task.Exception);
return;
}
Debug.Log("google login success! :D " + auth.CurrentUser.UserId);
_accountStatus = eMOBILEPLATFORM_STATUS.CONNECTED;
UserConfig.Instance.SaveMobilePlatformLogin(true);
PlayerInfo.Instance.m_bGlobalMobilePlatformLogin = true;
if (DoesProvierExist("facebook.com"))
{
PlayerInfo.Instance.m_bGlobalGuestLogin = false;
UserConfig.Instance.SaveFacebookLogin(true);
FacebookManager.Instance.ChangeFacebookStatus(FacebookManager.eFACEBOOK_STATUS.CONNECTED);
}
});
}
public void LinkGoogleFirebase()
{
if (auth != null)
{
string idToken = ((PlayGamesLocalUser)Social.localUser).GetIdToken();
Credential credential = GoogleAuthProvider.GetCredential(idToken, null);
auth.CurrentUser.LinkWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("google-LinkWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("google-LinkWithCredentialAsync encountered an error: " + task.Exception);
return;
}
FirebaseUser newUser = task.Result;
Debug.Log("google-Credentials successfully linked to Firebase user: " + newUser.DisplayName + "(" + newUser.UserId + ")");
_accountStatus = eMOBILEPLATFORM_STATUS.CONNECTED;
});
}
}
#endif
public IEnumerator TryFacebookFirebaseLogin(Facebook.Unity.AccessToken accessToken, bool newLogin = false)
{
if (newLogin)
{
auth = FirebaseAuth.DefaultInstance;
}
while (System.String.IsNullOrEmpty(accessToken.TokenString))
yield return null;
_accessToken = accessToken.TokenString;
auth = FirebaseAuth.DefaultInstance;
Credential credential = FacebookAuthProvider.GetCredential(accessToken.TokenString);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("facebook-CredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("facebook-CredentialAsync encountered and error: " + task.Exception);
return;
}
Debug.Log("facebook firebase success! :D " + auth.CurrentUser.UserId);
#if !UNITY_IOS
if(DoesProvierExist("google.com"))
#else
if(DoesProvierExist("apple.com"))
#endif
{
_accountStatus = eMOBILEPLATFORM_STATUS.CONNECTED;
}
});
}
public void LinkFacebookFirebase(Facebook.Unity.AccessToken accessToken)
{
if (auth != null)
{
Credential credential = FacebookAuthProvider.GetCredential(accessToken.TokenString);
auth.CurrentUser.LinkWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("facebook-LinkWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("facebook-LinkWithCredentialAsync encountered an error: " + task.Exception);
return;
}
FirebaseUser newUser = task.Result;
Debug.Log("facebook-Credentials successfully linked to Firebase user: " + newUser.DisplayName + "(" + newUser.UserId + ")");
FacebookManager.Instance.ChangeFacebookStatus(FacebookManager.eFACEBOOK_STATUS.CONNECTED);
});
}
}
#if UNITY_IOS
public IEnumerator TryAppleFirebaseLogin(bool newLogin = true)
{
if (newLogin)
{
auth.Dispose();
auth = FirebaseAuth.DefaultInstance;
}
string rawNonce = GenerateRandomString(32);
string nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
var loginArgs = new AppleAuthLoginArgs(LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, nonce);
while (auth==null)
yield return null;
_appleAuthManager.LoginWithAppleId(
loginArgs,
credential =>
{
var appleIdCredential = credential as IAppleIDCredential;
var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
var firebaseCredential = OAuthProvider.GetCredential("apple.com", identityToken, rawNonce, authorizationCode);
auth.SignInWithCredentialAsync(firebaseCredential);
if (appleIdCredential.FullName != null)
{
var userName = appleIdCredential.FullName.ToLocalizedString();
var profile = new UserProfile();
profile.DisplayName = userName;
auth.CurrentUser.UpdateUserProfileAsync(profile);
}
_accessToken = authorizationCode.ToString();
Debug.Log("apple firebase success! :D " + auth.CurrentUser.UserId);
_accountStatus = eMOBILEPLATFORM_STATUS.CONNECTED;
UserConfig.Instance.SaveMobilePlatformLogin(true);
PlayerInfo.Instance.m_bGlobalMobilePlatformLogin = true;
if (DoesProvierExist("facebook.com"))
{
PlayerInfo.Instance.m_bGlobalGuestLogin = false;
UserConfig.Instance.SaveFacebookLogin(true);
}
},
error =>
{
var authorizationErrorCode = error.GetAuthorizationErrorCode();
switch (authorizationErrorCode)
{
case AuthorizationErrorCode.Canceled:
break;
case AuthorizationErrorCode.Unknown:
case AuthorizationErrorCode.InvalidResponse:
case AuthorizationErrorCode.NotHandled:
case AuthorizationErrorCode.Failed:
Debug.LogError("apple-login failed :/ error code: " + authorizationErrorCode);
break;
}
return;
});
}
#endif
public IEnumerator TryGuestFirebaseLogin(bool newLogin = false)
{
if (newLogin)
{
auth.Dispose();
auth = FirebaseAuth.DefaultInstance;
}
while (auth == null)
yield return null;
auth.SignInAnonymouslyAsync().ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("guest-SignInAnonymouslyAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("guest-SignInAnonymouslyAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.Log("guest-User signed in successfully :D " + newUser.DisplayName + " (" + newUser.UserId + ")");
});
}
public void UnlinkFirebase(string providerID)
{
if (auth != null && DoesProvierExist(providerID))
{
auth.CurrentUser.UnlinkAsync(providerID).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError(providerID + "-UnlinkAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError(providerID + "-UnlinkAsync encountered an error: " + task.Exception);
return;
}
// The user has been unlinked from the provider.
FirebaseUser newUser = task.Result;
Debug.LogFormat(providerID + " Credentials successfully unlinked from user: {0} ({1})", newUser.DisplayName, newUser.UserId);
#if !UNITY_IOS
if (providerID == "google.com")
#else
if (providerID == "apple.com")
#endif
{
_accountStatus = eMOBILEPLATFORM_STATUS.DISCONNECT;
}
});
}
}
public void TryFirebaseLogout()
{
auth.SignOut();
}
private string GenerateRandomString(int length)
{
const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
var cryptographicallySecureRandomNumberGenerator = new RNGCryptoServiceProvider();
var result = string.Empty;
var remainingLength = length;
var randomNumberHolder = new byte[1];
while (remainingLength > 0)
{
var randomNumbers = new List<int>(16);
for (var randomNumberCount = 0; randomNumberCount < 16; randomNumberCount++)
{
cryptographicallySecureRandomNumberGenerator.GetBytes(randomNumberHolder);
randomNumbers.Add(randomNumberHolder[0]);
}
for (var randomNumberIndex = 0; randomNumberIndex < randomNumbers.Count; randomNumberIndex++)
{
if (remainingLength == 0)
{
break;
}
var randomNumber = randomNumbers[randomNumberIndex];
if (randomNumber < charset.Length)
{
result += charset[randomNumber];
remainingLength--;
}
}
}
return result;
}
private string GenerateSHA256NonceFromRawNonce(string rawNonce)
{
var sha = new SHA256Managed();
var utf8RawNonce = Encoding.UTF8.GetBytes(rawNonce);
var hash = sha.ComputeHash(utf8RawNonce);
var result = string.Empty;
for (var i = 0; i < hash.Length; i++)
{
result += hash[i].ToString("x2");
}
return result;
}
public string GetAccessToken()
{
return _accessToken;
}
/// <summary>
/// Check the current user has certain provierID.
/// </summary>
/// <param name="providerID"> Such as "facebook.com", "apple.com", "google.com"</param>
/// <returns>false if auth or user is null</returns>
public bool DoesProvierExist(string providerID)
{
FirebaseUser user = auth.CurrentUser;
bool result = false;
int checkCount = 0;
List<string> providers = new List<string>();
if (user != null)
{
foreach (var profile in user.ProviderData)
{
checkCount++;
providers.Add(profile.ProviderId);
if (profile.ProviderId == providerID)
result = true;
}
}
#if _USE_DEBUG_LOG_
string temp = string.Empty;
for (int i = 0; i < providers.Count; i++)
{
temp += providers[i];
if (i != providers.Count - 1)
temp += ", ";
}
Debug.Log("Just in case if you wanna know. There are " + checkCount + " providerID at this account\n"+temp);
#endif
return result;
}
}
and here's xcode log of iphone.
ClickButton (GlobalStart)(04/16/2021 10:22:56)
UIEventButtonClick:OnClick()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
LoginFacebook()False
FacebookManager:LoginFacebook()
UIL_Login:Event_Click(String)
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
2021-04-16 10:22:56.640715+0900 firebaseProject[1006:67274] -canOpenURL: failed for URL: "fbauth2:/" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"
-> applicationWillResignActive()
-> applicationDidBecomeActive()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
2021-04-16 10:22:58.701301+0900 firebaseProject[1006:68330] [tcp] tcp_input [C19.1:3] flags=[R] seq=3046356287, ack=0, win=0 state=CLOSED rcv_nxt=3046356287, snd_una=716434727
2021-04-16 10:22:58.705125+0900 firebaseProject[1006:68330] [tcp] tcp_input [C19.1:3] flags=[R] seq=3046356287, ack=0, win=0 state=CLOSED rcv_nxt=3046356287, snd_una=716434727
CONNECTTING() True
FacebookManager:Connectting()
Facebook.Unity.CallbackManager:TryCallCallback(Object, IResult)
Facebook.Unity.CallbackManager:CallCallback(Object, IResult)
Facebook.Unity.CallbackManager:OnFacebookResponse(IInternalResult)
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
facebook-LinkWithCredentialAsync encountered an error: System.AggregateException: Exception of type 'System.AggregateException' was thrown.
-----------------
Firebase.FirebaseException: [ERROR_PROVIDER_ALREADY_LINKED] - User can only be linked to one identity for the given provider.
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetException(AggregateException)
System.Threading.Tasks.TaskCompletionSource`1:SetException(Exception)
Firebase.Auth.<GetTask>c__AnonStorey0:<>m__0()
Firebase.Auth.Future_User:SWIG_CompletionDispatcher(Int32)
Firebase.AppUtil:PollCallbacks()
Firebase.Platform.FirebaseHandler:Update()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
2021-04-16 10:23:01.367311+0900 firebaseProject[1006:67777] [tcp] tcp_input [C23.1:3] flags=[R] seq=3109184249, ack=0, win=0 state=CLOSED rcv_nxt=3109184249, snd_una=2797527191
2021-04-16 10:23:01.367444+0900 firebaseProject[1006:67777] [tcp] tcp_input [C23.1:3] flags=[R] seq=3109184249, ack=0, win=0 state=CLOSED rcv_nxt=3109184249, snd_una=2797527191
facebook firebase success! :D 5ThQ■■■■■■■■■■■■■■
<TryFacebookFirebaseLogin>c__Iterator0:<>m__0(Task`1)
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetResult(T)
System.Threading.Tasks.TaskCompletionSource`1:SetResult(T)
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetResult(T)
System.Threading.Tasks.TaskCompletionSource`1:SetResult(T)
Firebase.Auth.<GetTask>c__AnonStorey0:<>m__0()
Firebase.Auth.Future_User:SWIG_CompletionDispatcher(Int32)
Firebase.AppUtil:PollCallbacks()
Firebase.Platform.FirebaseHandler:Update()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
Just in case if you wanna know. There are 1 providerID at this account
facebook.com
FirebaseAuth:DoesProvierExist(String)
<TryFacebookFirebaseLogin>c__Iterator0:<>m__0(Task`1)
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetResult(T)
System.Threading.Tasks.TaskCompletionSource`1:SetResult(T)
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetResult(T)
System.Threading.Tasks.TaskCompletionSource`1:SetResult(T)
Firebase.Auth.<GetTask>c__AnonStorey0:<>m__0()
Firebase.Auth.Future_User:SWIG_CompletionDispatcher(Int32)
Firebase.AppUtil:PollCallbacks()
Firebase.Platform.FirebaseHandler:Update()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
ClickButton (LogInBtn)(04/16/2021 10:23:11)
UIEventButtonClick:OnClick()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
firebase apple in
<TryAppleFirebaseLogin>c__Iterator1:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
UIC_OptionPanel:_LinkMobilePlatform(E_MESSAGEBOX_CONFIRM_TYPE)
Messenger`1:Broadcast(String, T, MessengerMode)
UIC_MessageBox_Button:OnClick()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
-> applicationWillResignActive()
-> applicationDidBecomeActive()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
apple firebase success! :D 5ThQ■■■■■■■■■■■■■■
<TryAppleFirebaseLogin>c__AnonStorey3:<>m__0(ICredential)
AppleAuth.CallbackHandler:ExecutePendingCallbacks()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
Just in case if you wanna know. There are 1 providerID at this account
facebook.com
FirebaseAuth:DoesProvierExist(String)
<TryAppleFirebaseLogin>c__AnonStorey3:<>m__0(ICredential)
AppleAuth.CallbackHandler:ExecutePendingCallbacks()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
So... I found that lack of knowledge about whatever I'm doing goes very wrong...
I used apple-signin-unity by lupidan from github and mixing various tutorials that I could found on google to expect it's working. :|
Firebase blocks linking multiple accounts that already registered.
Mine just dispose previous FirebaseAuth and overwrite new one.
that is why it looked like merging accounts that actually doesn't do anything right.
I should have been more careful before I ask question.
Edit:
What was actually going on is that there's no exception at apple login.
Others have exceptions by using "ContinueWith" function but apple doesn't.
by adding it, everything works okay.
I am currently trying to create user authentication in unity and I am having some issues.
The code below is what I have at the moment and I keep receiving the error saying Auth does not exist in the current context.
Does anyone have any idea why this is? Probably a simple fix that I am just overlooking.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;
using System.Text.RegularExpressions;
using Firebase;
using Firebase.Auth;
public class Register : MonoBehaviour {
public GameObject email;
public GameObject password;
public GameObject confPassword;
private string Email;
private string Password;
private string ConfPassword;
private string form;
private bool EmailValid = false;
private string[] Characters = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"1","2","3","4","5","6","7","8","9","0","_","-"};
public void RegisterButton(){
bool EM = false;
bool PW = false;
bool CPW = false;
if (Email != ""){
EmailValidation();
if (EmailValid){
if(Email.Contains("#")){
if(Email.Contains(".")){
EM = true;
} else {
Debug.LogWarning("Email is Incorrect");
}
} else {
Debug.LogWarning("Email is Incorrect");
}
} else {
Debug.LogWarning("Email is Incorrect");
}
} else {
Debug.LogWarning("Email Field Empty");
}
if (Password != ""){
if(Password.Length > 5){
PW = true;
} else {
Debug.LogWarning("Password Must Be atleast 6 Characters long");
}
} else {
Debug.LogWarning("Password Field Empty");
}
if (ConfPassword != ""){
if (ConfPassword == Password){
CPW = true;
} else {
Debug.LogWarning("Passwords Don't Match");
}
} else {
Debug.LogWarning("Confirm Password Field Empty");
}
if (EM == true&&PW == true&&CPW == true)
{
auth.CreateUserWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("CreateUserWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted) {
Debug.LogError("CreateUserWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
// Firebase user has been created.
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("Firebase user created successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Tab)){
if (email.GetComponent<InputField>().isFocused){
password.GetComponent<InputField>().Select();
}
if (password.GetComponent<InputField>().isFocused){
confPassword.GetComponent<InputField>().Select();
}
}
if (Input.GetKeyDown(KeyCode.Return)){
if (Password != ""&&Email != ""&&Password != ""&&ConfPassword != ""){
RegisterButton();
}
}
Email = email.GetComponent<InputField>().text;
Password = password.GetComponent<InputField>().text;
ConfPassword = confPassword.GetComponent<InputField>().text;
}
void EmailValidation(){
bool SW = false;
bool EW = false;
for(int i = 0;i<Characters.Length;i++){
if (Email.StartsWith(Characters[i])){
SW = true;
}
}
for(int i = 0;i<Characters.Length;i++){
if (Email.EndsWith(Characters[i])){
EW = true;
}
}
if(SW == true&&EW == true){
EmailValid = true;
} else {
EmailValid = false;
}
}
}
I do not see you ever creating the variable auth?
While you refer to it here:
auth.CreateUserWithEmailAndPasswordAsync(email, password).ContinueWith(task =>
So I assume you will have to create a variable auth and instantiate it with something related to FireBase.Auth (unless I am overlooking something).