IdentityServer在使用引用令牌时获取声明

时间:2017-11-05 20:45:09

标签: c# authentication identityserver3

我发现这确实令人困惑,无法在任何地方找到任何文档。我希望我没有打算转移到身份服务器,我遇到的所有问题是什么.... 无论如何,当前的问题是,当我进行身份验证时,没有任何声明基于 User.Identity

我尝试使用 ClaimsTransformer 来使用 ClaimsTransformer 添加我的声明but it turns out that you can't use DI to get database claims。所以我不得不想出另一个解决方案。

我经过大量挖掘后发现,您可以使用 ClaimsProvider 。但是当我添加它时,我无法授权我的请求。 这是我的配置之前我添加了 ClaimsProvider

public static class Config
{
    /// <summary>
    ///     Configures identity server
    /// </summary>
    public static void ConfigureIdentityServer(this IAppBuilder app, CormarConfig config)
    {
        // Create our options
        var identityServerOptions = new IdentityServerOptions
        {
            SiteName = "Cormar API",
            SigningCertificate = LoadCertificate(),
            IssuerUri = "https://cormarapi-test.azurewebsites.net",

            LoggingOptions = new LoggingOptions
            {
                EnableHttpLogging = true,
                EnableWebApiDiagnostics = true,
                EnableKatanaLogging = true,
                WebApiDiagnosticsIsVerbose = true
            },

            Factory = new IdentityServerServiceFactory().Configure(config),

            // Disable when live
            EnableWelcomePage = true
        };

        // Setup our auth path
        app.Map("/identity", idsrvApp => { idsrvApp.UseIdentityServer(identityServerOptions); });
    }


    /// <summary>
    ///     Configures the identity server to use token authentication
    /// </summary>
    public static void ConfigureIdentityServerTokenAuthentication(this IAppBuilder app, HttpConfiguration config)
    {
        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = "https://cormarapi-test.azurewebsites.net/identity",
            DelayLoadMetadata = true,
            ValidationMode = ValidationMode.Both,
            RequiredScopes = new[] {"api"},

            ClientId = "api",
            ClientSecret = "8at?7nAtaB!E"
        });
    }

    /// <summary>
    ///     Loads the certificate
    /// </summary>
    /// <returns></returns>
    private static X509Certificate2 LoadCertificate()
    {
        var certPath = $"{AppDomain.CurrentDomain.BaseDirectory}App_Data\\idsrv3test.pfx";
        var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        certStore.Open(OpenFlags.ReadOnly);
        var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, "3A1AFB6E1DC5C3F341E63651542C740DA4148866", false);
        certStore.Close();

        // If we are on azure, get the actual self signed certificate, otherwise return the test one
        return certCollection.Count > 0 ? certCollection[0] : new X509Certificate2(certPath, "idsrv3test");
    }

    /// <summary>
    ///     Configure the identity service factory with custom services
    /// </summary>
    /// <returns></returns>
    private static IdentityServerServiceFactory Configure(this IdentityServerServiceFactory factory, CormarConfig config)
    {
        var serviceOptions = new EntityFrameworkServiceOptions {ConnectionString = config.SqlConnectionString};
        factory.RegisterOperationalServices(serviceOptions);
        factory.RegisterConfigurationServices(serviceOptions);
        factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService {AllowAll = true}); // Allow all domains to access authentication

        factory.Register(new Registration<DbContext>(dr => dr.ResolveFromAutofacOwinLifetimeScope<DbContext>()));
        factory.UserService = new Registration<IUserService>(dr => dr.ResolveFromAutofacOwinLifetimeScope<IUserService>());
        factory.ClientStore = new Registration<IClientStore>(dr => dr.ResolveFromAutofacOwinLifetimeScope<IClientStore>());
        factory.ScopeStore = new Registration<IScopeStore>(dr => dr.ResolveFromAutofacOwinLifetimeScope<IScopeStore>());

        return factory;
    }
}

如果设置如此,我在 Startup.cs 中调用它:

app.ConfigureIdentityServer(scope.Resolve<CormarConfig>());
app.ConfigureIdentityServerTokenAuthentication(config);
app.UseWebApi(config);

一切正常(索赔除外)。我可以进行身份​​验证,我可以授权我的请求。 现在我需要获取 UserClaims ,因此我创建了 ClaimsProvider ,如下所示:

public class ClaimsProvider: DefaultClaimsProvider
{

    private readonly IUserStore<User> _userStore;
    public ClaimsProvider(IUserService userService, IUserStore<User> userStore)
        : base(userService)
    {
        _userStore = userStore;
    }

    public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, ValidatedRequest request)
    {
        var id = subject.GetSubjectId();
        var user = await _userStore.FindByIdAsync(id);

        return user.Claims.Select(ModelFactory.Create).ToList();
    }
}

然后我将它添加到我的 IdentityServerServiceFactory配置方法中,如下所示:

factory.ClaimsProvider = new Registration<IClaimsProvider>(dr => dr.ResolveFromAutofacOwinLifetimeScope<IClaimsProvider>());

ClaimsProvider 已在 Autofac 中注册,如下所示:

builder.RegisterType<ClaimsProvider>().As<idsrv.IClaimsProvider>().InstancePerDependency();

我仍然可以验证并获取我的令牌,但是一旦我尝试使用我的令牌访问任何端点,我就会收到此错误:

  

收到无效的承载令牌

我不知道为什么。有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

我在这里完全看错了地方。 要向用户添加声明,您必须在自定义 UserService 中覆盖 GetProfileDataAsync 。我之前已经这样做了,但声称没有坚持下去。然后我读到了 ScopeClaims 。在我的情况下,我的声明属于“角色”类型,所以我只添加了一个名为role的新 ScopeClaim ,然后当我登录时,我看到 GetProfileDataAsync ,果然,声明已添加到我的 User.Identity 。 例如,以下是我的 GetProfileDataAsync

    public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var subject = context.Subject;
        var requestedClaimTypes = context.RequestedClaimTypes;

        var user = await _userProvider.FindByIdAsync(subject.GetSubjectId());
        if (user == null) throw new ArgumentException("Invalid user id");

        var claims = GetClaimsFromUser(user);
        if (requestedClaimTypes != null && requestedClaimTypes.Any()) claims = claims.Where(m => requestedClaimTypes.Contains(m.Type));

        context.IssuedClaims = claims;
    }

    private IEnumerable<Claim> GetClaimsFromUser(User model)
    {
        var claims = model.Claims.Select(ModelFactory.Create).ToList();

        if (model.EmailConfirmed) claims.Add(new Claim(Constants.ClaimTypes.EmailVerified, model.EmailConfirmed ? "true": "false"));

        return claims;
    }