Как проверить, существует ли пользователь внутри IdentityServer4 после проверки подлинности от внешнего поставщика? - PullRequest
0 голосов
/ 12 декабря 2018

Я пытаюсь найти правильный способ, с помощью которого я могу внедрить службу для проверки, если пользователь существует или зарегистрирован в моем приложении после успешной аутентификации от внешнего поставщика удостоверений, такого как Azure Active Directory.Я хочу перенаправить пользователя на пользовательскую страницу ошибки или отобразить сообщение «Неавторизовано», если его учетная запись еще не зарегистрирована в моем приложении.

Я попытался использовать интерфейс IProfileService, но, похоже, это не правильный способgo.

Вот мои настройки 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, и у меня есть собственный механизм регистрации.Я не хочу перенаправлять обратно в мой SPA, если пользователь не существует, и обрабатывать его внутри IdentityServer4.Кстати, некоторые из приведенного выше кода не включены для краткости.

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Интерфейс IdentityServer4 QuickStart настроен для автоматической подготовки учетных записей локальных пользователей при входе через внешнего поставщика.Все это обрабатывается в 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");
    }

    ...
}
0 голосов
/ 13 декабря 2018

Вы можете написать свою собственную логику в ExternalLoginCallback функции в AccountController, если вы используете ASP.NET Identity.После получения токена JWT, выпущенного из Azure AD, вы можете декодировать токен, получить утверждения пользователя, такие как электронная почта / имя:

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

Затем вы можете записать свой сервисный инструмент / логику в базу данных, чтобы подтвердить, является ли пользовательуже в базе данных.Если да, войдите в систему пользователя, если нет, перенаправьте пользователя в режим подтверждения / регистрации.Что-то вроде:

// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync("YourProvider", userIdClaim.Value, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
    _logger.LogInformation("User logged in with {Name} provider.", "YourProvider");

    // delete temporary cookie used during external authentication
    await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);

    return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
    return RedirectToAction(nameof(Lockout));
}
else
{
    // If the user does not have an account, then ask the user to create an account.
    ViewData["ReturnUrl"] = returnUrl;
    ViewData["LoginProvider"] = "YourProvider";
    var email = claims.FirstOrDefault(x => x.Type == ClaimTypes.Upn).Value;
    return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
}

От вас зависит, как связать пользователя AD с пользователем локальной базы данных .use ID объекта Azure AD или UPN.

...