⚠️ This is an initial version. Feel free to comment or suggest modifications.
https://firebase.google.com/docs/unity/setup
https://firebase.google.com/docs/auth/ios/apple#before-you-begin
https://github.com/lupidan/apple-signin-unity#installation
We will adapt the code in step 4.
https://github.com/lupidan/apple-signin-unity#implement-sign-in-with-apple
We need to generate some random string to send to Firebase, and check that the credentials we received from Apple are correct. Firebase guide recommends this method to generate one:
https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
This is my personal take on a C# version of it:
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
private static string GenerateRandomString(int length)
{
if (length <= 0)
{
throw new Exception("Expected nonce to have positive 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;
}
This is the nonce we will send to Apple when attempting a Login. We will generate the SHA256 hash of the Raw nonce we generated in Step 2.
using System.Security.Cryptography;
using System.Text;
private static 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;
}
Once we have all the pieces ready, we can assenble everything toghether.
Adapting our current LoginWithAppleId
and QuickLogin
calls, and performing a common authentication with Firebase, receiving, finally, a FirebaseUser
if everything goes well.
using System;
using AppleAuth;
using AppleAuth.Enums;
using AppleAuth.Interfaces;
using Firebase.Auth;
public void PerformLoginWithAppleIdAndFirebase(Action<FirebaseUser> firebaseAuthCallback)
{
var rawNonce = GenerateRandomString(32);
var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
var loginArgs = new AppleAuthLoginArgs(
LoginOptions.IncludeEmail | LoginOptions.IncludeFullName,
nonce);
this.appleAuthManager.LoginWithAppleId(
loginArgs,
credential =>
{
var appleIdCredential = credential as IAppleIDCredential;
if (appleIdCredential != null)
{
this.PerformFirebaseAuthentication(appleIdCredential, rawNonce, firebaseAuthCallback);
}
},
error =>
{
// Something went wrong
});
}
using System;
using AppleAuth;
using AppleAuth.Enums;
using AppleAuth.Interfaces;
using Firebase.Auth;
public void PerformQuickLoginWithFirebase(Action<FirebaseUser> firebaseAuthCallback)
{
var rawNonce = GenerateRandomString(32);
var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
var quickLoginArgs = new AppleAuthQuickLoginArgs(nonce);
this.appleAuthManager.QuickLogin(
quickLoginArgs,
credential =>
{
var appleIdCredential = credential as IAppleIDCredential;
if (appleIdCredential != null)
{
this.PerformFirebaseAuthentication(appleIdCredential, rawNonce, firebaseAuthCallback);
}
},
error =>
{
// Something went wrong
});
}
using System;
using System.Text;
using AppleAuth.Interfaces;
using Firebase.Auth;
using Firebase.Extensions;
using UnityEngine;
// Your Firebase authentication client
private FirebaseAuth firebaseAuth;
private void PerformFirebaseAuthentication(
IAppleIDCredential appleIdCredential,
string rawNonce,
Action<FirebaseUser> firebaseAuthCallback)
{
var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
var firebaseCredential = OAuthProvider.GetCredential(
"apple.com",
identityToken,
rawNonce,
authorizationCode);
this.firebaseAuth.SignInWithCredentialAsync(firebaseCredential)
.ContinueWithOnMainThread(task => HandleSignInWithUser(task, firebaseAuthCallback));
}
private static void HandleSignInWithUser(Task<FirebaseUser> task, Action<FirebaseUser> firebaseUserCallback)
{
if (task.IsCanceled)
{
Debug.Log("Firebase auth was canceled");
firebaseUserCallback(null);
}
else if (task.IsFaulted)
{
Debug.Log("Firebase auth failed");
firebaseUserCallback(null);
}
else
{
var firebaseUser = task.Result;
Debug.Log("Firebase auth completed | User ID:" + firebaseUser.UserId);
firebaseUserCallback(firebaseUser);
}
}