具有身份 server4 并发登录的隐式授权 SPA [英] Implicit grant SPA with identity server4 concurrent login

查看:21
本文介绍了具有身份 server4 并发登录的隐式授权 SPA的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何限制特定于具有授权类型的 SPA 客户端的每个客户端应用程序的 x 登录量 - 隐式

这超出了身份服务器的范围

尝试过的解决方案 -

  1. 访问令牌保留到数据库中,但是这种方法客户端不断更新访问令牌而没有进入代码,因为客户端浏览器请求带有有效令牌,尽管它已过期,静默身份验证正在通过发出一个更新令牌新的引用令牌(可以在表中看到persistGrants token_type 'reference_token')

  2. Cookie 事件 - 在 validateAsync 上 - 虽然这仅适用于服务器 Web,但运气不佳,我们不能将此逻辑放在 SPA 客户端的 oidc 库中.

  3. 通过覆盖 SignInAsync 自定义 signInManager - 但在调试模式下执行未达到这一点,因为 IDM 不断识别用户具有有效令牌(尽管已过期)不断重新发布令牌(请注意有这里没有刷新令牌通过存储和修改来管理它!!!)

即使访问令牌已过期,IDM 如何在不让用户进入登录屏幕的情况下重新发布令牌?(静默身份验证.??

解决方案

实现配置文件服务覆盖 activeasync

 public override async Task IsActiveAsync(IsActiveContext context){var sub = context.Subject.GetSubjectId();var user = await userManager.FindByIdAsync(sub);//检查现有会话if (context.Caller.Equals("AccessTokenValidation", StringComparison.OrdinalIgnoreCase)){如果(用户!= null)context.IsActive = !appuser.VerifyRenewToken(sub, context.Client.ClientId);别的context.IsActive = false;}别的context.IsActive = 用户 != null;}

启动

services.AddTransient();

同时将身份服务器服务添加到配置服务下的集合

 .AddProfileService();

更新

Session.Abandon();//仅在aspnet先前版本中不在核心中Session.Clear();//清除会话并不意味着会话过期这应该由包含服务时的addSession生命周期控制.

我碰巧找到了一种更好的方法,即使用 aspnetuser securitystamp,每次用户登录时都会更新安全标记,这样之前的任何活动会话/cookie 都会失效.

_userManager.UpdateSecurityStampAsync(_userManager.FindByEmailAsync(model.Email).Result).Result

更新(最终版):

<块引用>

登录时:-

var 结果 = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberLogin, false);如果(结果.成功){//更新安全标记以使现有会话无效var user = _userManager.FindByEmailAsync(model.Email).Result;var test= _userManager.UpdateSecurityStampAsync(user).Result;//刷新cookie以更新authenticationmanager responsegrant对当前请求的securitystamp等待 _signInManager.RefreshSignInAsync(user);}

<块引用>

配置文件服务实现:-

public class ProfileService : ProfileService{公共覆盖异步任务 IsActiveAsync(IsActiveContext context){if (context == null) throw new ArgumentNullException(nameof(context));if (context.Subject == null) throw new ArgumentNullException(nameof(context.Subject));context.IsActive = false;var 主题 = context.Subject;var user = await userManager.FindByIdAsync(context.Subject.GetSubjectId());如果(用户!= null){var security_stamp_changed = false;如果(userManager.SupportsUserSecurityStamp){var security_stamp = (来自主题中的索赔.索赔其中 claim.Type =="AspNet.Identity.SecurityStamp"选择 claim.Value).SingleOrDefault();if (security_stamp != null){var latest_security_stamp = await userManager.GetSecurityStampAsync(user);security_stamp_changed = security_stamp != latest_security_stamp;}}上下文.IsActive =!security_stamp_changed &&!await userManager.IsLockedOutAsync(user);}}}

*

<块引用>

钩入服务集合:-

*

services.AddIdentityServer().AddAspNetIdentity().AddProfileService();

即每次登录时,用户的安全标记都会更新并推送到 cookie,当令牌过期时,授权端点将验证安全更改,如果有则重定向用户登录.这样我们就可以确保只有一个活动会话

how to restrict x amount of login on each client app in specific the SPA client with grant type - implicit

This is out of scope within Identity server

Solutions tried -

  1. Access tokens persisted to DB, however this approach the client kept updating the access token without coming to code because the client browser request is coming with a valid token though its expired the silent authentication is renewing the token by issues a new reference token ( that can be seen in the table persistGrants token_type 'reference_token')

  2. Cookie event - on validateAsync - not much luck though this only works for the server web, we can't put this logic on the oidc library on the client side for SPA's.

  3. Custom signInManager by overriding SignInAsync - but the the executing is not reaching to this point in debug mode because the IDM kept recognising the user has a valid toke ( though expired) kept re issueing the token ( please note there is no refresh token here to manage it by storing and modifying!!!)

Any clues how the IDM re issue the token without taking user to login screen, even though the access token is expired??(Silent authentication. ??

解决方案

implement profile service overrride activeasync

  public override async Task IsActiveAsync(IsActiveContext context)
    {
        var sub = context.Subject.GetSubjectId();
        var user = await userManager.FindByIdAsync(sub);

        //Check existing sessions
        if (context.Caller.Equals("AccessTokenValidation", StringComparison.OrdinalIgnoreCase))
        {
            if (user != null)
                context.IsActive = !appuser.VerifyRenewToken(sub, context.Client.ClientId);
            else
                context.IsActive = false;
        }
        else
            context.IsActive = user != null;
    }

startup

services.AddTransient<IProfileService, ProfileService>();

while adding the identity server service to collection under configure services

 .AddProfileService<ProfileService>();

Update

Session.Abandon(); //is only in aspnet prior versions not in core
Session.Clear();//clears the session doesn't mean that session expired this should be controlled by addSession life time when including service.

I have happened to found a better way i.e. using aspnetuser securitystamp, every time user log-in update the security stamp so that any prior active session/cookies will get invalidated.

_userManager.UpdateSecurityStampAsync(_userManager.FindByEmailAsync(model.Email).Result).Result

Update (final):

On sign-in:-

var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberLogin, false);
                if (result.Succeeded)
                {
            //Update security stamp to invalidate existing sessions           
                    var user = _userManager.FindByEmailAsync(model.Email).Result;
                    var test= _userManager.UpdateSecurityStampAsync(user).Result;
                    //Refresh the cookie to update securitystamp on authenticationmanager responsegrant to the current request
                    await _signInManager.RefreshSignInAsync(user);
          }

Profile service implementation :-

public class ProfileService : ProfileService<ApplicationUser>

{
public override async Task IsActiveAsync(IsActiveContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
            if (context.Subject == null) throw new ArgumentNullException(nameof(context.Subject));

            context.IsActive = false;

            var subject = context.Subject;
            var user = await userManager.FindByIdAsync(context.Subject.GetSubjectId());

            if (user != null)
            {
                var security_stamp_changed = false;

                if (userManager.SupportsUserSecurityStamp)
                {
                    var security_stamp = (
                        from claim in subject.Claims
                        where claim.Type =="AspNet.Identity.SecurityStamp"
                        select claim.Value
                        ).SingleOrDefault();

                    if (security_stamp != null)
                    {
                        var latest_security_stamp = await userManager.GetSecurityStampAsync(user);
                        security_stamp_changed = security_stamp != latest_security_stamp;
                    }
                }

                context.IsActive =
                    !security_stamp_changed &&
                    !await userManager.IsLockedOutAsync(user);
            }
        }
    }   

*

Hook in the service collection:-

*

services.AddIdentityServer()
    .AddAspNetIdentity<ApplicationUser>()                
         .AddProfileService<ProfileService>();

i.e. on every login, the security stamp of the user gets updated and pushed to the cookie, when the token expires, the authorize end point will verify on the security change, If there is any then redirects the user to login. This way we are ensuring there will only be one active session

这篇关于具有身份 server4 并发登录的隐式授权 SPA的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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