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

时间:2017-08-16 09:30:16

标签: javascript c# angular asp.net-identity identityserver4

使用带有ASP.NET身份的Identity Server 4添加身份验证功能后,我计划添加Google提供商,以便用户也可以使用他们的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提供商,我添加了一个额外的按钮和以下方法:

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表中创建新记录?

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

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

注意:一种理想的情况是,您可以通过将用户定向到身份提供程序的登录页面(在本例中为Identity Server)来允许用户在Google / FB上登录/注册。他们在那里登录到Google或FB,然后由Identity Server重定向回应用程序。这就是隐式或PKCE流。 PKCE还有其他步骤,但是声明处理等都由IdSrv代码完成。这是您看到最多的记录文件。

经常需要的场景

我经常有客户要求我们通过它们相应的Javascript库简单地使用Java Google和FB登录。这就是您正在谈论的场景。这种情况下的流程如下所示:

  1. 用户到达您的角度或反应应用页面

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

  3. 然后,您可以获取此令牌信息,并将其与任何其他信息(例如客户端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;
    }
相关问题