3

Integrating swagger and Firebase is really hard because Google token request and response is not standard. So I used the password flow and added a middleware in .NET Core 3.1 Web API to authenticate swagger 5.x. I hope it helps.

The first step is configuring your API to use Firebase for token validation. So we need to add this code to the startup:

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.Authority = authority; // this is a url like this "https://securetoken.google.com/{firebase project Id}"
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = authority, //url again
                    ValidateAudience = true,
                    ValidAudience = projectId, // {Firebase Project ID}
                    ValidateLifetime = true
                };
                options.RequireHttpsMetadata = false;
            });

and you need to middleware for Authentication and Authorization in your startup as well.

 app.UseAuthentication();
 app.UseAuthorization();

Here we go. Now if you use the [Authorize] attribute on your controller methods and send bearer token in the header it will validate the token by Firebase and works fine. Now I want to show how you can configure your Swagger to provide the token automatically.

At first, you need to implement a middleware post method to convert Standard Password flow to Google password flow and also convert the result. I did it

[ApiController]
[Route("v1/[controller]")]
public class AuthController : Controller
{
    [HttpPost]
    public async Task<ActionResult> GetToken([FromForm]LoginInfo loginInfo)
    {
        string uri = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={your project api key in firebase}";
        using (HttpClient client = new HttpClient())
        {
            FireBaseLoginInfo fireBaseLoginInfo = new FireBaseLoginInfo
            {
                Email = loginInfo.Username,
                Password = loginInfo.Password
            };
            var result = await client.PostAsJsonAsync<FireBaseLoginInfo, GoogleToken>(uri, fireBaseLoginInfo);
            Token token = new Token
            {
                token_type = "Bearer",
                access_token = result.idToken,
                id_token = result.idToken,
                expires_in = int.Parse(result.expiresIn),
                refresh_token = result.refreshToken

            };
            return Ok(token);
        }
    }
}

public class LoginInfo
{
    public string Username { get; set; }
    public string Password { get; set; }

}

public class FireBaseLoginInfo
{
    public string Email { get; set; }
    public string Password { get; set; }
    public bool ReturnSecureToken { get; set; } = true;
}

public class GoogleToken
{
    public string kind { get; set; }
    public string localId { get; set; }
    public string email { get; set; }
    public string displayName { get; set; }
    public string idToken { get; set; }
    public bool registered { get; set; }
    public string refreshToken { get; set; }
    public string expiresIn { get; set; }
}


public class Token
{
    internal string refresh_token;

    public string token_type { get; set; }
    public int expires_in { get; set; }
    public int ext_expires_in { get; set; }
    public string access_token { get; set; }
    public string id_token { get; set; }
}

You know we don't have PostAsJsonAsync in .NET Core, so I add an extension for it.

public static class HttpExtensions
{
    public static async Task<R> PostAsJsonAsync<T, R>(
        this HttpClient httpClient, string url, T data)
    {
        var response = await httpClient.PostAsJsonAsync<T>(url, data);
        if(response.IsSuccessStatusCode)
        {
            return await response.Content.ReadAsJsonAsync<R>();
        }
        else
        {
            return default(R);
        }
       

    }
    public static Task<HttpResponseMessage> PostAsJsonAsync<T>(
        this HttpClient httpClient, string url, T data)
    {
        var options = new JsonSerializerSettings
        {
            ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() },
            Formatting = Formatting.Indented
        };

        var dataAsString = JsonConvert.SerializeObject(data, options);
        var content = new StringContent(dataAsString);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        return httpClient.PostAsync(url, content);
    }

    public static async Task<T> ReadAsJsonAsync<T>(this HttpContent content)
    {
        var dataAsString = await content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(dataAsString);
    }
}

Now swagger configuration.

services
            .AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "My Api", Version = "v1" });
                
                c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    
                    Type = SecuritySchemeType.OAuth2,
                    
                    Flows = new OpenApiOAuthFlows
                    {
                        Password = new OpenApiOAuthFlow
                        {
                            TokenUrl = new Uri("/v1/auth",UriKind.Relative),
                            Extensions = new Dictionary<string, IOpenApiExtension>
                                {
                                    { "returnSecureToken", new OpenApiBoolean(true) },
                                },

                        }

                    }
                });
                c.OperationFilter<AuthorizeCheckOperationFilter>();
            })

AuthorizeCheckOperationFilter filter is a custom filter

public class AuthorizeCheckOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {


        var requiredScopes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
                 .OfType<AuthorizeAttribute>()
                 .Select(attr => attr.Policy)
                 .Distinct();

        if (requiredScopes.Any())
        {

            var oAuthScheme = new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
            };

            operation.Security = new List<OpenApiSecurityRequirement>
             {
                 new OpenApiSecurityRequirement
                 {
                     [ oAuthScheme ] = requiredScopes.ToList()
                 }
             };

        }
    }
}

and finally you know that you need to add swagger middleware

 app.UseSwagger()
          .UseSwaggerUI(c =>
          {
              c.SwaggerEndpoint($"/swagger/v1/swagger.json", "Chivado Api");
          });

and now you can use it very simple here is a snapshot enter image description here

3
  • Have any idea how to do something similar in django? Thanks for the post. Commented Aug 26, 2020 at 2:22
  • Thanks for this. Swagger works well like this with firebase authentication. Extra info: When you authenticate in swagger, you don't need to set the client id and secret. Commented Mar 4, 2021 at 7:27
  • j/s... learn.microsoft.com/en-us/dotnet/api/… Commented Sep 11, 2023 at 18:11

0

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.