Настройка аутентификации Swagger с Firebase (Google) в. Net ядро - PullRequest
0 голосов
/ 01 мая 2020

Интегрировать swagger и firebase действительно сложно, потому что запрос и ответ токена Google не являются стандартными. поэтому я использовал поток паролей и добавил промежуточное ПО в. net core 3.1 Web API для аутентификации Swagger 5.x. Надеюсь, это поможет. Первым шагом является настройка вашего API для использования Firebase для проверки токена. поэтому нам нужно добавить этот код в автозагрузку.

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;
            });

и вам также потребуется промежуточное программное обеспечение для аутентификации и авторизации при запуске.

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

Здесь мы go. Теперь, если вы используете атрибут [Authorize] в своих методах контроллера и отправляете токен носителя в заголовке, он будет проверять токен с помощью firebase и работает нормально. Теперь я хочу показать, как вы можете настроить свой Swagger так, чтобы он автоматически предоставлял токен.

Сначала вам нужно реализовать метод промежуточного программного обеспечения для преобразования потока стандартного пароля в поток пароля Google, а также преобразовать результат. Я сделал это

[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; }
}

}

вы знаете, у нас нет ядра PostAsJsonAsyn c in. net, поэтому я добавляю для него расширение.

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);
    }
}

теперь конфигурация swagger.

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 является настраиваемым фильтром

 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()
                 }
             };

        }
    }
}

и, наконец, вы знаете, что вам нужно добавить промежуточное ПО Swagger

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

и теперь вы можете использовать его очень просто вот снимок enter image description here

...