如何将具有IdentityServer3的帖子作为身份验证服务器处理



tl; dr

使用IdentityServer3作为身份验证服务器,如何在ASP.NET MVC项目(表格,jQuery,Axios(中发布数据。另外,要使用什么流程来进行此工作?

我经历的

我有一个工作的IdentityServer3实例。我也有一个ASP.NET MVC项目。使用混合流,因为我将不得不将用户的令牌传递给其他服务。身份验证本身有效 - 当页面仅使用get时。即使身份验证的用户令牌已过期,后台也将请求重定向到AUTH。服务器,用户可以继续其工作,而无需要求用户再次登录。(据我了解,混合流可以使用刷新令牌,因此我认为这就是它可以重新认证用户的方式。即使HttpContext.Current.User.Identity.IsAuthenticated=false(

(

出于测试目的,我将AccessTokenLifetimeAuthorizationCodeLifetimeIdentityTokenLifetime值设置为auth中的5秒。服务器。据我所知,刷新令牌的到期时间是在几天内测得的,我没有更改默认值。

但是当我尝试使用帖子时,事情变得"丑陋"。

  • 使用表单帖子,带有已过期的令牌,请求将重定向到IdentityServer3。它确实是魔术(用户被身份验证(并将重定向到我的页面 - 作为获取请求...我在URL中看到了response_mode=form_post,但已发布的有效载荷消失了。
  • 使用Axios Post,请求将请求重定向到IdentityServer3,但在飞行前选项请求中失败。
  • 使用默认的jQuery帖子,有同样的错误。(即使默认的jQuery帖子使用application/x-www-form-urlencoded来解决飞行前问题。(

startup.cs

  const string authType = "Cookies";
  // resetting Microsoft's default mapper
  JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
  // ensure, that the MVC anti forgery key engine will use our "custom" user id
  AntiForgeryConfig.UniqueClaimTypeIdentifier = "sub";
  app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions
  {
    AuthenticationType = authType
  });
  app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
  {
    ClientId = clientId,
    RedirectUri = adminUri,
    PostLogoutRedirectUri = adminUri,
    Authority = idServerIdentityEndpoint,
    SignInAsAuthenticationType = authType,
    ResponseType = "code id_token",
    Scope = "openid profile roles email offline_access",
    Notifications = new OpenIdConnectAuthenticationNotifications
    {
      #region Handle automatic redirect (on logout)
      RedirectToIdentityProvider = async n =>
      {
        // if signing out, add the id_token_hint
        if (n.ProtocolMessage.RequestType ==
              OpenIdConnectRequestType.LogoutRequest)
        {
          var token = n.OwinContext.Authentication.User.FindFirst(idTokenName);
          if (token != null)
          {
            var idTokenHint =
              token.Value;
            n.ProtocolMessage.IdTokenHint = idTokenHint;
          }
        }
      },
      #endregion
      AuthorizationCodeReceived = async n =>
      {
        System.Diagnostics.Debug.Print("AuthorizationCodeReceived " + n.ProtocolMessage.ToString());
        // fetch the identity from authentication response
        var identity = n.AuthenticationTicket.Identity;
        // exchange the "code" token for access_token, id_token, refresh_token, using the client secret
        var requestResponse = await OidcClient.CallTokenEndpointAsync(
          new Uri(idServerTokenEndpoint),
          new Uri(adminUri),
          n.Code,
          clientId,
          clientSecret
        );
        // fetch tokens from the exchange response
        identity.AddClaims(new []
        {
          new Claim("access_token", requestResponse.AccessToken), 
          new Claim("id_token", requestResponse.IdentityToken), 
          new Claim("refresh_token", requestResponse.RefreshToken)
        });
        // store the refresh_token in the session, as the user might be logged out, when the authorization attribute is executed
        // see OrganicaAuthorize.cs
        HttpContext.Current.Session["refresh_token"] = requestResponse.RefreshToken;
        // get the userinfo from the openId endpoint
        // this actually retreives all the claims, but using the normal access token
        var userInfo = await EndpointAndTokenHelper.CallUserInfoEndpoint(idServerUserInfoEndpoint, requestResponse.AccessToken); // todo: userinfo
        if (userInfo == null) throw new Exception("Could not retreive user information from identity server.");
        #region Extract individual claims
        // extract claims we are interested in
        var nameClaim = new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.Name,
          userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.Name)); // full name
        var givenNameClaim = new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName,
          userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName)); // given name
        var familyNameClaim = new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.FamilyName,
          userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.FamilyName)); // family name
        var emailClaim = new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.Email,
          userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.Email)); // email
        var subClaim = new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.Subject,
          userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.Subject)); // userid
        #endregion
        #region Extract roles
        List<string> roles;
        try
        {
          roles = userInfo.Value<JArray>(Thinktecture.IdentityModel.Client.JwtClaimTypes.Role).Select(r => r.ToString()).ToList();
        }
        catch (InvalidCastException) // if there is only 1 item
        {
          roles = new List<string> { userInfo.Value<string>(Thinktecture.IdentityModel.Client.JwtClaimTypes.Role) };
        }
        #endregion
        // attach the claims we just extracted
        identity.AddClaims(new[] { nameClaim, givenNameClaim, familyNameClaim, subClaim, emailClaim });
        // attach roles
        identity.AddClaims(roles.Select(r => new Claim(Thinktecture.IdentityModel.Client.JwtClaimTypes.Role, r.ToString())));
        // update the return value of the SecurityTokenValidated method (this method...)
        n.AuthenticationTicket = new AuthenticationTicket(
          identity,
          n.AuthenticationTicket.Properties);
      },
      AuthenticationFailed = async n => 
      {
        System.Diagnostics.Debug.Print("AuthenticationFailed " + n.Exception.ToString());
      },
      MessageReceived = async n =>
      {
        System.Diagnostics.Debug.Print("MessageReceived " + n.State.ToString());
      },
      SecurityTokenReceived = async n =>
      {
        System.Diagnostics.Debug.Print("SecurityTokenReceived " + n.State.ToString());
      },
      SecurityTokenValidated = async n =>
      {
        System.Diagnostics.Debug.Print("SecurityTokenValidated " + n.State.ToString());
      }
    }
  });

您是否在MVC应用中配置了Cookie Authentication Mudindware?使用身份服务器进行身份验证后,应设置身份验证cookie。设置身份验证cookie并在cookie到期/删除之前不会发生有效的身份服务器重定向。

更新1:

好吧,我误解了问题。会话时间淘汰时,重定向到身份服务器是合乎逻辑的。它无法与后有效载荷一起使用。您可以尝试执行以下操作。

  1. 如果请求是普通帖子,请再次将用户重定向到表单填写页面。
  2. 如果请求是AJAX帖子,请返回未经授权的结果,并基于该响应从JavaScript刷新页面。

无论如何,我认为除非您为此设计自己的解决方案,否则您将无法保留发布的数据。(例如,在本地存储数据(。

但是,如果您谨慎地确定身份服务器的会话超时和应用程序的会话超时,您可能会完全避免这种情况。

OpenIdConnectAuthenticationOptions设置UseTokenLifetime = false中,它将打破身份令牌的寿命和cookie session Lifetime之间的连接。

CookieAuthenticationOptions中进行滑动到期 SlidingExpiration = true, ExpireTimeSpan = TimeSpan.FromMinutes(50),

现在,您是应用程序会话生命周期的不对制。调整它以符合您的需求和安全性。

最新更新