Search code examples
.netfirebasefirebase-authenticationfirebase-adminfirebase-tools

Creating and validating (JWT)-tokens with FireBase Auth Emulator


I aim to issue tokens via firebase admin to use for authentication for local development with the emulator.

Using the FireBase Admin targeting a local project(demo-project) in the emulator. The following code is used to lookup a user and issue and validate a token. But this results is a mismatch of the token audience (aud). How could this be resolved ?

Firebase ID token has incorrect audience (aud) claim. Expected demo-project but got https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit. Make sure the ID token comes from the same Firebase project as the credential used to initialize this SDK

    Environment.SetEnvironmentVariable("FIREBASE_AUTH_EMULATOR_HOST", "localhost:9099");
    Environment.SetEnvironmentVariable("GCLOUD_PROJECT", "demo-project");
    
    var email = [EMAIL];
    
    GoogleCredential dummyCredential =
               GoogleCredential.FromAccessToken("test-token");
    
    var app = FirebaseApp.Create(new AppOptions
    {
        Credential = dummyCredential
    });
    var auth = FirebaseAuth.GetAuth(app);
    var user = await auth.GetUserByEmailAsync(email);
    var token = await auth.CreateCustomTokenAsync(user.Uid);
    var verifiedToken = await auth.VerifyIdTokenAsync(token);

It seems that the audience depends on how the Admin SDK is initiated, and that is the cause of the mismatch. How should the SDK be initiated for emulator development ? Hoping that it possible to achieve this without any ties to any firebase hosted project.


Solution

  • Wrote two util functions for sign-in, with token or email/password.

    Environment.SetEnvironmentVariable("FIREBASE_AUTH_EMULATOR_HOST", "localhost:9099");
    Environment.SetEnvironmentVariable("GCLOUD_PROJECT", "demo-project");
    
    var email = [EMAIL];
    
    GoogleCredential dummyCredential =
                GoogleCredential.FromAccessToken("test-token");
    
    var app = FirebaseApp.Create(new AppOptions
    {
        Credential = dummyCredential,
        ProjectId = "demo-project"
    });
    
    var auth = FirebaseAuth.DefaultInstance;
    var user = await auth.GetUserByEmailAsync(email);
    var token = await auth.CreateCustomTokenAsync(user.Uid);
    var verifiedToken = await auth.VerifyIdTokenAsync(await SignIn.FireBaseSignIn(token));
    

    Instead of using the creating a custom token, signing in an existing user via a sign-in util;

    var user = await auth.GetUserByEmailAsync(email);
    var verifiedToken = await auth.VerifyIdTokenAsync(await SignIn.FireBaseSignIn(email, [PASSWORD]));
    

    Sign-in utils;

    public record SignInResponse(string IdToken);
    
    public static async Task<string> FireBaseSignIn(string email, string password)
    {
        var authClient = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:9099/")
        };
    
        var res = await authClient.PostAsJsonAsync("identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=fake-api-key",
            new { email, password, returnSecureToken = true });
        res.EnsureSuccessStatusCode();
    
        var json = await res.Content.ReadFromJsonAsync<SignInResponse>();
    
        return json.IdToken;
    }
    
    public static async Task<string> FireBaseSignIn(string token)
    {
        var authClient = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:9099/")
        };
    
        var res = await authClient.PostAsJsonAsync("identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=fake-api-key",
            new { token, returnSecureToken = true });
        res.EnsureSuccessStatusCode();
    
        var json = await res.Content.ReadFromJsonAsync<SignInResponse>();
    
        return json.IdToken;
    }