googleauth 1.14.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Credentials.md +106 -0
- data/Errors.md +152 -0
- data/lib/googleauth/api_key.rb +9 -0
- data/lib/googleauth/application_default.rb +3 -1
- data/lib/googleauth/base_client.rb +5 -0
- data/lib/googleauth/bearer_token.rb +16 -2
- data/lib/googleauth/client_id.rb +9 -5
- data/lib/googleauth/compute_engine.rb +64 -18
- data/lib/googleauth/credentials.rb +23 -6
- data/lib/googleauth/credentials_loader.rb +9 -4
- data/lib/googleauth/default_credentials.rb +16 -4
- data/lib/googleauth/errors.rb +117 -0
- data/lib/googleauth/external_account/aws_credentials.rb +85 -18
- data/lib/googleauth/external_account/base_credentials.rb +31 -2
- data/lib/googleauth/external_account/external_account_utils.rb +15 -4
- data/lib/googleauth/external_account/identity_pool_credentials.rb +40 -15
- data/lib/googleauth/external_account/pluggable_credentials.rb +34 -19
- data/lib/googleauth/external_account.rb +30 -6
- data/lib/googleauth/iam.rb +19 -3
- data/lib/googleauth/id_tokens/errors.rb +13 -7
- data/lib/googleauth/id_tokens/key_sources.rb +13 -7
- data/lib/googleauth/id_tokens/verifier.rb +2 -3
- data/lib/googleauth/id_tokens.rb +4 -4
- data/lib/googleauth/impersonated_service_account.rb +64 -17
- data/lib/googleauth/json_key_reader.rb +11 -2
- data/lib/googleauth/oauth2/sts_client.rb +9 -4
- data/lib/googleauth/scope_util.rb +1 -1
- data/lib/googleauth/service_account.rb +12 -1
- data/lib/googleauth/service_account_jwt_header.rb +9 -2
- data/lib/googleauth/signet.rb +24 -6
- data/lib/googleauth/user_authorizer.rb +35 -7
- data/lib/googleauth/user_refresh.rb +25 -7
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +46 -9
- data/lib/googleauth.rb +1 -0
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34a8ff0141f7866d8f253bea4adbdd406c8aab873c7f18be6ac78ee981e16a39
|
4
|
+
data.tar.gz: 1191ce29114f13eb0d855ef46502015eb309cf694bd053171614b15d571e15f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9286311e4de659c8820cdbdfb4ae48f1a3b0c519c66208a3c3f56a3a1079597b10306d6ab8aef05687df347262f26c75af63c4cbeac7570162829d926edf0317
|
7
|
+
data.tar.gz: 43954778b73cf95537a29a2317abe5b22eebfc3336e560657ec580542df9a7e9f42513968d27fc2b13621b0da0aa8fb8510ef59c9170bad611b0bd5b86f16888
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 1.15.0 (2025-08-25)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* add typed errors to authentication library ([#533](https://github.com/googleapis/google-auth-library-ruby/issues/533))
|
8
|
+
* Support for JWT 3.x ([#542](https://github.com/googleapis/google-auth-library-ruby/issues/542))
|
9
|
+
#### Bug Fixes
|
10
|
+
|
11
|
+
* fix incorrect error and apply some code complexity refactoring ([#529](https://github.com/googleapis/google-auth-library-ruby/issues/529))
|
12
|
+
* support Pathname for cred loading ([#537](https://github.com/googleapis/google-auth-library-ruby/issues/537))
|
13
|
+
#### Documentation
|
14
|
+
|
15
|
+
* add summary documentation on credentials types and improve YARD comments
|
16
|
+
* add summary documentation on credentials types and improve YARD comments ([#530](https://github.com/googleapis/google-auth-library-ruby/issues/530))
|
17
|
+
|
3
18
|
### 1.14.0 (2025-03-14)
|
4
19
|
|
5
20
|
#### Features
|
data/Credentials.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
The closest thing to a base credentials class is the `BaseClient` module.
|
4
|
+
It includes functionality common to most credentials, such as applying authentication tokens to request headers, managing token expiration and refresh, handling logging, and providing updater procs for API clients.
|
5
|
+
|
6
|
+
Many credentials classes inherit from `Signet::OAuth2::Client` (`lib/googleauth/signet.rb`) class which provides OAuth-based authentication.
|
7
|
+
The `Signet::OAuth2::Client` includes the `BaseClient` functionality.
|
8
|
+
|
9
|
+
Most credential types either inherit from `Signet::OAuth2::Client` or include the `BaseClient` module directly.
|
10
|
+
|
11
|
+
Notably, `Google::Auth::Credentials` (`lib/googleauth/credentials.rb`) is not a base type or a credentials type per se. It is a wrapper for other credential classes
|
12
|
+
that exposes common initialization functionality, such as creating credentials from environment variables, default paths, or application defaults. It is used and subclassed by Google's API client libraries.
|
13
|
+
|
14
|
+
# List of credentials types
|
15
|
+
|
16
|
+
## Simple Authentication (non-OAuth)
|
17
|
+
|
18
|
+
**Google::Auth::APIKeyCredentials** - `lib/googleauth/api_key.rb`
|
19
|
+
- Includes `Google::Auth::BaseClient` module
|
20
|
+
- Implements Google API Key authentication
|
21
|
+
- API Keys are text strings that don't have an associated JSON file
|
22
|
+
- API Keys provide project information but don't reference an IAM principal
|
23
|
+
- They do not expire and cannot be refreshed
|
24
|
+
- Can be loaded from the `GOOGLE_API_KEY` environment variable
|
25
|
+
|
26
|
+
2. **Google::Auth::BearerTokenCredentials** - `lib/googleauth/bearer_token.rb`
|
27
|
+
- Includes `Google::Auth::BaseClient` module
|
28
|
+
- Implements Bearer Token authentication
|
29
|
+
- Bearer tokens are strings representing an authorization grant
|
30
|
+
- Can be OAuth2 tokens, JWTs, ID tokens, or any token sent as a `Bearer` in an `Authorization` header
|
31
|
+
- Used when the end-user is managing the token separately (e.g., with another service)
|
32
|
+
- Token lifetime tracking and refresh are outside this class's scope
|
33
|
+
- No JSON representation for this type of credentials
|
34
|
+
|
35
|
+
## GCP-Specialized authentication
|
36
|
+
|
37
|
+
3. **Google::Auth::GCECredentials < Signet::OAuth2::Client** - `lib/googleauth/compute_engine.rb`
|
38
|
+
- For obtaining authentication tokens from GCE metadata server
|
39
|
+
- Used automatically when code is running on Google Compute Engine
|
40
|
+
- Fetches tokens from the metadata server with no additional configuration needed
|
41
|
+
|
42
|
+
4. **Google::Auth::IAMCredentials < Signet::OAuth2::Client** - `lib/googleauth/iam.rb`
|
43
|
+
- For IAM-based authentication (e.g. service-to-service)
|
44
|
+
- Implements authentication-as-a-service for systems already authenticated
|
45
|
+
- Exchanges existing credentials for a short-lived access token
|
46
|
+
|
47
|
+
## Service Account Authentication
|
48
|
+
|
49
|
+
5. **Google::Auth::ServiceAccountCredentials < Signet::OAuth2::Client** - `lib/googleauth/service_account.rb`
|
50
|
+
- Authenticates requests using Service Account credentials via an OAuth access token
|
51
|
+
- Created from JSON key file downloaded from Google Cloud Console
|
52
|
+
- Supports both OAuth access tokens and self-signed JWT authentication
|
53
|
+
- Can specify scopes for access token requests
|
54
|
+
|
55
|
+
6. **Google::Auth::ServiceAccountJwtHeaderCredentials** - `lib/googleauth/service_account_jwt_header.rb`
|
56
|
+
- Authenticates using Service Account credentials with JWT headers
|
57
|
+
- Typically used via `ServiceAccountCredentials` and not by itself
|
58
|
+
- Creates JWT directly for making authenticated calls
|
59
|
+
- Does not require a round trip to the authorization server
|
60
|
+
- Doesn't support OAuth scopes - uses audience (target API) instead
|
61
|
+
|
62
|
+
7. **Google::Auth::ImpersonatedServiceAccountCredentials < Signet::OAuth2::Client** - `lib/googleauth/impersonated_service_account.rb`
|
63
|
+
- For service account impersonation
|
64
|
+
- Allows a GCP principal identified by a set of source credentials to impersonate a service account
|
65
|
+
- Useful for delegation of authority and managing permissions across service accounts
|
66
|
+
- Source credentials must have the Service Account Token Creator role on the target
|
67
|
+
|
68
|
+
## User Authentication
|
69
|
+
|
70
|
+
8. **Google::Auth::UserRefreshCredentials < Signet::OAuth2::Client** - `lib/googleauth/user_refresh.rb`
|
71
|
+
- For user refresh token authentication (from 3-legged OAuth flow)
|
72
|
+
- Authenticates on behalf of a user who has authorized the application
|
73
|
+
- Handles token refresh when original access token expires
|
74
|
+
- Typically obtained through web or installed application flow
|
75
|
+
|
76
|
+
`Google::Auth::UserAuthorizer` (`lib/googleauth/user_authorizer.rb`) and `Google::Auth::WebUserAuthorizer` (`lib/googleauth/web_user_authorizer.rb`)
|
77
|
+
are used to facilitate user authentication. The `UserAuthorizer` handles interactive 3-Legged-OAuth2 (3LO) user consent authorization for command-line applications.
|
78
|
+
The `WebUserAuthorizer` is a variation of UserAuthorizer adapted for Rack-based web applications that manages OAuth state and provides callback handling.
|
79
|
+
|
80
|
+
## External Account Authentication
|
81
|
+
`Google::Auth::ExternalAccount::Credentials` (`lib/googleauth/external_account.rb`) is not a credentials type, it is a module
|
82
|
+
that procides an entry point for External Account credentials. It also serves as a factory that creates appropriate credential
|
83
|
+
types based on credential source (similar to `Google::Auth::get_application_default`).
|
84
|
+
It is included in all External Account credentials types, and it itself includes `Google::Auth::BaseClient` module so all External
|
85
|
+
Account credentials types include `Google::Auth::BaseClient`.
|
86
|
+
|
87
|
+
9. **Google::Auth::ExternalAccount::AwsCredentials** - `lib/googleauth/external_account/aws_credentials.rb`
|
88
|
+
- Includes `Google::Auth::BaseClient` module
|
89
|
+
- Includes `ExternalAccount::BaseCredentials` module
|
90
|
+
- Uses AWS credentials to authenticate to Google Cloud
|
91
|
+
- Exchanges temporary AWS credentials for Google access tokens
|
92
|
+
- Used for workloads running on AWS that need to access Google Cloud
|
93
|
+
|
94
|
+
10. **Google::Auth::ExternalAccount::IdentityPoolCredentials** - `lib/googleauth/external_account/identity_pool_credentials.rb`
|
95
|
+
- Includes `Google::Auth::BaseClient` module
|
96
|
+
- Includes `ExternalAccount::BaseCredentials` module
|
97
|
+
- Authenticates using external identity pool
|
98
|
+
- Exchanges external identity tokens for Google access tokens
|
99
|
+
- Supports file-based and URL-based credential sources
|
100
|
+
|
101
|
+
11. **Google::Auth::ExternalAccount::PluggableCredentials** - `lib/googleauth/external_account/pluggable_credentials.rb`
|
102
|
+
- Includes `Google::Auth::BaseClient` module
|
103
|
+
- Includes `ExternalAccount::BaseCredentials` module
|
104
|
+
- Supports executable-based credential sources
|
105
|
+
- Executes external programs to retrieve credentials
|
106
|
+
- Allows for custom authentication mechanisms via external executables
|
data/Errors.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# Error Handling in Google Auth Library for Ruby
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
The Google Auth Library for Ruby provides a structured approach to error handling. This document explains the error hierarchy, how to access detailed error information, and provides examples of handling errors effectively.
|
6
|
+
|
7
|
+
## Error Hierarchy
|
8
|
+
|
9
|
+
The Google Auth Library has two main error hierarchies: the core authentication errors and the specialized ID token flow errors.
|
10
|
+
|
11
|
+
### Core Authentication Errors
|
12
|
+
|
13
|
+
These errors are used throughout the main library for general authentication and credential operations:
|
14
|
+
|
15
|
+
```
|
16
|
+
Google::Auth::Error (module)
|
17
|
+
├── Google::Auth::InitializationError (class)
|
18
|
+
└── Google::Auth::DetailedError (module)
|
19
|
+
├── Google::Auth::CredentialsError (class)
|
20
|
+
├── Google::Auth::AuthorizationError (class)
|
21
|
+
├── Google::Auth::UnexpectedStatusError (class)
|
22
|
+
└── Google::Auth::ParseError (class)
|
23
|
+
```
|
24
|
+
|
25
|
+
### ID Token Errors
|
26
|
+
|
27
|
+
These specialized errors are used specifically for ID token flow. They also include the `Google::Auth::Error` module, allowing them to be caught with the same error handling as the core authentication errors:
|
28
|
+
|
29
|
+
```
|
30
|
+
Google::Auth::Error (module)
|
31
|
+
├── Google::Auth::IDTokens::KeySourceError (class)
|
32
|
+
└── Google::Auth::IDTokens::VerificationError (class)
|
33
|
+
├── ExpiredTokenError (class)
|
34
|
+
├── SignatureError (class)
|
35
|
+
├── IssuerMismatchError (class)
|
36
|
+
├── AudienceMismatchError (class)
|
37
|
+
└── AuthorizedPartyMismatchError (class)
|
38
|
+
```
|
39
|
+
|
40
|
+
### Error Module Types
|
41
|
+
|
42
|
+
- **`Google::Auth::Error`**: Base module that all Google Auth errors include. Use this to catch any error from the library.
|
43
|
+
|
44
|
+
- **`Google::Auth::DetailedError`**: Extends `Error` to include detailed information about the credential that caused the error, including the credential type and principal.
|
45
|
+
|
46
|
+
## Core Authentication Error Classes
|
47
|
+
|
48
|
+
- **`InitializationError`**: Raised during credential initialization when required parameters are missing or invalid.
|
49
|
+
|
50
|
+
- **`CredentialsError`**: Generic error raised during authentication flows.
|
51
|
+
|
52
|
+
- **`AuthorizationError`**: Raised when a remote server refuses to authorize the client. Inherits from `Signet::AuthorizationError`. Is being raised where `Signet::AuthorizationError` was raised previously.
|
53
|
+
|
54
|
+
- **`UnexpectedStatusError`**: Raised when a server returns an unexpected HTTP status code. Inherits from `Signet::UnexpectedStatusError`. Is being raised where `Signet::UnexpectedStatusError` was raised previously.
|
55
|
+
|
56
|
+
- **`ParseError`**: Raised when the client fails to parse a value from a response. Inherits from `Signet::ParseError`. Is being raised where `Signet::ParseError` was raised previously.
|
57
|
+
|
58
|
+
## Detailed Error Information
|
59
|
+
|
60
|
+
Errors that include the `DetailedError` module provide additional context about what went wrong:
|
61
|
+
|
62
|
+
- **`credential_type_name`**: The class name of the credential that raised the error (e.g., `"Google::Auth::ServiceAccountCredentials"`)
|
63
|
+
|
64
|
+
- **`principal`**: The identity associated with the credentials (e.g., an email address for service accounts, `:api_key` for API key credentials)
|
65
|
+
|
66
|
+
### Example: Catching and Handling Core Errors
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
begin
|
70
|
+
credentials = Google::Auth::ServiceAccountCredentials.make_creds(
|
71
|
+
json_key_io: File.open("your-key.json")
|
72
|
+
)
|
73
|
+
# Use credentials...
|
74
|
+
rescue Google::Auth::InitializationError => e
|
75
|
+
puts "Failed to initialize credentials: #{e.message}"
|
76
|
+
# e.g., Missing required fields in the service account key file
|
77
|
+
rescue Google::Auth::DetailedError => e
|
78
|
+
puts "Authorization failed: #{e.message}"
|
79
|
+
puts "Credential type: #{e.credential_type_name}"
|
80
|
+
puts "Principal: #{e.principal}"
|
81
|
+
# e.g., Invalid or revoked service account
|
82
|
+
rescue Google::Auth::Error => e
|
83
|
+
puts "Unknown Google Auth error: #{e.message}"
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Backwards compatibility
|
88
|
+
|
89
|
+
Some classes in the Google Auth Library raise standard Ruby `ArgumentError` and `TypeError`. These errors are preserved for backward compatibility, however the new code will raise `Google::Auth::InitializationError` instead.
|
90
|
+
|
91
|
+
## ID Token Verification
|
92
|
+
|
93
|
+
The Google Auth Library includes functionality for verifying ID tokens through the `Google::Auth::IDTokens` namespace. These operations have their own specialized error classes that also include the `Google::Auth::Error` module, allowing them to be caught with the same error handling as other errors in the library.
|
94
|
+
|
95
|
+
### ID Token Error Classes
|
96
|
+
|
97
|
+
- **`KeySourceError`**: Raised when the library fails to obtain the keys needed to verify a token, typically from a JWKS (JSON Web Key Set) endpoint.
|
98
|
+
|
99
|
+
- **`VerificationError`**: Base class for all errors related to token verification failures.
|
100
|
+
|
101
|
+
- **`ExpiredTokenError`**: Raised when a token has expired according to its expiration time claim (`exp`).
|
102
|
+
|
103
|
+
- **`SignatureError`**: Raised when a token's signature cannot be verified, indicating it might be tampered with or corrupted.
|
104
|
+
|
105
|
+
- **`IssuerMismatchError`**: Raised when a token's issuer (`iss` claim) doesn't match the expected issuer.
|
106
|
+
|
107
|
+
- **`AudienceMismatchError`**: Raised when a token's audience (`aud` claim) doesn't match the expected audience.
|
108
|
+
|
109
|
+
- **`AuthorizedPartyMismatchError`**: Raised when a token's authorized party (`azp` claim) doesn't match the expected client ID.
|
110
|
+
|
111
|
+
### Example: Handling ID Token Verification Errors
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
require "googleauth/id_tokens"
|
115
|
+
|
116
|
+
begin
|
117
|
+
# Verify the provided ID token
|
118
|
+
payload = Google::Auth::IDTokens.verify_oidc(
|
119
|
+
id_token,
|
120
|
+
audience: "expected-audience-12345.apps.googleusercontent.com"
|
121
|
+
)
|
122
|
+
|
123
|
+
# Use the verified token payload
|
124
|
+
user_email = payload["email"]
|
125
|
+
|
126
|
+
rescue Google::Auth::IDTokens::ExpiredTokenError => e
|
127
|
+
puts "The token has expired. Please obtain a new one."
|
128
|
+
|
129
|
+
rescue Google::Auth::IDTokens::SignatureError => e
|
130
|
+
puts "Invalid token signature."
|
131
|
+
|
132
|
+
rescue Google::Auth::IDTokens::IssuerMismatchError => e
|
133
|
+
puts "Invalid token issuer."
|
134
|
+
|
135
|
+
rescue Google::Auth::IDTokens::AudienceMismatchError => e
|
136
|
+
puts "This token is not intended for this application (invalid audience)."
|
137
|
+
|
138
|
+
rescue Google::Auth::IDTokens::AuthorizedPartyMismatchError => e
|
139
|
+
puts "Invalid token authorized party."
|
140
|
+
|
141
|
+
rescue Google::Auth::IDTokens::VerificationError => e
|
142
|
+
puts "Token verification failed: #{e.message}"
|
143
|
+
# Generic verification error handling
|
144
|
+
|
145
|
+
rescue Google::Auth::IDTokens::KeySourceError => e
|
146
|
+
puts "Unable to retrieve verification keys: #{e.message}"
|
147
|
+
|
148
|
+
rescue Google::Auth::Error => e
|
149
|
+
puts "Unknown Google Auth error: #{e.message}"
|
150
|
+
# This will catch any Google Auth error
|
151
|
+
end
|
152
|
+
```
|
data/lib/googleauth/api_key.rb
CHANGED
@@ -85,6 +85,7 @@ module Google
|
|
85
85
|
# @option options [String] :universe_domain
|
86
86
|
# The universe domain of the universe this API key
|
87
87
|
# belongs to (defaults to googleapis.com)
|
88
|
+
# @raise [ArgumentError] If the API key is nil or empty
|
88
89
|
def initialize options = {}
|
89
90
|
raise ArgumentError, "API key must be provided" if options[:api_key].nil? || options[:api_key].empty?
|
90
91
|
@api_key = options[:api_key]
|
@@ -139,6 +140,14 @@ module Google
|
|
139
140
|
a_hash
|
140
141
|
end
|
141
142
|
|
143
|
+
# For credentials that are initialized with a token without a principal,
|
144
|
+
# the type of that token should be returned as a principal instead
|
145
|
+
# @private
|
146
|
+
# @return [Symbol] the token type in lieu of the principal
|
147
|
+
def principal
|
148
|
+
token_type
|
149
|
+
end
|
150
|
+
|
142
151
|
protected
|
143
152
|
|
144
153
|
# The token type should be :api_key
|
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
require "googleauth/compute_engine"
|
16
16
|
require "googleauth/default_credentials"
|
17
|
+
require "googleauth/errors"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -50,12 +51,13 @@ module Google
|
|
50
51
|
# connection to use for token refresh requests.
|
51
52
|
# * `:connection` The connection to use to determine whether GCE
|
52
53
|
# metadata credentials are available.
|
54
|
+
# @raise [Google::Auth::InitializationError] If the credentials cannot be found
|
53
55
|
def get_application_default scope = nil, options = {}
|
54
56
|
creds = DefaultCredentials.from_env(scope, options) ||
|
55
57
|
DefaultCredentials.from_well_known_path(scope, options) ||
|
56
58
|
DefaultCredentials.from_system_default_path(scope, options)
|
57
59
|
return creds unless creds.nil?
|
58
|
-
raise NOT_FOUND_ERROR unless GCECredentials.on_gce? options
|
60
|
+
raise InitializationError, NOT_FOUND_ERROR unless GCECredentials.on_gce? options
|
59
61
|
GCECredentials.new options.merge(scope: scope)
|
60
62
|
end
|
61
63
|
end
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require "googleauth/base_client"
|
16
|
+
require "googleauth/errors"
|
16
17
|
|
17
18
|
module Google
|
18
19
|
module Auth
|
@@ -80,6 +81,7 @@ module Google
|
|
80
81
|
# If `expires_at` is `nil`, it is treated as "token never expires".
|
81
82
|
# @option options [String] :universe_domain The universe domain of the universe
|
82
83
|
# this token is for (defaults to googleapis.com)
|
84
|
+
# @raise [ArgumentError] If the bearer token is nil or empty
|
83
85
|
def initialize options = {}
|
84
86
|
raise ArgumentError, "Bearer token must be provided" if options[:token].nil? || options[:token].empty?
|
85
87
|
@token = options[:token]
|
@@ -119,6 +121,14 @@ module Google
|
|
119
121
|
)
|
120
122
|
end
|
121
123
|
|
124
|
+
# For credentials that are initialized with a token without a principal,
|
125
|
+
# the type of that token should be returned as a principal instead
|
126
|
+
# @private
|
127
|
+
# @return [Symbol] the token type in lieu of the principal
|
128
|
+
def principal
|
129
|
+
token_type
|
130
|
+
end
|
131
|
+
|
122
132
|
protected
|
123
133
|
|
124
134
|
##
|
@@ -129,10 +139,14 @@ module Google
|
|
129
139
|
#
|
130
140
|
# @param [Hash] _options Options for fetching a new token (not used).
|
131
141
|
# @return [nil] Always returns nil.
|
132
|
-
# @raise [
|
142
|
+
# @raise [Google::Auth::CredentialsError] If the token is expired.
|
133
143
|
def fetch_access_token! _options = {}
|
134
144
|
if @expires_at && Time.now >= @expires_at
|
135
|
-
raise
|
145
|
+
raise CredentialsError.with_details(
|
146
|
+
"Bearer token has expired.",
|
147
|
+
credential_type_name: self.class.name,
|
148
|
+
principal: principal
|
149
|
+
)
|
136
150
|
end
|
137
151
|
|
138
152
|
nil
|
data/lib/googleauth/client_id.rb
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
require "multi_json"
|
16
16
|
require "googleauth/credentials_loader"
|
17
|
+
require "googleauth/errors"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
module Auth
|
@@ -62,10 +63,11 @@ module Google
|
|
62
63
|
# @note Direct instantiation is discouraged to avoid embedding IDs
|
63
64
|
# and secrets in source. See {#from_file} to load from
|
64
65
|
# `client_secrets.json` files.
|
66
|
+
# @raise [Google::Auth::InitializationError] If id or secret is nil
|
65
67
|
#
|
66
68
|
def initialize id, secret
|
67
|
-
raise "Client id can not be nil" if id.nil?
|
68
|
-
raise "Client secret can not be nil" if secret.nil?
|
69
|
+
raise InitializationError, "Client id can not be nil" if id.nil?
|
70
|
+
raise InitializationError, "Client secret can not be nil" if secret.nil?
|
69
71
|
@id = id
|
70
72
|
@secret = secret
|
71
73
|
end
|
@@ -77,9 +79,10 @@ module Google
|
|
77
79
|
# @param [String, File] file
|
78
80
|
# Path of file to read from
|
79
81
|
# @return [Google::Auth::ClientID]
|
82
|
+
# @raise [Google::Auth::InitializationError] If file is nil
|
80
83
|
#
|
81
84
|
def self.from_file file
|
82
|
-
raise "File can not be nil." if file.nil?
|
85
|
+
raise InitializationError, "File can not be nil." if file.nil?
|
83
86
|
File.open file.to_s do |f|
|
84
87
|
json = f.read
|
85
88
|
config = MultiJson.load json
|
@@ -94,11 +97,12 @@ module Google
|
|
94
97
|
# @param [hash] config
|
95
98
|
# Parsed contents of the JSON file
|
96
99
|
# @return [Google::Auth::ClientID]
|
100
|
+
# @raise [Google::Auth::InitializationError] If config is nil or missing required elements
|
97
101
|
#
|
98
102
|
def self.from_hash config
|
99
|
-
raise "Hash can not be nil." if config.nil?
|
103
|
+
raise InitializationError, "Hash can not be nil." if config.nil?
|
100
104
|
raw_detail = config[INSTALLED_APP] || config[WEB_APP]
|
101
|
-
raise MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
|
105
|
+
raise InitializationError, MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
|
102
106
|
ClientId.new raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET]
|
103
107
|
end
|
104
108
|
end
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require "google-cloud-env"
|
16
|
+
require "googleauth/errors"
|
16
17
|
require "googleauth/signet"
|
17
18
|
|
18
19
|
module Google
|
@@ -126,31 +127,25 @@ module Google
|
|
126
127
|
|
127
128
|
# Overrides the super class method to change how access tokens are
|
128
129
|
# fetched.
|
130
|
+
#
|
131
|
+
# @param [Hash] _options Options for token fetch (not used)
|
132
|
+
# @return [Hash] The token data hash
|
133
|
+
# @raise [Google::Auth::UnexpectedStatusError] On unexpected HTTP status codes
|
134
|
+
# @raise [Google::Auth::AuthorizationError] If metadata server is unavailable or returns error
|
129
135
|
def fetch_access_token _options = {}
|
130
|
-
query, entry =
|
131
|
-
if token_type == :id_token
|
132
|
-
[{ "audience" => target_audience, "format" => "full" }, "service-accounts/default/identity"]
|
133
|
-
else
|
134
|
-
[{}, "service-accounts/default/token"]
|
135
|
-
end
|
136
|
-
query[:scopes] = Array(scope).join "," if scope
|
136
|
+
query, entry = build_metadata_request_params
|
137
137
|
begin
|
138
138
|
log_fetch_query
|
139
139
|
resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
|
140
140
|
log_fetch_resp resp
|
141
|
-
|
142
|
-
when 200
|
143
|
-
build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
|
144
|
-
when 403, 500
|
145
|
-
raise Signet::UnexpectedStatusError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
146
|
-
when 404
|
147
|
-
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
148
|
-
else
|
149
|
-
raise Signet::AuthorizationError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
150
|
-
end
|
141
|
+
handle_metadata_response resp
|
151
142
|
rescue Google::Cloud::Env::MetadataServerNotResponding => e
|
152
143
|
log_fetch_err e
|
153
|
-
raise
|
144
|
+
raise AuthorizationError.with_details(
|
145
|
+
e.message,
|
146
|
+
credential_type_name: self.class.name,
|
147
|
+
principal: principal
|
148
|
+
)
|
154
149
|
end
|
155
150
|
end
|
156
151
|
|
@@ -175,8 +170,47 @@ module Google
|
|
175
170
|
self
|
176
171
|
end
|
177
172
|
|
173
|
+
# Returns the principal identifier for GCE credentials
|
174
|
+
# @private
|
175
|
+
# @return [Symbol] :gce to represent Google Compute Engine identity
|
176
|
+
def principal
|
177
|
+
:gce_metadata
|
178
|
+
end
|
179
|
+
|
178
180
|
private
|
179
181
|
|
182
|
+
# @private
|
183
|
+
# Builds query parameters and endpoint for metadata request
|
184
|
+
# @return [Array] The query parameters and endpoint path
|
185
|
+
def build_metadata_request_params
|
186
|
+
query, entry =
|
187
|
+
if token_type == :id_token
|
188
|
+
[{ "audience" => target_audience, "format" => "full" }, "service-accounts/default/identity"]
|
189
|
+
else
|
190
|
+
[{}, "service-accounts/default/token"]
|
191
|
+
end
|
192
|
+
query[:scopes] = Array(scope).join "," if scope
|
193
|
+
[query, entry]
|
194
|
+
end
|
195
|
+
|
196
|
+
# @private
|
197
|
+
# Handles the response from the metadata server
|
198
|
+
# @param [Google::Cloud::Env::MetadataResponse] resp The metadata server response
|
199
|
+
# @return [Hash] The token hash on success
|
200
|
+
# @raise [Google::Auth::UnexpectedStatusError, Google::Auth::AuthorizationError] On error
|
201
|
+
def handle_metadata_response resp
|
202
|
+
case resp.status
|
203
|
+
when 200
|
204
|
+
build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
|
205
|
+
when 403, 500
|
206
|
+
raise Signet::UnexpectedStatusError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
207
|
+
when 404
|
208
|
+
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
209
|
+
else
|
210
|
+
raise Signet::AuthorizationError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
180
214
|
def log_fetch_query
|
181
215
|
if token_type == :id_token
|
182
216
|
logger&.info do
|
@@ -213,6 +247,18 @@ module Google
|
|
213
247
|
end
|
214
248
|
end
|
215
249
|
|
250
|
+
# Constructs a token hash from the metadata server response
|
251
|
+
#
|
252
|
+
# @private
|
253
|
+
# @param [String] body The response body from the metadata server
|
254
|
+
# @param [String] content_type The content type of the response
|
255
|
+
# @param [Float] retrieval_time The monotonic time when the response was retrieved
|
256
|
+
#
|
257
|
+
# @return [Hash] A hash containing:
|
258
|
+
# - access_token/id_token: The actual token depending on what was requested
|
259
|
+
# - token_type: The type of token (usually "Bearer")
|
260
|
+
# - expires_in: Seconds until token expiration (adjusted for freshness)
|
261
|
+
# - universe_domain: The universe domain for the token (if not overridden)
|
216
262
|
def build_token_hash body, content_type, retrieval_time
|
217
263
|
hash =
|
218
264
|
if ["text/html", "application/text"].include? content_type
|
@@ -14,9 +14,11 @@
|
|
14
14
|
|
15
15
|
require "forwardable"
|
16
16
|
require "json"
|
17
|
+
require "pathname"
|
17
18
|
require "signet/oauth_2/client"
|
18
19
|
|
19
20
|
require "googleauth/credentials_loader"
|
21
|
+
require "googleauth/errors"
|
20
22
|
|
21
23
|
module Google
|
22
24
|
module Auth
|
@@ -357,12 +359,13 @@ module Google
|
|
357
359
|
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
358
360
|
# values configured on the class.
|
359
361
|
#
|
360
|
-
# @param [String, Hash,
|
362
|
+
# @param [String, Pathname, Hash, Google::Auth::BaseClient] source_creds
|
361
363
|
# The source of credentials. It can be provided as one of the following:
|
362
364
|
#
|
363
|
-
# * The path to a JSON keyfile (as a `String`)
|
365
|
+
# * The path to a JSON keyfile (as a `String` or a `Pathname`)
|
364
366
|
# * The contents of a JSON keyfile (as a `Hash`)
|
365
|
-
# * A `
|
367
|
+
# * A `Google::Auth::BaseClient` credentials object, including but not limited to
|
368
|
+
# a `Signet::OAuth2::Client` object.
|
366
369
|
# * Any credentials object that supports the methods this wrapper delegates to an inner client.
|
367
370
|
#
|
368
371
|
# If this parameter is an object (`Signet::OAuth2::Client` or other) it will be used as an inner client.
|
@@ -391,14 +394,20 @@ module Google
|
|
391
394
|
# parameters of the `Signet::OAuth2::Client`, such as connection parameters,
|
392
395
|
# timeouts, etc.
|
393
396
|
#
|
397
|
+
# @raise [Google::Auth::InitializationError] If source_creds is nil
|
398
|
+
# @raise [ArgumentError] If both scope and target_audience are specified
|
399
|
+
#
|
394
400
|
def initialize source_creds, options = {}
|
395
|
-
|
401
|
+
if source_creds.nil?
|
402
|
+
raise InitializationError,
|
403
|
+
"The source credentials passed to Google::Auth::Credentials.new were nil."
|
404
|
+
end
|
396
405
|
|
397
406
|
options = symbolize_hash_keys options
|
398
407
|
@project_id = options[:project_id] || options[:project]
|
399
408
|
@quota_project_id = options[:quota_project_id]
|
400
409
|
case source_creds
|
401
|
-
when String
|
410
|
+
when String, Pathname
|
402
411
|
update_from_filepath source_creds, options
|
403
412
|
when Hash
|
404
413
|
update_from_hash source_creds, options
|
@@ -554,9 +563,12 @@ module Google
|
|
554
563
|
protected
|
555
564
|
|
556
565
|
# Verify that the keyfile argument is a file.
|
566
|
+
#
|
567
|
+
# @param [String] keyfile Path to the keyfile
|
568
|
+
# @raise [Google::Auth::InitializationError] If the keyfile does not exist
|
557
569
|
def verify_keyfile_exists! keyfile
|
558
570
|
exists = ::File.file? keyfile
|
559
|
-
raise "The keyfile '#{keyfile}' is not a valid file." unless exists
|
571
|
+
raise InitializationError, "The keyfile '#{keyfile}' is not a valid file." unless exists
|
560
572
|
end
|
561
573
|
|
562
574
|
# Initializes the Signet client.
|
@@ -577,6 +589,11 @@ module Google
|
|
577
589
|
hash.to_h.transform_keys(&:to_sym)
|
578
590
|
end
|
579
591
|
|
592
|
+
# Updates client options with defaults from the credential class
|
593
|
+
#
|
594
|
+
# @param [Hash] options Options to update
|
595
|
+
# @return [Hash] Updated options hash
|
596
|
+
# @raise [ArgumentError] If both scope and target_audience are specified
|
580
597
|
def update_client_options options
|
581
598
|
options = options.dup
|
582
599
|
|