How to direct to AspNet.Security.OAuth.Providers login pages within Swagger UI

I currently have a controller that redirects the user to a login page. If possible, I was wondering if this redirection could happen if you were to use the swagger UI to run the controller instead. The code for the controller is bellow, which uses the community AspNet.Security.OAuth.Providers plugin, specifically the Discord provider. Hopes are this would speed up development time, as we would be able to access everything from within the Swagger UI, without having to go manually type in the address.

Adding the authentication in Program.cs

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie("Cookies", options =>
            {
                options.LoginPath = "/api/v1/login";
                options.LogoutPath = "/api/v1/logout";
                options.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0);
                options.Cookie.MaxAge = new TimeSpan(7, 0, 0, 0);
                options.Cookie.Name = "access_token";
                options.Cookie.HttpOnly = false;
                options.Events.OnRedirectToLogin = context =>
                {
                    context.Response.Headers["Location"] = context.RedirectUri;
                    context.Response.StatusCode = 401;
                    return Task.CompletedTask;
                };
            })
            .AddDiscord(options =>
            {
                options.ClientId = settings.ClientId.ToString();
                options.ClientSecret = settings.ClientSecret;
                options.Scope.Add("guilds");
                options.Scope.Add("identify");
                options.SaveTokens = true;
                options.Prompt = "none";
                options.AccessDeniedPath = "/oauthfailed";
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.CorrelationCookie.SameSite = SameSiteMode.Lax;
                options.CorrelationCookie.HttpOnly = false;
            });

How we set up Swagger

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
        options.RoutePrefix = string.Empty;
    });
}

Our GET query for our AuthenticationController.cs

    [HttpGet("login")]
    public IActionResult Login([FromQuery] string ReturnUrl)
    {
        if (string.IsNullOrEmpty(ReturnUrl))
            ReturnUrl = "/guilds";

        var properties = new AuthenticationProperties()
        {
            RedirectUri = ReturnUrl,
            Items =
            {
                { "LoginProvider", "Discord" },
                { "scheme", "Discord" }
            },
            AllowRefresh = true,
        };

        return Challenge(properties, "Discord");
    }

Our current response in Swagger:

Swagger Failing (Maybe CORS?)

Instead of directing you to the discord.com website, like the controller would usually do.

If this isn't possible, or isn't part of what Swagger supports, that's fine! It'd just be mainly a time-saver for us.

Any feedback would be greatly appreciated.


Solution 1:

Ok, here's what I do with my api sites. First off allow users to login manually and authenticate them any way you like (as you do). Setup cookie auth and add cookie auth to your api controllers (like you do).

So what's left is to prevent unauthorized access to your swagger pages - since swagger doesn't know/care about your cookie auth.

All that's needed is a little middleware to check the request path and verify if the user has been authenticated. This code will check the request path for /swagger then check if the user has been authenticated. If not, a 401 is returned.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;


namespace Middleware.Classes
{
    public static class SwaggerAuthorizeExtensions
    {
        public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<SwaggerAuthorizedMiddleware>();
        }
    }
    public class SwaggerAuthorizedMiddleware
    {
        private readonly RequestDelegate _next;

        public SwaggerAuthorizedMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/swagger")
                && !context.User.Identity.IsAuthenticated)
            {
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                return;
            }

            await _next.Invoke(context);
        }
    }
}

Now in your startup code call the IApplicationBuilder.UseSwaggerAuthorized() extension funct to enable the middleware. Then bask in the love and affection of your users.

app.UseSwaggerAuthorized();