首页 > 解决方案 > How to validate if user exist inside IdentityServer4 after being authenticated from External Provider?

问题描述

I'm trying to find a proper way where I can inject a service to validate if user exists or registered in my application after being successfully authenticated from an external identity provider like Azure Active Directory. What I want to do is to redirect user to a custom error page or display an Unauthorized message if his account is not yet registered in my application.

I tried utilizing the IProfileService interface but it seems not the right way to go.

Here is my Startup.cs setup:

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);
}

Is there any available service or interface I can use where I can inject my user validation as well as allowing me to inject my user repository in that service? Is it possible to inject this kind of process inside IdentityServer4? Can someone point me in the right direction to accomplish my goal using IdentityServer4?

Note: Lets assume I have SPA web app and I have my own separate registration mechanism. I don't want to redirect back to my SPA if user doesn't exist and handle it inside IdentityServer4 instead. Btw, some of the code above are not included for brevity.

标签: c#asp.net-coreasp.net-core-2.0single-page-applicationidentityserver4

解决方案


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");
    }

    ...
}

推荐阅读