rails_api_auth 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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