首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Azure AD的Identity Server 4--“我们无法登录您,请再试一次。”

使用Azure AD的Identity Server 4--“我们无法登录您,请再试一次。”
EN

Stack Overflow用户
提问于 2020-07-21 16:36:20
回答 1查看 1.8K关注 0票数 0

我正在使用.NET Core3.1和Identity Server 4,并通过OpenIdConnect连接到Azure。我使用的是Vue.js前端和.NET核心API。IdentityServer、前端和API都托管在同一个服务器(相同的域)上。所有东西都使用https。我首先使用带有EF模型的Oracle数据库,使用完全定制的IdentityServer存储和自定义用户存储(我实现了接口)。我正在使用IdentityServer的Quickstart,编辑了一些来连接我的自定义用户存储,而不是测试用户。我正在开发环境中运行这个程序。

如果我输入到IdentityServer的url,则重定向到Azure,成功登录,并显示以下页面:

赠款-成功登录

这些声明是从Azure AD回来的,自动配置是成功的。它已成功地写入数据库。

通过我的JS客户端进行身份验证,点击IdentityServer,重定向到Azure AD,我登录,然后重定向到IdentityServer的ExternalController,然后重定向回Microsoft,然后继续重复,直到这个页面最终失败为止:

Azure广告的登录失败

我猜是我搞砸了什么地方的重定向。下面是我的代码和IdentityServer日志

IdentityServer日志

那块伐木重复6-10次。最后没有任何错误或任何不同之处。

我不得不分解C#代码,因为这个站点无法处理我的一个长长的选项行。

IdentityServer Startup.cs

代码语言:javascript
复制
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();

        var builder = services.AddIdentityServer(options =>
        {
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;
            options.UserInteraction.LoginUrl = "/Account/Login";
            options.UserInteraction.LogoutUrl = "/Account/Logout";
            options.Authentication = new AuthenticationOptions()
            {
                CookieLifetime = TimeSpan.FromHours(10),
                CookieSlidingExpiration = true
            };
        }).AddClientStore<ClientStore>()
          .AddCorsPolicyService<CorsPolicyService>()
          .AddResourceStore<ResourceStore>()
          .AddPersistedGrantStore<PersistedGrantStore>()
          .AddProfileService<UserProfileService>();

        services.AddScoped<IUserStore, UserStore>();

        if (env.IsDevelopment())
        {
            // not recommended for production
            builder.AddDeveloperSigningCredential();
        }
        else
        {
            // TODO: Load Signing Credentials for Production.
        }

        services.AddAuthentication()
            .AddOpenIdConnect("aad", "Azure AD", options =>
            {

options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

代码语言:javascript
复制
                options.SignOutScheme = IdentityServerConstants.SignoutScheme;

                options.Authority = "https://login.windows.net/[authority]";
                options.CallbackPath = "/callback-aad";
                options.ClientId = "[ClientId]";
                options.RemoteSignOutPath = "/signout-aad";
                options.RequireHttpsMetadata = true;
                options.ResponseType = OpenIdConnectResponseType.IdToken;
                options.SaveTokens = true;
                options.SignedOutCallbackPath = "/signout-callback-aad";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
                options.UsePkce = true;
            });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseStaticFiles();
        app.UseSerilogRequestLogging();
        app.UseRouting();
        app.UseIdentityServer();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }

客户端OIDC配置

代码语言:javascript
复制
const oidcSettings = {
    authority: '[IdentityServerUrl]',
    client_id: '[ClientId]',
    post_logout_redirect_uri: '[front-end url]/logout-aad',
    redirect_uri: '[front-end url]/callback-aad',
    response_type: 'code',
    save_tokens: true,
    scope: 'openid profile',
}

为ExternalController命中的ExternalController回调方法

代码语言:javascript
复制
    [HttpGet]
    public async Task<IActionResult> Callback()
    {
        // read external identity from the temporary cookie
        var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
        if (result?.Succeeded != true)
        {
            throw new Exception("External authentication error");
        }

        if (_logger.IsEnabled(LogLevel.Debug))
        {
            var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
            _logger.LogDebug("External claims: {@claims}", externalClaims);
        }

        // lookup our user and external provider info
        var (user, provider, providerUserId, claims) = await 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 = await AutoProvisionUser(provider, providerUserId, claims);
        }

        // this allows us to collect any additional claims or properties
        // for the specific protocols used and store them in the local auth cookie.
        // this is typically used to store data needed for signout from those protocols.
        var additionalLocalClaims = new List<Claim>();
        var localSignInProps = new AuthenticationProperties();
        ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);

        // issue authentication cookie for user
        var isuser = new IdentityServerUser(user.SubjectId)
        {
            DisplayName = user.Username,
            IdentityProvider = provider,
            AdditionalClaims = additionalLocalClaims
        };

        await HttpContext.SignInAsync(isuser, localSignInProps);

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

        // retrieve return URL
        var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";

        // check if external login is in the context of an OIDC request
        var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
        await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId));

        if (context != null)
        {
            if (context.IsNativeClient())
            {
                // The client is native, so this change in how to
                // return the response is for better UX for the end user.
                return this.LoadingPage("Redirect", returnUrl);
            }
        }

        return Redirect(returnUrl);
    }

Azure AD配置:

重定向uri: IdentityServer url/回调-aad

数据库表数据:

客户端表IMG1

客户端表IMG2

ClientScopes表

ClientRedirectUris表

如果您需要更多的信息,请告诉我。谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-04 20:37:03

问题在于我自定义的UserStore。我是通过Azure AD SubjectId而不是UserSubjectId来获得用户的。因此,在ExternalController中,ApplicationUser对象显示为null。它不再是异常,而是继续返回Azure AD,试图再次获得用户,但很明显,这只是创建了一个无限循环。我没有想过要查看那里,因为我的用户已经成功地获得了Id和声明。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63019029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档