6
\$\begingroup\$

I'm about to go live with an app, and would, therefore, appreciate a review of my firebase authentication setup. I haven't been able to find many resources describing how to perform such authentication and most importantly securely using OWIN and ASP.NET 4.6. It's worthwhile to note that I've had some issues with resolving the SigningKeys (using an IssuerSigningKeyResolver) and I really need to know if I've missed something important so authentication doesn't suddenly start to fail.

Before adding the resolver, authentication would fail after what seems to be 24 hours - probably because Google replaces their certificates and that the key used to sign the JWT token can't be found and validated. It seems to be working in its current state.

The method below is called during startup:

public static async void ConfigureAuth(IAppBuilder app)
{
    const string validAudience = "MY-FIREBASE-PROJECT";
    const string validIssuer = "https://securetoken.google.com/MY-FIREBASE-PROJECT";
    const string openIdDiscoveryEndpoint = "https://securetoken.google.com/MY-FIREBASE-PROJECT/.well-known/openid-configuration";

    var openidConfigManager = new ConfigurationManager<OpenIdConnectConfiguration>(openIdDiscoveryEndpoint);
    var openidConfig = await openidConfigManager.GetConfigurationAsync();
    var certificates = await GetGoogleCertificatesAsync();
    var issuer = openidConfig.Issuer;
    var tokens = openidConfig.SigningTokens;

    app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        AllowedAudiences = new[] { validAudience },
        Provider = new OAuthBearerAuthenticationProvider
        {
            OnValidateIdentity = OnValidateIdentity
        },
        IssuerSecurityTokenProviders = new[]
        {
            new OpenIdConnectCachingSecurityTokenProvider(
                openIdDiscoveryEndpoint)
        },
        TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "user_id",
            RoleClaimType = ClaimTypes.Role,
            IssuerSigningTokens = tokens,
            ValidAudience = validAudience,
            ValidIssuer = validIssuer,
            IssuerSigningKeys = certificates.Values.Select(x => new X509SecurityKey(x)),
            IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
            {
                var updatedCertificates = GetGoogleCertificates();

                return identifier
                    .Select(x => !updatedCertificates
                        .ContainsKey(x.Id)
                        ? null
                        : new X509SecurityKey(updatedCertificates[x.Id]))
                    .First(x => x != null);
            }
        }
    });
}

The following methods are used to fetch Google certificates.

public static async Task<Dictionary<string, X509Certificate2>> GetGoogleCertificatesAsync()
{
    using (var http = new HttpClient())
    {
        var response = await http.GetAsync("https://www.googleapis.com/robot/v1/metadata/x509/[email protected]");

        var dictionary = await response.Content.ReadAsAsync<Dictionary<string, string>>();
        return dictionary.ToDictionary(x => x.Key, x => new X509Certificate2(Encoding.UTF8.GetBytes(x.Value)));
    }
}

public static Dictionary<string, X509Certificate2> GetGoogleCertificates()
{
    var task = Task.Run(async () => await GetGoogleCertificatesAsync());
    return task.Result;
}

OpenIdConnectCachingSecurityTokenProvider is used to fetch OpenId Connect metadata and SecurityTokens. It can be found on GitHub here. OnValidateIdentity is only used to add custom roles/claims and is thus omitted.

Please, ask if you have any questions or feel that something is missing.

\$\endgroup\$
2
  • \$\begingroup\$ thx for the example. I have a problem with OnValidateIdentity which is not available in my case, but also not neccessary like u describe. Unfortunately my webapi returns "Authorization has been denied for this request". using the "Authorize" tag above my webapi endpoint. Can u please provide the complete source code? mb i find the error then \$\endgroup\$
    – Andreas
    Mar 29, 2018 at 10:06
  • 1
    \$\begingroup\$ You can not tell exactly what's wrong with your implementation just by looking at the request response. I'll suggest you to enable trace output for the owin middleware by adding the following to your web.config: <system.diagnostics> <switches> <add name="Microsoft.Owin" value="Verbose"/> </switches> </system.diagnostics> \$\endgroup\$
    – thilemann
    Mar 29, 2018 at 12:00

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.