authinator 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +2 -2
- data/lib/authinator.rb +2 -0
- data/lib/authinator/auth_code_exchanger.rb +80 -80
- data/lib/authinator/client_token_issuer.rb +4 -0
- data/lib/authinator/end_point_listener.rb +58 -0
- data/lib/authinator/version.rb +1 -1
- data/spec/authinator/{authinator_spec.rb → auth_code_exchanger_spec.rb} +6 -17
- data/spec/authinator/client_token_issuer_spec.rb +8 -0
- data/spec/authinator/end_point_listener_spec.rb +71 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3b66719b8c7bb8dd6f60aa7e7ed026b661672f3
|
4
|
+
data.tar.gz: f9d3351fe755049be4cf3ed7b2c1cf3ab8dd6876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d3cdbe4ffd69f2fcfab7931eefc43ebbadc7214f02207bf42ad822d5330a62dbe2f6a46fc3a1c16239da4c6a33e180d7f021378252112c87b1bb40a2508cef3
|
7
|
+
data.tar.gz: cc7c802bae55aaf6e44a4c3ef220bdbfa4ab8ae6014a2236e841d27c4235dd53e98938f49f729560121988bd4edbf19cf0f1cb7450006d841297bde4b899e99c
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/lib/authinator.rb
CHANGED
@@ -2,99 +2,99 @@
|
|
2
2
|
# require 'omniauth-oauth2'
|
3
3
|
require 'oauth2'
|
4
4
|
|
5
|
-
|
6
|
-
class AuthCodeExchanger
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
PROVIDER_HASHES = {
|
21
|
-
google: {
|
22
|
-
client_id: 'cl_id',
|
23
|
-
client_secret: 'cl_sec',
|
24
|
-
site: 'https://accounts.google.com',
|
25
|
-
token_url: '/o/oauth2/token',
|
26
|
-
},
|
27
|
-
stub: {
|
28
|
-
client_id: 'cl_id',
|
29
|
-
client_secret: 'cl_sec',
|
30
|
-
site: 'https://example.org',
|
31
|
-
token_url: '/extoken',
|
32
|
-
},
|
33
|
-
}
|
34
|
-
|
35
|
-
def initialize(provider, client_options = {})
|
36
|
-
unless VALID_PROVIDERS.include? provider
|
37
|
-
fail ArgumentError,
|
38
|
-
"Provider #{provider} not in supported parameter list:\n" <<
|
39
|
-
VALID_PROVIDERS.inspect
|
5
|
+
module Authinator
|
6
|
+
class AuthCodeExchanger
|
7
|
+
VALID_PROVIDERS = [:stub, :google]
|
8
|
+
STUB_SAMPLE_TOKEN = {
|
9
|
+
token: 'ya29.token',
|
10
|
+
refresh_token: '1/refresh',
|
11
|
+
expires_in: 3600,
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :provider
|
15
|
+
|
16
|
+
def self.valid_providers
|
17
|
+
VALID_PROVIDERS
|
40
18
|
end
|
41
19
|
|
42
|
-
|
43
|
-
|
44
|
-
|
20
|
+
PROVIDER_HASHES = {
|
21
|
+
google: {
|
22
|
+
client_id: 'cl_id',
|
23
|
+
client_secret: 'cl_sec',
|
24
|
+
site: 'https://accounts.google.com',
|
25
|
+
token_url: '/o/oauth2/token',
|
26
|
+
},
|
27
|
+
stub: {
|
28
|
+
client_id: 'cl_id',
|
29
|
+
client_secret: 'cl_sec',
|
30
|
+
site: 'https://example.org',
|
31
|
+
token_url: '/extoken',
|
32
|
+
},
|
33
|
+
}
|
34
|
+
|
35
|
+
def initialize(provider, client_options = {})
|
36
|
+
unless VALID_PROVIDERS.include? provider
|
37
|
+
fail ArgumentError,
|
38
|
+
"Provider #{provider} not in supported parameter list:\n" <<
|
39
|
+
VALID_PROVIDERS.inspect
|
40
|
+
end
|
41
|
+
|
42
|
+
@provider = provider
|
43
|
+
build_provider_hash(client_options)
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def site_token_url
|
47
|
+
@provider_hash[:site] + @provider_hash[:token_url]
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
def exchange(auth_code)
|
51
|
+
# auth_code = params[:code]
|
52
|
+
return if auth_code.nil? || auth_code.empty?
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
case @provider.to_sym
|
55
|
+
when :google
|
56
|
+
exchange_with_google(auth_code)
|
57
|
+
when :stub
|
58
|
+
exchange_with_stub(auth_code)
|
59
|
+
end
|
59
60
|
end
|
60
|
-
end
|
61
61
|
|
62
|
-
private
|
62
|
+
private
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
def build_provider_hash(client_options)
|
65
|
+
@provider_hash = PROVIDER_HASHES[provider]
|
66
|
+
@provider_hash = client_options.delete(:client_id) if client_options[:client_id]
|
67
|
+
@provider_hash = client_options.delete(:client_secret) if client_options[:client_secret]
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
70
|
+
def exchange_with_google(code)
|
71
|
+
provider_hash = @provider_hash.dup
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
client_id = provider_hash.delete(:client_id)
|
74
|
+
client_secret = provider_hash.delete(:client_secret)
|
75
|
+
@client = OAuth2::Client.new(client_id, client_secret, provider_hash)
|
76
76
|
|
77
|
-
|
77
|
+
token = @client.auth_code.get_token(code)
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
# response = token.get('/api/resource', :params => { 'query_foo' => 'bar' })
|
80
|
+
# response.class.name
|
81
|
+
# => OAuth2::Response
|
82
82
|
|
83
|
-
|
84
|
-
|
83
|
+
token
|
84
|
+
end
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
86
|
+
def exchange_with_stub(_code)
|
87
|
+
@client = OAuth2::Client.new(
|
88
|
+
@provider_hash[:client_id],
|
89
|
+
@provider_hash[:client_secret],
|
90
|
+
)
|
91
|
+
|
92
|
+
OAuth2::AccessToken.new(
|
93
|
+
@client,
|
94
|
+
STUB_SAMPLE_TOKEN[:token],
|
95
|
+
refresh_token: STUB_SAMPLE_TOKEN[:refresh_token],
|
96
|
+
expires_in: STUB_SAMPLE_TOKEN[:expires_in],
|
97
|
+
)
|
98
|
+
end
|
98
99
|
end
|
99
100
|
end
|
100
|
-
# end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Authinator
|
2
|
+
class EndPointListener
|
3
|
+
attr_reader :email, :provider, :auth_code, :options, :errors
|
4
|
+
|
5
|
+
def initialize(hash = {})
|
6
|
+
@email = hash.delete :email
|
7
|
+
@provider = hash.delete :provider
|
8
|
+
@auth_code = hash.delete :auth_code
|
9
|
+
@options = hash
|
10
|
+
@errors = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
validator_presence? &&
|
15
|
+
validator_valid_email? &&
|
16
|
+
validator_valid_provider?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validator_presence?
|
22
|
+
email_present = present? @email
|
23
|
+
provider_present = present? @provider
|
24
|
+
auth_code_present = present? @auth_code
|
25
|
+
|
26
|
+
return true if email_present && provider_present && auth_code_present
|
27
|
+
errors << 'A required param is missing'
|
28
|
+
errors << '"email" field missing' unless email_present
|
29
|
+
errors << '"provider" field missing' unless provider_present
|
30
|
+
errors << '"auth_code" field missing' unless auth_code_present
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Regexp lovingly borrowed from https://github.com/balexand/email_validator
|
35
|
+
def validator_valid_email?
|
36
|
+
name_validation = @options[:strict_mode] ? '-a-z0-9+._' : '^@\\s'
|
37
|
+
regexp = /\A\s*([#{name_validation}]{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i
|
38
|
+
return true if @email =~ regexp
|
39
|
+
errors << "Email '#{@email}' is invalid"
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def validator_valid_provider?
|
44
|
+
return true if Authinator::AuthCodeExchanger.valid_providers.include? @provider.to_sym
|
45
|
+
errors << "Provider '#{@provider}' is invalid"
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# recreate rails method
|
50
|
+
def present?(el)
|
51
|
+
!blank?(el)
|
52
|
+
end
|
53
|
+
|
54
|
+
def blank?(el)
|
55
|
+
el.nil? || el.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/authinator/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
-
describe AuthCodeExchanger do
|
4
|
+
describe Authinator::AuthCodeExchanger do
|
5
5
|
before :all do
|
6
6
|
@token_hash = {
|
7
7
|
access_token: 'ya29.token',
|
@@ -29,7 +29,7 @@ describe AuthCodeExchanger do
|
|
29
29
|
}
|
30
30
|
end
|
31
31
|
it 'should correctly process a generic (stub) token' do
|
32
|
-
ace = AuthCodeExchanger.new(:stub)
|
32
|
+
ace = Authinator::AuthCodeExchanger.new(:stub)
|
33
33
|
stub_request(:post, ace.site_token_url).
|
34
34
|
with(body: @test_env,
|
35
35
|
headers: @req_headers).
|
@@ -45,8 +45,8 @@ describe AuthCodeExchanger do
|
|
45
45
|
it 'should return an AccessToken for each provider' do
|
46
46
|
klass = OAuth2::AccessToken
|
47
47
|
|
48
|
-
AuthCodeExchanger.valid_providers.each do |provider|
|
49
|
-
ace = AuthCodeExchanger.new(provider)
|
48
|
+
Authinator::AuthCodeExchanger.valid_providers.each do |provider|
|
49
|
+
ace = Authinator::AuthCodeExchanger.new(provider)
|
50
50
|
stub_request(:post, ace.site_token_url).
|
51
51
|
with(body: @test_env,
|
52
52
|
headers: @req_headers).
|
@@ -57,7 +57,7 @@ describe AuthCodeExchanger do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
it 'should correctly process a google token' do
|
60
|
-
ace = AuthCodeExchanger.new(:google)
|
60
|
+
ace = Authinator::AuthCodeExchanger.new(:google)
|
61
61
|
stub_request(:post, ace.site_token_url).
|
62
62
|
with(body: @test_env,
|
63
63
|
headers: @req_headers).
|
@@ -72,18 +72,7 @@ describe AuthCodeExchanger do
|
|
72
72
|
|
73
73
|
it 'should gracefully not allow unsupported providers' do
|
74
74
|
expect do
|
75
|
-
AuthCodeExchanger.new(:some_fake_provider)
|
75
|
+
Authinator::AuthCodeExchanger.new(:some_fake_provider)
|
76
76
|
end.to raise_error(ArgumentError)
|
77
77
|
end
|
78
78
|
end
|
79
|
-
|
80
|
-
describe 'ClientTokenIssuer' do
|
81
|
-
pending
|
82
|
-
it 'should accept valid-looking credentials from the client'
|
83
|
-
it 'should exchange client-provided credentials for auth codes'
|
84
|
-
it 'should return an error to the client if the credentials were invalid'
|
85
|
-
it 'should verify that the tokens belong to the provided email before returning them'
|
86
|
-
it 'should generate our own set of tokens for the client if the provided ones exchanged successfully'
|
87
|
-
|
88
|
-
it 'should all integrate to follow a standard flow to auth the api client'
|
89
|
-
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
describe Authinator::ClientTokenIssuer do
|
2
|
+
it 'should exchange client-provided credentials for auth codes'
|
3
|
+
it 'should gracefully handle and return error condition if client-provided credentials are invalid'
|
4
|
+
it 'should verify that the tokens belong to the provided email before returning them'
|
5
|
+
it 'should generate our own set of tokens for the client if the provided ones exchanged successfully'
|
6
|
+
|
7
|
+
it 'should all integrate to follow a standard flow to auth the api client'
|
8
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Authinator::EndPointListener do
|
4
|
+
before :each do
|
5
|
+
@creds_hash = {
|
6
|
+
email: 'test@foogi.me',
|
7
|
+
auth_code: '4/auth_code',
|
8
|
+
provider: 'google',
|
9
|
+
}
|
10
|
+
end
|
11
|
+
it 'should accept valid-looking credentials from the client' do
|
12
|
+
listener = Authinator::EndPointListener.new(@creds_hash)
|
13
|
+
expect(listener.valid?).to be_truthy
|
14
|
+
end
|
15
|
+
|
16
|
+
# These tests extend on the test above.
|
17
|
+
# Given the test above handles the happy path, everything below are sad path tests
|
18
|
+
|
19
|
+
it 'should return an error to the client if the credentials were invalid' do
|
20
|
+
bad_creds_hash = {
|
21
|
+
a: 'b',
|
22
|
+
c: 'd',
|
23
|
+
}
|
24
|
+
listener = Authinator::EndPointListener.new(bad_creds_hash)
|
25
|
+
expect(listener.valid?).to be_falsey
|
26
|
+
end
|
27
|
+
it 'should return an error unless all three fields are provided' do
|
28
|
+
listener1 = Authinator::EndPointListener.new(@creds_hash.dup.tap { |hs| hs.delete(:email) }) # remove email
|
29
|
+
listener2 = Authinator::EndPointListener.new(@creds_hash.dup.tap { |hs| hs.delete(:auth_code) }) # remove ac
|
30
|
+
listener3 = Authinator::EndPointListener.new(@creds_hash.dup.tap { |hs| hs.delete(:provider) }) # remove prov
|
31
|
+
|
32
|
+
expect(listener1.valid?).to be_falsey
|
33
|
+
expect(listener2.valid?).to be_falsey
|
34
|
+
expect(listener3.valid?).to be_falsey
|
35
|
+
end
|
36
|
+
it 'should reject invalid emails' do
|
37
|
+
bad_email_hash1 = @creds_hash.merge(email: 'abc')
|
38
|
+
bad_email_hash2 = @creds_hash.merge(email: '')
|
39
|
+
bad_email_hash3 = @creds_hash.merge(email: 'a@')
|
40
|
+
|
41
|
+
listener1 = Authinator::EndPointListener.new(bad_email_hash1)
|
42
|
+
listener2 = Authinator::EndPointListener.new(bad_email_hash2)
|
43
|
+
listener3 = Authinator::EndPointListener.new(bad_email_hash3)
|
44
|
+
|
45
|
+
expect(listener1.valid?).to be_falsey
|
46
|
+
expect(listener2.valid?).to be_falsey
|
47
|
+
expect(listener3.valid?).to be_falsey
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should reject invalid providers' do
|
51
|
+
bad_provider_hash1 = @creds_hash.merge(provider: 'some_fake_provider')
|
52
|
+
bad_provider_hash2 = @creds_hash.merge(provider: '')
|
53
|
+
|
54
|
+
listener1 = Authinator::EndPointListener.new(bad_provider_hash1)
|
55
|
+
listener2 = Authinator::EndPointListener.new(bad_provider_hash2)
|
56
|
+
|
57
|
+
expect(listener1.valid?).to be_falsey
|
58
|
+
expect(listener2.valid?).to be_falsey
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should return a human-readable list of errors if there are any' do
|
62
|
+
listener = Authinator::EndPointListener.new({})
|
63
|
+
listener.valid?
|
64
|
+
expect(listener.errors).to eq [
|
65
|
+
'A required param is missing',
|
66
|
+
'"email" field missing',
|
67
|
+
'"provider" field missing',
|
68
|
+
'"auth_code" field missing',
|
69
|
+
]
|
70
|
+
end
|
71
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authinator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Bradner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth2
|
@@ -114,8 +114,12 @@ files:
|
|
114
114
|
- authinator.gemspec
|
115
115
|
- lib/authinator.rb
|
116
116
|
- lib/authinator/auth_code_exchanger.rb
|
117
|
+
- lib/authinator/client_token_issuer.rb
|
118
|
+
- lib/authinator/end_point_listener.rb
|
117
119
|
- lib/authinator/version.rb
|
118
|
-
- spec/authinator/
|
120
|
+
- spec/authinator/auth_code_exchanger_spec.rb
|
121
|
+
- spec/authinator/client_token_issuer_spec.rb
|
122
|
+
- spec/authinator/end_point_listener_spec.rb
|
119
123
|
- spec/spec_helper.rb
|
120
124
|
homepage: https://github.com/abradner/authinator
|
121
125
|
licenses:
|
@@ -142,5 +146,7 @@ signing_key:
|
|
142
146
|
specification_version: 4
|
143
147
|
summary: Single-Sign-On for the front and rails backend of a Single-Page-App
|
144
148
|
test_files:
|
145
|
-
- spec/authinator/
|
149
|
+
- spec/authinator/auth_code_exchanger_spec.rb
|
150
|
+
- spec/authinator/client_token_issuer_spec.rb
|
151
|
+
- spec/authinator/end_point_listener_spec.rb
|
146
152
|
- spec/spec_helper.rb
|