ServiceStack 6 CredentialsAuthProvider 没有 return BearerToken

ServiceStack 6 CredentialsAuthProvider does not return BearerToken

我们的商店多年来一直在使用 ServiceStack 库,没有出现过很多问题。 最近,从5.12版本升级到6.0版本后,我们看到一个基于凭证的认证请求没有返回BearerToken。

我已阅读有关 JWT 更改的发行说明部分,here, and I understand the "breaking" change, as well as here,其中描述了如何恢复为在响应中返回不记名令牌。但是,建议的更改是针对 JwtAuthProvider,而不是 CredentialsAuthProvider。 CredentialsAuthProvider 没有 UseTokenCookie 属性.

当身份验证提供程序是 CredentialsAuthProvider 时,是否有办法恢复为在身份验证响应中返回不记名令牌?

关于它的价值,这里是相关的服务器代码:

public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
    private readonly IDbContext _dbContext;
    private const string AuthProviderType = "credentials";

    public CustomCredentialsAuthProvider(IDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public override async Task<bool> TryAuthenticateAsync(IServiceBase authService,
        string userName, string password, CancellationToken token=default)
    {
        var userAuthentication = _dbContext.GetUserAuthenticationData(userName);

        return (userAuthentication != null && HashHelper.VerifyHash(password, userAuthentication.PasswordSalt, userAuthentication.PasswordHash));
    }

    public override async Task<IHttpResult> OnAuthenticatedAsync(IServiceBase authService,
        IAuthSession session, IAuthTokens tokens,
        Dictionary<string, string> authInfo, CancellationToken token=default)
    {
        var customUserSession = (CustomUserSession)session;
        var userId = _dbContext.GetUserId(string.Empty, customUserSession.UserAuthName);
        if (!userId.HasValue) throw new AuthenticationException("Unable to locate UserId");

        var claims = _dbContext.GetClaims(userId.Value);
        var postalCode = _dbContext.GetPostalCode(userId.Value);

        HydrateCustomUserSession(claims.ToList(), customUserSession, postalCode);

        //Call base method to Save Session and fire Auth/Session callbacks:
        return result = await base.OnAuthenticatedAsync(authService, customUserSession, tokens, authInfo, token);
    }

    private void HydrateCustomUserSession(IReadOnlyCollection<Claims> claims, CustomUserSession customUserSession, string postalCode)
    {
        customUserSession.AuthProvider = AuthProviderType;
        customUserSession.CreatedAt = DateTime.UtcNow;
        customUserSession.UserId = Convert.ToInt32(claims.First(item => item.ClaimName == "sub").ClaimValue);
        customUserSession.UserStatusId = Convert.ToInt32(claims.First(item => item.ClaimName == "user_status_id").ClaimValue);
        customUserSession.UserAuthId = claims.First(item => item.ClaimName == "sub").ClaimValue;
        customUserSession.FirstName = claims.First(item => item.ClaimName == "given_name").ClaimValue;
        customUserSession.LastName = claims.First(item => item.ClaimName == "family_name").ClaimValue;
        customUserSession.Email = claims.First(item => item.ClaimName == "email").ClaimValue;
        customUserSession.Company = claims.First(item => item.ClaimName == "company_name").ClaimValue;
        customUserSession.CompanyId = Convert.ToInt32(claims.First(item => item.ClaimName == "company_id").ClaimValue);
        customUserSession.CompanyTypeId = Convert.ToInt32(claims.First(item => item.ClaimName == "company_type_id").ClaimValue);
        customUserSession.PostalCode = postalCode;

        var productIds = claims.First(item => item.ClaimName == "product_ids").ClaimValue;
        if (!string.IsNullOrEmpty(productIds))
        {
            customUserSession.ProductIds = productIds.Split(",").Select(int.Parse).ToList();
        }
        else
        {
            customUserSession.ProductIds = new List<int>();
        }

        var productFeatureIds = claims.First(item => item.ClaimName == "product_feature_ids").ClaimValue;
        if (!string.IsNullOrEmpty(productFeatureIds))
        {
            customUserSession.ProductFeatureIds = productFeatureIds.Split(",").Select(int.Parse).ToList();
        }
        else
        {
            customUserSession.ProductFeatureIds = new List<int>();
        }

        var userRoles = claims.First(item => item.ClaimName == "user_roles").ClaimValue;
        if (!string.IsNullOrEmpty(userRoles))
        {
            customUserSession.UserRoles = userRoles.Split(",").Select(x => new UserRole(x)).ToList();
        }
        else
        {
            customUserSession.UserRoles = new List<UserRole>();
        }
    }
}

在客户端,这是一个批处理驱动的处理器,而不是网络应用程序。身份验证部分在这里:

        private static void GetBearerToken()
    {
        var authRequest = new Authenticate()
        {
            UserName = ConfigHelper.ServiceUserName,
            Password = ConfigHelper.ServicePassword,
            provider = "credentials",
            RememberMe = true
        };

        var authResponse = _authServiceClient.Post(authRequest);

        // BearerToken was returned until the auth service was updated to using ServiceStack 6.0
        // After using 6.0, BearerToken is empty.
        _myOtherServiceClient.BearerToken = authResponse.BearerToken;
    }

填充后,不记名令牌用于验证对其他服务的调用。 感谢您的帮助!

如果您使用的是 JWT,您将在 AuthFeature 插件中配置 JwtAuthProvider,这是需要使用 UseTokenCookie=false 配置的 JWT 令牌在响应 DTO 上填充(即,而不是使用令牌 Cookie),如 explained in the linked docs.

new JwtAuthProvider(AppSettings) {
    UseTokenCookie = false
}

虽然您应该考虑使用推荐的 JWT 令牌 cookie,因为成功的身份验证仍然会导致经过身份验证的 _authServiceClient:

var authResponse = _authServiceClient.Post(authRequest);

// manual JWT handling is unnecessary when using Token Cookies
//_myOtherServiceClient.BearerToken = authResponse.BearerToken;

由于 JWT 令牌是在 HTTP Cookie 中返回的,这将节省手动 handling/populating 承载令牌所需的额外工作,包括服务器 auto-refreshing 在 v6.[=17 中过期的 JWT 承载令牌=]