234 lines
9.5 KiB
Ruby
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
|