rails_api_auth 0.0.2 → 0.0.3

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +159 -2
  3. data/app/controllers/oauth2_controller.rb +9 -9
  4. data/app/lib/login_not_found.rb +3 -0
  5. data/app/models/login.rb +55 -8
  6. data/app/services/facebook_authenticator.rb +15 -10
  7. data/db/migrate/20150709221755_create_logins.rb +2 -1
  8. data/lib/rails_api_auth.rb +26 -0
  9. data/lib/rails_api_auth/authentication.rb +67 -11
  10. data/lib/rails_api_auth/engine.rb +1 -4
  11. data/lib/rails_api_auth/version.rb +1 -1
  12. data/spec/dummy/app/controllers/authenticated_controller.rb +2 -2
  13. data/spec/dummy/app/controllers/custom_authenticated_controller.rb +21 -0
  14. data/spec/dummy/app/models/account.rb +5 -0
  15. data/spec/dummy/config/application.rb +0 -2
  16. data/spec/dummy/config/environments/development.rb +2 -2
  17. data/spec/dummy/config/environments/production.rb +1 -1
  18. data/spec/dummy/config/environments/test.rb +1 -1
  19. data/spec/dummy/config/initializers/assets.rb +1 -1
  20. data/spec/dummy/config/initializers/cookies_serializer.rb +1 -1
  21. data/spec/dummy/config/initializers/filter_parameter_logging.rb +1 -1
  22. data/spec/dummy/config/initializers/rails_api_auth.rb +8 -0
  23. data/spec/dummy/config/initializers/secret_token.rb +1 -0
  24. data/spec/dummy/config/initializers/session_store.rb +1 -1
  25. data/spec/dummy/config/routes.rb +2 -1
  26. data/spec/dummy/config/secrets.yml +1 -1
  27. data/spec/dummy/db/development.sqlite3 +0 -0
  28. data/spec/dummy/db/migrate/20150803185817_create_accounts.rb +12 -0
  29. data/spec/dummy/db/schema.rb +9 -9
  30. data/spec/dummy/db/test.sqlite3 +0 -0
  31. data/spec/dummy/log/development.log +21 -11
  32. data/spec/dummy/log/test.log +11767 -6995
  33. data/spec/factories/accounts.rb +6 -0
  34. data/spec/factories/logins.rb +2 -2
  35. data/spec/models/login_spec.rb +10 -10
  36. data/spec/requests/authenticated_spec.rb +29 -26
  37. data/spec/requests/custom_authenticated_spec.rb +45 -0
  38. data/spec/requests/oauth2_spec.rb +30 -34
  39. data/spec/services/facebook_authenticator_spec.rb +15 -17
  40. data/spec/spec_helper.rb +10 -3
  41. metadata +17 -38
  42. data/app/controllers/rails_api_auth/application_controller.rb +0 -7
  43. data/config/initializers/facebook.rb +0 -6
  44. data/lib/tasks/rails_api_auth_tasks.rake +0 -4
  45. data/spec/dummy/db/migrate/20150709221900_create_users.rb +0 -11
  46. data/spec/dummy/db/production.sqlite3 +0 -0
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :account do
3
+ first_name { Faker::Name.first_name }
4
+ last_name { Faker::Name.last_name }
5
+ end
6
+ end
@@ -1,7 +1,7 @@
1
1
  FactoryGirl.define do
2
2
  factory :login do
3
- email { Faker::Internet.email }
4
- password { Faker::Lorem.word }
3
+ identification { Faker::Internet.email }
4
+ password { Faker::Lorem.word }
5
5
 
6
6
  trait :facebook do
7
7
  facebook_uid { Faker::Number.number }
@@ -1,24 +1,24 @@
1
- require 'spec_helper'
2
-
3
1
  describe Login do
4
- it { is_expected.to validate_presence_of(:email) }
5
- it { is_expected.to allow_value('test@example.com').for(:email) }
6
- it { is_expected.to_not allow_value('test_example.com').for(:email) }
2
+ it 'belongs to the configured user model' do
3
+ expect(subject).to belong_to(:account).with_foreign_key(:user_id)
4
+ end
5
+
6
+ it { is_expected.to validate_presence_of(:identification) }
7
7
 
8
8
  it 'validates presence of either password or Facebook UID' do
9
- login = described_class.new(email: 'test@example.com', oauth2_token: 'token')
9
+ login = described_class.new(identification: 'test@example.com', oauth2_token: 'token')
10
10
 
11
11
  expect(login).to_not be_valid
12
12
  end
13
13
 
14
14
  it "doesn't validate presence of password when Facebook UID is present" do
15
- login = described_class.new(email: 'test@example.com', oauth2_token: 'token', facebook_uid: '123')
15
+ login = described_class.new(identification: 'test@example.com', oauth2_token: 'token', facebook_uid: '123')
16
16
 
17
17
  expect(login).to be_valid
18
18
  end
19
19
 
20
20
  it "doesn't validate presence of Facebook UID when password is present" do
21
- login = described_class.new(email: 'test@example.com', oauth2_token: 'token', password: '123')
21
+ login = described_class.new(identification: 'test@example.com', oauth2_token: 'token', password: '123')
22
22
 
23
23
  expect(login).to be_valid
24
24
  end
@@ -61,8 +61,8 @@ describe Login do
61
61
  end
62
62
 
63
63
  context 'when the supplied token is invalid' do
64
- it 'raises an InvalidSingleUseOAuth2Token' do
65
- expect { subject.consume_single_use_oauth2_token!('invalid token') }.to raise_error(Login::InvalidSingleUseOAuth2Token)
64
+ it 'raises an InvalidOAuth2Token' do
65
+ expect { subject.consume_single_use_oauth2_token!('invalid token') }.to raise_error(Login::InvalidOAuth2Token)
66
66
  end
67
67
  end
68
68
  end
@@ -1,47 +1,50 @@
1
- require 'spec_helper'
2
-
3
- describe 'Authenticated route' do
4
- let!(:login) { create(:login) }
5
- let(:headers) do
6
- {
7
- 'Authorization': "Bearer #{login.oauth2_token}"
8
- }
9
- end
10
-
1
+ describe 'an authenticated route' do
11
2
  subject { get '/authenticated', {}, headers }
12
3
 
13
- it 'assigns login found to @current_login' do
14
- subject
4
+ let(:headers) { {} }
15
5
 
16
- assigns[:current_login] = login
17
- end
6
+ context 'when a valid Bearer token is present' do
7
+ let(:login) { create(:login) }
8
+ let(:headers) do
9
+ { 'Authorization' => "Bearer #{login.oauth2_token}" }
10
+ end
18
11
 
19
- it '200' do
20
- subject
12
+ it 'assigns the authenticated login to @current_login' do
13
+ subject
21
14
 
22
- expect(response.status).to eq 200
23
- end
15
+ expect(assigns[:current_login]).to eq(login)
16
+ end
24
17
 
25
- it 'lets the action get rendered' do
26
- subject
18
+ it "responds with the actual action's status" do
19
+ subject
27
20
 
28
- expect(response.body).to eql 'zuper content'
21
+ expect(response).to have_http_status(201)
22
+ end
23
+
24
+ it "responds with the actual action's body" do
25
+ subject
26
+
27
+ expect(response.body).to eql('zuper content')
28
+ end
29
29
  end
30
30
 
31
- context 'no token' do
31
+ context 'when no valid Bearer token is present' do
32
+ it 'does not assign the authenticated login to @current_login' do
33
+ subject
32
34
 
33
- subject { get '/authenticated' }
35
+ expect(assigns[:current_login]).to be_nil
36
+ end
34
37
 
35
- it '401' do
38
+ it 'responds with status 401' do
36
39
  subject
37
40
 
38
- expect(response.status).to eq 401
41
+ expect(response).to have_http_status(401)
39
42
  end
40
43
 
41
44
  it 'responds with an empty body' do
42
45
  subject
43
46
 
44
- expect(response.body).to be_empty
47
+ expect(response.body.strip).to be_empty
45
48
  end
46
49
  end
47
50
  end
@@ -0,0 +1,45 @@
1
+ describe 'a custom authenticated route' do
2
+ subject { get '/custom-authenticated', {}, headers }
3
+
4
+ let(:account) { create(:account) }
5
+ let(:login) { create(:login, account: account) }
6
+ let(:headers) do
7
+ { 'Authorization' => "Bearer #{login.oauth2_token}" }
8
+ end
9
+
10
+ context 'when the block returns true' do
11
+ let(:account) { create(:account, first_name: 'user x') }
12
+
13
+ it 'assigns the authenticated login to @current_login' do
14
+ subject
15
+
16
+ expect(assigns[:current_login]).to eq(login)
17
+ end
18
+
19
+ it "responds with the actual action's status" do
20
+ subject
21
+
22
+ expect(response).to have_http_status(201)
23
+ end
24
+
25
+ it "responds with the actual action's body" do
26
+ subject
27
+
28
+ expect(response.body).to eql('zuper content')
29
+ end
30
+ end
31
+
32
+ context 'when the block returns false' do
33
+ it 'responds with status 401' do
34
+ subject
35
+
36
+ expect(response).to have_http_status(401)
37
+ end
38
+
39
+ it 'responds with an empty body' do
40
+ subject
41
+
42
+ expect(response.body.strip).to be_empty
43
+ end
44
+ end
45
+ end
@@ -1,17 +1,14 @@
1
- require 'spec_helper'
2
-
3
1
  describe 'Oauth2 API' do
4
-
5
2
  let!(:login) { create(:login) }
6
3
 
7
4
  describe 'POST /token' do
8
- let(:params) { { grant_type: 'password', username: login.email, password: login.password } }
5
+ let(:params) { { grant_type: 'password', username: login.identification, password: login.password } }
9
6
 
10
7
  subject { post '/token', params }
11
8
 
12
9
  context 'for grant_type "password"' do
13
10
  context 'with valid login credentials' do
14
- it 'succeeds' do
11
+ it 'responds with status 200' do
15
12
  subject
16
13
 
17
14
  expect(response).to have_http_status(200)
@@ -25,7 +22,7 @@ describe 'Oauth2 API' do
25
22
  end
26
23
 
27
24
  context 'with invalid login credentials' do
28
- let(:params) { { grant_type: 'password', username: 'bad@email.com', password: 'badpassword' } }
25
+ let(:params) { { grant_type: 'password', username: login.identification, password: 'badpassword' } }
29
26
 
30
27
  it 'responds with status 400' do
31
28
  subject
@@ -43,62 +40,62 @@ describe 'Oauth2 API' do
43
40
 
44
41
  context 'for grant_type "facebook_auth_code"' do
45
42
  let(:secret) { described_class::FB_APP_SECRET }
46
- let(:params) { { grant_type: 'facebook_auth_code', auth_code: 'fb auth code' } }
47
- let(:facebook_email) { login.email }
48
- let(:facebook_data) do
43
+ let(:params) { { grant_type: 'facebook_auth_code', auth_code: 'authcode' } }
44
+ let(:facebook_email) { login.identification }
45
+ let(:facebook_data) do
49
46
  {
50
- id: '1238190321',
51
- email: facebook_email
47
+ id: '1238190321',
48
+ email: facebook_email
52
49
  }
53
50
  end
54
51
 
55
52
  before do
56
- stub_request(:get, %r{https://graph.facebook.com/v2.3/oauth/access_token}).to_return(body: '{ "access_token": "access_token" }')
57
- stub_request(:get, %r{https://graph.facebook.com/v2.3/me}).to_return(body: JSON.generate(facebook_data), headers: { 'Content-Type' => 'application/json' })
53
+ stub_request(:get, 'https://graph.facebook.com/oauth/access_token?client_id=app_id&client_secret=app_secret&code=authcode&redirect_uri=redirect_uri').to_return({ body: '{ "access_token": "access_token" }' })
54
+ stub_request(:get, 'https://graph.facebook.com/me?access_token=access_token').to_return({ body: JSON.generate(facebook_data), headers: { 'Content-Type' => 'application/json' } })
58
55
  end
59
56
 
60
- context 'when a login with the posted Facebook email exists' do
57
+ context 'when a login with for the Facebook account exists' do
61
58
  it 'connects the login to the Facebook account' do
62
59
  subject
63
60
 
64
61
  expect(login.reload.facebook_uid).to eq(facebook_data[:id])
65
62
  end
66
63
 
67
- it 'succeeds' do
64
+ it 'responds with status 200' do
68
65
  subject
69
66
 
70
67
  expect(response).to have_http_status(200)
71
68
  end
72
69
 
73
- it 'responds with an oauth2 token' do
70
+ it "responds with the login's OAuth 2.0 token" do
74
71
  subject
75
72
 
76
73
  expect(response.body).to be_json_eql({ access_token: login.oauth2_token }.to_json)
77
74
  end
78
75
  end
79
76
 
80
- context 'when no login with the posted Facebook email exists' do
77
+ context 'when no login for the Facebook account exists' do
81
78
  let(:facebook_email) { Faker::Internet.email }
82
79
 
83
- it 'succeeds' do
80
+ it 'responds with status 200' do
84
81
  subject
85
82
 
86
83
  expect(response).to have_http_status(200)
87
84
  end
88
85
 
89
- it 'creates a login with it' do
90
- expect { subject }.to change { Login.where(email: facebook_email).count }.by(1)
86
+ it 'creates a login for the Facebook account' do
87
+ expect { subject }.to change { Login.where(identification: facebook_email).count }.by(1)
91
88
  end
92
89
 
93
- it 'responds with an oauth2 token' do
90
+ it "responds with the login's OAuth 2.0 token" do
94
91
  subject
95
- login = Login.find_by(email: facebook_email)
92
+ login = Login.where(identification: facebook_email).first
96
93
 
97
94
  expect(response.body).to be_json_eql({ access_token: login.oauth2_token }.to_json)
98
95
  end
99
96
  end
100
97
 
101
- context 'when no facebook code is sent' do
98
+ context 'when no Facebook auth code is sent' do
102
99
  let(:params) { { grant_type: 'facebook_auth_code' } }
103
100
 
104
101
  it 'responds with status 400' do
@@ -107,7 +104,7 @@ describe 'Oauth2 API' do
107
104
  expect(response).to have_http_status(400)
108
105
  end
109
106
 
110
- it 'responds with a no authorization code error' do
107
+ it 'responds with a "no_authorization_code" error' do
111
108
  subject
112
109
 
113
110
  expect(response.body).to be_json_eql({ error: 'no_authorization_code' }.to_json)
@@ -116,19 +113,19 @@ describe 'Oauth2 API' do
116
113
 
117
114
  context 'when Facebook responds with an error' do
118
115
  before do
119
- stub_request(:get, %r{https://graph.facebook.com/v2.3/oauth/access_token}).to_return(status: 422)
116
+ stub_request(:get, 'https://graph.facebook.com/me?access_token=access_token').to_return(status: 422)
120
117
  end
121
118
 
122
- it 'responds with status 500' do
119
+ it 'responds with status 502' do
123
120
  subject
124
121
 
125
- expect(response).to have_http_status(500)
122
+ expect(response).to have_http_status(502)
126
123
  end
127
124
 
128
125
  it 'responds with an empty response body' do
129
126
  subject
130
127
 
131
- expect(response.body).to eql('')
128
+ expect(response.body.strip).to eql('')
132
129
  end
133
130
  end
134
131
  end
@@ -142,7 +139,7 @@ describe 'Oauth2 API' do
142
139
  expect(response).to have_http_status(400)
143
140
  end
144
141
 
145
- it 'responds with an invalid grant error' do
142
+ it 'responds with an "unsupported_grant_type" error' do
146
143
  subject
147
144
 
148
145
  expect(response.body).to be_json_eql({ error: 'unsupported_grant_type' }.to_json)
@@ -155,29 +152,28 @@ describe 'Oauth2 API' do
155
152
 
156
153
  subject { post '/revoke', params }
157
154
 
158
- it 'succeeds' do
155
+ it 'responds with status 200' do
159
156
  subject
160
157
 
161
158
  expect(response).to have_http_status(200)
162
159
  end
163
160
 
164
- it 'resets login token' do
161
+ it "resets the login's OAuth 2.0 token" do
165
162
  expect { subject }.to change { login.reload.oauth2_token }
166
163
 
167
164
  subject
168
165
  end
169
166
 
170
- context 'for an unknown (or stale) token' do
167
+ context 'for an invalid token' do
171
168
  let(:params) { { token_type_hint: 'access_token', token: 'badtoken' } }
172
169
 
173
- it 'succeeds' do
170
+ it 'responds with status 200' do
174
171
  subject
175
172
 
176
173
  expect(response).to have_http_status(200)
177
174
  end
178
175
 
179
176
  it "doesn't reset any logins' token" do
180
-
181
177
  expect_any_instance_of(LoginNotFound).to receive(:refresh_oauth2_token!)
182
178
 
183
179
  subject
@@ -1,50 +1,48 @@
1
- require 'spec_helper'
2
-
3
1
  describe FacebookAuthenticator do
4
- describe '#authenticate' do
2
+ describe '#authenticate!' do
5
3
  let(:auth_code) { 'authcode' }
6
4
  let(:email) { 'email@facebook.com' }
7
5
  let(:facebook_data) do
8
6
  {
9
- id: '1238190321',
10
- email: email
7
+ id: '1238190321',
8
+ email: email
11
9
  }
12
10
  end
13
11
  let(:response_with_fb_token) { { body: '{ "access_token": "access_token" }' } }
14
12
  let(:response_with_fb_user) { { body: JSON.generate(facebook_data), headers: { 'Content-Type' => 'application/json' } } }
15
13
  let(:login) { double('login') }
16
14
 
17
- subject { described_class.new(auth_code).authenticate }
15
+ subject { described_class.new(auth_code).authenticate! }
18
16
 
19
17
  before do
20
- stub_request(:get, %r{https://graph.facebook.com/v2.3/oauth/access_token}).to_return(response_with_fb_token)
21
- stub_request(:get, %r{https://graph.facebook.com/v2.3/me}).to_return(response_with_fb_user)
18
+ stub_request(:get, 'https://graph.facebook.com/oauth/access_token?client_id=app_id&client_secret=app_secret&code=authcode&redirect_uri=redirect_uri').to_return({ body: '{ "access_token": "access_token" }' })
19
+ stub_request(:get, 'https://graph.facebook.com/me?access_token=access_token').to_return(response_with_fb_user)
22
20
  end
23
21
 
24
- context 'new login' do
22
+ context 'when no login for the Facebook account exists' do
25
23
  let(:login_attributes) do
26
24
  {
27
- email: facebook_data[:email],
28
- facebook_uid: facebook_data[:id]
25
+ identification: facebook_data[:email],
26
+ facebook_uid: facebook_data[:id]
29
27
  }
30
28
  end
31
29
 
32
30
  before do
33
- allow(Login).to receive(:create!).with(login_attributes).and_return login
31
+ allow(Login).to receive(:create!).with(login_attributes).and_return(login)
34
32
  end
35
33
 
36
- it 'returns a login created from facebook account' do
37
- expect(subject).to eql login
34
+ it 'returns a login created from the Facebook account' do
35
+ expect(subject).to eql(login)
38
36
  end
39
37
  end
40
38
 
41
- context 'existing login' do
39
+ context 'when a login for the Facebook account exists already' do
42
40
  before do
43
- expect(Login).to receive(:find_by).with(email: facebook_data[:email]).and_return login
41
+ expect(Login).to receive(:where).with(identification: facebook_data[:email]).and_return([login])
44
42
  allow(login).to receive(:update_attributes!).with(facebook_uid: facebook_data[:id])
45
43
  end
46
44
 
47
- it 'connects login to facebook account' do
45
+ it 'connects the login to the Facebook account' do
48
46
  expect(login).to receive(:update_attributes!).with(facebook_uid: facebook_data[:id])
49
47
 
50
48
  subject
data/spec/spec_helper.rb CHANGED
@@ -1,14 +1,21 @@
1
1
  ENV['RAILS_ENV'] ||= 'test'
2
2
 
3
- require File.expand_path('../dummy/config/environment.rb', __FILE__)
3
+ require 'simplecov'
4
+ SimpleCov.start do ||
5
+ minimum_coverage 95
6
+ refuse_coverage_drop
7
+ end
8
+
9
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
4
10
  require 'rspec/rails'
5
- # require 'rspec/autorun'
6
11
  require 'factory_girl_rails'
7
12
  require 'faker'
8
13
 
9
14
  Rails.backtrace_cleaner.remove_silencers!
10
15
 
11
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
16
+ %w(factories support).each do |path|
17
+ Dir["#{File.dirname(__FILE__)}/#{path}/**/*.rb"].each { |f| require f }
18
+ end
12
19
 
13
20
  RSpec.configure do |config|
14
21
  config.mock_with :rspec