Description
@mkane91301 I think it makes sense to speak about some specific code. Once you have it, open up an issue on the okta .NET SDK with specific feedback on what you think the 3-line experience should look like. The APIs will only get better *real* customer feedback.
Originally posted by @davidfowl in #42158 (comment)
The main story of simply adding auth isn't my complaint. The actual real-world story of adding auth with all the extras is where I don't think I should have to write so much code and it should be easy for identity vendors to expose what's needed to identity consumers, such as Swagger, that we don't need to glue them together.
Here's what I need to glue Okta and Swagger together:
internal static class OktaExtensions
{
internal static void AddOkta(this WebApplicationBuilder builder, string sectionName = "Okta")
=> builder.Services.AddOkta(builder.Configuration.GetSection(sectionName));
private static void AddOkta(this IServiceCollection services, IConfiguration configSection)
{
services.AddAuthentication(ConfigureAuthentication)
.AddOktaWebApi(configSection.Get<OktaWebApiOptions>());
services.Configure<OktaWebApiOptions>(configSection)
.AddOptions<SwaggerGenOptions>()
.Configure<IOptions<OktaWebApiOptions>>(ConfigureSwagger);
services.Configure<OktaSwaggerOptions>(configSection)
.AddOptions<SwaggerUIOptions>()
.Configure<IOptions<OktaSwaggerOptions>>(ConfigureSwaggerUi);
services.AddAuthorization();
}
private static void ConfigureAuthentication(AuthenticationOptions options)
{
options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
}
private static void ConfigureSwagger(SwaggerGenOptions swaggerOpts, IOptions<OktaWebApiOptions> oktaOpts)
{
var okta = oktaOpts.Value;
swaggerOpts.AddSecurityDefinition("Okta", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{okta.OktaDomain}/oauth2/{okta.AuthorizationServerId}/v1/authorize"),
Scopes = new Dictionary<string, string>
{
{"openid", "openid"},
{"profile", "profile"},
{"email", "email"}
},
TokenUrl = new Uri($"{okta.OktaDomain}/oauth2/{okta.AuthorizationServerId}/v1/token")
}
}
});
swaggerOpts.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Okta"
}
},
new[] { "openIdConnect" }
}
});
}
private static void ConfigureSwaggerUi(SwaggerUIOptions swaggerOpts, IOptions<OktaSwaggerOptions> oktaOpts)
{
var okta = oktaOpts.Value;
swaggerOpts.OAuthClientId(okta.ClientId);
swaggerOpts.OAuth2RedirectUrl($"{okta.RedirectHost}/swagger/oauth2-redirect.html");
swaggerOpts.OAuthAppName(Assembly.GetEntryAssembly()!.GetName().Name);
swaggerOpts.OAuthScopeSeparator(" ");
swaggerOpts.OAuthAdditionalQueryStringParams(new Dictionary<string, string>
{
{ "nonce", $"{Guid.NewGuid() :N}" }
});
}
private class OktaSwaggerOptions
{
public string ClientId { get; set; } = "";
public string RedirectHost { get; set; } = "";
}
}
I would like it that when I add an identity vendor's auth implementation, it would either automatically provide the OpenApiSecurityScheme
and OpenApiSecurityRequirement
or make it a simple option to ask it to provide them. And then Swagger would automatically know where to find them.
Similarly on the client side, there is no standard way to take an IHttpClientBuilder
and add an OAuth bearer token to its calls from such-and-such a vendor's identity server according to some configuration. I wrote much more code than I can snip here to create a standard way for our shop. I'd open source it if my company allowed, but this should already be part of Microsoft.Extensions anyway.