Как пройти проверку подлинности с помощью Azure AD / OpenId, но использовать данные пользователя / роли на основе Entity Framework - PullRequest
0 голосов
/ 19 мая 2018

Я пытаюсь улучшить историю аутентификации для устаревшего приложения ASPNet MVC / OWIN - в настоящее время оно использует таблицы AspNetUsers / AspNetRoles / утверждений и т. Д. Вместе с формами + аутентификация на основе файлов cookie.

Я хочуиспользуйте Azure AD / OpenID Connect для аутентификации, но затем загрузите профиль / роли пользователя из базы данных, как в настоящий момент.По сути, больше нет управления паролями в приложении.Сами пользователи по-прежнему должны будут существовать / создаваться в приложении.

Приложение в значительной степени зависит от некоторых пользовательских данных, связанных с этими пользователями, поэтому использование ролей из Active Directory просто невозможно.

Аутентификация OpenID работает, однако я не уверен, как использовать существующую систему Identityuser / IdentityUserRole / RoleManager вместе с ней.

В основном, когда пользователь аутентифицируется с помощью Open ID, мы хотим загрузитьсоответствующего пользователя из базы данных (совпадающего по адресу электронной почты) и использующего этот профиль / роли пользователя в будущем.

В частности, атрибут AuthorizeAttribute (с указанными конкретными ролями) должен продолжать функционировать, как и раньше.

Это то, что я имею до сих пор:

public class IdentityConfig
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext(AppIdentityDbContext.Create);
        app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
        app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);

        ConfigureAuth(app);
    }

    /// <summary>
    /// Configures OpenIDConnect Authentication & Adds Custom Application Authorization Logic on User Login.
    /// </summary>
    /// <param name="app">The application represented by a <see cref="IAppBuilder"/> object.</param>
    private void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        //Configure OpenIDConnect, register callbacks for OpenIDConnect Notifications
        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = ConfigHelper.ClientId,
                Authority = String.Format(CultureInfo.InvariantCulture, ConfigHelper.AadInstance,
                    ConfigHelper.Tenant), // For Single-Tenant
                PostLogoutRedirectUri = ConfigHelper.PostLogoutRedirectUri,

                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    RoleClaimType = "roles",
                },

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Error/OtherError?errorDescription=" +
                                                  context.Exception.Message);
                        return Task.FromResult(0);
                    },
                    SecurityTokenValidated = async context =>
                    {
                        string userIdentityName = context.AuthenticationTicket.Identity.Name;
                        var userManager = context.OwinContext.GetUserManager<AppUserManager>();
                        var user = userManager.FindByEmail(userIdentityName);
                        if (user == null)
                        {
                            Log.Error("User {name} authenticated with open ID, but unable to find matching user in store", userIdentityName);
                            context.HandleResponse();
                            context.Response.Redirect("/Error/NoAccess?identity=" + userIdentityName);
                            return;
                        }

                        user.DateLastLogin = DateTime.Now;
                        IdentityResult result = await userManager.UpdateAsync(user);

                        if (result.Succeeded)
                        {
                            var authManager = context.OwinContext.Authentication;
                            ClaimsIdentity ident = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ExternalBearer);


                            // Attach additional claims from DB user
                            authManager.User.AddIdentity(ident);

                            // authManager.SignOut();
                            // authManager.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);

                            return;
                        }

                        throw new Exception(string.Format("Failed to update user {0} after log-in", userIdentityName));
                    }
                }
            });
    }
}

Ответы [ 2 ]

0 голосов
/ 30 июля 2018

Вот что я в итоге сделал:

public class IdentityConfig
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext(AppIdentityDbContext.Create);
        app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
        app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
        ConfigureAuth(app);
    }

    /// <summary>
    /// Configures OpenIDConnect Authentication & Adds Custom Application Authorization Logic on User Login.
    /// </summary>
    /// <param name="app">The application represented by a <see cref="IAppBuilder"/> object.</param>
    private void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            CookieDomain = ConfigHelper.AuthCookieDomain,
            SlidingExpiration = true,
            ExpireTimeSpan = TimeSpan.FromHours(2)
        });

        //Configure OpenIDConnect, register callbacks for OpenIDConnect Notifications
        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = ConfigHelper.ClientId,
                Authority = String.Format(CultureInfo.InvariantCulture, ConfigHelper.AadInstance, ConfigHelper.Tenant),

                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    RoleClaimType = ClaimTypes.Role
                },

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Error/OtherError?errorDescription=" + context.Exception.Message);
                        return Task.FromResult(0);
                    },
                    RedirectToIdentityProvider = context =>
                    {
                        // Set the post-logout & redirect URI dynamically depending on the incoming request.
                        // That allows us to use the same Azure AD app for two subdomains (these two domains give different app behaviour)
                        var builder = new UriBuilder(context.Request.Uri);
                        builder.Fragment = builder.Path = builder.Query = "";
                        context.ProtocolMessage.PostLogoutRedirectUri = builder.ToString();
                        context.ProtocolMessage.RedirectUri = builder.ToString();
                        return Task.FromResult(0);
                    }
                }
            });

        app.Use<EnrichIdentityWithAppUserClaims>();
    }
}

public class EnrichIdentityWithAppUserClaims : OwinMiddleware
{
    public EnrichIdentityWithAppUserClaims(OwinMiddleware next) : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        await MaybeEnrichIdentity(context);
        await Next.Invoke(context);
    }

    private async Task MaybeEnrichIdentity(IOwinContext context)
    {
        ClaimsIdentity openIdUserIdentity = (ClaimsIdentity)context.Authentication.User.Identity;
        string userIdentityName = openIdUserIdentity.Name;

        var userManager = context.GetUserManager<AppUserManager>();
        var appUser = userManager.FindByEmail(userIdentityName);

        if (appUser == null)
        {
            Log.Error("User {name} authenticated with open ID, but unable to find matching user in store", userIdentityName);
            return;
        }

        appUser.DateLastLogin = DateTime.Now;
        IdentityResult result = await userManager.UpdateAsync(appUser);
        if (result.Succeeded)
        {
            ClaimsIdentity appUserIdentity = await userManager.CreateIdentityAsync(appUser, DefaultAuthenticationTypes.ExternalBearer);
            openIdUserIdentity.AddClaims(appUserIdentity.Claims);
        }
    }
}

Это очень похоже на то, что у меня было изначально - (примечание: RoleClaimType = ClaimTypesRoles не "роли"), за исключением того, что вместо попытки разобраться с пользователем вSecurityTokenValidated обратный вызов, я добавил некоторое специальное промежуточное ПО, которое находит подходящего пользователя (по адресу электронной почты) и добавляет утверждения (роли приложения) от соответствующего пользователя приложения к идентифицированному идентификатору пользователя (идентификатору OpenID).

Наконец, я защитил все действия контроллера с помощью (пользовательского) AuthorizeAttribute (здесь не показан), который гарантирует, что прошедший проверку пользователь, по крайней мере, принадлежит роли «Пользователь» (если нет, перенаправляет их на страницу «нет доступа», указывая, чтомы их аутентифицировали, но у них нет доступа к системе).

0 голосов
/ 28 мая 2018

Аутентификация OpenID работает, однако я не уверен, как использовать существующую систему Identityuser / IdentityUserRole / RoleManager вместе с ней.

Приложение в значительной степени зависит от некоторых пользовательских данных, связанных сэти пользователи так просто не могут использовать роли из Active Directory.

В соответствии с вашими требованиями, я предполагаю, что вы могли бы создать свой сервер идентификации (например, IdentityServer3 ) и использовать его. IdentityServer3.AspNetIdentity для управления идентификацией с использованием ASP.NET Identity.

Для приложения веб-клиента вы могли бы использовать промежуточное программное обеспечение OpenID Connect и установить Authority на свой сервер индивидуальной идентификации и установить предварительно настроенный ClientId на своем сервере идентификации.

Более того, вы можете выполнить это учебник для быстрого начала работы с IdentityServer3 и полными образцами IdentityServer3 Образцы .

...