验证Google ID令牌 [英] Validate Google Id Token

查看:309
本文介绍了验证Google ID令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用ASP.NET Core为Android客户端提供API。 Android以Google帐户的身份登录,并将JWT(ID令牌)作为不记名令牌传递给API。我有应用程序工作,它确实通过了验证检查,但我认为它不验证令牌签名。

根据Google的文档,我可以调用此网址来执行此操作: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123 ,但我无法在服务器端找到相应的挂钩来执行此操作。同样根据Google文档,我可以以某种方式使用客户端访问API来执行此操作,而无需每次都调用服务器。

我的配置代码:

  app.UseJwtBearerAuthentication(new JwtBearerOptions )
{

权威=https://accounts.google.com,
Audience =hiddenfromyou.apps.googleusercontent.com,
TokenValidationParameters =新的TokenValidationParameters()
{
ValidateAudience = true,
ValidIssuer =accounts.google.com
},
RequireHttpsMetadata = false,
AutomaticAuthenticate = true,
AutomaticChallenge = false,
});

如何让JWTBearer中间件验证签名?我已经接近放弃使用MS中间件和自己开发了。

解决方案

有几种不同的方法,您可以在其中验证服务器上的ID令牌的完整性side:


  1. 手动 - 不断下载Google的公钥,验证签名,然后每个字段,包括 iss one;我在这里看到的主要优势(尽管我认为很小),可以最大限度地减少发送给Google的请求数量。

  2. 自动 - 在Google端点上执行GET操作验证此令牌
    https://www.googleapis.com/oauth2/v3/tokeninfo?id_token= {0}

  3. 使用Google API客户端库 - 与官方一样。
  4. >

下面是第二个例子的样子: $ b

  private const string GoogleApiTokenInfoUrl =https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}; 
$ b $ public ProviderUserDetails GetUserDetails(string providerToken)
{
var httpClient = new MonitoredHttpClient();
var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl,providerToken));

HttpResponseMessage httpResponseMessage;
尝试
{
httpResponseMessage = httpClient.GetAsync(requestUri).Result;
}
catch(Exception ex)
{
return null;
}

if(httpResponseMessage.StatusCode!= HttpStatusCode.OK)
{
return null;
}

var response = httpResponseMessage.Content.ReadAsStringAsync()。
var googleApiTokenInfo = JsonConvert.DeserializeObject< GoogleApiTokenInfo>(response); $!
$ b if(!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
{
Log.WarnFormat(Google API令牌信息审计字段({0})不包含所需的客户端id,googleApiTokenInfo.aud);
返回null;
}

返回新的ProviderUserDetails
{
Email = googleApiTokenInfo.email,
FirstName = googleApiTokenInfo.given_name,
LastName = googleApiTokenInfo.family_name ,
Locale = googleApiTokenInfo.locale,
Name = googleApiTokenInfo.name,
ProviderUserId = googleApiTokenInfo.sub
};
}

GoogleApiTokenInfo类:

  public class GoogleApiTokenInfo 
{
///< summary>
///响应发布者的发行者标识符。对于Google ID令牌,请始终使用https://accounts.google.com或accounts.google.com。
///< / summary>
public string iss {get;组; }

///< summary>
///访问令牌散列。提供访问令牌绑定到身份令牌的验证。如果ID令牌在服务器流程中与访问令牌一起发布,则总是包含
///。这可以用作防止跨站点请求伪造攻击的备用机制,但如果按照步骤1和步骤3进行操作,则不需要验证
///访问令牌。
///< / summary>
public string at_hash {get;组; }

///< summary>
///标识此ID标记用于的受众。它必须是您的应用程序的OAuth 2.0客户端ID之一。
///< / summary>
public string aud {get;组; }

///< summary>
///用户的标识符,在所有Google帐户中都是唯一的,并且不会重复使用。 Google帐户可以在不同时间点发送多封电子邮件,但其子值绝不会变为
///。在您的应用程序中使用sub作为用户的唯一标识符密钥。
///< / summary>
public string sub {get;组; }

///< summary>
///如果用户的电子邮件地址已经过验证,则为true;否则为false。
///< / summary>
public string email_verified {get;组; }

///< summary>
///授权演示者的client_id。只有当请求ID令牌的一方与ID令牌的观众不相同时才需要此声明。这可能是谷歌对混合应用程序的
///情况,其中一个Web应用程序和Android应用程序具有不同的client_id但共享相同的项目。
///< / summary>
public string azp {get;组; }

///< summary>
///用户的电子邮件地址。这可能不是唯一的,不适合用作主键。仅在您的范围包含字符串email时提供。
///< / summary>
public string email {get;组; }

///< summary>
///发布ID令牌的时间,以Unix时间(整数秒)表示。
///< / summary>
public string iat {get;组; }

///< summary>
/// ID令牌到期的时间,以Unix时间(整数秒)表示。
///< / summary>
public string exp {get;组; }

///< summary>
///用户的全名,可显示的形式。可能在以下情况下提供:
///请求范围包含字符串profile
/// ID令牌从令牌刷新返回
///当名称声明存在时,你可以使用它们来更新你的应用的用户记录。请注意,此声明绝不会保证存在。
///< / summary>
公共字符串名称{get;组; }

///< summary>
///用户个人资料图片的网址。可能在以下情况下提供:
///请求范围包含字符串profile
/// ID令牌从令牌刷新返回
///当图片声明存在时,你可以使用它们来更新你的应用的用户记录。请注意,此声明绝不会保证存在。
///< / summary>
public string picture {get;组; }

公共字符串given_name {get;组; }

public string family_name {get;组; }

public string locale {get;组; }

public string alg {get;组; }

public string kid {get;组; }
}


I'm using ASP.NET Core to serve an API to an Android client. Android signs in as a Google account and passes a JWT, the ID Token, to API as a bearer token. I have the app working, it does pass the auth checks, but I don't think it's validating the token signature.

Per Google's documents, I can call this url to do it: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123, but I can't find the appropriate hooks on the server side to do it. Also according to the Google docs, I can somehow use the Client Access APIs to do it without calling to the server every time.

My configuration code:

app.UseJwtBearerAuthentication( new JwtBearerOptions()
{

    Authority = "https://accounts.google.com",
    Audience = "hiddenfromyou.apps.googleusercontent.com",
    TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateAudience = true,
        ValidIssuer = "accounts.google.com"
    },
    RequireHttpsMetadata = false,
    AutomaticAuthenticate = true,
    AutomaticChallenge = false,
});

How do I get the JWTBearer middleware to validate the signature? I'm close to giving up on using the MS middleware and rolling my own.

解决方案

There are a couple of different ways in which you can validate the integrity of the ID token on the server side:

  1. "Manually" - constantly download Google's public keys, verify signature and then each and every field, including the iss one; the main advantage (albeit a small one in my opinion) I see here is that you can minimize the number of requests sent to Google.
  2. "Automatically" - do a GET on Google's endpoint to verify this token https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
  3. Using a Google API Client Library - like the official one.

Here's how the second one could look:

private const string GoogleApiTokenInfoUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}";

public ProviderUserDetails GetUserDetails(string providerToken)
{
    var httpClient = new MonitoredHttpClient();
    var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken));

    HttpResponseMessage httpResponseMessage;
    try
    {
        httpResponseMessage = httpClient.GetAsync(requestUri).Result;
    }
    catch (Exception ex)
    {
        return null;
    }

    if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
    {
        return null;
    }

    var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
    var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response);

    if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
    {
        Log.WarnFormat("Google API Token Info aud field ({0}) not containing the required client id", googleApiTokenInfo.aud);
        return null;
    }

    return new ProviderUserDetails
    {
        Email = googleApiTokenInfo.email,
        FirstName = googleApiTokenInfo.given_name,
        LastName = googleApiTokenInfo.family_name,
        Locale = googleApiTokenInfo.locale,
        Name = googleApiTokenInfo.name,
        ProviderUserId = googleApiTokenInfo.sub
    };
}

GoogleApiTokenInfo class:

public class GoogleApiTokenInfo
{
/// <summary>
/// The Issuer Identifier for the Issuer of the response. Always https://accounts.google.com or accounts.google.com for Google ID tokens.
/// </summary>
public string iss { get; set; }

/// <summary>
/// Access token hash. Provides validation that the access token is tied to the identity token. If the ID token is issued with an access token in the server flow, this is always
/// included. This can be used as an alternate mechanism to protect against cross-site request forgery attacks, but if you follow Step 1 and Step 3 it is not necessary to verify the 
/// access token.
/// </summary>
public string at_hash { get; set; }

/// <summary>
/// Identifies the audience that this ID token is intended for. It must be one of the OAuth 2.0 client IDs of your application.
/// </summary>
public string aud { get; set; }

/// <summary>
/// An identifier for the user, unique among all Google accounts and never reused. A Google account can have multiple emails at different points in time, but the sub value is never
/// changed. Use sub within your application as the unique-identifier key for the user.
/// </summary>
public string sub { get; set; }

/// <summary>
/// True if the user's e-mail address has been verified; otherwise false.
/// </summary>
public string email_verified { get; set; }

/// <summary>
/// The client_id of the authorized presenter. This claim is only needed when the party requesting the ID token is not the same as the audience of the ID token. This may be the
/// case at Google for hybrid apps where a web application and Android app have a different client_id but share the same project.
/// </summary>
public string azp { get; set; }

/// <summary>
/// The user's email address. This may not be unique and is not suitable for use as a primary key. Provided only if your scope included the string "email".
/// </summary>
public string email { get; set; }

/// <summary>
/// The time the ID token was issued, represented in Unix time (integer seconds).
/// </summary>
public string iat { get; set; }

/// <summary>
/// The time the ID token expires, represented in Unix time (integer seconds).
/// </summary>
public string exp { get; set; }

/// <summary>
/// The user's full name, in a displayable form. Might be provided when:
/// The request scope included the string "profile"
/// The ID token is returned from a token refresh
/// When name claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present.
/// </summary>
public string name { get; set; }

/// <summary>
/// The URL of the user's profile picture. Might be provided when:
/// The request scope included the string "profile"
/// The ID token is returned from a token refresh
/// When picture claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present.
/// </summary>
public string picture { get; set; }

public string given_name { get; set; }

public string family_name { get; set; }

public string locale { get; set; }

public string alg { get; set; }

public string kid { get; set; }
}

这篇关于验证Google ID令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆