【问题标题】:Error code 500 "Unable to unprotect the message.State" when redirecting to Client in IdentityServer4重定向到 IdentityServer4 中的客户端时出现错误代码 500“无法取消保护 message.State”
【发布时间】:2020-10-28 15:27:35
【问题描述】:

我正在使用 IdentityServer4 并尝试手动验证我的 Asp.Net Core 3.1 客户端(手动创建请求以了解流程)。 这是我的客户登录信息:

[HttpGet]
public IActionResult ManualLogin()
{
    var myNonce = Guid.NewGuid().ToString();    
    var myState = Guid.NewGuid().ToString();

    var req = "https://localhost:5000/connect/authorize?" +
        "client_id=mvc" +
        "&redirect_uri=https://localhost:44381/signin-oidc" +
        "&response_type=code id_token" +
        "&scope=openid profile offline_access email" +
        "&response_mode=form_post" +
        $"&nonce={myNonce}" +
        $"&state={myState}";
        
    return Redirect(req);
}

这个登录方法工作正常,一切正常,但我不想使用它:

//[HttpGet]
//public async Task LoginAsync()
//{
//     await HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
//     {
//         RedirectUri = "https://localhost:44381/Home/external-login-callback"
//    });
// }

我客户的 startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // for using IHttpClientFactory
    services.AddHttpClient();

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    // adding Authentication services to DependencyInjection
    services.AddAuthentication(config =>
    {
        config.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;

        config.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)

        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, config =>
        {
            config.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

            config.Authority = "https://localhost:5000";

            config.ClientId = "mvc";

            config.ClientSecret = "secret";

            config.SaveTokens = true;

            config.UseTokenLifetime = false;

            // Hybrid Flow
            config.ResponseType = "code id_token";

            config.Scope.Add("openid");
            config.Scope.Add("offline_access");
            config.Scope.Add("profile");
            config.Scope.Add("email");

            config.GetClaimsFromUserInfoEndpoint = true;

            config.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.GivenName,
                RoleClaimType = JwtClaimTypes.Role,
            };

            // config.AuthenticationMethod = OpenIdConnectRedirectBehavior.FormPost;
        });

    services.AddControllersWithViews();
}

我的客户的定义:

new Client
{
    ClientId = "mvc",
    ClientName ="My mvc client testing",
    ClientSecrets = { new Secret("secret".Sha256()) },

    AllowedGrantTypes = GrantTypes.Hybrid,

    // where to redirect to after login
    RedirectUris = { "https://localhost:44381/signin-oidc" },

    // where to redirect to after logout
    PostLogoutRedirectUris = { "https://localhost:44381/signout-callback-oidc" },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
    },

    AllowOfflineAccess = true,
    UpdateAccessTokenClaimsOnRefresh = true,
    AccessTokenType = AccessTokenType.Reference,
    RequireConsent = false,

    RequireClientSecret = true,
    //AlwaysIncludeUserClaimsInIdToken = true,
    RequirePkce = false,
}

我的 IS4 的 startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();

        var builder = services.AddIdentityServer()
            .AddInMemoryIdentityResources(Config.IdentityResources)
            .AddInMemoryApiScopes(Config.ApiScopes)
            .AddInMemoryClients(Config.Clients)
            .AddTestUsers(TestUsers.Users);

        builder.AddDeveloperSigningCredential();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();
        app.UseRouting();

        app.UseIdentityServer();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });
    }
}

客户端成功重定向到IS4登录页面,然后我可以对用户进行身份验证,并且在我的客户端上重定向回signin-oidc url时,我得到了500 Internal Server Error

Unable to unprotect the message.State.

Exception: Unable to unprotect the message.State.
Unknown location

Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>.HandleRequestAsync()

Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>.HandleRequestAsync()
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

我没有多个 oidc,只有一个!我错过了什么?


更新1:@ToreNestenius 评论后:

我把请求的redirect_uri改成了:

&redirect_uri=https://localhost:44381/home/MyCallback"

并将回调添加到 IS4 客户端的配置中,然后这是我的回调:

[HttpPost]
[ActionName("mycallback")]
public async Task mycallbackAsync(
    string code, 
    string scope, 
    string state, 
    string session_state,
    string login_required)
{
    var theRequest = $"https://localhost:5000/connect/token";

    var client = _httpClientFactory.CreateClient();
    
    var theContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string,string>("client_id","mvc"),
        new KeyValuePair<string,string>("client_secret","secret"),
        new KeyValuePair<string,string>("grant_type","hybrid"),
        new KeyValuePair<string,string>("code",code),
        new KeyValuePair<string,string>("redirect_uri", "https://localhost:5002/home/mycallback"),
    });

    theContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

    var base64StringUserPass = Convert.ToBase64String(Encoding.ASCII.GetBytes($"mvc:secret"));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64StringUserPass);

    var response = await client.PostAsync(req, theContent);
    if (!response.IsSuccessStatusCode)
    {
        Console.WriteLine(response.StatusCode);
        return;
    }
 
    var content = await response.Content.ReadAsStringAsync();
    var theAccessToken = JsonConvert.DeserializeObject<Token>(content);
    
    // -----------------------------
    // get user info
    //var access_token = theAccessToken.access_token;
    //string userInfo = await getUserInfoAsync(access_token);
}

现在我可以正确处理回调,然后发出 accessToken 并获取 userInfo。

【问题讨论】:

    标签: asp.net-core oauth-2.0 identityserver4 openid-connect


    【解决方案1】:

    在您使用的代码中

        "&redirect_uri=https://localhost:44381/signin-oidc" +
    

    这意味着您正在尝试重定向回客户端中的 OpenIDConnect 身份验证处理程序/方案。但它希望传入的请求包含它无法识别的状态和随机值。因为初始身份验证请求不是来自该处理程序。

    因为您想学习 OpenID-Connect 并手动完成(就像我学习它时所做的那样)。我建议您将 redirectURi 更改为您自己的控制器中的操作方法。比如 https://localhost:44381/test/callback"

    我建议您在了解完整的手动流程之前避免涉及 OpenIDConnect 处理程序。

    回调方法签名应如下所示:

    /// <summary>
    /// This method is called with the authorization code and state parameter
    /// </summary>
    /// <param name="code">authorization code generated by the authorization server. This code is relatively short-lived, typically lasting between 1 to 10 minutes depending on the OAuth service.</param>
    /// <param name="state"></param>
    /// <returns></returns>
    
    [HttpPost]
    public IActionResult Callback(string code, string state)
    {
    
        //To be secure then the state parameter should be compared 
        to the state sent in the previous step
    
        var url = new Url(_openIdSettings.token_endpoint);
    
        //Get the tokens based on the code, using https://flurl.dev/docs/fluent-http/
        var token = url.PostUrlEncodedAsync(new
        {
            client_id = "authcodeflowclient",     
            client_secret = "mysecret",
            grant_type = "authorization_code",
            code_verifier = code_verifier,
            code = code,
            redirect_uri = "https://localhost:5001/CodeFlow/callback"
    
        }).ReceiveJson<Token>().Result;
    
        return View(token);
    }
    

    【讨论】:

    • 是的,我使用的是最新版本Quickstart 2_InteractiveAspNetCore。我设置了RequirePkce=false,但没有解决问题
    • 您是在生产模式还是开发模式下运行 IdentityServer?如果在生产中,您是否添加了 AddSigningCredential ?
    • 我处于开发模式。我更新了我的问题并添加了我的 IS4 的 startup.cs
    • 谢谢,我根据您的回答更新了我的问题(更新 1),回调已到达,但用户未在此处进行身份验证
    • 真正获得身份验证还有很长的路要走,当您像您一样手动进行身份验证时,您的第一项工作是实际获取代码,然后是 ID/访问令牌。当您获得 ID 令牌时,您实际上可以尝试登录用户。但这超出了这个问题。
    猜你喜欢
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多