ASPNET CORE InvalidOperationException:无法重定向到授权端点,配置可能丢失或无效

ASPNET CORE InvalidOperationException: Cannot redirect to the authorization endpoint, the configuration may be missing or invalid

我收到错误 InvalidOperationException:无法重定向到授权端点,配置可能丢失或无效。

在此之前,我遇到了类似“权限、元地址、配置或配置管理器丢失”之类的错误。我觉得这很重要。所以,我有:

        /// <summary>
        /// Configures OpenID Connect authentication.
        /// </summary>
        /// <param name="builder">The <see cref="IPersonalIdentityServerBuilder"/> object.</param>
        /// <returns>The <see cref="IPersonalIdentityServerBuilder"/>.</returns>
        public static IPersonalIdentityServerBuilder AddOpenIdConnectAuthentication(this IPersonalIdentityServerBuilder builder)
        {
                IServiceCollection _services = builder?.Services ?? throw new ArgumentNullException(nameof(builder));

            _services
                .AddAuthentication(opt => opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme)
                .AddOpenIdConnect();

            _services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, ConfigureOpenIdConnectOptions>();

            return builder;
        }

然后我有以下 class 配置选项

/// <summary>
/// Configuration class for the <see cref="OpenIdConnectOptions"/>.
/// </summary>
internal class ConfigureOpenIdConnectOptions : 
    IPostConfigureOptions<OpenIdConnectOptions>
{
    /// <summary>
    /// My personal OpenId options.
    /// </summary>
    private readonly IOptions<PersonalIentityServerOpenIdOptions> _openIdOptions;

    /// <summary>
    /// The class that has the events for OpenId authentication.
    /// </summary>
    private readonly OpenIdNotificationEventHandler _eventHandler;

    /// <summary>
    /// Initializes a new instance of the <see cref="ConfigureOpenIdConnectOptions"/> class.
    /// </summary>
    /// <param name="openIdOptions">The personal OpenId options.</param>
    /// <param name="eventHandler">The handler for OpenId authentication events.</param>
    public ConfigureOpenIdConnectOptions(
        IOptions<PersonalIdentityServerOpenIdOptions> openIdOptions,
        OpenIdNotificationEventHandler eventHandler)
    {
        this._openIdOptions = openIdOptions ?? throw new ArgumentNullException(nameof(openIdOptions));
        this._eventHandler = eventHandler ?? throw new ArgumentNullException(nameof(eventHandler));
    }

    /// <summary>
    /// Configures the options.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="options">The options to configure.</param>
    public void PostConfigure(string name, OpenIdConnectOptions options)
    {
        PersonalIdentityServerOpenIdOptions _opt = this._openIdOptions.Value;

        options.Authority = _opt.Authority;
        options.ClientId = _opt.ClientId;
        options.ClientSecret = _opt.Secret;
        options.MetadataAddress = "/" + ProtocolPath.Discovery;
        options.ProtocolValidator.RequireNonce = true;
        options.ResponseType = OpenIdConnectResponseType.Code;
        options.UsePkce = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = JwtClaimTypes.Name,
            RoleClaimType = JwtClaimTypes.Role,
        };

        // Keeps id_token smaller
        options.GetClaimsFromUserInfoEndpoint = true;

        // The callback paths require a relative URL. This was a change that Microsoft made in the .NET Core version. We, however,
        // support an absolute URL. Attempting to set the callback paths to an absolute URL will cause an exception to be thrown.
        // Therefore, we will now support either. If it's an absolute URL then it gets set in the RedirectToIdentityProvider
        // event. If it's a relative URL, we'll set it here.
        if (!_opt.SetLowLevelRedirectUri) { options.CallbackPath = _opt.RedirectUri; }
        if (!_opt.SetLowLevelPostLogoutRedirectUri) { options.SignedOutCallbackPath = _opt.PostLogoutRedirectUri; }

        _opt.Scopes.ForEach(s => options.Scope.Add(s));

        options.Events = new OpenIdConnectEvents
        {
            OnAuthorizationCodeReceived = this._eventHandler.AuthorizationCodeRecieved,
            OnTokenResponseReceived = this._eventHandler.TokenResponseReceived,
            OnTokenValidated = this._eventHandler.TokenValidated,
            OnRedirectToIdentityProvider = this._eventHandler.RedirectToIdentityProvider,
        };
    }
}

经过多次反编译和 Google 搜索,我最终将以下行添加到上面定义的 AddOpenIdConnectAuthentication 方法中。这是微软对开放ID设置的配置。

            _services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>();

这解决了那个问题,但现在我有了标题中的那个。从源头上看,好像是因为 IssuerAddress 是空的。对于我的生活,我不明白为什么。此外,我不明白为什么我需要添加 Microsoft 自己的配置 class 才能使其正常工作。

我知道我可以以某种方式绕过它并且可能会找到解决方法,但我认为它不应该这么难。

如果有人有任何想法,我们将非常欢迎。

更新

我想扩展我发现的一些东西,但由于我花在这上面的时间而最终改变了。我想特别感谢下面的https://whosebug.com/users/68490/tore-nestenius

我最终从使用 IPostConfigure<OpenIdConnectOptions> 更改为 IConfigure<OpenIdConnectOptions>。这样做时,它似乎通过 IConfigureNamedOptions<OpenIdConnectOptions>Configure(name, options) 而不是 IConfigureOptions<OpenIdConnectOptions>Configure(options)。因此,我同时实施了两者。我不知道你是否必须这样做,因为我没有尝试所有可能的组合,但这样做有效。

使用 IPostConfigurationOptions<OpenIdConnectOptions> 时,您 必须 在调用 AddOpenIdConnect().

之前将其添加到服务 collection

我还删除了手动注册 Microsoft 的行 IPostConfigreOptions,不需要这个。

元数据地址应该是以 https:// 开头的绝对地址,而不是像您的代码中那样的相对 URL:

   options.MetadataAddress = "/" + ProtocolPath.Discovery;

那么我有点困惑为什么你需要添加你自己的 ConfigureOpenIdConnectOptions class?要配置 openid-connect,我只需:

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
    options.LoginPath = "/User/Login";
    options.LogoutPath = "/User/Logout";
    options.AccessDeniedPath = "/User/AccessDenied";
}).AddMyTestOpenIdConnect(options =>
{
    options.Authority = "https://localhost:6001";
    options.ClientId = "authcodeflowclient";
    options.ClientSecret = "mysecret";
    options.ResponseType = "code";
    ...
});

如果元数据 URL 在 HTTP 上,您可能需要设置

options.RequireHttpsMetadata = false;

但是如果您设置了 Authority 字段,那么我怀疑您还需要设置 MetaDataAddress。