Configure Swagger Authentication with Firebase (google) in .Net core

久未见 提交于 2020-06-08 15:12:05

问题


Integrating swagger and firebase is really hard because google token request and response is not standard. so I used password flow and add 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 [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 token automatically.

At the 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 dont 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

来源:https://stackoverflow.com/questions/61540706/configure-swagger-authentication-with-firebase-google-in-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!