Skip to content

Using 3rd-party identity providers is simple ... until you need auth for Swagger😱 or need to write a client #47461

Closed as not planned
@mkane91301

Description

@mkane91301
          @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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-identityIncludes: Identity and providersenhancementThis issue represents an ask for new feature or an enhancement to an existing one

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions