Skip to content

Commit

Permalink
Merge pull request #54 from sqrrrl/0.5-dev
Browse files Browse the repository at this point in the history
Prep for 0.5 release
  • Loading branch information
tbetbetbe committed Dec 12, 2015
2 parents 273be13 + 9be6475 commit c75d739
Show file tree
Hide file tree
Showing 15 changed files with 244 additions and 244 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ rvm:
- jruby
matrix:
allow_failures:
- rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
- rvm: rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
script: "bundle exec rake"
addons:
apt:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.5.0 (12/10/2015)

### Changes

* Initial support for user credentials ([@sqrrrl][])
* Update Signet to 0.7

## 0.4.2 (05/08/2015)

### Changes
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ group :development do
gem 'fakeredis', '~> 0.5'
gem 'webmock', '~> 1.21'
gem 'rack-test', '~> 0.6'
gem 'sinatra'
end

platforms :jruby do
Expand Down
81 changes: 80 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ $ gem install googleauth
require 'googleauth'

# Get the environment configured authorization
scopes = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/compute']
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
Expand All @@ -61,6 +62,84 @@ 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 Idenity 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(user_id: 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'

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)

credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(base_url: 'urn:ietf:wg:oauth:2.0:oob')
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
```

### 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](lib/googleauth/token_store.rb) for additional details.

## What about auth in google-apis-ruby-client?

The goal is for all auth done by
Expand Down
2 changes: 1 addition & 1 deletion googleauth.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ Gem::Specification.new do |s|
s.add_dependency 'jwt', '~> 1.4'
s.add_dependency 'memoist', '~> 0.12'
s.add_dependency 'multi_json', '~> 1.11'
s.add_dependency 'signet', '~> 0.6'
s.add_dependency 'signet', '~> 0.7'
end
4 changes: 2 additions & 2 deletions lib/googleauth/signet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Client
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 access_token.nil? || expired?
fetch_access_token!(opts) if access_token.nil? || expires_within?(60)
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
end

Expand All @@ -65,7 +65,7 @@ def on_refresh(&block)
end

alias_method :orig_fetch_access_token!, :fetch_access_token!
def fetch_access_token!(options)
def fetch_access_token!(options = {})
info = orig_fetch_access_token!(options)
notify_refresh_listeners
info
Expand Down
2 changes: 1 addition & 1 deletion lib/googleauth/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
VERSION = '0.4.2'
VERSION = '0.5.0'
end
end
9 changes: 5 additions & 4 deletions lib/googleauth/web_user_authorizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ module Auth
# user_id = request.session['user_email']
# credentials = authorizer.get_credentials(user_id, request)
# if credentials.nil?
# redirect authorizer.get_redirect_uri(user_id, request)
# redirect authorizer.get_authorization_url(user_id: user_id,
# request: request)
# end
# # Credentials are valid, can call APIs
# ...
# end
#
# get('/oauth2callback') do
# user_id = request.session['user_email']
# _, return_uri = authorizer.handle_auth_callback(user_id, request)
# redirect return_uri
# url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
# request)
# redirect url
# end
#
# Instead of implementing the callback directly, applications are
Expand Down
85 changes: 25 additions & 60 deletions spec/googleauth/apply_auth_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,6 @@
require 'faraday'
require 'spec_helper'

def build_json_response(payload)
[200,
{ 'Content-Type' => 'application/json; charset=utf-8' },
MultiJson.dump(payload)]
end

def build_access_token_json(token)
build_json_response('access_token' => token,
'token_type' => 'Bearer',
'expires_in' => 3600)
end

shared_examples 'apply/apply! are OK' do
let(:auth_key) { :Authorization }

Expand All @@ -57,124 +45,101 @@ def build_access_token_json(token)
# auth client
describe '#fetch_access_token' do
let(:token) { '1/abcdef1234567890' }
let(:stubs) do
let(:stub) do
make_auth_stubs access_token: token
end
let(:connection) do
Faraday.new do |b|
b.adapter(:test, stubs)
end
end

it 'should set access_token to the fetched value' do
@client.fetch_access_token!(connection: connection)
stub
@client.fetch_access_token!
expect(@client.access_token).to eq(token)
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end

it 'should notify refresh listeners after updating' do
stub
expect do |b|
@client.on_refresh(&b)
@client.fetch_access_token!(connection: connection)
@client.fetch_access_token!
end.to yield_with_args(have_attributes(
access_token: '1/abcdef1234567890'))
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end
end

describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
stub = make_auth_stubs access_token: token

md = { foo: 'bar' }
@client.apply!(md, connection: c)
@client.apply!(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(md).to eq(want)
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end
end

describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end

stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
the_proc = @client.updater_proc
got = the_proc.call(md, connection: c)
got = the_proc.call(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end
end

describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
stub = make_auth_stubs access_token: token

md = { foo: 'bar' }
@client.apply(md, connection: c)
@client.apply(md)
want = { foo: 'bar' }
expect(md).to eq(want)
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end

it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
stub = make_auth_stubs access_token: token

md = { foo: 'bar' }
got = @client.apply(md, connection: c)
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end

it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
stub = make_auth_stubs access_token: token

n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
end
stubs.verify_stubbed_calls
expect(stub).to have_been_requested
end

it 'should fetch a new token if the current one is expired' do
token_1 = '1/abcdef1234567890'
token_2 = '2/abcdef1234567890'
token_2 = '2/abcdef1234567891'

[token_1, token_2].each do |t|
stubs = make_auth_stubs access_token: t
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
make_auth_stubs access_token: t
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{t}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
@client.expires_at -= 3601 # default is to expire in 1hr
end
end
Expand Down
Loading

0 comments on commit c75d739

Please sign in to comment.