使用AspNet Core 2.0进行Google JWT身份验证

时间:2018-02-11 03:07:51

标签: asp.net-core oauth-2.0 google-api asp.net-core-2.0 google-authentication

我正在尝试将Google身份验证集成到我的ASP.NET Core 2.0 web api中,我无法弄清楚如何让它工作。

我的Startup.cs中有这段代码ConfigureServices

services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders();

services.AddAuthentication()
.AddGoogle(googleOptions => 
 {
     googleOptions.ClientId = Configuration["Authentication:Google:ClientId"];
     googleOptions.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
});

这是Configure(IApplicationBuilder app, IHostingEnvironment env)

 app.UseAuthentication();

当我导航到Authorized端点时,结果是302 Found,因为可能是它重定向到某个登录端点(我从未创建过)。如果没有提供令牌,我如何阻止重定向并让API期望一个令牌并返回401

3 个答案:

答案 0 :(得分:11)

为后代发布我的终极方法。

正如Tratcher所指出的,AddGoogle中间件实际上并不适用于JWT身份验证流程。经过更多的研究,我意识到我最终想要的是这里描述的内容: https://developers.google.com/identity/sign-in/web/backend-auth

所以我的下一个问题是

  1. 我不能再依赖标准的dotnet核心Jwt auth中间件,因为我需要将谷歌令牌验证委托给谷歌图书馆
  2. 没有C#google验证器列为该页面上的外部客户端库之一。
  3. 经过多次挖掘后,我发现使用此类和方法将JWT验证支持添加到C#hereGoogle.Apis.Auth.Task<GoogleJsonWebSignature.Payload> ValidateAsync(string jwt, GoogleJsonWebSignature.ValidationSettings validationSettings)

    接下来我需要弄清楚如何替换内置的JWT验证。从这个问题我想出了一个方法: ASP.NET Core JWT Bearer Token Custom Validation

    这是我的自定义GoogleTokenValidator:

    public class GoogleTokenValidator : ISecurityTokenValidator
    {
        private readonly JwtSecurityTokenHandler _tokenHandler;
    
        public GoogleTokenValidator()
        {
            _tokenHandler = new JwtSecurityTokenHandler();
        }
    
        public bool CanValidateToken => true;
    
        public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
    
        public bool CanReadToken(string securityToken)
        {
            return _tokenHandler.CanReadToken(securityToken);
        }
    
        public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
            validatedToken = null;
            var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings()).Result; // here is where I delegate to Google to validate
    
            var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.NameIdentifier, payload.Name),
                        new Claim(ClaimTypes.Name, payload.Name),
                        new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
                        new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
                        new Claim(JwtRegisteredClaimNames.Email, payload.Email),
                        new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
                        new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
                    };
    
            try
            {
                var principle = new ClaimsPrincipal();
                principle.AddIdentity(new ClaimsIdentity(claims, AuthenticationTypes.Password));
                return principle;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
    
            }
        }
    }
    

    Startup.cs中,我还需要清除默认的JWT验证,并添加我的自定义验证:

    services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    
                })
                .AddJwtBearer(o =>
                    {
                        o.SecurityTokenValidators.Clear();
                        o.SecurityTokenValidators.Add(new GoogleTokenValidator());
                    }
    

    也许有一种更简单的方法,但这是我降落的地方,似乎工作正常!我做了额外的工作,为了简单起见,我离开了这里,例如,检查我的用户的数据库中是否已经存在与谷歌提供的声明匹配的用户,所以如果上面的代码没有,我很抱歉100%工作,因为我可能无意中删除了一些东西。

答案 1 :(得分:9)

我刚刚发布了NuGet package来处理Google OpenID Connect令牌的验证。

该软件包依赖于Microsoft.AspNetCore.Authentication.JwtBearer中Microsoft的JWT验证和身份验证处理程序,并在托管域周围添加了一些验证。

它在UseGoogle上包含一个公共扩展方法JwtBearerOptions,可让您配置处理程序以验证Google OpenID Connect令牌,而不需要其他依赖项:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(jwt => jwt.UseGoogle(
        clientId: "<client-id-from-Google-API-console>",
        hostedDomain: "<optional-hosted-domain>"));

如果您想查看来源,可以找到它here

答案 2 :(得分:0)

Mikeyg36 的回答非常棒,最终帮助我解决了 jwt 令牌问题。但是,我添加了我认为很重要的 clientId,因为您不想验证任何传入的 id 令牌。我还在 AddIdentity 中添加了“JwtBearerDefaults.AuthenticationScheme”。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Google.Apis.Auth;

namespace Some.Namespace
{
    public class GoogleTokenValidator : ISecurityTokenValidator
    {
        private readonly string _clientId;
        private readonly JwtSecurityTokenHandler _tokenHandler;

        public GoogleTokenValidator(string clientId)
        {
            _clientId = clientId;
            _tokenHandler = new JwtSecurityTokenHandler();
        }

        public bool CanValidateToken => true;

        public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;

        public bool CanReadToken(string securityToken)
        {
            return _tokenHandler.CanReadToken(securityToken);
        }

        public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
            validatedToken = null;
            try {
                var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings() { Audience =  new[] { _clientId }}).Result; // here is where I delegate to Google to validate
            
                var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.NameIdentifier, payload.Name),
                        new Claim(ClaimTypes.Name, payload.Name),
                        new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
                        new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
                        new Claim(JwtRegisteredClaimNames.Email, payload.Email),
                        new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
                        new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
                    };

                var principle = new ClaimsPrincipal();
                principle.AddIdentity(new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme));
                return principle;
            }
            catch (Exception e)
            {
                Debug.WriteLine(e);
                throw;
            }
        }
    }
}