googleauth-1.3.0/ 0000755 0000041 0000041 00000000000 14333131016 013674 5 ustar www-data www-data googleauth-1.3.0/README.md 0000644 0000041 0000041 00000017122 14333131016 015156 0 ustar www-data www-data # Google Auth Library for Ruby
- Homepage
- http://www.github.com/googleapis/google-auth-library-ruby
- Authors
- Tim Emiola
- Copyright
- Copyright © 2015 Google, Inc.
- License
- Apache 2.0
[](http://badge.fury.io/rb/googleauth)
## Description
This is Google's officially supported ruby client library for using OAuth 2.0
authorization and authentication with Google APIs.
## Install
Be sure `https://rubygems.org/` is in your gem sources.
For normal client usage, this is sufficient:
```bash
$ gem install googleauth
```
## Example Usage
```ruby
require 'googleauth'
# Get the environment configured authorization
scopes = ['https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/compute']
authorization = Google::Auth.get_application_default(scopes)
# Add the the access token obtained using the authorization to a hash, e.g
# headers.
some_headers = {}
authorization.apply(some_headers)
```
## Application Default Credentials
This library provides an implementation of
[application default credentials][application default credentials] for Ruby.
The Application Default Credentials provide a simple way to get authorization
credentials for use in calling Google APIs.
They are best suited for cases when the call needs to have the same identity
and authorization level for the application independent of the user. This is
the recommended approach to authorize calls to Cloud APIs, particularly when
you're building an application that uses Google Compute Engine.
## User Credentials
The library also provides support for requesting and storing user
credentials (3-Legged OAuth2.) Two implementations are currently available,
a generic authorizer useful for command line apps or custom integrations as
well as a web variant tailored toward Rack-based applications.
The authorizers are intended for authorization use cases. For sign-on,
see [Google Identity Platform](https://developers.google.com/identity/)
### Example (Web)
```ruby
require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'
require 'redis'
client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
scope = ['https://www.googleapis.com/auth/drive']
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
authorizer = Google::Auth::WebUserAuthorizer.new(
client_id, scope, token_store, '/oauth2callback')
get('/authorize') do
# NOTE: Assumes the user is already authenticated to the app
user_id = request.session['user_id']
credentials = authorizer.get_credentials(user_id, request)
if credentials.nil?
redirect authorizer.get_authorization_url(login_hint: user_id, request: request)
end
# Credentials are valid, can call APIs
# ...
end
get('/oauth2callback') do
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
request)
redirect target_url
end
```
### Example (Command Line)
```ruby
require 'googleauth'
require 'googleauth/stores/file_token_store'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
scope = 'https://www.googleapis.com/auth/drive'
client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
token_store = Google::Auth::Stores::FileTokenStore.new(
:file => '/path/to/tokens.yaml')
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
user_id = ENV['USER']
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(base_url: OOB_URI )
puts "Open #{url} in your browser and enter the resulting code:"
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
# OK to use credentials
```
### Example (Service Account)
```ruby
scope = 'https://www.googleapis.com/auth/androidpublisher'
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: File.open('/path/to/service_account_json_key.json'),
scope: scope)
authorizer.fetch_access_token!
```
You can also use a JSON keyfile by setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable.
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account_json_key.json
```
```ruby
require 'googleauth'
require 'google/apis/drive_v3'
Drive = ::Google::Apis::DriveV3
drive = Drive::DriveService.new
scope = 'https://www.googleapis.com/auth/drive'
authorizer = Google::Auth::ServiceAccountCredentials.from_env(scope: scope)
drive.authorization = authorizer
list_files = drive.list_files()
```
### 3-Legged OAuth with a Service Account
This is similar to regular service account authorization (see [this answer](https://support.google.com/a/answer/2538798?hl=en) for more details on the differences), but you'll need to indicate which user your service account is impersonating by manually updating the `sub` field.
```ruby
scope = 'https://www.googleapis.com/auth/androidpublisher'
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: File.open('/path/to/service_account_json_key.json'),
scope: scope
)
authorizer.update!(sub: "email-to-impersonate@your-domain.com")
authorizer.fetch_access_token!
```
### Example (Environment Variables)
```bash
export GOOGLE_ACCOUNT_TYPE=service_account
export GOOGLE_CLIENT_ID=000000000000000000000
export GOOGLE_CLIENT_EMAIL=xxxx@xxxx.iam.gserviceaccount.com
export GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
```
```ruby
require 'googleauth'
require 'google/apis/drive_v3'
Drive = ::Google::Apis::DriveV3
drive = Drive::DriveService.new
# Auths with ENV vars:
# "GOOGLE_CLIENT_ID",
# "GOOGLE_CLIENT_EMAIL",
# "GOOGLE_ACCOUNT_TYPE",
# "GOOGLE_PRIVATE_KEY"
auth = ::Google::Auth::ServiceAccountCredentials
.make_creds(scope: 'https://www.googleapis.com/auth/drive')
drive.authorization = auth
list_files = drive.list_files()
```
### Storage
Authorizers require a storage instance to manage long term persistence of
access and refresh tokens. Two storage implementations are included:
* Google::Auth::Stores::FileTokenStore
* Google::Auth::Stores::RedisTokenStore
Custom storage implementations can also be used. See
[token_store.rb](https://googleapis.dev/ruby/googleauth/latest/Google/Auth/TokenStore.html) for additional details.
## Supported Ruby Versions
This library is supported on Ruby 2.6+.
Google provides official support for Ruby versions that are actively supported
by Ruby Core—that is, Ruby versions that are either in normal maintenance or
in security maintenance, and not end of life. Older versions of Ruby _may_
still work, but are unsupported and not recommended. See
https://www.ruby-lang.org/en/downloads/branches/ for details about the Ruby
support schedule.
## License
This library is licensed under Apache 2.0. Full license text is
available in [LICENSE][license].
## Contributing
See [CONTRIBUTING][contributing].
## Support
Please
[report bugs at the project on Github](https://github.com/google/google-auth-library-ruby/issues). Don't
hesitate to
[ask questions](http://stackoverflow.com/questions/tagged/google-auth-library-ruby)
about the client or APIs on [StackOverflow](http://stackoverflow.com).
[application default credentials]: https://developers.google.com/accounts/docs/application-default-credentials
[contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/main/.github/CONTRIBUTING.md
[license]: https://github.com/googleapis/google-auth-library-ruby/tree/main/LICENSE
googleauth-1.3.0/CHANGELOG.md 0000644 0000041 0000041 00000014050 14333131016 015505 0 ustar www-data www-data # Release History
### 1.3.0 (2022-10-18)
#### Features
* Use OpenSSL 3.0 compatible interfaces for IDTokens ([#397](https://github.com/googleapis/google-auth-library-ruby/issues/397))
### 1.2.0 (2022-06-23)
* Updated minimum Ruby version to 2.6
### 1.1.3 (2022-04-20)
#### Documentation
* Add README instructions for 3-Legged OAuth with a service account
### 1.1.2 (2022-02-22)
#### Bug Fixes
* Support Faraday 2
### 1.1.1 (2022-02-14)
#### Bug Fixes
* add quota_project to user refresh credentials
### 1.1.0 (2021-10-24)
#### Features
* Support short-lived tokens in Credentials
### 1.0.0 (2021-09-27)
Bumped version to 1.0.0. Releases from this point will follow semver.
* Allow dependency on future 1.x versions of signet
* Prevented gcloud from authenticating on the console when getting the gcloud project
### 0.17.1 (2021-09-01)
* Updates to gem metadata
### 0.17.0 (2021-07-30)
* Allow scopes to be self-signed into jwts
### 0.16.2 (2021-04-28)
* Stop attempting to get the project from gcloud when applying self-signed JWTs
### 0.16.1 (2021-04-01)
* Accept application/text content-type for plain idtoken response
### 0.16.0 (2021-03-04)
* Drop support for Ruby 2.4 and add support for Ruby 3.0
### 0.15.1 (2021-02-08)
* Fix crash when using a client credential without any paths or env_vars set
### 0.15.0 (2021-01-26)
* Credential parameters inherit from superclasses
* Service accounts apply a self-signed JWT if scopes are marked as default
* Retry fetch_access_token when GCE metadata server returns unexpected errors
* Support correct service account and user refresh behavior for custom credential env variables
### 0.14.0 / 2020-10-09
* Honor GCE_METADATA_HOST environment variable
* Fix errors in some environments when requesting an access token for multiple scopes
### 0.13.1 / 2020-07-30
* Support scopes when using GCE Metadata Server authentication ([@ball-hayden][])
### 0.13.0 / 2020-06-17
* Support for validating ID tokens.
* Fixed header application of ID tokens from service accounts.
### 0.12.0 / 2020-04-08
* Support for ID token credentials.
* Support reading quota_id_project from service account credentials.
### 0.11.0 / 2020-02-24
* Support Faraday 1.x.
* Allow special "postmessage" value for redirect_uri.
### 0.10.0 / 2019-10-09
Note: This release now requires Ruby 2.4 or later
* Increase metadata timeout to improve reliability in some hosting environments
* Support an environment variable to suppress Cloud SDK credentials warnings
* Make the header check case insensitive
* Set instance variables at initialization to avoid spamming warnings
* Pass "Metadata-Flavor" header to metadata server when checking for GCE
### 0.9.0 / 2019-08-05
* Restore compatibility with Ruby 2.0. This is the last release that will work on end-of-lifed versions of Ruby. The 0.10 release will require Ruby 2.4 or later.
* Update Credentials to use methods for values that are intended to be changed by users, replacing constants.
* Add retry on error for fetch_access_token
* Allow specifying custom state key-values
* Add verbosity none to gcloud command
* Make arity of WebUserAuthorizer#get_credentials compatible with the base class
### 0.8.1 / 2019-03-27
* Silence unnecessary gcloud warning
* Treat empty credentials environment variables as unset
### 0.8.0 / 2019-01-02
* Support connection options :default_connection and :connection_builder when creating credentials that need to refresh OAuth tokens. This lets clients provide connection objects with custom settings, such as proxies, needed for the client environment.
* Removed an unnecessary warning about project IDs.
### 0.7.1 / 2018-10-25
* Make load_gcloud_project_id module function.
### 0.7.0 / 2018-10-24
* Add project_id instance variable to UserRefreshCredentials, ServiceAccountCredentials, and Credentials.
### 0.6.7 / 2018-10-16
* Update memoist dependency to ~> 0.16.
### 0.6.6 / 2018-08-22
* Remove ruby version warnings.
### 0.6.5 / 2018-08-16
* Fix incorrect http verb when revoking credentials.
* Warn on EOL ruby versions.
### 0.6.4 / 2018-08-03
* Resolve issue where DefaultCredentials constant was undefined.
### 0.6.3 / 2018-08-02
* Resolve issue where token_store was being written to twice
### 0.6.2 / 2018-08-01
* Add warning when using cloud sdk credentials
### 0.6.1 / 2017-10-18
* Fix file permissions
### 0.6.0 / 2017-10-17
* Support ruby-jwt 2.0
* Add simple credentials class
### 0.5.3 / 2017-07-21
* Fix file permissions on the gem's `.rb` files.
### 0.5.2 / 2017-07-19
* Add retry mechanism when fetching access tokens in `GCECredentials` and `UserRefreshCredentials` classes.
* Update Google API OAuth2 token credential URI to v4.
### 0.5.1 / 2016-01-06
* Change header name emitted by `Client#apply` from "Authorization" to "authorization" ([@murgatroid99][])
* Fix ADC not working on some windows machines ([@vsubramani][])
### 0.5.0 / 2015-10-12
* Initial support for user credentials ([@sqrrrl][])
* Update Signet to 0.7
### 0.4.2 / 2015-08-05
* Updated UserRefreshCredentials hash to use string keys ([@haabaato][])
* Add support for a system default credentials file. ([@mr-salty][])
* Fix bug when loading credentials from ENV ([@dwilkie][])
* Relax the constraint of dependent version of multi_json ([@igrep][])
* Enables passing credentials via environment variables. ([@haabaato][])
### 0.4.1 / 2015-04-25
* Improves handling of --no-scopes GCE authorization ([@tbetbetbe][])
* Refactoring and cleanup ([@joneslee85][])
### 0.4.0 / 2015-03-25
* Adds an implementation of JWT header auth ([@tbetbetbe][])
### 0.3.0 / 2015-03-23
* makes the scope parameter's optional in all APIs. ([@tbetbetbe][])
* changes the scope parameter's position in various constructors. ([@tbetbetbe][])
[@dwilkie]: https://github.com/dwilkie
[@haabaato]: https://github.com/haabaato
[@igrep]: https://github.com/igrep
[@joneslee85]: https://github.com/joneslee85
[@mr-salty]: https://github.com/mr-salty
[@tbetbetbe]: https://github.com/tbetbetbe
[@murgatroid99]: https://github.com/murgatroid99
[@vsubramani]: https://github.com/vsubramani
[@ball-hayden]: https://github.com/ball-hayden
googleauth-1.3.0/CODE_OF_CONDUCT.md 0000644 0000041 0000041 00000003675 14333131016 016506 0 ustar www-data www-data # Contributor Code of Conduct
As contributors and maintainers of this project,
and in the interest of fostering an open and welcoming community,
we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in this project
a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information,
such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct.
By adopting this Code of Conduct,
project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project.
Project maintainers who do not follow or enforce the Code of Conduct
may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue
or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
googleauth-1.3.0/LICENSE 0000644 0000041 0000041 00000026116 14333131016 014707 0 ustar www-data www-data
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
googleauth-1.3.0/googleauth.gemspec 0000644 0000041 0000041 00000006377 14333131016 017414 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: googleauth 1.3.0 ruby lib
Gem::Specification.new do |s|
s.name = "googleauth".freeze
s.version = "1.3.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "bug_tracker_uri" => "https://github.com/googleapis/google-auth-library-ruby/issues", "changelog_uri" => "https://github.com/googleapis/google-auth-library-ruby/blob/main/CHANGELOG.md", "source_code_uri" => "https://github.com/googleapis/google-auth-library-ruby" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Tim Emiola".freeze]
s.date = "2022-10-18"
s.description = "Implements simple authorization for accessing Google APIs, and provides support for Application Default Credentials.".freeze
s.email = ["temiola@google.com".freeze]
s.files = [".yardopts".freeze, "CHANGELOG.md".freeze, "CODE_OF_CONDUCT.md".freeze, "LICENSE".freeze, "README.md".freeze, "SECURITY.md".freeze, "lib/googleauth.rb".freeze, "lib/googleauth/application_default.rb".freeze, "lib/googleauth/client_id.rb".freeze, "lib/googleauth/compute_engine.rb".freeze, "lib/googleauth/credentials.rb".freeze, "lib/googleauth/credentials_loader.rb".freeze, "lib/googleauth/default_credentials.rb".freeze, "lib/googleauth/iam.rb".freeze, "lib/googleauth/id_tokens.rb".freeze, "lib/googleauth/id_tokens/errors.rb".freeze, "lib/googleauth/id_tokens/key_sources.rb".freeze, "lib/googleauth/id_tokens/verifier.rb".freeze, "lib/googleauth/json_key_reader.rb".freeze, "lib/googleauth/scope_util.rb".freeze, "lib/googleauth/service_account.rb".freeze, "lib/googleauth/signet.rb".freeze, "lib/googleauth/stores/file_token_store.rb".freeze, "lib/googleauth/stores/redis_token_store.rb".freeze, "lib/googleauth/token_store.rb".freeze, "lib/googleauth/user_authorizer.rb".freeze, "lib/googleauth/user_refresh.rb".freeze, "lib/googleauth/version.rb".freeze, "lib/googleauth/web_user_authorizer.rb".freeze]
s.homepage = "https://github.com/googleapis/google-auth-library-ruby".freeze
s.licenses = ["Apache-2.0".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.6".freeze)
s.rubygems_version = "3.2.5".freeze
s.summary = "Google Auth Library for Ruby".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
end
if s.respond_to? :add_runtime_dependency then
s.add_runtime_dependency(%q.freeze, [">= 0.17.3", "< 3.a"])
s.add_runtime_dependency(%q.freeze, [">= 1.4", "< 3.0"])
s.add_runtime_dependency(%q.freeze, ["~> 0.16"])
s.add_runtime_dependency(%q.freeze, ["~> 1.11"])
s.add_runtime_dependency(%q.freeze, [">= 0.9", "< 2.0"])
s.add_runtime_dependency(%q.freeze, [">= 0.16", "< 2.a"])
else
s.add_dependency(%q.freeze, [">= 0.17.3", "< 3.a"])
s.add_dependency(%q.freeze, [">= 1.4", "< 3.0"])
s.add_dependency(%q.freeze, ["~> 0.16"])
s.add_dependency(%q.freeze, ["~> 1.11"])
s.add_dependency(%q.freeze, [">= 0.9", "< 2.0"])
s.add_dependency(%q.freeze, [">= 0.16", "< 2.a"])
end
end
googleauth-1.3.0/lib/ 0000755 0000041 0000041 00000000000 14333131016 014442 5 ustar www-data www-data googleauth-1.3.0/lib/googleauth/ 0000755 0000041 0000041 00000000000 14333131016 016600 5 ustar www-data www-data googleauth-1.3.0/lib/googleauth/client_id.rb 0000644 0000041 0000041 00000005572 14333131016 021070 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "multi_json"
require "googleauth/credentials_loader"
module Google
module Auth
# Representation of an application's identity for user authorization
# flows.
class ClientId
INSTALLED_APP = "installed".freeze
WEB_APP = "web".freeze
CLIENT_ID = "client_id".freeze
CLIENT_SECRET = "client_secret".freeze
MISSING_TOP_LEVEL_ELEMENT_ERROR =
"Expected top level property 'installed' or 'web' to be present.".freeze
# Text identifier of the client ID
# @return [String]
attr_reader :id
# Secret associated with the client ID
# @return [String]
attr_reader :secret
class << self
attr_accessor :default
end
# Initialize the Client ID
#
# @param [String] id
# Text identifier of the client ID
# @param [String] secret
# Secret associated with the client ID
# @note Direction instantion is discouraged to avoid embedding IDs
# & secrets in source. See {#from_file} to load from
# `client_secrets.json` files.
def initialize id, secret
CredentialsLoader.warn_if_cloud_sdk_credentials id
raise "Client id can not be nil" if id.nil?
raise "Client secret can not be nil" if secret.nil?
@id = id
@secret = secret
end
# Constructs a Client ID from a JSON file downloaded from the
# Google Developers Console.
#
# @param [String, File] file
# Path of file to read from
# @return [Google::Auth::ClientID]
def self.from_file file
raise "File can not be nil." if file.nil?
File.open file.to_s do |f|
json = f.read
config = MultiJson.load json
from_hash config
end
end
# Constructs a Client ID from a previously loaded JSON file. The hash
# structure should
# match the expected JSON format.
#
# @param [hash] config
# Parsed contents of the JSON file
# @return [Google::Auth::ClientID]
def self.from_hash config
raise "Hash can not be nil." if config.nil?
raw_detail = config[INSTALLED_APP] || config[WEB_APP]
raise MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
ClientId.new raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET]
end
end
end
end
googleauth-1.3.0/lib/googleauth/scope_util.rb 0000644 0000041 0000041 00000002550 14333131016 021275 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/signet"
require "googleauth/credentials_loader"
require "multi_json"
module Google
module Auth
# Small utility for normalizing scopes into canonical form
module ScopeUtil
ALIASES = {
"email" => "https://www.googleapis.com/auth/userinfo.email",
"profile" => "https://www.googleapis.com/auth/userinfo.profile",
"openid" => "https://www.googleapis.com/auth/plus.me"
}.freeze
def self.normalize scope
list = as_array scope
list.map { |item| ALIASES[item] || item }
end
def self.as_array scope
case scope
when Array
scope
when String
scope.split
else
raise "Invalid scope value. Must be string or array"
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/version.rb 0000644 0000041 0000041 00000001360 14333131016 020612 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
VERSION = "1.3.0".freeze
end
end
googleauth-1.3.0/lib/googleauth/id_tokens/ 0000755 0000041 0000041 00000000000 14333131016 020557 5 ustar www-data www-data googleauth-1.3.0/lib/googleauth/id_tokens/verifier.rb 0000644 0000041 0000041 00000011224 14333131016 022717 0 ustar www-data www-data # frozen_string_literal: true
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "jwt"
module Google
module Auth
module IDTokens
##
# An object that can verify ID tokens.
#
# A verifier maintains a set of default settings, including the key
# source and fields to verify. However, individual verification calls can
# override any of these settings.
#
class Verifier
##
# Create a verifier.
#
# @param key_source [key source] The default key source to use. All
# verification calls must have a key source, so if no default key
# source is provided here, then calls to {#verify} _must_ provide
# a key source.
# @param aud [String,nil] The default audience (`aud`) check, or `nil`
# for no check.
# @param azp [String,nil] The default authorized party (`azp`) check,
# or `nil` for no check.
# @param iss [String,nil] The default issuer (`iss`) check, or `nil`
# for no check.
#
def initialize key_source: nil,
aud: nil,
azp: nil,
iss: nil
@key_source = key_source
@aud = aud
@azp = azp
@iss = iss
end
##
# Verify the given token.
#
# @param token [String] the ID token to verify.
# @param key_source [key source] If given, override the key source.
# @param aud [String,nil] If given, override the `aud` check.
# @param azp [String,nil] If given, override the `azp` check.
# @param iss [String,nil] If given, override the `iss` check.
#
# @return [Hash] the decoded payload, if verification succeeded.
# @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 token,
key_source: :default,
aud: :default,
azp: :default,
iss: :default
key_source = @key_source if key_source == :default
aud = @aud if aud == :default
azp = @azp if azp == :default
iss = @iss if iss == :default
raise KeySourceError, "No key sources" unless key_source
keys = key_source.current_keys
payload = decode_token token, keys, aud, azp, iss
unless payload
keys = key_source.refresh_keys
payload = decode_token token, keys, aud, azp, iss
end
raise SignatureError, "Token not verified as issued by Google" unless payload
payload
end
private
def decode_token token, keys, aud, azp, iss
payload = nil
keys.find do |key|
options = { algorithms: key.algorithm }
decoded_token = JWT.decode token, key.key, true, options
payload = decoded_token.first
rescue JWT::ExpiredSignature
raise ExpiredTokenError, "Token signature is expired"
rescue JWT::DecodeError
nil # Try the next key
end
normalize_and_verify_payload payload, aud, azp, iss
end
def normalize_and_verify_payload payload, aud, azp, iss
return nil unless payload
# Map the legacy "cid" claim to the canonical "azp"
payload["azp"] ||= payload["cid"] if payload.key? "cid"
# Payload content validation
if aud && (Array(aud) & Array(payload["aud"])).empty?
raise AudienceMismatchError, "Token aud mismatch: #{payload['aud']}"
end
if azp && (Array(azp) & Array(payload["azp"])).empty?
raise AuthorizedPartyMismatchError, "Token azp mismatch: #{payload['azp']}"
end
if iss && (Array(iss) & Array(payload["iss"])).empty?
raise IssuerMismatchError, "Token iss mismatch: #{payload['iss']}"
end
payload
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/id_tokens/errors.rb 0000644 0000041 0000041 00000003112 14333131016 022415 0 ustar www-data www-data # frozen_string_literal: true
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Google
module Auth
module IDTokens
##
# Failed to obtain keys from the key source.
#
class KeySourceError < StandardError; end
##
# Failed to verify a token.
#
class VerificationError < StandardError; end
##
# Failed to verify a token because it is expired.
#
class ExpiredTokenError < VerificationError; end
##
# Failed to verify a token because its signature did not match.
#
class SignatureError < VerificationError; end
##
# Failed to verify a token because its issuer did not match.
#
class IssuerMismatchError < VerificationError; end
##
# Failed to verify a token because its audience did not match.
#
class AudienceMismatchError < VerificationError; end
##
# Failed to verify a token because its authorized party did not match.
#
class AuthorizedPartyMismatchError < VerificationError; end
end
end
end
googleauth-1.3.0/lib/googleauth/id_tokens/key_sources.rb 0000644 0000041 0000041 00000027666 14333131016 023460 0 ustar www-data www-data # frozen_string_literal: true
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "base64"
require "json"
require "monitor"
require "net/http"
require "openssl"
require "jwt"
module Google
module Auth
module IDTokens
##
# A public key used for verifying ID tokens.
#
# This includes the public key data, ID, and the algorithm used for
# signature verification. RSA and Elliptical Curve (EC) keys are
# supported.
#
class KeyInfo
##
# Create a public key info structure.
#
# @param id [String] The key ID.
# @param key [OpenSSL::PKey::RSA,OpenSSL::PKey::EC] The key itself.
# @param algorithm [String] The algorithm (normally `RS256` or `ES256`)
#
def initialize id: nil, key: nil, algorithm: nil
@id = id
@key = key
@algorithm = algorithm
end
##
# The key ID.
# @return [String]
#
attr_reader :id
##
# The key itself.
# @return [OpenSSL::PKey::RSA,OpenSSL::PKey::EC]
#
attr_reader :key
##
# The signature algorithm. (normally `RS256` or `ES256`)
# @return [String]
#
attr_reader :algorithm
class << self
##
# Create a KeyInfo from a single JWK, which may be given as either a
# hash or an unparsed JSON string.
#
# @param jwk [Hash,String] The JWK specification.
# @return [KeyInfo]
# @raise [KeySourceError] If the key could not be extracted from the
# JWK.
#
def from_jwk jwk
jwk = symbolize_keys ensure_json_parsed jwk
key = case jwk[:kty]
when "RSA"
extract_rsa_key jwk
when "EC"
extract_ec_key jwk
when nil
raise KeySourceError, "Key type not found"
else
raise KeySourceError, "Cannot use key type #{jwk[:kty]}"
end
new id: jwk[:kid], key: key, algorithm: jwk[:alg]
end
##
# Create an array of KeyInfo from a JWK Set, which may be given as
# either a hash or an unparsed JSON string.
#
# @param jwk [Hash,String] The JWK Set specification.
# @return [Array]
# @raise [KeySourceError] If a key could not be extracted from the
# JWK Set.
#
def from_jwk_set jwk_set
jwk_set = symbolize_keys ensure_json_parsed jwk_set
jwks = jwk_set[:keys]
raise KeySourceError, "No keys found in jwk set" unless jwks
jwks.map { |jwk| from_jwk jwk }
end
private
def ensure_json_parsed input
return input unless input.is_a? String
JSON.parse input
rescue JSON::ParserError
raise KeySourceError, "Unable to parse JSON"
end
def symbolize_keys hash
result = {}
hash.each { |key, val| result[key.to_sym] = val }
result
end
def extract_rsa_key jwk
begin
n_data = Base64.urlsafe_decode64 jwk[:n]
e_data = Base64.urlsafe_decode64 jwk[:e]
rescue ArgumentError
raise KeySourceError, "Badly formatted key data"
end
n_bn = OpenSSL::BN.new n_data, 2
e_bn = OpenSSL::BN.new e_data, 2
sequence = [OpenSSL::ASN1::Integer.new(n_bn), OpenSSL::ASN1::Integer.new(e_bn)]
rsa_key = OpenSSL::PKey::RSA.new OpenSSL::ASN1::Sequence(sequence).to_der
rsa_key.public_key
end
# @private
CURVE_NAME_MAP = {
"P-256" => "prime256v1",
"P-384" => "secp384r1",
"P-521" => "secp521r1",
"secp256k1" => "secp256k1"
}.freeze
def extract_ec_key jwk
begin
x_data = Base64.urlsafe_decode64 jwk[:x]
y_data = Base64.urlsafe_decode64 jwk[:y]
rescue ArgumentError
raise KeySourceError, "Badly formatted key data"
end
curve_name = CURVE_NAME_MAP[jwk[:crv]]
raise KeySourceError, "Unsupported EC curve #{jwk[:crv]}" unless curve_name
group = OpenSSL::PKey::EC::Group.new curve_name
x_hex = x_data.unpack1 "H*"
y_hex = y_data.unpack1 "H*"
bn = OpenSSL::BN.new ["04#{x_hex}#{y_hex}"].pack("H*"), 2
point = OpenSSL::PKey::EC::Point.new group, bn
sequence = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
OpenSSL::ASN1::ObjectId(curve_name)]),
OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
])
OpenSSL::PKey::EC.new sequence.to_der
end
end
end
##
# A key source that contains a static set of keys.
#
class StaticKeySource
##
# Create a static key source with the given keys.
#
# @param keys [Array] The keys
#
def initialize keys
@current_keys = Array(keys)
end
##
# Return the current keys. Does not perform any refresh.
#
# @return [Array]
#
attr_reader :current_keys
alias refresh_keys current_keys
class << self
##
# Create a static key source containing a single key parsed from a
# single JWK, which may be given as either a hash or an unparsed
# JSON string.
#
# @param jwk [Hash,String] The JWK specification.
# @return [StaticKeySource]
#
def from_jwk jwk
new KeyInfo.from_jwk jwk
end
##
# Create a static key source containing multiple keys parsed from a
# JWK Set, which may be given as either a hash or an unparsed JSON
# string.
#
# @param jwk_set [Hash,String] The JWK Set specification.
# @return [StaticKeySource]
#
def from_jwk_set jwk_set
new KeyInfo.from_jwk_set jwk_set
end
end
end
##
# A base key source that downloads keys from a URI. Subclasses should
# override {HttpKeySource#interpret_json} to parse the response.
#
class HttpKeySource
##
# The default interval between retries in seconds (3600s = 1hr).
#
# @return [Integer]
#
DEFAULT_RETRY_INTERVAL = 3600
##
# Create an HTTP key source.
#
# @param uri [String,URI] The URI from which to download keys.
# @param retry_interval [Integer,nil] Override the retry interval in
# seconds. This is the minimum time between retries of failed key
# downloads.
#
def initialize uri, retry_interval: nil
@uri = URI uri
@retry_interval = retry_interval || DEFAULT_RETRY_INTERVAL
@allow_refresh_at = Time.now
@current_keys = []
@monitor = Monitor.new
end
##
# The URI from which to download keys.
# @return [Array]
#
attr_reader :uri
##
# Return the current keys, without attempting to re-download.
#
# @return [Array]
#
attr_reader :current_keys
##
# Attempt to re-download keys (if the retry interval has expired) and
# return the new keys.
#
# @return [Array]
# @raise [KeySourceError] if key retrieval failed.
#
def refresh_keys
@monitor.synchronize do
return @current_keys if Time.now < @allow_refresh_at
@allow_refresh_at = Time.now + @retry_interval
response = Net::HTTP.get_response uri
raise KeySourceError, "Unable to retrieve data from #{uri}" unless response.is_a? Net::HTTPSuccess
data = begin
JSON.parse response.body
rescue JSON::ParserError
raise KeySourceError, "Unable to parse JSON"
end
@current_keys = Array(interpret_json(data))
end
end
protected
def interpret_json _data
nil
end
end
##
# A key source that downloads X509 certificates.
# Used by the legacy OAuth V1 public certs endpoint.
#
class X509CertHttpKeySource < HttpKeySource
##
# Create a key source that downloads X509 certificates.
#
# @param uri [String,URI] The URI from which to download keys.
# @param algorithm [String] The algorithm to use for signature
# verification. Defaults to "`RS256`".
# @param retry_interval [Integer,nil] Override the retry interval in
# seconds. This is the minimum time between retries of failed key
# downloads.
#
def initialize uri, algorithm: "RS256", retry_interval: nil
super uri, retry_interval: retry_interval
@algorithm = algorithm
end
protected
def interpret_json data
data.map do |id, cert_str|
key = OpenSSL::X509::Certificate.new(cert_str).public_key
KeyInfo.new id: id, key: key, algorithm: @algorithm
end
rescue OpenSSL::X509::CertificateError
raise KeySourceError, "Unable to parse X509 certificates"
end
end
##
# A key source that downloads a JWK set.
#
class JwkHttpKeySource < HttpKeySource
##
# Create a key source that downloads a JWT Set.
#
# @param uri [String,URI] The URI from which to download keys.
# @param retry_interval [Integer,nil] Override the retry interval in
# seconds. This is the minimum time between retries of failed key
# downloads.
#
def initialize uri, retry_interval: nil
super uri, retry_interval: retry_interval
end
protected
def interpret_json data
KeyInfo.from_jwk_set data
end
end
##
# A key source that aggregates other key sources. This means it will
# aggregate the keys provided by its constituent sources. Additionally,
# when asked to refresh, it will refresh all its constituent sources.
#
class AggregateKeySource
##
# Create a key source that aggregates other key sources.
#
# @param sources [Array] The key sources to aggregate.
#
def initialize sources
@sources = Array(sources)
end
##
# Return the current keys, without attempting to refresh.
#
# @return [Array]
#
def current_keys
@sources.flat_map(&:current_keys)
end
##
# Attempt to refresh keys and return the new keys.
#
# @return [Array]
# @raise [KeySourceError] if key retrieval failed.
#
def refresh_keys
@sources.flat_map(&:refresh_keys)
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/credentials.rb 0000644 0000041 0000041 00000047625 14333131016 021440 0 ustar www-data www-data # Copyright 2017 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "forwardable"
require "json"
require "signet/oauth_2/client"
require "googleauth/credentials_loader"
module Google
module Auth
##
# Credentials is a high-level base class used by Google's API client
# libraries to represent the authentication when connecting to an API.
# In most cases, it is subclassed by API-specific credential classes that
# can be instantiated by clients.
#
# ## Options
#
# Credentials classes are configured with options that dictate default
# values for parameters such as scope and audience. These defaults are
# expressed as class attributes, and may differ from endpoint to endpoint.
# Normally, an API client will provide subclasses specific to each
# endpoint, configured with appropriate values.
#
# Note that these options inherit up the class hierarchy. If a particular
# options is not set for a subclass, its superclass is queried.
#
# Some older users of this class set options via constants. This usage is
# deprecated. For example, instead of setting the `AUDIENCE` constant on
# your subclass, call the `audience=` method.
#
# ## Example
#
# class MyCredentials < Google::Auth::Credentials
# # Set the default scope for these credentials
# self.scope = "http://example.com/my_scope"
# end
#
# # creds is a credentials object suitable for Google API clients
# creds = MyCredentials.default
# creds.scope # => ["http://example.com/my_scope"]
#
# class SubCredentials < MyCredentials
# # Override the default scope for this subclass
# self.scope = "http://example.com/sub_scope"
# end
#
# creds2 = SubCredentials.default
# creds2.scope # => ["http://example.com/sub_scope"]
#
class Credentials # rubocop:disable Metrics/ClassLength
##
# The default token credential URI to be used when none is provided during initialization.
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze
##
# The default target audience ID to be used when none is provided during initialization.
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
@audience = @scope = @target_audience = @env_vars = @paths = @token_credential_uri = nil
##
# The default token credential URI to be used when none is provided during initialization.
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
# refreshing expired tokens.
#
# @return [String]
#
def self.token_credential_uri
lookup_auth_param :token_credential_uri do
lookup_local_constant :TOKEN_CREDENTIAL_URI
end
end
##
# Set the default token credential URI to be used when none is provided during initialization.
#
# @param [String] new_token_credential_uri
#
def self.token_credential_uri= new_token_credential_uri
@token_credential_uri = new_token_credential_uri
end
##
# The default target audience ID to be used when none is provided during initialization.
# Used only by the assertion grant type.
#
# @return [String]
#
def self.audience
lookup_auth_param :audience do
lookup_local_constant :AUDIENCE
end
end
##
# Sets the default target audience ID to be used when none is provided during initialization.
#
# @param [String] new_audience
#
def self.audience= new_audience
@audience = new_audience
end
##
# The default scope to be used when none is provided during initialization.
# A scope is an access range defined by the authorization server.
# The scope can be a single value or a list of values.
#
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
# If {#scope} is set, this credential will produce access tokens.
# If {#target_audience} is set, this credential will produce ID tokens.
#
# @return [String, Array, nil]
#
def self.scope
lookup_auth_param :scope do
vals = lookup_local_constant :SCOPE
vals ? Array(vals).flatten.uniq : nil
end
end
##
# Sets the default scope to be used when none is provided during initialization.
#
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
# If {#scope} is set, this credential will produce access tokens.
# If {#target_audience} is set, this credential will produce ID tokens.
#
# @param [String, Array, nil] new_scope
#
def self.scope= new_scope
new_scope = Array new_scope unless new_scope.nil?
@scope = new_scope
end
##
# The default final target audience for ID tokens, to be used when none
# is provided during initialization.
#
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
# If {#scope} is set, this credential will produce access tokens.
# If {#target_audience} is set, this credential will produce ID tokens.
#
# @return [String, nil]
#
def self.target_audience
lookup_auth_param :target_audience
end
##
# Sets the default final target audience for ID tokens, to be used when none
# is provided during initialization.
#
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
# If {#scope} is set, this credential will produce access tokens.
# If {#target_audience} is set, this credential will produce ID tokens.
#
# @param [String, nil] new_target_audience
#
def self.target_audience= new_target_audience
@target_audience = new_target_audience
end
##
# The environment variables to search for credentials. Values can either be a file path to the
# credentials file, or the JSON contents of the credentials file.
# The env_vars will never be nil. If there are no vars, the empty array is returned.
#
# @return [Array]
#
def self.env_vars
env_vars_internal || []
end
##
# @private
# Internal recursive lookup for env_vars.
#
def self.env_vars_internal
lookup_auth_param :env_vars, :env_vars_internal do
# Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
path_env_vars = lookup_local_constant :PATH_ENV_VARS
json_env_vars = lookup_local_constant :JSON_ENV_VARS
(Array(path_env_vars) + Array(json_env_vars)).flatten.uniq if path_env_vars || json_env_vars
end
end
##
# Sets the environment variables to search for credentials.
# Setting to `nil` "unsets" the value, and defaults to the superclass
# (or to the empty array if there is no superclass).
#
# @param [String, Array, nil] new_env_vars
#
def self.env_vars= new_env_vars
new_env_vars = Array new_env_vars unless new_env_vars.nil?
@env_vars = new_env_vars
end
##
# The file paths to search for credentials files.
# The paths will never be nil. If there are no paths, the empty array is returned.
#
# @return [Array]
#
def self.paths
paths_internal || []
end
##
# @private
# Internal recursive lookup for paths.
#
def self.paths_internal
lookup_auth_param :paths, :paths_internal do
# Pull in values if the DEFAULT_PATHS constant exists.
vals = lookup_local_constant :DEFAULT_PATHS
vals ? Array(vals).flatten.uniq : nil
end
end
##
# Set the file paths to search for credentials files.
# Setting to `nil` "unsets" the value, and defaults to the superclass
# (or to the empty array if there is no superclass).
#
# @param [String, Array, nil] new_paths
#
def self.paths= new_paths
new_paths = Array new_paths unless new_paths.nil?
@paths = new_paths
end
##
# @private
# Return the given parameter value, defaulting up the class hierarchy.
#
# First returns the value of the instance variable, if set.
# Next, calls the given block if provided. (This is generally used to
# look up legacy constant-based values.)
# Otherwise, calls the superclass method if present.
# Returns nil if all steps fail.
#
# @param name [Symbol] The parameter name
# @param method_name [Symbol] The lookup method name, if different
# @return [Object] The value
#
def self.lookup_auth_param name, method_name = name
val = instance_variable_get "@#{name}".to_sym
val = yield if val.nil? && block_given?
return val unless val.nil?
return superclass.send method_name if superclass.respond_to? method_name
nil
end
##
# @private
# Return the value of the given constant if it is defined directly in
# this class, or nil if not.
#
# @param [Symbol] Name of the constant
# @return [Object] The value
#
def self.lookup_local_constant name
const_defined?(name, false) ? const_get(name) : nil
end
##
# The Signet::OAuth2::Client object the Credentials instance is using.
#
# @return [Signet::OAuth2::Client]
#
attr_accessor :client
##
# Identifier for the project the client is authenticating with.
#
# @return [String]
#
attr_reader :project_id
##
# Identifier for a separate project used for billing/quota, if any.
#
# @return [String,nil]
#
attr_reader :quota_project_id
# @private Delegate client methods to the client object.
extend Forwardable
##
# @!attribute [r] token_credential_uri
# @return [String] The token credential URI. The URI is the authorization server's HTTP
# endpoint capable of issuing tokens and refreshing expired tokens.
#
# @!attribute [r] audience
# @return [String] The target audience ID when issuing assertions. Used only by the
# assertion grant type.
#
# @!attribute [r] scope
# @return [String, Array] The scope for this client. A scope is an access range
# defined by the authorization server. The scope can be a single value or a list of values.
#
# @!attribute [r] target_audience
# @return [String] The final target audience for ID tokens returned by this credential.
#
# @!attribute [r] issuer
# @return [String] The issuer ID associated with this client.
#
# @!attribute [r] signing_key
# @return [String, OpenSSL::PKey] The signing key associated with this client.
#
# @!attribute [r] updater_proc
# @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
# suitable for passing as a closure.
#
def_delegators :@client,
:token_credential_uri, :audience,
:scope, :issuer, :signing_key, :updater_proc, :target_audience
##
# Creates a new Credentials instance with the provided auth credentials, and with the default
# values configured on the class.
#
# @param [String, Hash, Signet::OAuth2::Client] keyfile
# The keyfile can be provided as one of the following:
#
# * The path to a JSON keyfile (as a +String+)
# * The contents of a JSON keyfile (as a +Hash+)
# * A +Signet::OAuth2::Client+ object
# @param [Hash] options
# The options for configuring the credentials instance. The following is supported:
#
# * +:scope+ - the scope for the client
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
# * +:connection_builder+ - the connection builder to use for the client
# * +:default_connection+ - the default connection to use for the client
#
def initialize keyfile, options = {}
verify_keyfile_provided! keyfile
@project_id = options["project_id"] || options["project"]
@quota_project_id = options["quota_project_id"]
case keyfile
when Signet::OAuth2::Client
update_from_signet keyfile
when Hash
update_from_hash keyfile, options
else
update_from_filepath keyfile, options
end
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
@project_id ||= CredentialsLoader.load_gcloud_project_id
@client.fetch_access_token! if @client.needs_access_token?
@env_vars = nil
@paths = nil
@scope = nil
end
##
# Creates a new Credentials instance with auth credentials acquired by searching the
# environment variables and paths configured on the class, and with the default values
# configured on the class.
#
# The auth credentials are searched for in the following order:
#
# 1. configured environment variables (see {Credentials.env_vars})
# 2. configured default file paths (see {Credentials.paths})
# 3. application default (see {Google::Auth.get_application_default})
#
# @param [Hash] options
# The options for configuring the credentials instance. The following is supported:
#
# * +:scope+ - the scope for the client
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
# * +:connection_builder+ - the connection builder to use for the client
# * +:default_connection+ - the default connection to use for the client
#
# @return [Credentials]
#
def self.default options = {}
# First try to find keyfile file or json from environment variables.
client = from_env_vars options
# Second try to find keyfile file from known file paths.
client ||= from_default_paths options
# Finally get instantiated client from Google::Auth
client ||= from_application_default options
client
end
##
# @private Lookup Credentials from environment variables.
def self.from_env_vars options
env_vars.each do |env_var|
str = ENV[env_var]
next if str.nil?
io =
if ::File.file? str
::StringIO.new ::File.read str
else
json = ::JSON.parse str rescue nil
json ? ::StringIO.new(str) : nil
end
next if io.nil?
return from_io io, options
end
nil
end
##
# @private Lookup Credentials from default file paths.
def self.from_default_paths options
paths.each do |path|
next unless path && ::File.file?(path)
io = ::StringIO.new ::File.read path
return from_io io, options
end
nil
end
##
# @private Lookup Credentials using Google::Auth.get_application_default.
def self.from_application_default options
scope = options[:scope] || self.scope
auth_opts = {
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
audience: options[:audience] || audience,
target_audience: options[:target_audience] || target_audience,
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?
}
client = Google::Auth.get_application_default scope, auth_opts
new client, options
end
# @private Read credentials from a JSON stream.
def self.from_io io, options
creds_input = {
json_key_io: io,
scope: options[:scope] || scope,
target_audience: options[:target_audience] || target_audience,
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?,
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
audience: options[:audience] || audience
}
client = Google::Auth::DefaultCredentials.make_creds creds_input
new client
end
private_class_method :from_env_vars,
:from_default_paths,
:from_application_default,
:from_io
protected
# Verify that the keyfile argument is provided.
def verify_keyfile_provided! keyfile
return unless keyfile.nil?
raise "The keyfile passed to Google::Auth::Credentials.new was nil."
end
# Verify that the keyfile argument is a file.
def verify_keyfile_exists! keyfile
exists = ::File.file? keyfile
raise "The keyfile '#{keyfile}' is not a valid file." unless exists
end
# Initializes the Signet client.
def init_client keyfile, connection_options = {}
client_opts = client_options keyfile
Signet::OAuth2::Client.new(client_opts)
.configure_connection(connection_options)
end
# returns a new Hash with string keys instead of symbol keys.
def stringify_hash_keys hash
hash.to_h.transform_keys(&:to_s)
end
# rubocop:disable Metrics/AbcSize
def client_options options
# Keyfile options have higher priority over constructor defaults
options["token_credential_uri"] ||= self.class.token_credential_uri
options["audience"] ||= self.class.audience
options["scope"] ||= self.class.scope
options["target_audience"] ||= self.class.target_audience
if !Array(options["scope"]).empty? && options["target_audience"]
raise ArgumentError, "Cannot specify both scope and target_audience"
end
needs_scope = options["target_audience"].nil?
# client options for initializing signet client
{ token_credential_uri: options["token_credential_uri"],
audience: options["audience"],
scope: (needs_scope ? Array(options["scope"]) : nil),
target_audience: options["target_audience"],
issuer: options["client_email"],
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
end
# rubocop:enable Metrics/AbcSize
def update_from_signet client
@project_id ||= client.project_id if client.respond_to? :project_id
@quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
@client = client
end
def update_from_hash hash, options
hash = stringify_hash_keys hash
hash["scope"] ||= options[:scope]
hash["target_audience"] ||= options[:target_audience]
@project_id ||= (hash["project_id"] || hash["project"])
@quota_project_id ||= hash["quota_project_id"]
@client = init_client hash, options
end
def update_from_filepath path, options
verify_keyfile_exists! path
json = JSON.parse ::File.read(path)
json["scope"] ||= options[:scope]
json["target_audience"] ||= options[:target_audience]
@project_id ||= (json["project_id"] || json["project"])
@quota_project_id ||= json["quota_project_id"]
@client = init_client json, options
end
end
end
end
googleauth-1.3.0/lib/googleauth/application_default.rb 0000644 0000041 0000041 00000005444 14333131016 023143 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/compute_engine"
require "googleauth/default_credentials"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
NOT_FOUND_ERROR = <<~ERROR_MESSAGE.freeze
Could not load the default credentials. Browse to
https://developers.google.com/accounts/docs/application-default-credentials
for more information
ERROR_MESSAGE
module_function
# Obtains the default credentials implementation to use in this
# environment.
#
# Use this to obtain the Application Default Credentials for accessing
# Google APIs. Application Default Credentials are described in detail
# at https://cloud.google.com/docs/authentication/production.
#
# If supplied, scope is used to create the credentials instance, when it can
# be applied. E.g, on google compute engine and for user credentials the
# scope is ignored.
#
# @param scope [string|array|nil] the scope(s) to access
# @param options [Hash] Connection options. These may be used to configure
# the `Faraday::Connection` used for outgoing HTTP requests. For
# example, if a connection proxy must be used in the current network,
# you may provide a connection with with the needed proxy options.
# The following keys are recognized:
# * `:default_connection` The connection object to use for token
# refresh requests.
# * `:connection_builder` A `Proc` that creates and returns a
# connection to use for token refresh requests.
# * `:connection` The connection to use to determine whether GCE
# metadata credentials are available.
def get_application_default scope = nil, options = {}
creds = DefaultCredentials.from_env(scope, options) ||
DefaultCredentials.from_well_known_path(scope, options) ||
DefaultCredentials.from_system_default_path(scope, options)
return creds unless creds.nil?
unless GCECredentials.on_gce? options
# Clear cache of the result of GCECredentials.on_gce?
GCECredentials.unmemoize_all
raise NOT_FOUND_ERROR
end
GCECredentials.new scope: scope
end
end
end
googleauth-1.3.0/lib/googleauth/web_user_authorizer.rb 0000644 0000041 0000041 00000025020 14333131016 023213 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "multi_json"
require "googleauth/signet"
require "googleauth/user_authorizer"
require "googleauth/user_refresh"
require "securerandom"
module Google
module Auth
# Varation on {Google::Auth::UserAuthorizer} adapted for Rack based
# web applications.
#
# Example usage:
#
# get('/') do
# user_id = request.session['user_email']
# credentials = authorizer.get_credentials(user_id, request)
# if credentials.nil?
# redirect authorizer.get_authorization_url(user_id: user_id,
# request: request)
# end
# # Credentials are valid, can call APIs
# ...
# end
#
# get('/oauth2callback') do
# url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
# request)
# redirect url
# end
#
# Instead of implementing the callback directly, applications are
# encouraged to use {Google::Auth::WebUserAuthorizer::CallbackApp} instead.
#
# @see CallbackApp
# @note Requires sessions are enabled
class WebUserAuthorizer < Google::Auth::UserAuthorizer
STATE_PARAM = "state".freeze
AUTH_CODE_KEY = "code".freeze
ERROR_CODE_KEY = "error".freeze
SESSION_ID_KEY = "session_id".freeze
CALLBACK_STATE_KEY = "g-auth-callback".freeze
CURRENT_URI_KEY = "current_uri".freeze
XSRF_KEY = "g-xsrf-token".freeze
SCOPE_KEY = "scope".freeze
NIL_REQUEST_ERROR = "Request is required.".freeze
NIL_SESSION_ERROR = "Sessions must be enabled".freeze
MISSING_AUTH_CODE_ERROR = "Missing authorization code in request".freeze
AUTHORIZATION_ERROR = "Authorization error: %s".freeze
INVALID_STATE_TOKEN_ERROR =
"State token does not match expected value".freeze
class << self
attr_accessor :default
end
# Handle the result of the oauth callback. This version defers the
# exchange of the code by temporarily stashing the results in the user's
# session. This allows apps to use the generic
# {Google::Auth::WebUserAuthorizer::CallbackApp} handler for the callback
# without any additional customization.
#
# Apps that wish to handle the callback directly should use
# {#handle_auth_callback} instead.
#
# @param [Rack::Request] request
# Current request
def self.handle_auth_callback_deferred request
callback_state, redirect_uri = extract_callback_state request
request.session[CALLBACK_STATE_KEY] = MultiJson.dump callback_state
redirect_uri
end
# Initialize the authorizer
#
# @param [Google::Auth::ClientID] client_id
# Configured ID & secret for this application
# @param [String, Array] scope
# Authorization scope to request
# @param [Google::Auth::Stores::TokenStore] token_store
# Backing storage for persisting user credentials
# @param [String] callback_uri
# URL (either absolute or relative) of the auth callback. Defaults
# to '/oauth2callback'
def initialize client_id, scope, token_store, callback_uri = nil
super client_id, scope, token_store, callback_uri
end
# Handle the result of the oauth callback. Exchanges the authorization
# code from the request and persists to storage.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [Rack::Request] request
# Current request
# @return (Google::Auth::UserRefreshCredentials, String)
# credentials & next URL to redirect to
def handle_auth_callback user_id, request
callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
request
)
WebUserAuthorizer.validate_callback_state callback_state, request
credentials = get_and_store_credentials_from_code(
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
base_url: request.url
)
[credentials, redirect_uri]
end
# Build the URL for requesting authorization.
#
# @param [String] login_hint
# Login hint if need to authorize a specific account. Should be a
# user's email address or unique profile ID.
# @param [Rack::Request] request
# Current request
# @param [String] redirect_to
# Optional URL to proceed to after authorization complete. Defaults to
# the current URL.
# @param [String, Array] scope
# Authorization scope to request. Overrides the instance scopes if
# not nil.
# @param [Hash] state
# Optional key-values to be returned to the oauth callback.
# @return [String]
# Authorization url
def get_authorization_url options = {}
options = options.dup
request = options[:request]
raise NIL_REQUEST_ERROR if request.nil?
raise NIL_SESSION_ERROR if request.session.nil?
state = options[:state] || {}
redirect_to = options[:redirect_to] || request.url
request.session[XSRF_KEY] = SecureRandom.base64
options[:state] = MultiJson.dump(state.merge(
SESSION_ID_KEY => request.session[XSRF_KEY],
CURRENT_URI_KEY => redirect_to
))
options[:base_url] = request.url
super options
end
# Fetch stored credentials for the user from the given request session.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [Rack::Request] request
# Current request. Optional. If omitted, this will attempt to fall back
# on the base class behavior of reading from the token store.
# @param [Array, String] scope
# If specified, only returns credentials that have all the \
# requested scopes
# @return [Google::Auth::UserRefreshCredentials]
# Stored credentials, nil if none present
# @raise [Signet::AuthorizationError]
# May raise an error if an authorization code is present in the session
# and exchange of the code fails
def get_credentials user_id, request = nil, scope = nil
if request&.session&.key? CALLBACK_STATE_KEY
# Note - in theory, no need to check required scope as this is
# expected to be called immediately after a return from authorization
state_json = request.session.delete CALLBACK_STATE_KEY
callback_state = MultiJson.load state_json
WebUserAuthorizer.validate_callback_state callback_state, request
get_and_store_credentials_from_code(
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
base_url: request.url
)
else
super user_id, scope
end
end
def self.extract_callback_state request
state = MultiJson.load(request[STATE_PARAM] || "{}")
redirect_uri = state[CURRENT_URI_KEY]
callback_state = {
AUTH_CODE_KEY => request[AUTH_CODE_KEY],
ERROR_CODE_KEY => request[ERROR_CODE_KEY],
SESSION_ID_KEY => state[SESSION_ID_KEY],
SCOPE_KEY => request[SCOPE_KEY]
}
[callback_state, redirect_uri]
end
# Verifies the results of an authorization callback
#
# @param [Hash] state
# Callback state
# @option state [String] AUTH_CODE_KEY
# The authorization code
# @option state [String] ERROR_CODE_KEY
# Error message if failed
# @param [Rack::Request] request
# Current request
def self.validate_callback_state state, request
raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR if state[AUTH_CODE_KEY].nil?
if state[ERROR_CODE_KEY]
raise Signet::AuthorizationError,
format(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
end
end
# Small Rack app which acts as the default callback handler for the app.
#
# To configure in Rails, add to routes.rb:
#
# match '/oauth2callback',
# to: Google::Auth::WebUserAuthorizer::CallbackApp,
# via: :all
#
# With Rackup, add to config.ru:
#
# map '/oauth2callback' do
# run Google::Auth::WebUserAuthorizer::CallbackApp
# end
#
# Or in a classic Sinatra app:
#
# get('/oauth2callback') do
# Google::Auth::WebUserAuthorizer::CallbackApp.call(env)
# end
#
# @see Google::Auth::WebUserAuthorizer
class CallbackApp
LOCATION_HEADER = "Location".freeze
REDIR_STATUS = 302
ERROR_STATUS = 500
# Handle a rack request. Simply stores the results the authorization
# in the session temporarily and redirects back to to the previously
# saved redirect URL. Credentials can be later retrieved by calling.
# {Google::Auth::Web::WebUserAuthorizer#get_credentials}
#
# See {Google::Auth::Web::WebUserAuthorizer#get_authorization_uri}
# for how to initiate authorization requests.
#
# @param [Hash] env
# Rack environment
# @return [Array]
# HTTP response
def self.call env
request = Rack::Request.new env
return_url = WebUserAuthorizer.handle_auth_callback_deferred request
if return_url
[REDIR_STATUS, { LOCATION_HEADER => return_url }, []]
else
[ERROR_STATUS, {}, ["No return URL is present in the request."]]
end
end
def call env
self.class.call env
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/signet.rb 0000644 0000041 0000041 00000007030 14333131016 020416 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "signet/oauth_2/client"
module Signet
# OAuth2 supports OAuth2 authentication.
module OAuth2
AUTH_METADATA_KEY = :authorization
# Signet::OAuth2::Client creates an OAuth2 client
#
# This reopens Client to add #apply and #apply! methods which update a
# hash with the fetched authentication token.
class Client
def configure_connection options
@connection_info =
options[:connection_builder] || options[:default_connection]
self
end
# The token type as symbol, either :id_token or :access_token
def token_type
target_audience ? :id_token : :access_token
end
# Whether the id_token or access_token is missing or about to expire.
def needs_access_token?
send(token_type).nil? || expires_within?(60)
end
# Updates a_hash updated with the authentication token
def apply! a_hash, opts = {}
# fetch the access token there is currently not one, or if the client
# has expired
fetch_access_token! opts if needs_access_token?
a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
end
# Returns a clone of a_hash updated with the authentication token
def apply a_hash, opts = {}
a_copy = a_hash.clone
apply! a_copy, opts
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
proc { |a_hash, opts = {}| apply a_hash, opts }
end
def on_refresh &block
@refresh_listeners = [] unless defined? @refresh_listeners
@refresh_listeners << block
end
alias orig_fetch_access_token! fetch_access_token!
def fetch_access_token! options = {}
unless options[:connection]
connection = build_default_connection
options = options.merge connection: connection if connection
end
info = retry_with_error do
orig_fetch_access_token! options
end
notify_refresh_listeners
info
end
def notify_refresh_listeners
listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
listeners.each do |block|
block.call self
end
end
def build_default_connection
if !defined?(@connection_info)
nil
elsif @connection_info.respond_to? :call
@connection_info.call
else
@connection_info
end
end
def retry_with_error max_retry_count = 5
retry_count = 0
begin
yield
rescue StandardError => e
raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
if retry_count < max_retry_count
retry_count += 1
sleep retry_count * 0.3
retry
else
msg = "Unexpected error: #{e.inspect}"
raise Signet::AuthorizationError, msg
end
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/stores/ 0000755 0000041 0000041 00000000000 14333131016 020117 5 ustar www-data www-data googleauth-1.3.0/lib/googleauth/stores/file_token_store.rb 0000644 0000041 0000041 00000002763 14333131016 024007 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "yaml/store"
require "googleauth/token_store"
module Google
module Auth
module Stores
# Implementation of user token storage backed by a local YAML file
class FileTokenStore < Google::Auth::TokenStore
# Create a new store with the supplied file.
#
# @param [String, File] file
# Path to storage file
def initialize options = {}
super()
path = options[:file]
@store = YAML::Store.new path
end
# (see Google::Auth::Stores::TokenStore#load)
def load id
@store.transaction { @store[id] }
end
# (see Google::Auth::Stores::TokenStore#store)
def store id, token
@store.transaction { @store[id] = token }
end
# (see Google::Auth::Stores::TokenStore#delete)
def delete id
@store.transaction { @store.delete id }
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/stores/redis_token_store.rb 0000644 0000041 0000041 00000004624 14333131016 024174 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "redis"
require "googleauth/token_store"
module Google
module Auth
module Stores
# Implementation of user token storage backed by Redis. Tokens
# are stored as JSON using the supplied key, prefixed with
# `g-user-token:`
class RedisTokenStore < Google::Auth::TokenStore
DEFAULT_KEY_PREFIX = "g-user-token:".freeze
# Create a new store with the supplied redis client.
#
# @param [::Redis, String] redis
# Initialized redis client to connect to.
# @param [String] prefix
# Prefix for keys in redis. Defaults to 'g-user-token:'
# @note If no redis instance is provided, a new one is created and
# the options passed through. You may include any other keys accepted
# by `Redis.new`
def initialize options = {}
super()
redis = options.delete :redis
prefix = options.delete :prefix
@redis = case redis
when Redis
redis
else
Redis.new options
end
@prefix = prefix || DEFAULT_KEY_PREFIX
end
# (see Google::Auth::Stores::TokenStore#load)
def load id
key = key_for id
@redis.get key
end
# (see Google::Auth::Stores::TokenStore#store)
def store id, token
key = key_for id
@redis.set key, token
end
# (see Google::Auth::Stores::TokenStore#delete)
def delete id
key = key_for id
@redis.del key
end
private
# Generate a redis key from a token ID
#
# @param [String] id
# ID of the token
# @return [String]
# Redis key
def key_for id
@prefix + id
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/id_tokens.rb 0000644 0000041 0000041 00000021074 14333131016 021110 0 ustar www-data www-data # frozen_string_literal: true
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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]
#
OIDC_ISSUERS = ["accounts.google.com", "https://accounts.google.com"].freeze
##
# A list of issuers expected for Google IAP-issued tokens.
#
# @return [Array]
#
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,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,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,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,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,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,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
googleauth-1.3.0/lib/googleauth/service_account.rb 0000644 0000041 0000041 00000020147 14333131016 022305 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/signet"
require "googleauth/credentials_loader"
require "googleauth/json_key_reader"
require "jwt"
require "multi_json"
require "stringio"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# Authenticates requests using Google's Service Account credentials via an
# OAuth access token.
#
# This class allows authorizing requests for service accounts directly
# from credentials from a json key file downloaded from the developer
# console (via 'Generate new Json Key').
#
# cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
extend CredentialsLoader
extend JsonKeyReader
attr_reader :project_id
attr_reader :quota_project_id
def enable_self_signed_jwt?
@enable_self_signed_jwt
end
# Creates a ServiceAccountCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def self.make_creds options = {}
json_key_io, scope, enable_self_signed_jwt, target_audience, audience, token_credential_uri =
options.values_at :json_key_io, :scope, :enable_self_signed_jwt, :target_audience,
:audience, :token_credential_uri
raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
if json_key_io
private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
else
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
quota_project_id = nil
end
project_id ||= CredentialsLoader.load_gcloud_project_id
new(token_credential_uri: token_credential_uri || TOKEN_CRED_URI,
audience: audience || TOKEN_CRED_URI,
scope: scope,
enable_self_signed_jwt: enable_self_signed_jwt,
target_audience: target_audience,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key),
project_id: project_id,
quota_project_id: quota_project_id)
.configure_connection(options)
end
# Handles certain escape sequences that sometimes appear in input.
# Specifically, interprets the "\n" sequence for newline, and removes
# enclosing quotes.
def self.unescape str
str = str.gsub '\n', "\n"
str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
str
end
def initialize options = {}
@project_id = options[:project_id]
@quota_project_id = options[:quota_project_id]
@enable_self_signed_jwt = options[:enable_self_signed_jwt] ? true : false
super options
end
# Extends the base class to use a transient
# ServiceAccountJwtHeaderCredentials for certain cases.
def apply! a_hash, opts = {}
# Use a self-singed JWT if there's no information that can be used to
# obtain an OAuth token, OR if there are scopes but also an assertion
# that they are default scopes that shouldn't be used to fetch a token.
if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
apply_self_signed_jwt! a_hash
else
super
end
end
private
def apply_self_signed_jwt! a_hash
# Use the ServiceAccountJwtHeaderCredentials using the same cred values
cred_json = {
private_key: @signing_key.to_s,
client_email: @issuer,
project_id: @project_id,
quota_project_id: @quota_project_id
}
key_io = StringIO.new MultiJson.dump(cred_json)
alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io, scope: scope
alt.apply! a_hash
end
end
# Authenticates requests using Google's Service Account credentials via
# JWT Header.
#
# This class allows authorizing requests for service accounts directly
# from credentials from a json key file downloaded from the developer
# console (via 'Generate new Json Key'). It is not part of any OAuth2
# flow, rather it creates a JWT and sends that as a credential.
#
# cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
class ServiceAccountJwtHeaderCredentials
JWT_AUD_URI_KEY = :jwt_aud_uri
AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
SIGNING_ALGORITHM = "RS256".freeze
EXPIRY = 60
extend CredentialsLoader
extend JsonKeyReader
attr_reader :project_id
attr_reader :quota_project_id
# Create a ServiceAccountJwtHeaderCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def self.make_creds options = {}
json_key_io, scope = options.values_at :json_key_io, :scope
new json_key_io: json_key_io, scope: scope
end
# Initializes a ServiceAccountJwtHeaderCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
def initialize options = {}
json_key_io = options[:json_key_io]
if json_key_io
@private_key, @issuer, @project_id, @quota_project_id =
self.class.read_json_key json_key_io
else
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
@quota_project_id = nil
end
@project_id ||= CredentialsLoader.load_gcloud_project_id
@signing_key = OpenSSL::PKey::RSA.new @private_key
@scope = options[:scope]
end
# Construct a jwt token if the JWT_AUD_URI key is present in the input
# hash.
#
# The jwt token is used as the value of a 'Bearer '.
def apply! a_hash, opts = {}
jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
return a_hash if jwt_aud_uri.nil? && @scope.nil?
jwt_token = new_jwt_token jwt_aud_uri, opts
a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
a_hash
end
# Returns a clone of a_hash updated with the authoriation header
def apply a_hash, opts = {}
a_copy = a_hash.clone
apply! a_copy, opts
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
proc { |a_hash, opts = {}| apply a_hash, opts }
end
protected
# Creates a jwt uri token.
def new_jwt_token jwt_aud_uri = nil, options = {}
now = Time.new
skew = options[:skew] || 60
assertion = {
"iss" => @issuer,
"sub" => @issuer,
"exp" => (now + EXPIRY).to_i,
"iat" => (now - skew).to_i
}
jwt_aud_uri = nil if @scope
assertion["scope"] = Array(@scope).join " " if @scope
assertion["aud"] = jwt_aud_uri if jwt_aud_uri
JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
end
end
end
end
googleauth-1.3.0/lib/googleauth/default_credentials.rb 0000644 0000041 0000041 00000005160 14333131016 023130 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "multi_json"
require "stringio"
require "googleauth/credentials_loader"
require "googleauth/service_account"
require "googleauth/user_refresh"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# DefaultCredentials is used to preload the credentials file, to determine
# which type of credentials should be loaded.
class DefaultCredentials
extend CredentialsLoader
# override CredentialsLoader#make_creds to use the class determined by
# loading the json.
def self.make_creds options = {}
json_key_io = options[:json_key_io]
if json_key_io
json_key, clz = determine_creds_class json_key_io
warn_if_cloud_sdk_credentials json_key["client_id"]
io = StringIO.new MultiJson.dump(json_key)
clz.make_creds options.merge(json_key_io: io)
else
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
clz = read_creds
clz.make_creds options
end
end
def self.read_creds
env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
type = ENV[env_var]
raise "#{env_var} is undefined in env" unless type
case type
when "service_account"
ServiceAccountCredentials
when "authorized_user"
UserRefreshCredentials
else
raise "credentials type '#{type}' is not supported"
end
end
# Reads the input json and determines which creds class to use.
def self.determine_creds_class json_key_io
json_key = MultiJson.load json_key_io.read
key = "type"
raise "the json is missing the '#{key}' field" unless json_key.key? key
type = json_key[key]
case type
when "service_account"
[json_key, ServiceAccountCredentials]
when "authorized_user"
[json_key, UserRefreshCredentials]
else
raise "credentials type '#{type}' is not supported"
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/token_store.rb 0000644 0000041 0000041 00000003115 14333131016 021461 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Google
module Auth
# Interface definition for token stores. It is not required that
# implementations inherit from this class. It is provided for documentation
# purposes to illustrate the API contract.
class TokenStore
class << self
attr_accessor :default
end
# Load the token data from storage for the given ID.
#
# @param [String] id
# ID of token data to load.
# @return [String]
# The loaded token data.
def load _id
raise "Not implemented"
end
# Put the token data into storage for the given ID.
#
# @param [String] id
# ID of token data to store.
# @param [String] token
# The token data to store.
def store _id, _token
raise "Not implemented"
end
# Remove the token data from storage for the given ID.
#
# @param [String] id
# ID of the token data to delete
def delete _id
raise "Not implemented"
end
end
end
end
googleauth-1.3.0/lib/googleauth/credentials_loader.rb 0000644 0000041 0000041 00000020201 14333131016 022743 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "os"
require "rbconfig"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# CredentialsLoader contains the behaviour used to locate and find default
# credentials files on the file system.
module CredentialsLoader
ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS".freeze
PRIVATE_KEY_VAR = "GOOGLE_PRIVATE_KEY".freeze
CLIENT_EMAIL_VAR = "GOOGLE_CLIENT_EMAIL".freeze
CLIENT_ID_VAR = "GOOGLE_CLIENT_ID".freeze
CLIENT_SECRET_VAR = "GOOGLE_CLIENT_SECRET".freeze
REFRESH_TOKEN_VAR = "GOOGLE_REFRESH_TOKEN".freeze
ACCOUNT_TYPE_VAR = "GOOGLE_ACCOUNT_TYPE".freeze
PROJECT_ID_VAR = "GOOGLE_PROJECT_ID".freeze
GCLOUD_POSIX_COMMAND = "gcloud".freeze
GCLOUD_WINDOWS_COMMAND = "gcloud.cmd".freeze
GCLOUD_CONFIG_COMMAND = "config config-helper --format json --verbosity none".freeze
CREDENTIALS_FILE_NAME = "application_default_credentials.json".freeze
NOT_FOUND_ERROR = "Unable to read the credential file specified by #{ENV_VAR}".freeze
WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}".freeze
WELL_KNOWN_ERROR = "Unable to read the default credential file".freeze
SYSTEM_DEFAULT_ERROR = "Unable to read the system default credential file".freeze
CLOUD_SDK_CLIENT_ID = "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.app" \
"s.googleusercontent.com".freeze
CLOUD_SDK_CREDENTIALS_WARNING =
"Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most " \
"server applications use service accounts instead. If your application continues to use end user credentials " \
'from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about ' \
"service accounts, see https://cloud.google.com/docs/authentication/. To suppress this message, set the " \
"GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS environment variable.".freeze
# make_creds proxies the construction of a credentials instance
#
# By default, it calls #new on the current class, but this behaviour can
# be modified, allowing different instances to be created.
def make_creds *args
creds = new(*args)
creds = creds.configure_connection args[0] if creds.respond_to?(:configure_connection) && args.size == 1
creds
end
# Creates an instance from the path specified in an environment
# variable.
#
# @param scope [string|array|nil] the scope(s) to access
# @param options [Hash] Connection options. These may be used to configure
# how OAuth tokens are retrieved, by providing a suitable
# `Faraday::Connection`. For example, if a connection proxy must be
# used in the current network, you may provide a connection with
# with the needed proxy options.
# The following keys are recognized:
# * `:default_connection` The connection object to use.
# * `:connection_builder` A `Proc` that returns a connection.
def from_env scope = nil, options = {}
options = interpret_options scope, options
if ENV.key?(ENV_VAR) && !ENV[ENV_VAR].empty?
path = ENV[ENV_VAR]
raise "file #{path} does not exist" unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
elsif service_account_env_vars? || authorized_user_env_vars?
make_creds options
end
rescue StandardError => e
raise "#{NOT_FOUND_ERROR}: #{e}"
end
# Creates an instance from a well known path.
#
# @param scope [string|array|nil] the scope(s) to access
# @param options [Hash] Connection options. These may be used to configure
# how OAuth tokens are retrieved, by providing a suitable
# `Faraday::Connection`. For example, if a connection proxy must be
# used in the current network, you may provide a connection with
# with the needed proxy options.
# The following keys are recognized:
# * `:default_connection` The connection object to use.
# * `:connection_builder` A `Proc` that returns a connection.
def from_well_known_path scope = nil, options = {}
options = interpret_options scope, options
home_var = OS.windows? ? "APPDATA" : "HOME"
base = WELL_KNOWN_PATH
root = ENV[home_var].nil? ? "" : ENV[home_var]
base = File.join ".config", base unless OS.windows?
path = File.join root, base
return nil unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
rescue StandardError => e
raise "#{WELL_KNOWN_ERROR}: #{e}"
end
# Creates an instance from the system default path
#
# @param scope [string|array|nil] the scope(s) to access
# @param options [Hash] Connection options. These may be used to configure
# how OAuth tokens are retrieved, by providing a suitable
# `Faraday::Connection`. For example, if a connection proxy must be
# used in the current network, you may provide a connection with
# with the needed proxy options.
# The following keys are recognized:
# * `:default_connection` The connection object to use.
# * `:connection_builder` A `Proc` that returns a connection.
def from_system_default_path scope = nil, options = {}
options = interpret_options scope, options
if OS.windows?
return nil unless ENV["ProgramData"]
prefix = File.join ENV["ProgramData"], "Google/Auth"
else
prefix = "/etc/google/auth/"
end
path = File.join prefix, CREDENTIALS_FILE_NAME
return nil unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
rescue StandardError => e
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
end
module_function
# Issues warning if cloud sdk client id is used
def warn_if_cloud_sdk_credentials client_id
return if ENV["GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS"]
warn CLOUD_SDK_CREDENTIALS_WARNING if client_id == CLOUD_SDK_CLIENT_ID
end
# Finds project_id from gcloud CLI configuration
def load_gcloud_project_id
gcloud = GCLOUD_WINDOWS_COMMAND if OS.windows?
gcloud = GCLOUD_POSIX_COMMAND unless OS.windows?
gcloud_json = IO.popen("#{gcloud} #{GCLOUD_CONFIG_COMMAND}", in: :close, err: :close, &:read)
config = MultiJson.load gcloud_json
config["configuration"]["properties"]["core"]["project"]
rescue StandardError
nil
end
private
def interpret_options scope, options
if scope.is_a? Hash
options = scope
scope = nil
end
return options.merge scope: scope if scope && !options[:scope]
options
end
def service_account_env_vars?
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty? &&
!ENV.to_h.fetch_values(PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR).join(" ").empty?
end
def authorized_user_env_vars?
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] - ENV.keys).empty? &&
!ENV.to_h.fetch_values(CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR).join(" ").empty?
end
end
end
end
googleauth-1.3.0/lib/googleauth/iam.rb 0000644 0000041 0000041 00000003513 14333131016 017675 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/signet"
require "googleauth/credentials_loader"
require "multi_json"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# Authenticates requests using IAM credentials.
class IAMCredentials
SELECTOR_KEY = "x-goog-iam-authority-selector".freeze
TOKEN_KEY = "x-goog-iam-authorization-token".freeze
# Initializes an IAMCredentials.
#
# @param selector the IAM selector.
# @param token the IAM token.
def initialize selector, token
raise TypeError unless selector.is_a? String
raise TypeError unless token.is_a? String
@selector = selector
@token = token
end
# Adds the credential fields to the hash.
def apply! a_hash
a_hash[SELECTOR_KEY] = @selector
a_hash[TOKEN_KEY] = @token
a_hash
end
# Returns a clone of a_hash updated with the authoriation header
def apply a_hash
a_copy = a_hash.clone
apply! a_copy
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
proc { |a_hash, _opts = {}| apply a_hash }
end
end
end
end
googleauth-1.3.0/lib/googleauth/user_refresh.rb 0000644 0000041 0000041 00000010671 14333131016 021626 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/signet"
require "googleauth/credentials_loader"
require "googleauth/scope_util"
require "multi_json"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# Authenticates requests using User Refresh credentials.
#
# This class allows authorizing requests from user refresh tokens.
#
# This the end of the result of a 3LO flow. E.g, the end result of
# 'gcloud auth login' saves a file with these contents in well known
# location
#
# cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
class UserRefreshCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = "https://oauth2.googleapis.com/token".freeze
AUTHORIZATION_URI = "https://accounts.google.com/o/oauth2/auth".freeze
REVOKE_TOKEN_URI = "https://oauth2.googleapis.com/revoke".freeze
extend CredentialsLoader
attr_reader :project_id
attr_reader :quota_project_id
# Create a UserRefreshCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def self.make_creds options = {}
json_key_io, scope = options.values_at :json_key_io, :scope
user_creds = read_json_key json_key_io if json_key_io
user_creds ||= {
"client_id" => ENV[CredentialsLoader::CLIENT_ID_VAR],
"client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
"refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
"project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
"quota_project_id" => nil
}
new(token_credential_uri: TOKEN_CRED_URI,
client_id: user_creds["client_id"],
client_secret: user_creds["client_secret"],
refresh_token: user_creds["refresh_token"],
project_id: user_creds["project_id"],
quota_project_id: user_creds["quota_project_id"],
scope: scope)
.configure_connection(options)
end
# Reads the client_id, client_secret and refresh_token fields from the
# JSON key.
def self.read_json_key json_key_io
json_key = MultiJson.load json_key_io.read
wanted = ["client_id", "client_secret", "refresh_token"]
wanted.each do |key|
raise "the json is missing the #{key} field" unless json_key.key? key
end
json_key
end
def initialize options = {}
options ||= {}
options[:token_credential_uri] ||= TOKEN_CRED_URI
options[:authorization_uri] ||= AUTHORIZATION_URI
@project_id = options[:project_id]
@project_id ||= CredentialsLoader.load_gcloud_project_id
@quota_project_id = options[:quota_project_id]
super options
end
# Revokes the credential
def revoke! options = {}
c = options[:connection] || Faraday.default_connection
retry_with_error do
resp = c.post(REVOKE_TOKEN_URI, token: refresh_token || access_token)
case resp.status
when 200
self.access_token = nil
self.refresh_token = nil
self.expires_at = 0
else
raise(Signet::AuthorizationError,
"Unexpected error code #{resp.status}")
end
end
end
# Verifies that a credential grants the requested scope
#
# @param [Array, String] required_scope
# Scope to verify
# @return [Boolean]
# True if scope is granted
def includes_scope? required_scope
missing_scope = Google::Auth::ScopeUtil.normalize(required_scope) -
Google::Auth::ScopeUtil.normalize(scope)
missing_scope.empty?
end
end
end
end
googleauth-1.3.0/lib/googleauth/compute_engine.rb 0000644 0000041 0000041 00000010726 14333131016 022134 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "faraday"
require "googleauth/signet"
require "memoist"
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
NO_METADATA_SERVER_ERROR = <<~ERROR.freeze
Error code 404 trying to get security access token
from Compute Engine metadata for the default service account. This
may be because the virtual machine instance does not have permission
scopes specified.
ERROR
UNEXPECTED_ERROR_SUFFIX = <<~ERROR.freeze
trying to get security access token from Compute Engine metadata for
the default service account
ERROR
# Extends Signet::OAuth2::Client so that the auth token is obtained from
# the GCE metadata server.
class GCECredentials < Signet::OAuth2::Client
# The IP Address is used in the URIs to speed up failures on non-GCE
# systems.
DEFAULT_METADATA_HOST = "169.254.169.254".freeze
# @private Unused and deprecated
COMPUTE_AUTH_TOKEN_URI =
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
# @private Unused and deprecated
COMPUTE_ID_TOKEN_URI =
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
# @private Unused and deprecated
COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
class << self
extend Memoist
def metadata_host
ENV.fetch "GCE_METADATA_HOST", DEFAULT_METADATA_HOST
end
def compute_check_uri
"http://#{metadata_host}".freeze
end
def compute_auth_token_uri
"#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/token".freeze
end
def compute_id_token_uri
"#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/identity".freeze
end
# Detect if this appear to be a GCE instance, by checking if metadata
# is available.
def on_gce? options = {}
# TODO: This should use google-cloud-env instead.
c = options[:connection] || Faraday.default_connection
headers = { "Metadata-Flavor" => "Google" }
resp = c.get compute_check_uri, nil, headers do |req|
req.options.timeout = 1.0
req.options.open_timeout = 0.1
end
return false unless resp.status == 200
resp.headers["Metadata-Flavor"] == "Google"
rescue Faraday::TimeoutError, Faraday::ConnectionFailed
false
end
memoize :on_gce?
end
# Overrides the super class method to change how access tokens are
# fetched.
def fetch_access_token options = {}
c = options[:connection] || Faraday.default_connection
retry_with_error do
uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
query[:scopes] = Array(scope).join "," if scope
resp = c.get uri, query, "Metadata-Flavor" => "Google"
case resp.status
when 200
content_type = resp.headers["content-type"]
if ["text/html", "application/text"].include? content_type
{ (target_audience ? "id_token" : "access_token") => resp.body }
else
Signet::OAuth2.parse_credentials resp.body, content_type
end
when 403, 500
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
raise Signet::UnexpectedStatusError, msg
when 404
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
else
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
raise Signet::AuthorizationError, msg
end
end
end
end
end
end
googleauth-1.3.0/lib/googleauth/user_authorizer.rb 0000644 0000041 0000041 00000024602 14333131016 022363 0 ustar www-data www-data # Copyright 2014 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "uri"
require "multi_json"
require "googleauth/signet"
require "googleauth/user_refresh"
module Google
module Auth
# Handles an interactive 3-Legged-OAuth2 (3LO) user consent authorization.
#
# Example usage for a simple command line app:
#
# credentials = authorizer.get_credentials(user_id)
# if credentials.nil?
# url = authorizer.get_authorization_url(
# base_url: OOB_URI)
# puts "Open the following URL in the browser and enter the " +
# "resulting code after authorization"
# puts url
# code = gets
# credentials = authorizer.get_and_store_credentials_from_code(
# user_id: user_id, code: code, base_url: OOB_URI)
# end
# # Credentials ready to use, call APIs
# ...
class UserAuthorizer
MISMATCHED_CLIENT_ID_ERROR =
"Token client ID of %s does not match configured client id %s".freeze
NIL_CLIENT_ID_ERROR = "Client id can not be nil.".freeze
NIL_SCOPE_ERROR = "Scope can not be nil.".freeze
NIL_USER_ID_ERROR = "User ID can not be nil.".freeze
NIL_TOKEN_STORE_ERROR = "Can not call method if token store is nil".freeze
MISSING_ABSOLUTE_URL_ERROR =
'Absolute base url required for relative callback url "%s"'.freeze
# Initialize the authorizer
#
# @param [Google::Auth::ClientID] client_id
# Configured ID & secret for this application
# @param [String, Array] scope
# Authorization scope to request
# @param [Google::Auth::Stores::TokenStore] token_store
# Backing storage for persisting user credentials
# @param [String] callback_uri
# URL (either absolute or relative) of the auth callback.
# Defaults to '/oauth2callback'
def initialize client_id, scope, token_store, callback_uri = nil
raise NIL_CLIENT_ID_ERROR if client_id.nil?
raise NIL_SCOPE_ERROR if scope.nil?
@client_id = client_id
@scope = Array(scope)
@token_store = token_store
@callback_uri = callback_uri || "/oauth2callback"
end
# Build the URL for requesting authorization.
#
# @param [String] login_hint
# Login hint if need to authorize a specific account. Should be a
# user's email address or unique profile ID.
# @param [String] state
# Opaque state value to be returned to the oauth callback.
# @param [String] base_url
# Absolute URL to resolve the configured callback uri against. Required
# if the configured callback uri is a relative.
# @param [String, Array] scope
# Authorization scope to request. Overrides the instance scopes if not
# nil.
# @return [String]
# Authorization url
def get_authorization_url options = {}
scope = options[:scope] || @scope
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_secret: @client_id.secret,
scope: scope
)
redirect_uri = redirect_uri_for options[:base_url]
url = credentials.authorization_uri(access_type: "offline",
redirect_uri: redirect_uri,
approval_prompt: "force",
state: options[:state],
include_granted_scopes: true,
login_hint: options[:login_hint])
url.to_s
end
# Fetch stored credentials for the user.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [Array, String] scope
# If specified, only returns credentials that have all
# the requested scopes
# @return [Google::Auth::UserRefreshCredentials]
# Stored credentials, nil if none present
def get_credentials user_id, scope = nil
saved_token = stored_token user_id
return nil if saved_token.nil?
data = MultiJson.load saved_token
if data.fetch("client_id", @client_id.id) != @client_id.id
raise format(MISMATCHED_CLIENT_ID_ERROR,
data["client_id"], @client_id.id)
end
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_secret: @client_id.secret,
scope: data["scope"] || @scope,
access_token: data["access_token"],
refresh_token: data["refresh_token"],
expires_at: data.fetch("expiration_time_millis", 0) / 1000
)
scope ||= @scope
return monitor_credentials user_id, credentials if credentials.includes_scope? scope
nil
end
# Exchanges an authorization code returned in the oauth callback
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [String] code
# The authorization code from the OAuth callback
# @param [String, Array] scope
# Authorization scope requested. Overrides the instance
# scopes if not nil.
# @param [String] base_url
# Absolute URL to resolve the configured callback uri against.
# Required if the configured
# callback uri is a relative.
# @return [Google::Auth::UserRefreshCredentials]
# Credentials if exchange is successful
def get_credentials_from_code options = {}
user_id = options[:user_id]
code = options[:code]
scope = options[:scope] || @scope
base_url = options[:base_url]
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_secret: @client_id.secret,
redirect_uri: redirect_uri_for(base_url),
scope: scope
)
credentials.code = code
credentials.fetch_access_token!({})
monitor_credentials user_id, credentials
end
# Exchanges an authorization code returned in the oauth callback.
# Additionally, stores the resulting credentials in the token store if
# the exchange is successful.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [String] code
# The authorization code from the OAuth callback
# @param [String, Array] scope
# Authorization scope requested. Overrides the instance
# scopes if not nil.
# @param [String] base_url
# Absolute URL to resolve the configured callback uri against.
# Required if the configured
# callback uri is a relative.
# @return [Google::Auth::UserRefreshCredentials]
# Credentials if exchange is successful
def get_and_store_credentials_from_code options = {}
credentials = get_credentials_from_code options
store_credentials options[:user_id], credentials
end
# Revokes a user's credentials. This both revokes the actual
# grant as well as removes the token from the token store.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
def revoke_authorization user_id
credentials = get_credentials user_id
if credentials
begin
@token_store.delete user_id
ensure
credentials.revoke!
end
end
nil
end
# Store credentials for a user. Generally not required to be
# called directly, but may be used to migrate tokens from one
# store to another.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [Google::Auth::UserRefreshCredentials] credentials
# Credentials to store.
def store_credentials user_id, credentials
json = MultiJson.dump(
client_id: credentials.client_id,
access_token: credentials.access_token,
refresh_token: credentials.refresh_token,
scope: credentials.scope,
expiration_time_millis: credentials.expires_at.to_i * 1000
)
@token_store.store user_id, json
credentials
end
private
# @private Fetch stored token with given user_id
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @return [String] The saved token from @token_store
def stored_token user_id
raise NIL_USER_ID_ERROR if user_id.nil?
raise NIL_TOKEN_STORE_ERROR if @token_store.nil?
@token_store.load user_id
end
# Begin watching a credential for refreshes so the access token can be
# saved.
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @param [Google::Auth::UserRefreshCredentials] credentials
# Credentials to store.
def monitor_credentials user_id, credentials
credentials.on_refresh do |cred|
store_credentials user_id, cred
end
credentials
end
# Resolve the redirect uri against a base.
#
# @param [String] base_url
# Absolute URL to resolve the callback against if necessary.
# @return [String]
# Redirect URI
def redirect_uri_for base_url
return @callback_uri if uri_is_postmessage?(@callback_uri) || !URI(@callback_uri).scheme.nil?
raise format(MISSING_ABSOLUTE_URL_ERROR, @callback_uri) if base_url.nil? || URI(base_url).scheme.nil?
URI.join(base_url, @callback_uri).to_s
end
# Check if URI is Google's postmessage flow (not a valid redirect_uri by spec, but allowed)
def uri_is_postmessage? uri
uri.to_s.casecmp("postmessage").zero?
end
end
end
end
googleauth-1.3.0/lib/googleauth/json_key_reader.rb 0000644 0000041 0000041 00000002404 14333131016 022270 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
# JsonKeyReader contains the behaviour used to read private key and
# client email fields from the service account
module JsonKeyReader
def read_json_key json_key_io
json_key = MultiJson.load json_key_io.read
raise "missing client_email" unless json_key.key? "client_email"
raise "missing private_key" unless json_key.key? "private_key"
[
json_key["private_key"],
json_key["client_email"],
json_key["project_id"],
json_key["quota_project_id"]
]
end
end
end
end
googleauth-1.3.0/lib/googleauth.rb 0000644 0000041 0000041 00000001500 14333131016 017121 0 ustar www-data www-data # Copyright 2015 Google, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "googleauth/application_default"
require "googleauth/client_id"
require "googleauth/credentials"
require "googleauth/default_credentials"
require "googleauth/id_tokens"
require "googleauth/user_authorizer"
require "googleauth/web_user_authorizer"
googleauth-1.3.0/.yardopts 0000644 0000041 0000041 00000000222 14333131016 015536 0 ustar www-data www-data --no-private
--title=Google Auth
--markup markdown
--markup-provider redcarpet
./lib/**/*.rb
-
README.md
CHANGELOG.md
CODE_OF_CONDUCT.md
LICENSE
googleauth-1.3.0/SECURITY.md 0000644 0000041 0000041 00000000511 14333131016 015462 0 ustar www-data www-data # Security Policy
To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
The Google Security Team will respond within 5 working days of your report on g.co/vulnz.
We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.