使用 Identity Server 4 和 ASP.NET Identity 添加外部登录

Posted

技术标签:

【中文标题】使用 Identity Server 4 和 ASP.NET Identity 添加外部登录【英文标题】:Adding external login with Identity Server 4 and ASP.NET Identity 【发布时间】:2018-01-24 09:09:29 【问题描述】:

在使用 Identity Server 4 和 ASP.NET Identity 添加身份验证功能后,我计划添加 Google Provider,以便用户也可以使用他们的 google+ 帐户登录。我使用 Angular 作为前端,使用 ASP.NET Web Api (Core) 作为后端。

// Login client
public login(email: string, password: string): Observable<any> 
    let body: any = this.encodeParams( /* cliend_id, grant_type, username, password, scope */ );

    return this.http.post("http://localhost:64023/connect/token", body, this.options)
        .map((res: Response) => 
            const body: any = res.json();
                if (typeof body.access_token !== "undefined") 
                    // Set localStorage with id_token,..
                
        ).catch((error: any) =>  /**/ );


// Register Web API
[HttpPost("Create")]
[AllowAnonymous]
public async Task<IActionResult> Create([FromBody]CreateUserViewModel model)

    var user = new ApplicationUser
    
        FirstName = model.FirstName,
        LastName = model.LastName,
        AccessFailedCount = 0,
        Email = model.Email,
        EmailConfirmed = false,
        LockoutEnabled = true,
        NormalizedEmail = model.Email.ToUpper(),
        NormalizedUserName = model.Email.ToUpper(),
        TwoFactorEnabled = false,
        UserName = model.Email
    ;

    var result = await _userManager.CreateAsync(user, model.Password);

    if (result.Succeeded)
    
        await addToRole(model.Email, "user");
        await addClaims(model.Email);
    

    return new JsonResult(result);


// Identity Server Startup 
app.UseGoogleAuthentication(new GoogleOptions

    AuthenticationScheme = "Google",
    DisplayName = "Google",
    SignInScheme = "Identity.External",
    // ClientId, ClientSecret,..
);

用户登录后,localStorage 被设置,我可以保护安全控制器。对于 Google Provider,我添加了一个额外的按钮和以下方法:

initGoogleAPI() 
    let self = this;
    gapi.load('auth2', function () 
        self.auth2 = gapi.auth2.init( /* client_id, cookiepolicy, scope */ );
        self.externalLogin(document.getElementById('google-button'));
    );


externalLogin(element) 
    let self = this;
    this.auth2.attachClickHandler(element, ,
        function (googleUser) 
            // retrieved the id_token, name, email,...
        , function (error) 
        alert(JSON.stringify(error, undefined, 2));
    );

我找到了一些解决方案,但仅适用于 MVC 应用程序,而不适用于使用客户端框架的 SPA。接下来我需要采取哪些步骤才能使外部登录起作用?用户首次使用外部提供商登录时,是否需要在 AspNetUsers 表中创建新记录?

【问题讨论】:

【参考方案1】:

您可以检查此存储库,您可以忽略 ids4 服务器项目并检查您应该使用 openid 客户端执行此操作的 Angular 客户端,然后将客户端重定向到您登录的 ids4 项目登录页面并返回一个令牌你保存它以便以后使用它

https://github.com/nertilpoci/Aspnetcore-identityserver4-webapi-angular

https://github.com/nertilpoci/Aspnetcore-identityserver4-webapi-angular/tree/master/ClientApp

【讨论】:

感谢您分享该存储库。如果我没记错的话,用户只能在重定向到 MVC 视图时使用提供程序登录,但是如果我想在 Angular 组件中包含登录表单和 google 按钮会发生什么。这不可能吗? 我认为发送给权威机构的链接可以手动构建,因此它只指向 google auth,但我自己还没有实现一个,如果我让它工作,我会在这里发布。好问题【参考方案2】:

登录应该由 identityServer4 处理。无论是本地登录还是第 3 方,它都应该向 Web 应用返回类似的令牌。

如果您的 Web API 后端需要用户数据,则应将其作为声明传递。

IdentityServer4 快速入门可能对您的代码有帮助,它们有一个 External logins 示例,您可以将其添加到您的 IdentityServer 以及如何从 javascript application 进行登录流程。

【讨论】:

不应该由提供者处理登录吗?选择谷歌帐户后,我从提供者而不是身份服务器生成的响应中检索令牌。我已经按照您发送给我的指南进行操作,但不幸的是,它没有提供任何有关将外部提供程序添加到结合​​ ASP.NET 身份的 SPA 的信息。 您从 Identity Server 和 Google 接收令牌,具体取决于登录类型,它们可能不包含您需要的声明。如果您的身份服务器允许通过 Google 登录,那么您的应用程序只会从身份服务器获得 1 种类型的令牌。 Identity Server 仍会将您传递给 Google 进行身份验证,但它会在进入您的应用程序之前返回到 Identity Server。【参考方案3】:

我想在这里发表一些想法,因为我经常从完美的用户体验角度来解决这个问题。我很久以前就可以通过使用手动方法来实现这一点。我不确定这是否是 Identity Server 的东西,但尽管如此,它可以作为 ASP.NET Identity 系统的一部分与 Identity Server 一起实现。

注意:理想的情况是让用户在 Google/FB 上登录/注册,方法是将用户定向到身份提供者的登录页面(在本例中为身份服务器)。他们在那里登录 Google 或 FB,然后由 Identity Server 重定向回应用程序。这是隐式或 PKCE 流。 PKCE 有额外的步骤,但索赔处理等都是由 IdSrv 代码完成的。这是您看到的最广泛记录的内容。

最常请求的场景

我经常有客户要求我们通过他们相应的 Javascript 库简单地使用 Javascript Google 和 FB 登录。这就是你所说的场景。这种情况下的流程是这样的:

    用户到达您的 Angular 或 React 应用页面

    他们使用 Google/FB 登录 - 这将为我们提供“外部访问令牌”。即 Google 和 FB 的令牌

    然后您可以获取此令牌信息并将其与任何其他信息(例如客户端 ID、租户 ID 等)一起传递给您可以创建的新 API 方法。

API 可以有一个名为“POST api/local-token”的方法,该方法将在内部验证外部访问令牌的真实性。如果信息签出,您将为用户生成一个新的应用程序特定的承载访问令牌并将其作为响应传递回 Javascript 应用程序。然后,JS 应用程序可以将外部访问令牌(用于 FB/Google API 请求)和应用程序的访问令牌(用于您的应用程序 API 请求)存储在 cookie/本地存储中,然后从那里继续。

我已经能够很容易地做到这一点。

如果你想生成访问令牌,它非常适合 str8,这是我的代码中的一个示例。这实际上会生成一个类似于 Identity Server 生成的响应:

  public static async Task<JObject> GenerateLocalAccessTokenResponse(string userName, string role, string userId, string clientId, string provider)
    

        var tokenExpiration = TimeSpan.FromDays(1);

        var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);

        identity.AddClaim(new Claim(ClaimTypes.Name, userName));
        identity.AddClaim(new Claim("ClientId", clientId));
        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId));
        identity.AddClaim(new Claim(ClaimTypes.Role, role));


        var data = new Dictionary<string, string>
        
            "userName", userName,
            "client_id", clientId,
            "role", role,
            "provider", provider,
            "userId", userId
        ;

        var props = new AuthenticationProperties(data);

        var ticket = new AuthenticationTicket(identity, props);

        var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);

        var tokenResponse = new JObject(
            new JProperty("userName", userName),
            new JProperty("client_id", clientId),
            new JProperty("role", role),
            new JProperty("provider", provider),
            new JProperty("userId", userId),
            new JProperty("access_token", accessToken),
            new JProperty("token_type", "bearer"),
            new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()),
            new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
            new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
            );

        return tokenResponse;
    

【讨论】:

以上是关于使用 Identity Server 4 和 ASP.NET Identity 添加外部登录的主要内容,如果未能解决你的问题,请参考以下文章

使用Identity Server 4建立Authorization Server

.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4

使用Identity Server 4建立Authorization Server

使用Identity Server 4建立Authorization Server

谁为 Identity Server 4 提供客户端 ID 和客户端密码?

Identity Server 4备用注销重定向uri