opro 0.2.1.pre → 0.3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,9 @@
1
- ## 0.2.1
1
+ ## 0.3.0
2
2
 
3
3
  - Properly set attr_accessible for those apps that are requiring all attributes to be whitelisted.
4
-
4
+ - Allow access_token to be passed in header `curl -H "Authorization: token iAmAOaUthToken" http://localhost:3000`
5
+ - Default `config.allow_password_exchange' to true
6
+ - Allow multiple `find_user_for_auth` calls in setup to allow custom finders for facebook, etc.
5
7
 
6
8
  ## 0.2.0
7
9
 
data/README.md CHANGED
@@ -7,7 +7,7 @@ A production ready Rails Engine that turns your app into an [Oauth2](http://oaut
7
7
 
8
8
  ## Why would I use this?
9
9
 
10
- Lets say you've built a Rails app, awesome. Now you want to build a mobile app on say, the iPhone; cool. You start throwing around `#to_json` like nobody's business, but then you realize you need to authenticate users somehow. "Basic Auth!!", you exclaim, but then you realize that's not the most secure solution. You also realize that some users already signed up with Facebook & Twitter so they don't have a username/password combo. What ever shall you do?
10
+ Lets say you've built a Rails app, awesome. Now you want to build a mobile app on say, the iPhone... cool. You start throwing around `#to_json` like nobody's business, but then you realize you need to authenticate users somehow. "Basic Auth!!", you exclaim, but then you realize that's not the most secure solution. You also realize that some users already signed up with Facebook & Twitter so they don't have a username/password combo. What ever shall you do?
11
11
 
12
12
  Wouldn't it be great if we could have a token exchange where the user goes to a mobile web view and grants permission, and then we return back an auth token just like the big boys (Facebook, Twitter, *cough* Foursquare *cough*). With Opro, we can add this functionality pretty easily. We'll use your existing authentication strategy and provide some end integration points for your clients to use out of the box.
13
13
 
@@ -81,6 +81,12 @@ That should be all you need to do to get setup, congrats you're now able to auth
81
81
 
82
82
  Opro comes with built in documentation, so if you start your server you can view them at [http://localhost:3000/oauth_docs](http://localhost:3000/oauth_docs). Or you can [view the guide](http://opro-demo.herokuapp.com/oauth_docs) on the example app. This guide will walk you through creating your first OAuth client application, giving access to that app as a logged in user, getting an access token for that user, and using that token to access the server as an authenticated user!
83
83
 
84
+
85
+ # Advanced Setup
86
+
87
+ Opro is simple by default, but easily configurable for a number of common use cases. Check out the options below.
88
+
89
+
84
90
  ## Custom Auth
85
91
 
86
92
  If you're not using devise you can manually configure your own auth strategy. In the future I plan on adding more auth strategies, ping me or submit a pull request for your desired authentication scheme.
@@ -120,6 +126,47 @@ You can also skip permissions using `skip_oauth_permissions`. By default permiss
120
126
 
121
127
  The result is expected to be true or false.
122
128
 
129
+
130
+ ## Refresh Tokens
131
+
132
+ For added security you can require access_tokens be refreshed by client applications. This will help to mitigate risk of a leaked access_token, and enable an all around more secure system. This security comes at a price however, since implemeting the refresh_token functionality in a client can be quite difficult.
133
+
134
+ By default refresh tokens are disabled, you can enable them in your application and set the timeout period of the tokens by adding this line to your configuration.
135
+
136
+
137
+ config.require_refresh_within = 1.month
138
+
139
+
140
+ ## Password Token Exchange
141
+
142
+ If a client application has a user's password and username/email they can exchange these for a token. This is much safer than storing username and password on a local device, but does not offer the traditional OAuth "Flow". Because of this all available permissions will be granted to the client application. If you want to disable this feature you can set the configuration below to false:
143
+
144
+ config.allow_password_exchange = true
145
+
146
+ If you have this feature enabled you can further control what applications can use the feature. Some providers may wish to have "Blessed" client applications that have this ability while restricting all other clients. To accomplish this you can create a method in your ApplicationController called `oauth_valid_password_auth?` that accepts a client_id and client_secret, and returns a true or false based on whether that application can use password auth
147
+
148
+ def oauth_valid_password_auth?(client_id, client_secret)
149
+ BLESSED_APP_IDS.include?(client_id)
150
+ end
151
+
152
+
153
+ If you are using this password functionality without a supported authorization engine (like devise), you will need to add an additional method that supports validating whether or not a user's credentials are valid. The method for this is called `find_user_for_auth` and accepts a controller and the parameters. The output is expected to be a user. Add this to your config like you did to the other required methods in the Custom Auth section.
154
+
155
+ config.find_user_for_auth do |controller, params|
156
+ # user = User.find(params[:something])
157
+ # return user.valid_password?(params[:password]) ? user : false
158
+ end
159
+
160
+ If you're authenticating exchanging something other than a password (such as a facebook auth token) client's can still enable this functionality by setting `params[:auth_grant] == 'password'` in their initial request. You can then use `find_user_for_auth` method from above and implement your custom behavior. You can call `find_user_for_auth` multiple times and the application will try calling each auth method in order. It is suggested that you return from this block early if the params are missing a vital key like this:
161
+
162
+
163
+ config.find_user_for_auth do |controller, params|
164
+ return false if params[:fb_token].blank?
165
+ User.where(:fb_token => params[:fb_token]).first
166
+ end
167
+
168
+
169
+
123
170
  ## Assumptions
124
171
 
125
172
  * You have a user model and that is what your authenticating
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1.pre
1
+ 0.3.0.pre
@@ -20,8 +20,8 @@ class Opro::Oauth::TokenController < OproController
20
20
  auth_grant = Opro::Oauth::AuthGrant.auth_with_code!(params[:code], application.id)
21
21
  elsif params[:refresh_token]
22
22
  auth_grant = Opro::Oauth::AuthGrant.refresh_tokens!(params[:refresh_token], application.id)
23
- elsif params[:password] || passwords[:auth_grant] == "password"
24
- user = ::Opro.find_user_for_auth.call(self, params) if Opro.password_exchange_enabled? && oauth_valid_password_auth?(params[:client_id], params[:client_secret])
23
+ elsif params[:password] || params[:auth_grant] == "password"
24
+ user = ::Opro.find_user_for_all_auths!(self, params) if Opro.password_exchange_enabled? && oauth_valid_password_auth?(params[:client_id], params[:client_secret])
25
25
  auth_grant = Opro::Oauth::AuthGrant.auth_with_user!(user, application.id) if user.present?
26
26
  end
27
27
 
@@ -17,7 +17,7 @@ class Opro::Oauth::AuthGrant < ActiveRecord::Base
17
17
  attr_accessible :code, :access_token, :refresh_token, :access_token_expires_at, :permissions, :user_id, :user, :application_id, :application
18
18
 
19
19
  def can?(value)
20
- permissions[value.to_s]
20
+ HashWithIndifferentAccess.new(permissions)[value]
21
21
  end
22
22
 
23
23
  def expired?
@@ -6,6 +6,10 @@
6
6
 
7
7
  With curl we're able to arbitrarily add parameters to our requests and to send using arbitrary HTTP status codes (GET/POST/DELETE) that are difficult to simulate in the browser. If you need to `POST` data to a url doing so with curl is much easier than constructing a form for testing.
8
8
 
9
+ # Hurl
10
+
11
+ [Hurl](http://hurl.it/) is an open sourced browser based `curl` implementation. If you're going to do quite a few curl requests, using it can be easier than the command line.
12
+
9
13
  ## How do I use it?
10
14
 
11
15
  On the command line you should be able to get get help by typing `man curl` if your system supports man pages. Below are some simple and common use cases
@@ -32,11 +36,21 @@ You can ask for the headers of a request by adding the `-I` flag to a curl comma
32
36
  X-Frame-Options: SAMEORIGIN
33
37
  Transfer-Encoding: chunked
34
38
 
39
+ ### Set Headers
40
+
41
+ You can set a request header by using `-H` for example if you wanted to send an access_token in a header you could issue this request (assuming your access token is '9693accessTokena7ca570bbaf')
42
+
43
+ $ curl -H "Authorization: token 9693accessTokena7ca570bbaf" "http://localhost:3000/oauth_test/show_me_the_money"
44
+
45
+
46
+ ### HTTP Verb
47
+
48
+ You can specify the type of request you make in curl (GET, POST, PUT, DELETE, etc.) by specifying `-X`. For example if you wanted to POST to the /products url at localhost:3000/products you could do so like this:
49
+
50
+ $ curl -X POST http://localhost:3000
35
51
 
36
52
 
37
- # Hurl
38
53
 
39
- [Hurl](http://hurl.it/) is an open sourced browser based `curl` implementation. If you're going to do quite a few curl requests, using it can be easier than the command line.
40
54
 
41
55
 
42
56
 
@@ -9,6 +9,14 @@ OAuth is a secure way to grant authorization without having to transfer password
9
9
 
10
10
  The flow is simple, it is started when a user clicks on an authorization button, they are then directed to the OAuth provider's website, such as Facebook. They are then prompted to confirm with the OAuth provider that they are who they say they are by logging in. The user is then given the opportunity to grant authorization to the OAuth client (where the request was initiated, such as the iPhone). After returning to the client, a code is sent that can be exchanged for a secure token. This secure token can be used to authenticate as the user. This way an iPhone client can ask for personalized content to show to the user, such as a friend list, or messages. This is the mechanism that drives most of the web.
11
11
 
12
+
13
+ ## Watch "OAuth: A Tale of 2 Servers"
14
+
15
+ This video covers a typical flow an OAuth client follows with an OAuth provider for obtaining and using an access_token.
16
+
17
+
18
+ <object width="560" height="315"><param name="movie" value="http://www.youtube.com/v/tFYrq3d54Dc?version=3&amp;hl=en_US&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/tFYrq3d54Dc?version=3&amp;hl=en_US&amp;rel=0" type="application/x-shockwave-flash" width="560" height="315" allowscriptaccess="always" allowfullscreen="true"></embed></object>
19
+
12
20
  ## Not just Mobile
13
21
 
14
22
  Client and server side web applications can use this type of authorization to add features to their service such as posting things to a timeline, or adding personalization.
@@ -16,7 +24,7 @@ Client and server side web applications can use this type of authorization to ad
16
24
 
17
25
  ## Alternatives
18
26
 
19
- OAuth is simple in concept, but can be tricky to implement right. Many services also support basic auth. With basic auth you send a user's username and password along with every request. While this is fairly simple it means that the client application has access to your password, which is not very secure. There are other standards such as xAuth, and likely more to come in the future
27
+ OAuth is simple in concept, but can be tricky to implement right. Many services also support basic auth. With basic auth you send a user's username and password along with every request. While this is fairly simple it means that the client application has access to your password, which is not very secure.
20
28
 
21
29
 
22
30
  ## Clients
@@ -63,6 +63,11 @@ Try it out for yourself open up a browser and go to
63
63
 
64
64
  You should see a successful result ( again don't forget to replace the example access token with yours ). Not all urls will support OAuth authentication.
65
65
 
66
+ You can also use a header to pass the oauth token
67
+
68
+ <%= %Q{ curl -H "Authorization: token 9693accessTokena7ca570bbaf" "#{request.base_url}/oauth_test/show_me_the_money" } %>
69
+
70
+
66
71
  ## Security
67
72
 
68
73
  Don't share your client application's secret or any user's access_token with unknown or untrusted parties. Always use https when available and don't write any of these values to your application's logs.
@@ -21,6 +21,6 @@ Opro.setup do |config|
21
21
  # password, etc. for an access token.
22
22
  # Caution: This bypasses the traditional OAuth flow
23
23
  # as a result users cannot opt out of client permissions, all permissions are granted
24
- config.allow_password_exchange = false
24
+ config.allow_password_exchange = true
25
25
 
26
26
  end
@@ -27,14 +27,12 @@ module Opro
27
27
  authenticate_user_method { |controller| controller.authenticate_user! }
28
28
 
29
29
  find_user_for_auth do |controller, params|
30
+ return false if params[:password].blank?
30
31
  find_params = params.each_with_object({}) {|(key,value), hash| hash[key] = value if Devise.authentication_keys.include?(key.to_sym) }
31
- user = User.where(find_params).first
32
- if user && user.valid_password?(params[:password])
33
- return_user = user
34
- else
35
- return_user = false
36
- end
37
- return_user
32
+ user = User.where(find_params).first if find_params.present?
33
+ return false unless user.present?
34
+ return false unless user.valid_password?(params[:password])
35
+ user
38
36
  end
39
37
  else
40
38
  # nothing
@@ -107,9 +105,30 @@ module Opro
107
105
  end
108
106
  end
109
107
 
108
+ # calls all of the different auths made available,
109
+ def self.find_user_for_all_auths!(controller, params)
110
+ @user = false
111
+ find_user_for_auth.each do |auth_block|
112
+ break if @user.present?
113
+ @user = auth_block.call(controller, params)
114
+ end
115
+ @user
116
+ end
117
+
118
+
119
+ # Grossssss, don't use, needed to support `return` from the blocks provided to `find_user_for_auth`
120
+ def self.convert_to_lambda &block
121
+ obj = Object.new
122
+ obj.define_singleton_method(:_, &block)
123
+ return obj.method(:_).to_proc
124
+ end
125
+
126
+ # holds an Array of authentication blocks is called by find_user_for_all_auths! in token controller
127
+ # can be used for finding users using multiple methods (password, facebook, twitter, etc.)
110
128
  def self.find_user_for_auth(&block)
111
129
  if block.present?
112
- @find_for_authentication = block
130
+ @find_for_authentication ||= []
131
+ @find_for_authentication << convert_to_lambda(&block)
113
132
  else
114
133
  @find_for_authentication or raise 'find_for_authentication not set, please specify Opro auth_strategy'
115
134
  end
@@ -53,8 +53,19 @@ module Opro
53
53
  @use_oauth = true
54
54
  end
55
55
 
56
+ def oauth_access_token
57
+ params[:access_token] || oauth_access_token_from_header
58
+ end
59
+
60
+ def oauth_access_token_from_header
61
+ auth_header = request.env["HTTP_AUTHORIZATION"]||""
62
+ match = auth_header.match(/^token\s(.*)/)
63
+ return match[1] if match.present?
64
+ false
65
+ end
66
+
56
67
  def oauth?
57
- allow_oauth? && params[:access_token].present?
68
+ allow_oauth? && oauth_access_token.present?
58
69
  end
59
70
 
60
71
  # Override with custom logic to exclude or allow applications from exchanging
@@ -64,7 +75,7 @@ module Opro
64
75
  end
65
76
 
66
77
  def oauth_access_grant
67
- @oauth_access_grant ||= Opro::Oauth::AuthGrant.find_for_token(params[:access_token])
78
+ @oauth_access_grant ||= Opro::Oauth::AuthGrant.find_for_token(oauth_access_token)
68
79
  end
69
80
 
70
81
  def oauth_client_app
@@ -3,7 +3,7 @@ module Opro::Controllers::Concerns::ErrorMessages
3
3
 
4
4
  def generate_oauth_error_message!
5
5
  msg = ""
6
- msg << ' - No OAuth Token Provided!' if params[:access_token].blank?
6
+ msg << ' - No OAuth Token Provided!' if oauth_access_token.blank?
7
7
  msg << ' - Allow OAuth set to false!' if allow_oauth? == false
8
8
  msg << ' - OAuth user not found!' if oauth_user.blank?
9
9
  msg = generate_oauth_permissions_error_message!(msg)
@@ -31,6 +31,7 @@ module Opro::Controllers::Concerns::Permissions
31
31
  # oauth_client_can_:method? so to over-write a default check for
32
32
  # :write permission, you would need to define oauth_client_can_write?
33
33
  def oauth_client_has_permissions?
34
+ return false unless oauth_access_grant.present?
34
35
  permissions_valid_array = []
35
36
  oauth_required_permissions.each do |permission|
36
37
  permissions_valid_array << oauth_client_has_permission?(permission)
@@ -53,6 +54,7 @@ module Opro::Controllers::Concerns::Permissions
53
54
  # Returns boolean
54
55
  # if client has been granted write permissions or request is a 'GET' returns true
55
56
  def oauth_client_can_write?
57
+ return false unless oauth_access_grant.present?
56
58
  return true if env['REQUEST_METHOD'] == 'GET'
57
59
  return true if oauth_access_grant.can?(:write)
58
60
  false
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "opro"
8
- s.version = "0.2.1.pre"
8
+ s.version = "0.3.0.pre"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["schneems"]
12
- s.date = "2012-06-29"
12
+ s.date = "2012-06-30"
13
13
  s.description = " Enable OAuth clients (iphone, android, web sites, etc.) to access and use your Rails application, what you do with it is up to you"
14
14
  s.email = "richard.schneeman@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -30,5 +30,20 @@ class OauthTokenTest < ActionDispatch::IntegrationTest
30
30
  assert json_hash["refresh_token"], auth_grant.refresh_token
31
31
  end
32
32
 
33
+
34
+ test 'header authorization token' do
35
+ auth_grant = create_auth_grant_for_user(@user)
36
+ auth_grant.update_attributes(:permissions => {:write => true})
37
+
38
+ # curl -H "Authorization: token OAUTH-TOKEN" http://localhost:3000
39
+ # sets request.env["HTTP_AUTHORIZATION"] to "token OAUTH-TOKEN"
40
+ access_token = auth_grant.access_token
41
+
42
+ headers = {"HTTP_AUTHORIZATION" => "token #{access_token}"}
43
+ post oauth_tests_path, {}, headers
44
+
45
+ assert_equal 200, status
46
+ end
47
+
33
48
  end
34
49
 
@@ -57,5 +57,25 @@ class PasswordTokenTest < ActionDispatch::IntegrationTest
57
57
  assert json_hash['access_token'].blank?
58
58
  end
59
59
 
60
+
61
+ test "Allow multiple definitions of find_user_for_auth (no password)" do
62
+ Opro.setup do |config|
63
+ config.find_user_for_auth do |controller, params|
64
+ return false if params[:special_key].blank?
65
+ user = User.last if params[:special_key] == "fooBarzyrhaz"
66
+ user
67
+ end
68
+ end
69
+
70
+ params = {:client_id => @client_app.client_id ,
71
+ :client_secret => @client_app.client_secret,
72
+ :special_key => "fooBarzyrhaz",
73
+ :auth_grant => 'password' }
74
+
75
+ post oauth_token_path(params)
76
+ json_hash = JSON.parse(response.body)
77
+ assert json_hash['access_token'].present?
78
+ end
79
+
60
80
  end
61
81
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1.pre
4
+ version: 0.3.0.pre
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-29 00:00:00.000000000Z
12
+ date: 2012-06-30 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70215497549500 !ruby/object:Gem::Requirement
16
+ requirement: &70196426967100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70215497549500
24
+ version_requirements: *70196426967100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rails
27
- requirement: &70215497548240 !ruby/object:Gem::Requirement
27
+ requirement: &70196426966240 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 3.1.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70215497548240
35
+ version_requirements: *70196426966240
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bluecloth
38
- requirement: &70215497547600 !ruby/object:Gem::Requirement
38
+ requirement: &70196426965380 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70215497547600
46
+ version_requirements: *70196426965380
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mocha
49
- requirement: &70215497547000 !ruby/object:Gem::Requirement
49
+ requirement: &70196426964780 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70215497547000
57
+ version_requirements: *70196426964780
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: timecop
60
- requirement: &70215497546400 !ruby/object:Gem::Requirement
60
+ requirement: &70196426964140 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70215497546400
68
+ version_requirements: *70196426964140
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: jeweler
71
- requirement: &70215497539640 !ruby/object:Gem::Requirement
71
+ requirement: &70196426963520 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 1.6.4
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70215497539640
79
+ version_requirements: *70196426963520
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: bundler
82
- requirement: &70215497539100 !ruby/object:Gem::Requirement
82
+ requirement: &70196426962980 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 1.1.3
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70215497539100
90
+ version_requirements: *70196426962980
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: capybara
93
- requirement: &70215497538580 !ruby/object:Gem::Requirement
93
+ requirement: &70196426962460 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: 0.4.0
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70215497538580
101
+ version_requirements: *70196426962460
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: sqlite3
104
- requirement: &70215497537940 !ruby/object:Gem::Requirement
104
+ requirement: &70196426961860 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70215497537940
112
+ version_requirements: *70196426961860
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: launchy
115
- requirement: &70215497537200 !ruby/object:Gem::Requirement
115
+ requirement: &70196426961260 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '0'
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *70215497537200
123
+ version_requirements: *70196426961260
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: devise
126
- requirement: &70215497536340 !ruby/object:Gem::Requirement
126
+ requirement: &70196426960660 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *70215497536340
134
+ version_requirements: *70196426960660
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: rcov
137
- requirement: &70215497535020 !ruby/object:Gem::Requirement
137
+ requirement: &70196426960060 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *70215497535020
145
+ version_requirements: *70196426960060
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: simplecov
148
- requirement: &70215497534320 !ruby/object:Gem::Requirement
148
+ requirement: &70196426959380 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,7 +153,7 @@ dependencies:
153
153
  version: '0'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *70215497534320
156
+ version_requirements: *70196426959380
157
157
  description: ! ' Enable OAuth clients (iphone, android, web sites, etc.) to access
158
158
  and use your Rails application, what you do with it is up to you'
159
159
  email: richard.schneeman@gmail.com
@@ -275,7 +275,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
275
275
  version: '0'
276
276
  segments:
277
277
  - 0
278
- hash: 920875782846310180
278
+ hash: 2196355823502899805
279
279
  required_rubygems_version: !ruby/object:Gem::Requirement
280
280
  none: false
281
281
  requirements: