Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase .NET Token Verification

Working on a project that uses Firebase for some data storage, and our client requests that the server be implemented with C#.NET. We're setting up REST endpoints on the server so that the client is able to communicate with it for a few purposes (for example, triggering an algorithm to run which can only occur on the server).

Firebase recommends we identify users via an ID token, as noted here: https://firebase.google.com/docs/auth/server/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library

Since there is no official .NET Firebase server SDK that supports token authentication, we've resorted to using a 3rd-party JWT library to do this: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet

As specified in the Firebase documentation, we're first generating a sending a token to the server. After checking a few different fields in the token, we're using the kid field to grab the public key from https://www.googleapis.com/robot/v1/metadata/x509/[email protected]

We've been digging around through documentation and StackOverflow for a long time, but we can't find a way to use this public key to do this, as specified by the Firebase documentation:

Finally, ensure that the ID token was signed by the private key corresponding to the token's kid claim. Grab the public key from https://www.googleapis.com/robot/v1/metadata/x509/[email protected] and use a JWT library to verify the signature.

The Firebase documentation doesn't really offer any explanation for this, and neither does the documentation for the library we are using. So we've not been able to even get a basic idea as to how we could possibly verify the token was signed by a private key when all we are given is the public key.

What would be the best way to verify that the token was actually signed by the correct private key?

like image 534
nolasaint Avatar asked Oct 08 '16 23:10

nolasaint


3 Answers

You should be able to accomplish the token validation by doing something like the following, which leverages the System.IdentityModel.Tokens.Jwt Nuget package to perform most the validations:

class Program {
  static HttpClient client = new HttpClient();
  static void Main() { RunAsync().Wait(); }

  static async Task RunAsync() {
    string encodedJwt = "[TOKEN_TO_BE_VALIDATED]";
    // 1. Get Google signing keys
    client.BaseAddress = new Uri("https://www.googleapis.com/robot/v1/metadata/");
    HttpResponseMessage response = await client.GetAsync(
      "x509/[email protected]");
    if (!response.IsSuccessStatusCode) { return; }
    var x509Data = await response.Content.ReadAsAsync<Dictionary<string, string>>();
    SecurityKey[] keys = x509Data.Values.Select(CreateSecurityKeyFromPublicKey).ToArray();
    // 2. Configure validation parameters
    const string FirebaseProjectId = "[FIREBASE_PROJECT_ID]";
    var parameters = new TokenValidationParameters {
      ValidIssuer = "https://securetoken.google.com/" + FirebaseProjectId,
      ValidAudience = FirebaseProjectId,
      IssuerSigningKeys = keys,
    };
    // 3. Use JwtSecurityTokenHandler to validate signature, issuer, audience and lifetime
    var handler = new JwtSecurityTokenHandler();
    SecurityToken token;
    ClaimsPrincipal principal = handler.ValidateToken(encodedJwt, parameters, out token);
    var jwt = (JwtSecurityToken)token;
    // 4.Validate signature algorithm and other applicable valdiations
    if (jwt.Header.Alg != SecurityAlgorithms.RsaSha256) {
      throw new SecurityTokenInvalidSignatureException(
        "The token is not signed with the expected algorithm.");
    }
  }
  static SecurityKey CreateSecurityKeyFromPublicKey(string data) {
    return new X509SecurityKey(new X509Certificate2(Encoding.UTF8.GetBytes(data)));
  }
}

List of using statements for sample program:

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
like image 106
João Angelo Avatar answered Sep 27 '22 19:09

João Angelo


Now we can use the Firebase Admin SDK for .NET.

https://github.com/Firebase/firebase-admin-dotnet

var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
var uid = decoded.Uid;
like image 22
Suyon Won Avatar answered Sep 27 '22 21:09

Suyon Won


If you're using another Newtonsoft.JSON library like me and don't want to import the Microsoft one, try this one: https://gist.github.com/saltyJeff/41029c9facf3ba6159ac019c1a85711a

Use Verify(string token) to asynchronously verify that a token is valid: returns the unique identifier of the user if valid, and null if invalid.

like image 31
saltyJeff Avatar answered Sep 27 '22 19:09

saltyJeff