从外部提供程序进行身份验证后,如何验证用户是否存在于IdentityServer4中?

时间:2018-12-12 11:33:14

标签: c# asp.net-core-2.0 single-page-application identityserver4

我正在尝试找到一种适当的方法,在从外部身份提供商(例如Azure Active Directory)成功进行身份验证之后,可以注入服务来验证用户是否存在于应用程序中或在应用程序中注册。我想做的是将用户重定向到自定义错误页面,或者如果尚未在我的应用程序中注册他的帐户,则显示未经授权的消息。

我尝试利用IProfileService接口,但这似乎不是正确的方法。

这是我的Startup.cs设置:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services
        .AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddTestUsers(Config.GetUsers())
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients()) // Client was configured with RequireConsent = false, EnableLocalLogin = false,
        .AddProfileService<ProfileService>()
        .Services.AddTransient<IUserRepository,UserRepository>();

    services.AddAuthentication()
        .AddOpenIdConnect("AAD", "Azure Active Directory", options =>
        {
            options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
            options.SignOutScheme = IdentityServerConstants.SignoutScheme;
            options.Authority = "https://login.microsoftonline.com/MyTenant";
            options.ClientId = "MyClientId";
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false
            };

            options.GetClaimsFromUserInfoEndpoint = true;                    
        });
}


public class ProfileService : IProfileService
{
    private readonly IUserRepository _userRepository;

    public ProfileService(IUserRepository userRepository)
    {
        _userRepository = userRepository 
    }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = _userRepository.FindByUser(context.Subject.Identity.Name);

        // This will display HTTP 500 instead of 401 
        if(user == null) throw new UnauthorizedAccessException("You're not registered");

        // I add custom claims here

        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context) => Task.FromResult(0);
}

是否可以使用任何可用的服务或接口来注入用户验证,并允许我将用户存储库注入该服务中?是否可以在IdentityServer4内部注入这种过程?有人可以指出我正确的方向,以使用IdentityServer4实现我的目标吗?

注意::假设我有SPA Web应用程序,并且我有自己的单独注册机制。如果用户不存在,我不想重定向回我的SPA,而是在IdentityServer4内部处理它。顺便说一句,为简洁起见,上面的一些代码未包括在内。

2 个答案:

答案 0 :(得分:0)

如果使用ASP.NET Identity,则可以在ExternalLoginCallback的{​​{1}}函数中编写自定义逻辑。从Azure AD发出JWT令牌后,您可以对令牌进行解码,获得用户声明,例如email / name:

AccountController

然后,您可以在数据库中编写服务工具/逻辑,以确认用户是否已经在数据库中。是=>登录用户;否=>重定向到确认/注册视图。像这样:

if (remoteError != null)
{
    ErrorMessage = $"Error from external provider: {remoteError}";
    return RedirectToAction(nameof(Login));
}
// read external identity from the temporary cookie
var aadResult1 = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (aadResult1?.Succeeded != true)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var externalUser = aadResult1.Principal;
if (externalUser == null)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var claims = externalUser.Claims.ToList();

// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
    userIdClaim = claims.FirstOrDefault(x => x.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");
}
if (userIdClaim == null)
{
    throw new Exception("Unknown userid");
}

如何将AD用户链接到本地​​数据库用户取决于您。使用Azure AD的对象ID或UPN。

答案 1 :(得分:0)

IdentityServer4 QuickStart UI配置为在通过外部提供程序登录时自动设置本地用户帐户。这些全部在ExternalController.Callback中处理:

// lookup our user and external provider info
var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
if (user == null)
{
    // this might be where you might initiate a custom workflow for user registration
    // in this sample we don't show how that would be done, as our sample implementation
    // simply auto-provisions new external user
    user = AutoProvisionUser(provider, providerUserId, claims);
}

在您遇到的情况下,您可以执行所需执行的任何逻辑,而不必调用AutoProvisionUser。由于这只是一个正在执行的简单MVC操作,因此您可以将自己的类注入ExternalController的构造函数或Callback本身(使用[FromServices])中。这是您可能要进行的更改的粗略想法:

public async Task<IActionResult> Callback([FromServices] IUserRepository userRepository)
{
    ...

    // lookup our user and external provider info
    var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
    if (user == null)
    {
        // We don't have a local user.
        return RedirectToAction("SomeAction", "SomeController");
    }

    ...
}