google-auth-library-ruby/lib/googleauth/id_tokens.rb

234 lines
9.5 KiB
Ruby

# frozen_string_literal: true
# Copyright 2020 Google LLC
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require "googleauth/id_tokens/errors"
require "googleauth/id_tokens/key_sources"
require "googleauth/id_tokens/verifier"
module Google
module Auth
##
# ## Verifying Google ID tokens
#
# This module verifies ID tokens issued by Google. This can be used to
# authenticate signed-in users using OpenID Connect. See
# https://developers.google.com/identity/sign-in/web/backend-auth for more
# information.
#
# ### Basic usage
#
# To verify an ID token issued by Google accounts:
#
# payload = Google::Auth::IDTokens.verify_oidc the_token,
# aud: "my-app-client-id"
#
# If verification succeeds, you will receive the token's payload as a hash.
# If verification fails, an exception (normally a subclass of
# {Google::Auth::IDTokens::VerificationError}) will be raised.
#
# To verify an ID token issued by the Google identity-aware proxy (IAP):
#
# payload = Google::Auth::IDTokens.verify_iap the_token,
# aud: "my-app-client-id"
#
# These methods will automatically download and cache the Google public
# keys necessary to verify these tokens. They will also automatically
# verify the issuer (`iss`) field for their respective types of ID tokens.
#
# ### Advanced usage
#
# If you want to provide your own public keys, either by pointing at a
# custom URI or by providing the key data directly, use the Verifier class
# and pass in a key source.
#
# To point to a custom URI that returns a JWK set:
#
# source = Google::Auth::IDTokens::JwkHttpKeySource.new "https://example.com/jwk"
# verifier = Google::Auth::IDTokens::Verifier.new key_source: source
# payload = verifier.verify the_token, aud: "my-app-client-id"
#
# To provide key data directly:
#
# jwk_data = {
# keys: [
# {
# alg: "ES256",
# crv: "P-256",
# kid: "LYyP2g",
# kty: "EC",
# use: "sig",
# x: "SlXFFkJ3JxMsXyXNrqzE3ozl_0913PmNbccLLWfeQFU",
# y: "GLSahrZfBErmMUcHP0MGaeVnJdBwquhrhQ8eP05NfCI"
# }
# ]
# }
# source = Google::Auth::IDTokens::StaticKeySource.from_jwk_set jwk_data
# verifier = Google::Auth::IDTokens::Verifier key_source: source
# payload = verifier.verify the_token, aud: "my-app-client-id"
#
module IDTokens
##
# A list of issuers expected for Google OIDC-issued tokens.
#
# @return [Array<String>]
#
OIDC_ISSUERS = ["accounts.google.com", "https://accounts.google.com"].freeze
##
# A list of issuers expected for Google IAP-issued tokens.
#
# @return [Array<String>]
#
IAP_ISSUERS = ["https://cloud.google.com/iap"].freeze
##
# The URL for Google OAuth2 V3 public certs
#
# @return [String]
#
OAUTH2_V3_CERTS_URL = "https://www.googleapis.com/oauth2/v3/certs"
##
# The URL for Google IAP public keys
#
# @return [String]
#
IAP_JWK_URL = "https://www.gstatic.com/iap/verify/public_key-jwk"
class << self
##
# The key source providing public keys that can be used to verify
# ID tokens issued by Google OIDC.
#
# @return [Google::Auth::IDTokens::JwkHttpKeySource]
#
def oidc_key_source
@oidc_key_source ||= JwkHttpKeySource.new OAUTH2_V3_CERTS_URL
end
##
# The key source providing public keys that can be used to verify
# ID tokens issued by Google IAP.
#
# @return [Google::Auth::IDTokens::JwkHttpKeySource]
#
def iap_key_source
@iap_key_source ||= JwkHttpKeySource.new IAP_JWK_URL
end
##
# Reset all convenience key sources. Used for testing.
# @private
#
def forget_sources!
@oidc_key_source = @iap_key_source = nil
self
end
##
# A convenience method that verifies a token allegedly issued by Google
# OIDC.
#
# @param token [String] The ID token to verify
# @param aud [String,Array<String>,nil] The expected audience. At least
# one `aud` field in the token must match at least one of the
# provided audiences, or the verification will fail with
# {Google::Auth::IDToken::AudienceMismatchError}. If `nil` (the
# default), no audience checking is performed.
# @param azp [String,Array<String>,nil] The expected authorized party
# (azp). At least one `azp` field in the token must match at least
# one of the provided values, or the verification will fail with
# {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
# (the default), no azp checking is performed.
# @param aud [String,Array<String>,nil] The expected audience. At least
# one `iss` field in the token must match at least one of the
# provided issuers, or the verification will fail with
# {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
# checking is performed. Default is to check against {OIDC_ISSUERS}.
#
# @return [Hash] The decoded token payload.
# @raise [KeySourceError] if the key source failed to obtain public keys
# @raise [VerificationError] if the token verification failed.
# Additional data may be available in the error subclass and message.
#
def verify_oidc token,
aud: nil,
azp: nil,
iss: OIDC_ISSUERS
verifier = Verifier.new key_source: oidc_key_source,
aud: aud,
azp: azp,
iss: iss
verifier.verify token
end
##
# A convenience method that verifies a token allegedly issued by Google
# IAP.
#
# @param token [String] The ID token to verify
# @param aud [String,Array<String>,nil] The expected audience. At least
# one `aud` field in the token must match at least one of the
# provided audiences, or the verification will fail with
# {Google::Auth::IDToken::AudienceMismatchError}. If `nil` (the
# default), no audience checking is performed.
# @param azp [String,Array<String>,nil] The expected authorized party
# (azp). At least one `azp` field in the token must match at least
# one of the provided values, or the verification will fail with
# {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
# (the default), no azp checking is performed.
# @param aud [String,Array<String>,nil] The expected audience. At least
# one `iss` field in the token must match at least one of the
# provided issuers, or the verification will fail with
# {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
# checking is performed. Default is to check against {IAP_ISSUERS}.
#
# @return [Hash] The decoded token payload.
# @raise [KeySourceError] if the key source failed to obtain public keys
# @raise [VerificationError] if the token verification failed.
# Additional data may be available in the error subclass and message.
#
def verify_iap token,
aud: nil,
azp: nil,
iss: IAP_ISSUERS
verifier = Verifier.new key_source: iap_key_source,
aud: aud,
azp: azp,
iss: iss
verifier.verify token
end
end
end
end
end