Search code examples
c#.netasp.net-coreswaggergoogle-oauth

Swagger support for Google authentication in ASP.NET Core


I have an asp.net core api project and I want to be able to authenticate via google inside swagger. This is how I add the authentication services:

builder.Services
    .AddAuthentication()
    .AddCookie()
    .AddGoogle();

This is the options setup class for "AuthenticationOptions":

public sealed class AuthenticationOptionsSetup
    : IConfigureOptions<AuthenticationOptions>
{
    public void Configure(AuthenticationOptions options)
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
    }
}

The options setup class for "GoogleOptions":

public sealed class GoogleOptionsSetup(IConfiguration configuration)
    : IConfigureNamedOptions<GoogleOptions>
{
    public void Configure(GoogleOptions options)
    {
        options.ClientId = configuration[GoogleOAuthSettings.ClientIdName]!;
        options.ClientSecret = configuration[GoogleOAuthSettings.ClientSecretName]!;
        options.SaveTokens = true;
    }

    public void Configure(string? name, GoogleOptions options) => Configure(options);
}

The configuration of my "SwaggerGenOptions":

options.AddSecurityDefinition(
    SecurityDefinitionName,
    new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new()
        {
            AuthorizationCode = new()
            {
                AuthorizationUrl = new(GoogleDefaults.AuthorizationEndpoint),
                TokenUrl = new(GoogleDefaults.TokenEndpoint),
                Scopes = GoogleOAuthSettings.Scopes
            }
        }
    });

options.AddSecurityRequirement(new()
{
    {
        new OpenApiSecurityScheme
        {
            Reference = new OpenApiReference
            {
                Type = ReferenceType.SecurityScheme,
                Id = SecurityDefinitionName
            }
        },
        new List<string>()
    }
});

options.OperationFilter<AuthorizationOperationFilter>();

And the "AuthorizationOperationFilter" class:

public sealed class AuthorizationOperationFilter : IOperationFilter
{
    private const string SecurityDefinitionName = "google_auth";

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
        operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });

        operation.Security = new List<OpenApiSecurityRequirement>
        {
            new()
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = SecurityDefinitionName
                        }
                    },
                    new List<string>()
                }
            }
        };
    }
}

Also, this is my swagger UI options:

options.OAuthAppName(GoogleDefaults.DisplayName);
options.OAuthClientId(configuration[GoogleOAuthSettings.ClientIdName]!);
options.OAuthClientSecret(configuration[GoogleOAuthSettings.ClientSecretName]!);
options.OAuthScopes([.. GoogleOAuthSettings.Scopes.Keys]);

The problem is when I click the "Authorize" button in swagger page, I am redirected to google login page and after that, redirected back to my swagger page and everything seems to be working. Now, if I call a protected api via clicking the "Execute" button on swagger page, it returns "Undocumented, but if I enter the address of that api in my search bar and hit enter, The api is called properly and I am authorized. enter image description here As shown in the capture, I am apparently authorized but it does not work. What am I missing ?


Solution

  • So, I finally learned what was wrong with my approach. First of all, Apparently, swagger extracts the access token by default, not the ID token which is a JWT and the one I need. Also, in my case which is a web application that will eventually have a front-end, fetching the ID token is the responsibility of front-end as there are available tools and libraries apparently provided by google to achieve this. So now, instead of worrying about google authentication, I added regular JWT authentication to my APIs and my login API that allows anonymous, simply receives a JWT ID token from query and uses it to issue it's own JWT.