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.
- checksums.yaml +4 -4
- data/README.md +159 -2
- data/app/controllers/oauth2_controller.rb +9 -9
- data/app/lib/login_not_found.rb +3 -0
- data/app/models/login.rb +55 -8
- data/app/services/facebook_authenticator.rb +15 -10
- data/db/migrate/20150709221755_create_logins.rb +2 -1
- data/lib/rails_api_auth.rb +26 -0
- data/lib/rails_api_auth/authentication.rb +67 -11
- data/lib/rails_api_auth/engine.rb +1 -4
- data/lib/rails_api_auth/version.rb +1 -1
- data/spec/dummy/app/controllers/authenticated_controller.rb +2 -2
- data/spec/dummy/app/controllers/custom_authenticated_controller.rb +21 -0
- data/spec/dummy/app/models/account.rb +5 -0
- data/spec/dummy/config/application.rb +0 -2
- data/spec/dummy/config/environments/development.rb +2 -2
- data/spec/dummy/config/environments/production.rb +1 -1
- data/spec/dummy/config/environments/test.rb +1 -1
- data/spec/dummy/config/initializers/assets.rb +1 -1
- data/spec/dummy/config/initializers/cookies_serializer.rb +1 -1
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +1 -1
- data/spec/dummy/config/initializers/rails_api_auth.rb +8 -0
- data/spec/dummy/config/initializers/secret_token.rb +1 -0
- data/spec/dummy/config/initializers/session_store.rb +1 -1
- data/spec/dummy/config/routes.rb +2 -1
- data/spec/dummy/config/secrets.yml +1 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20150803185817_create_accounts.rb +12 -0
- data/spec/dummy/db/schema.rb +9 -9
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +21 -11
- data/spec/dummy/log/test.log +11767 -6995
- data/spec/factories/accounts.rb +6 -0
- data/spec/factories/logins.rb +2 -2
- data/spec/models/login_spec.rb +10 -10
- data/spec/requests/authenticated_spec.rb +29 -26
- data/spec/requests/custom_authenticated_spec.rb +45 -0
- data/spec/requests/oauth2_spec.rb +30 -34
- data/spec/services/facebook_authenticator_spec.rb +15 -17
- data/spec/spec_helper.rb +10 -3
- metadata +17 -38
- data/app/controllers/rails_api_auth/application_controller.rb +0 -7
- data/config/initializers/facebook.rb +0 -6
- data/lib/tasks/rails_api_auth_tasks.rake +0 -4
- data/spec/dummy/db/migrate/20150709221900_create_users.rb +0 -11
- data/spec/dummy/db/production.sqlite3 +0 -0
data/spec/factories/logins.rb
CHANGED
data/spec/models/login_spec.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
describe Login do
|
4
|
-
it
|
5
|
-
|
6
|
-
|
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(
|
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(
|
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(
|
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
|
65
|
-
expect { subject.consume_single_use_oauth2_token!('invalid token') }.to raise_error(Login::
|
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
|
-
|
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
|
-
|
14
|
-
subject
|
4
|
+
let(:headers) { {} }
|
15
5
|
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
12
|
+
it 'assigns the authenticated login to @current_login' do
|
13
|
+
subject
|
21
14
|
|
22
|
-
|
23
|
-
|
15
|
+
expect(assigns[:current_login]).to eq(login)
|
16
|
+
end
|
24
17
|
|
25
|
-
|
26
|
-
|
18
|
+
it "responds with the actual action's status" do
|
19
|
+
subject
|
27
20
|
|
28
|
-
|
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
|
-
|
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
|
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.
|
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 '
|
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:
|
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: '
|
47
|
-
let(:facebook_email) { login.
|
48
|
-
let(:facebook_data)
|
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:
|
51
|
-
email:
|
47
|
+
id: '1238190321',
|
48
|
+
email: facebook_email
|
52
49
|
}
|
53
50
|
end
|
54
51
|
|
55
52
|
before do
|
56
|
-
stub_request(:get,
|
57
|
-
stub_request(:get,
|
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
|
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 '
|
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
|
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
|
77
|
+
context 'when no login for the Facebook account exists' do
|
81
78
|
let(:facebook_email) { Faker::Internet.email }
|
82
79
|
|
83
|
-
it '
|
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
|
90
|
-
expect { subject }.to change { Login.where(
|
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
|
90
|
+
it "responds with the login's OAuth 2.0 token" do
|
94
91
|
subject
|
95
|
-
login = Login.
|
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
|
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
|
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,
|
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
|
119
|
+
it 'responds with status 502' do
|
123
120
|
subject
|
124
121
|
|
125
|
-
expect(response).to have_http_status(
|
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
|
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 '
|
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
|
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
|
167
|
+
context 'for an invalid token' do
|
171
168
|
let(:params) { { token_type_hint: 'access_token', token: 'badtoken' } }
|
172
169
|
|
173
|
-
it '
|
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:
|
10
|
-
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,
|
21
|
-
stub_request(:get,
|
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 '
|
22
|
+
context 'when no login for the Facebook account exists' do
|
25
23
|
let(:login_attributes) do
|
26
24
|
{
|
27
|
-
|
28
|
-
facebook_uid:
|
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
|
31
|
+
allow(Login).to receive(:create!).with(login_attributes).and_return(login)
|
34
32
|
end
|
35
33
|
|
36
|
-
it 'returns a login created from
|
37
|
-
expect(subject).to eql
|
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 '
|
39
|
+
context 'when a login for the Facebook account exists already' do
|
42
40
|
before do
|
43
|
-
expect(Login).to receive(:
|
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
|
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
|
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
|
-
|
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
|