Swagger authorization using Okta authorization code flow in ASP.NET Core app
Hi,
I am using Asp.Net core Swashbuckle packages to document my APIs. I use authorization code flow from Okta which uses a redirect_uri for sending code and state back to the application. I can successfully exchange that with access_token from Okta but my problem is that swagger has no knowledge of this successful authentication and the app redirects me a new page. Using swagger middleware, I intercept the callback uri from Okta as follows:
public class SwaggerAuthorizedMiddleware
{
private readonly RequestDelegate _next;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IzTrainAuthService _zTrainAuthService;
public SwaggerAuthorizedMiddleware(RequestDelegate next, IHttpContextAccessor httpContextAccessor, IzTrainAuthService zTrainAuthService)
{
_next = next;
_httpContextAccessor = httpContextAccessor;
_zTrainAuthService = zTrainAuthService;
}
public async System.Threading.Tasks.Task InvokeAsync(HttpContext context, IConfiguration configuration)
{
if (context.Request.Path.StartsWithSegments("/authorization-code/callback"))
{
StringValues code;
StringValues state;
context.Request.Query.TryGetValue("code", out code);
context.Request.Query.TryGetValue("state", out state);
var task = _zTrainAuthService.AuthorizeUser(new AuthroizeUserContract.Request { code = code[0] ?? "", state = state[0] ?? ""}).ConfigureAwait(true);
var result = task.GetAwaiter().GetResult();
if (result.isUserAuthorized)
{
string Issuer = "my API";
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Sid, result.userClaims?.sub, ClaimValueTypes.String, Issuer));
claims.Add(new Claim(ClaimTypes.Name, result.userClaims?.name ?? "", ClaimValueTypes.String, Issuer));
claims.Add(new Claim(ClaimTypes.Email, result.userClaims?.email ?? "", ClaimValueTypes.String, Issuer));
claims.Add(new Claim(ClaimTypes.GivenName, result.userClaims?.given_name ?? "", ClaimValueTypes.String, Issuer));
claims.Add(new Claim(ClaimTypes.Surname, result.userClaims?.family_name ?? "", ClaimValueTypes.String, Issuer));
var userIdentity = new ClaimsIdentity(claims, "Passport");
var userPrincipal = new ClaimsPrincipal(userIdentity);
string token = result.token.access_token;
context.User = userPrincipal;
context.Request.Headers.Add("Authorization", $"Bearer {token}");
new AuthenticationTicket(new ClaimsPrincipal(userIdentity), "Bearer");
context.Request.Path = "/swagger";
await _next.Invoke(context);
return;
}
}
else if (context.Request.Path.StartsWithSegments("/swagger"))
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Bearer"))
{
//Get the encoded username and password
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
//Decode from Base64 to string
var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));
// //Split username and password
var username = decodedUsernamePassword.Split(':', 2)[0];
var password = decodedUsernamePassword.Split(':', 2)[1];
await _next.Invoke(context);
//Check if login is correct
if (IsAuthorized(username, password, configuration))
{
await _next.Invoke(context);
return;
}
}
// Return authentication type(causes browser to show login dialog)
context.Response.Headers["WWW-Authenticate"] = "Bearer";
// Return unauthorized(401)
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
await _next.Invoke(context);
}
}
public bool IsAuthorized(string username, string password, IConfiguration configuration)
{
return true;
}
}