web-dev-qa-db-ja.com

Google IDトークンを検証する

ASP.NET Coreを使用してAPIをAndroid client。Android= Googleアカウントとしてサインインし、JWT、IDトークンを渡します、APIをベアラートークンとして使用します。アプリは機能していますが、認証チェックに合格していますが、トークンの署名を検証しているとは思いません。

Googleのドキュメントに従って、次のURLを呼び出してそれを行うことができます: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ12 が適切なフックを見つけることができませんそれを行うサーバー側。また、Googleのドキュメントによると、クライアントアクセスAPIを使用して、毎回サーバーを呼び出すことなくそれを行うことができます。

私の構成コード:

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,
});

JWTBearerミドルウェアで署名を検証するにはどうすればよいですか?私は、MSミドルウェアの使用をあきらめて、自分でミドルウェアを導入するのを間近に控えています。

16
Darthg8r

サーバー側でIDトークンの整合性を検証できるいくつかの異なる ways があります。

  1. 「手動」-常にGoogleの公開鍵をダウンロードし、署名を検証してから、issを含むすべてのフィールドを検証します。主な利点(私の意見では小さなものですが)は、Googleに送信されるリクエストの数を最小限にできることです。
  2. 「自動的に」-GoogleのエンドポイントでGETを実行して、このトークンを検証しますhttps://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
  3. official one のように、Google APIクライアントライブラリを使用します。

2つ目は次のようになります。

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クラス:

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; }
}
30

このgithub issue によると、これで GoogleJsonWebSignature.ValidateAsync Google署名のJWTを検証するメソッド。 idToken文字列をメソッドに渡すだけです。

var validPayload = await GoogleJsonWebSignature.ValidateAsync(idToken);
Assert.IsNotNull(validPayload);

有効なトークンでない場合は、nullを返します。

この方法を使用するには、まず Google.Apis.Auth nugetをインストールする必要があります。

18
edmundpie

Googleは openId connect のドキュメントで述べています

デバッグの目的で、Googleのtokeninfoエンドポイントを使用できます。 IDトークンの値がXYZ123であるとします。

そのエンドポイントを使用してJWTを検証しないでください。

IDトークンの検証には、いくつかの手順が必要です。

  1. IDトークンが発行者によって適切に署名されていることを確認します。 Googleが発行したトークンは、 discovery document のjwks_uriフィールドで指定されたURIにある証明書の1つを使用して署名されます。
  2. IDトークンのissの値が https://accounts.google.com またはaccounts.google.comに等しいことを確認します。
  3. IDトークンのaudの値がアプリのクライアントIDと等しいことを確認します。
  4. IDトークンの有効期限(exp)が経過していないことを確認します。
  5. リクエストでhdパラメータを渡した場合は、IDトークンにG Suiteホストドメインと一致するhdクレームがあることを確認してください。

それらを検証する方法についての公式のサンプルプロジェクト here があります。残念ながら、これをまだGoogle .Netクライアントライブラリに追加していません。 issue として記録されています

3
DaImTo

だから、私が見つけたのは、OpenIDConnectの仕様には、トークンの検証に必要な情報を含む/.well-known/のURLがあるためです。これには、署名の公開鍵へのアクセスが含まれます。 JWTミドルウェアは、オーソリティからの.well-known urlを形成し、情報を取得して、それ自体でそれを検証します。

質問に対する簡単な答えは、検証はミドルウェアですでに行われていることであり、何もすることはありません。

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

       Public ProviderUserDetails ValidateGoogleToken(string providerToken)        
       {

        var httpClient = new HttpClient();

        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);

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