Skip to content

Latest commit

 

History

History
223 lines (183 loc) · 6.37 KB

Firebase_NOTES.md

File metadata and controls

223 lines (183 loc) · 6.37 KB

Working with Firebase

⚠️ This is an initial version. Feel free to comment or suggest modifications.


Before you begin

Add Firebase to your Unity Project

https://firebase.google.com/docs/unity/setup

Read "Before you begin" Firebase guide to support Sign In With Apple

https://firebase.google.com/docs/auth/ios/apple#before-you-begin

Add the Sign in with Apple Unity Plugin to your Unity Project

https://github.com/lupidan/apple-signin-unity#installation


Step by step guide

Step 1: Read how to implement a default Sign In With Apple integration

We will adapt the code in step 4.

https://github.com/lupidan/apple-signin-unity#implement-sign-in-with-apple

Step 2: Generating a random RAW Nonce

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;
}

Step 3: Generate the SHA256 of the RAW Nonce

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;
}

Step 4: Assemble all the pieces

Once we have all the pieces ready, we can assenble everything toghether. Adapting our current LoginWithAppleId and QuickLogincalls, and performing a common authentication with Firebase, receiving, finally, a FirebaseUser if everything goes well.

Adapted Login With Apple Id call

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
        });
}

Adapted Quick Login call

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
        });
}

Common code to authenticate with Firebase

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);
    }
}