如何获取OpenIdConnectOptions StateDataFormat [英] How to get OpenIdConnectOptions StateDataFormat

查看:198
本文介绍了如何获取OpenIdConnectOptions StateDataFormat的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 IdentityServer4 的自定义Open Id Connect服务器.该SSO服务器具有用于非标准Open Id Connect操作的自定义终结点.

I have a custom Open Id Connect server using IdentityServer4. This SSO server has a custom endpoint for a non-standard Open Id Connect operation.

为了使用此操作,我需要在客户端应用程序的请求中创建一个状态参数.客户端应用程序是带有blazor服务器端的dotnet core 3.0预览应用程序.

In order to use this operation I need to create a state parameter in the client application's request. The client application is a dotnet core 3.0 preview application with blazor server side.

我正在通过以下方式注册SSO提供者:

I am registering the SSO provider with the following:

       services.AddAuthentication(options => {
         options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
         options.DefaultChallengeScheme = "oidc";
       })
         .AddCookie()
         .AddOpenIdConnect("oidc", options => {
           options.ClientId = oidcClientId;
           options.ClientSecret = "secret";
           options.Authority = "https://test.org";
           options.ResponseType = "code id_token";
           options.Scope.Clear();
           options.Scope.Add("openid");
           options.Scope.Add("extra");
           options.GetClaimsFromUserInfoEndpoint = true;
           options.SaveTokens = true;
           options.RequireHttpsMetadata = false;
       });

基本的登录/注销流程可以按预期工作,并且没有任何问题.

The basic login/logout flow works as expected and does not have any issues.

我为使用自定义SSO功能而生成的自定义链接如下所示(剃刀):

The custom link I generate to use the custom SSO function looks like the following (razor):

            <a href=@($"https://test.org/custom/change-organization/{organization.OrganizationCode}?client_id=local-data-manager&redirect_uri=http://localhost:5000/signin-oidc&response_type=code id_token&scope=openid custom profile&nonce={GenerateNonce()}&state={GenerateState()}")>@organization.Name</a>

使用已定义的方法:

  private string GenerateNonce()
  {
    string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString() + Guid.NewGuid().ToString()));
    return DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture) + "." + nonce;
  }

  private string GenerateState()
  {
    var state = GenerateNonce();
    AuthenticationProperties authProperties = new AuthenticationProperties
    (
      new Dictionary<string, string>
      {
        { OpenIdConnectDefaults.UserstatePropertiesKey, state },
      }
    );

    authProperties.RedirectUri = "http://localhost:5000";

    //This StateDataFormat does not use the correct DataProtectionProvider
    return openIdOptions.CurrentValue.StateDataFormat.Protect(authProperties);
  }

其中 openIdOptions 是依赖项注入的地方.作为 @inject IOptionsMonitor< OpenIdConnectOptions>openIdOptions

Where openIdOptions is dependency injected. As @inject IOptionsMonitor<OpenIdConnectOptions> openIdOptions

当我单击标记并触发自定义流程时,一切都会在SSO端按预期工作,但是当消息返回时,出现以下错误:

When I click the tag and trigger the custom flow everything works as expected on the SSO side, but when the message comes back I get the following error:

An unhandled exception has occurred while executing the request.                                                                                                                                             
System.Exception: An error was encountered while handling the remote login.                                           
System.Exception: Unable to unprotect the message.State.

这被抛出的noreferrer>会调用 Unprotect ,它与我之前注入的 StateDataFormat 保护器相同:

This is thrown from the OpenIdHandler which calls Unprotect on what I would assume to be the same StateDataFormat protector as was injected earlier:

Options.StateDataFormat.Unprotect(message.State);

从详细的日志记录中,我在调用 GenerateState()时看到以下内容:

From verbose logging I see the following when calling GenerateState():

trce: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector[31]
Performing protect operation to key {cdf7e79b-8d1a-4e7e-a093-fea402dbba8c} with purposes ('/app/DataManager', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', '', 'v1').

但是当请求返回时,我会得到取消保护的日志:

But when the request comes back I get the unprotect log:

trce: Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector[5]
Performing unprotect operation to key {cdf7e79b-8d1a-4e7e-a093-fea402dbba8c} with purposes ('/app/DataManager', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', 'oidc', 'v1').

主要区别在于DataProtector的用途"不同(一个具有",一个具有"oidc").根据文档,这意味着它们被不同地加密和解密.

The key difference is that the DataProtector "purposes" are different ( one having '' and one having 'oidc'). Which according to the documentation means they are being encrypted and decrypted differently.

我希望数据保护器是相同的,因为它们都引用已配置的相同 OpenIdConnectOptions .

I would expect the data protectors to be the same as they both refer to the same OpenIdConnectOptions that are configured.

为什么两个 DataProtector 提供不同的目的?它们是不同的对象吗?我的配置中缺少什么吗?如何从内部框架中获得相同的提供程序以及注入的内容?

Why is that the two DataProtector's are providing different purposes? Are they different objects? Am I missing something in my configuration? How do I get the same provider from the internal framework and what I inject?

推荐答案

我发现,除了生成状态字符串外,我还必须添加一个Correlation Cookie.请参阅 RemoteAuthenticationHandel.cs 上的 GenerateCorrelationId 方法,此处

I found that in addition to Generating the state string, I also had to add a Correlation Cookie. See the GenerateCorrelationId method on the RemoteAuthenticationHandel.cs here https://github.com/dotnet/aspnetcore/blob/c925f99cddac0df90ed0bc4a07ecda6b054a0b02/src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs#L220

我还发现我必须在 AuthenticationProperties 对象(.xsrf,.redirect等)上添加正确的键/值对

I also found I had to add the correct key/value pairs on the AuthenticationProperties object (.xsrf, .redirect, etc)

我的代码看起来像这样方法

My code looks something like this Methods

    private string GenerateState(OpenIdConnectOptions openIdOptions, string nonce)
    {
        AuthenticationProperties authProperties = new AuthenticationProperties();
        authProperties.Items.Add(".xsrf", nonce);
        authProperties.Items.Add(".redirect", "/");
        authProperties.Items.Add("OpenIdConnect.Code.RedirectUri", $"https://{this.Request.Host}/signin-auth0";

        //This StateDataFormat does not use the correct DataProtectionProvider
        return openIdOptions.StateDataFormat.Protect(authProperties);
    }

    private string GenerateNonce()
    {
        string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString() + Guid.NewGuid().ToString()));
        return DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture) + "." + nonce;
    }

主控制器动作

        OpenIdConnectOptions opts = _openIdOptions.Get("Auth0");
        string correlationId = GenerateNonce();
        string state = GenerateState(opts, correlationId);

        string cookieName = opts.CorrelationCookie.Name + correlationId;

        CookieOptions cookieOptions = opts.CorrelationCookie.Build(this.HttpContext, DateTime.UtcNow);

        Response.Cookies.Append(cookieName, "N", cookieOptions);

        ViewBag.State = state;
        return View();

将状态变量传递给外部提供程序将返回到OpenIdConnector中间件,并且应该正确验证.

Passing the state variable to the external provider will be returned to the OpenIdConnector middleware and should validate correctly.

这篇关于如何获取OpenIdConnectOptions StateDataFormat的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆