devise_oauth_token_authenticatable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_oauth_token_authenticatable.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Marc Leglise
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Devise OauthTokenAuthenticatable
2
+
3
+ Ruby gem that allows Rails 3 + Devise to authenticate users via an OAuth Access
4
+ Token from a 3rd party provider.
5
+
6
+ There are plenty of gems out there that deal with being an OAuth2 "consumer",
7
+ where you redirect users to an OAuth2 "provider", allowing you to hand off
8
+ authentication to a separate service. There are also plenty of gems that set
9
+ you up as your own OAuth2 provider. ***BUT***, what happens when you want to
10
+ separate your "authentication" server from your "resource" server?
11
+
12
+ This gem is meant to be used on an API "resource" server, where you want to
13
+ accept OAuth Access Tokens from a client, and validate them against a separate
14
+ "authentication" server. This communication is outside the scope of the
15
+ official [OAuth 2 spec](http://tools.ietf.org/html/draft-ietf-oauth-v2-27), but
16
+ there is a need for it anyway.
17
+
18
+ Comments and suggestions are welcome.
19
+
20
+ ## HERE BE DRAGONS!
21
+ This gem is an extraction from another project, and I'm ashamed to say does not
22
+ have any test coverage yet. If you have any suggestions for how to properly
23
+ test a Devise module like this, please drop me a line.
24
+
25
+ ## Requirements
26
+
27
+ * Devise authentication library
28
+ * Rails 3.1 or higher
29
+
30
+ ## Installation
31
+
32
+ ### Add this line to your application's Gemfile:
33
+
34
+ gem 'devise_oauth_token_authenticatable'
35
+
36
+ And then execute:
37
+
38
+ $ bundle
39
+
40
+ ### Configure Devise to point to your OAuth authorization server
41
+
42
+ ```ruby
43
+ # config/initializers/devise.rb
44
+ Devise.setup do |config|
45
+ # ==> Configuration for :oauth_token_authenticatable
46
+ config.oauth_client_id = "app-id-goes-here"
47
+ config.oauth_client_secret = "secret-key-goes-here"
48
+ config.oauth_token_validation_url = "/oauth/verify_credentials"
49
+ config.oauth_client_options = {
50
+ site: "https://your.oauth.host.com",
51
+ token_method: :get
52
+ }
53
+ end
54
+ ```
55
+
56
+ ### Configure User to support Access Token authentication
57
+
58
+ You must define the class method `find_for_oauth_token_authentication`, which
59
+ will be called by Devise when trying to lookup the user record. It will receive
60
+ one string parameter, the Access Token string, as provided by the client.
61
+
62
+ Your method can then call `validate_oauth_token`, which will do the heavy
63
+ lifting of contacting your OAuth Authentication Server at the
64
+ token_validation_url and returning an
65
+ [OAuth2::AccessToken](https://github.com/intridea/oauth2) object. Your method
66
+ can then act as appropriate with the returned data, including using the object
67
+ to make additional calls to the Authentication Server.
68
+
69
+ Your method should return `nil` if the user cannot be authenticated, or an
70
+ initialized (and persisted) User object if successful.
71
+
72
+ NOTE: The docs all refer to the `User` model, but it can be whatever model you
73
+ use with Devise.
74
+
75
+ ```ruby
76
+ class User
77
+ devise :oauth_token_authenticatable
78
+
79
+ def self.find_for_oauth_token_authentication(token_str)
80
+ access_token = validate_oauth_token(token_str)
81
+ return nil unless access_token
82
+ User.find_or_create_by_email( access_token.params['email'] )
83
+ end
84
+ end
85
+ ```
86
+
87
+ ## Practical Application
88
+
89
+ So what does this give you?
90
+
91
+ A client application can now make calls to your app, providing an access token
92
+ in either of two ways.
93
+
94
+ ### Option 1: Query string param
95
+
96
+ http://your.app.com/api/cool_stuff?access_token=1234abc
97
+
98
+ ### Option 2: Header param
99
+
100
+ GET /api/cool_stuff HTTP/1.1
101
+ Authorization: Bearer 1234abc
102
+ Host: your.app.com
103
+
104
+ Then, anywhere you would normally call `current_user` with Devise, it will be
105
+ automatically interpreted and turned into a real user object. But it's not
106
+ based on the session or cookie, it's based on the Access Token!
107
+
108
+ ## To Do
109
+
110
+ * Add tests!
111
+ * Remove the dependency on `rack-oauth2`
112
+ * Better error handling
113
+
114
+ ## Contributing
115
+
116
+ 1. Fork it
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Create new Pull Request
121
+
122
+ ## Credits
123
+
124
+ This gem was developed by Marc Leglise.
125
+ It borrows heavily from [devise\_oauth2\_providable](https://github.com/socialcast/devise_oauth2_providable)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/devise/oauth_token_authenticatable/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "devise_oauth_token_authenticatable"
6
+ gem.version = Devise::OauthTokenAuthenticatable::VERSION
7
+ gem.authors = ["Marc Leglise"]
8
+ gem.email = ["mleglise@gmail.com"]
9
+ gem.summary = %q{Allows Rails 3 + Devise to authenticate users via an OAuth Access Token from a 3rd party provider.}
10
+ gem.description = %q{
11
+ There are plenty of gems out there that deal with being an OAuth2 "consumer",
12
+ where you redirect users to an OAuth2 "provider", allowing you to hand off
13
+ authentication to a separate service. There are also plenty of gems that set
14
+ you up as your own OAuth2 provider. ***BUT***, what happens when you want to
15
+ separate your "authentication" server from your "resource" server?
16
+
17
+ This gem is meant to be used on an API "resource" server, where you want to
18
+ accept OAuth Access Tokens from a client, and validate them against a separate
19
+ "authentication" server. This communication is outside the scope of the
20
+ official OAuth 2 spec, but there is a need for it anyway.
21
+ }
22
+ gem.homepage = "https://github.com/mleglise/devise_oauth_token_authenticatable"
23
+
24
+ gem.files = `git ls-files`.split($\)
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
+ gem.require_paths = ["lib"]
28
+
29
+ gem.add_runtime_dependency("rails", [">= 3.1.0"])
30
+ gem.add_runtime_dependency("devise", [">= 2.1.0"])
31
+ gem.add_runtime_dependency("oauth2", ["~> 0.6.1"])
32
+ gem.add_runtime_dependency("rack-oauth2", [">= 0.14.0"])
33
+ end
@@ -0,0 +1,67 @@
1
+ # Originally based on:
2
+ # https://github.com/socialcast/devise_oauth2_providable/blob/master/lib/devise/oauth2_providable/models/oauth2_password_grantable.rb
3
+
4
+ require 'devise/models'
5
+
6
+ module Devise
7
+ module Models
8
+ # The OauthTokenAuthenticatable module is responsible for validating an authentication token
9
+ # and returning basic data about the account.
10
+ #
11
+ # == Options
12
+ #
13
+ # OauthTokenAuthenticatable adds the following options to devise_for:
14
+ #
15
+ # * +oauth_token_validation_url+: Defines the relative URL path to validate a given Access Token. E.g. /api/getUserInfoByAccessToken
16
+ # * +oauth_client_id+: Your app ID, as registered with your authentication server.
17
+ # * +oauth_client_secret+: Your app secret, provided by your authentication server.
18
+ # * +oauth_client_options+: A hash of options passed to the OAuth2::Client. Should include the 'site' param.
19
+ #
20
+ module OauthTokenAuthenticatable
21
+ extend ActiveSupport::Concern
22
+
23
+ # Hook called after oauth token authentication.
24
+ def after_oauth_token_authentication
25
+ end
26
+
27
+ module ClassMethods
28
+ def find_for_oauth_token_authentication(conditions)
29
+ raise NotImplementedError, "You must define find_for_oauth_token_authentication in your User model"
30
+ end
31
+
32
+ def validate_oauth_token(token_str)
33
+ # Make a new OAuth2 client and call the validation URL
34
+ @@client ||= ::OAuth2::Client.new(oauth_client_id, oauth_client_secret, oauth_client_options)
35
+ params = { # Params for client.request
36
+ client_id: oauth_client_id,
37
+ access_token: token_str
38
+ }
39
+ access_token_opts = {} # Params initializing AccessToken object
40
+
41
+ opts = {}
42
+ if @@client.options[:token_method] == :post
43
+ headers = params.delete(:headers)
44
+ opts[:body] = params
45
+ opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
46
+ opts[:headers].merge!(headers) if headers
47
+ else
48
+ opts[:params] = params
49
+ end
50
+
51
+ response = @@client.request(@@client.options[:token_method], oauth_token_validation_url, opts)
52
+ # Return the OAuth2::AccessToken object
53
+ raise ::OAuth2::Error.new(response) if @@client.options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
54
+ ::OAuth2::AccessToken.from_hash(@@client, response.parsed.merge(access_token_opts))
55
+ end
56
+
57
+ Devise::Models.config(self,
58
+ :oauth_token_validation_url,
59
+ :oauth_client_id,
60
+ :oauth_client_secret,
61
+ :oauth_client_options
62
+ )
63
+ end
64
+
65
+ end # OauthTokenAuthenticatable
66
+ end # Models
67
+ end # Devise
@@ -0,0 +1,47 @@
1
+ # Originally based on:
2
+ # https://github.com/socialcast/devise_oauth2_providable/blob/master/lib/devise/oauth2_providable/strategies/oauth2_providable_strategy.rb
3
+
4
+ require 'devise/strategies/base'
5
+
6
+ # TODO: Specify a failure app that's custom for this strategy.
7
+ # If the strategy was valid (because it had an access token in the header or params)
8
+ # but it failed, then the responses should be in the 4XX range, not 3XX redirects
9
+
10
+ module Devise
11
+ module Strategies
12
+ class OauthTokenAuthenticatable < Authenticatable
13
+
14
+ def valid?
15
+ @req = Rack::OAuth2::Server::Resource::Bearer::Request.new(env)
16
+ @req.oauth2?
17
+ end
18
+
19
+ def authenticate!
20
+ @req.setup!
21
+ resource = mapping.to.find_for_oauth_token_authentication( @req.access_token )
22
+ if validate(resource)
23
+ resource.after_oauth_token_authentication
24
+ success! resource
25
+ elsif !halted?
26
+ fail(:invalid_token)
27
+ end
28
+ end
29
+
30
+ # Do not store OauthToken validation in session.
31
+ # This forces the strategy to check the token on every request.
32
+ def store?
33
+ false
34
+ end
35
+
36
+ private
37
+
38
+ # Do not use remember_me behavior with token.
39
+ def remember_me?
40
+ false
41
+ end
42
+
43
+ end # OauthTokenAuthenticatable
44
+ end # Strategies
45
+ end # Devise
46
+
47
+ Warden::Strategies.add(:oauth_token_authenticatable, Devise::Strategies::OauthTokenAuthenticatable)
@@ -0,0 +1,5 @@
1
+ module Devise
2
+ module OauthTokenAuthenticatable
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,47 @@
1
+ # Originally based on:
2
+ # https://github.com/socialcast/devise_oauth2_providable/blob/master/lib/devise_oauth2_providable.rb
3
+
4
+ require 'devise'
5
+ require 'oauth2'
6
+ require 'rack/oauth2'
7
+ require 'devise/oauth_token_authenticatable/strategies/oauth_token_authenticatable_strategy'
8
+ require 'devise/oauth_token_authenticatable/models/oauth_token_authenticatable'
9
+ require 'devise/oauth_token_authenticatable/version'
10
+
11
+ module Devise
12
+ module OauthTokenAuthenticatable
13
+ CLIENT_ENV_REF = 'oauth2.client'
14
+ end
15
+
16
+ # Relative URL path to validate a given Access Token. E.g. /oauth/verify_credentials
17
+ mattr_accessor :oauth_token_validation_url
18
+ @@oauth_token_validation_url = nil
19
+
20
+ # Client ID for connecting to the authentication server.
21
+ mattr_accessor :oauth_client_id
22
+ @@oauth_client_id = nil
23
+
24
+ # Client Secret for connecting to the authentication server.
25
+ mattr_accessor :oauth_client_secret
26
+ @@oauth_client_secret = nil
27
+
28
+ # Client options hash, passed directly to ::OAuth2::Client.new
29
+ # From the OAuth2 documentation...
30
+ # @param [Hash] opts the options to create the client with
31
+ # @option opts [String] :site the OAuth2 provider site host
32
+ # @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
33
+ # @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
34
+ # @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
35
+ # @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
36
+ # @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
37
+ # @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
38
+ # on responses with 400+ status codes
39
+ mattr_accessor :oauth_client_options
40
+ @@oauth_client_options = {}
41
+
42
+ end
43
+
44
+ Devise.add_module(:oauth_token_authenticatable,
45
+ :strategy => true,
46
+ :model => 'devise/oauth_token_authenticatable/models/oauth_token_authenticatable'
47
+ )
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_oauth_token_authenticatable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Marc Leglise
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-09 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 3
32
+ - 1
33
+ - 0
34
+ version: 3.1.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: devise
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 11
46
+ segments:
47
+ - 2
48
+ - 1
49
+ - 0
50
+ version: 2.1.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: oauth2
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 5
62
+ segments:
63
+ - 0
64
+ - 6
65
+ - 1
66
+ version: 0.6.1
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-oauth2
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 39
78
+ segments:
79
+ - 0
80
+ - 14
81
+ - 0
82
+ version: 0.14.0
83
+ type: :runtime
84
+ version_requirements: *id004
85
+ description: "\n\
86
+ There are plenty of gems out there that deal with being an OAuth2 \"consumer\",\n\
87
+ where you redirect users to an OAuth2 \"provider\", allowing you to hand off\n\
88
+ authentication to a separate service. There are also plenty of gems that set\n\
89
+ you up as your own OAuth2 provider. ***BUT***, what happens when you want to\n\
90
+ separate your \"authentication\" server from your \"resource\" server?\n\n\
91
+ This gem is meant to be used on an API \"resource\" server, where you want to\n\
92
+ accept OAuth Access Tokens from a client, and validate them against a separate\n\
93
+ \"authentication\" server. This communication is outside the scope of the\n\
94
+ official OAuth 2 spec, but there is a need for it anyway.\n "
95
+ email:
96
+ - mleglise@gmail.com
97
+ executables: []
98
+
99
+ extensions: []
100
+
101
+ extra_rdoc_files: []
102
+
103
+ files:
104
+ - .gitignore
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - devise_oauth_token_authenticatable.gemspec
110
+ - lib/devise/oauth_token_authenticatable/models/oauth_token_authenticatable.rb
111
+ - lib/devise/oauth_token_authenticatable/strategies/oauth_token_authenticatable_strategy.rb
112
+ - lib/devise/oauth_token_authenticatable/version.rb
113
+ - lib/devise_oauth_token_authenticatable.rb
114
+ has_rdoc: true
115
+ homepage: https://github.com/mleglise/devise_oauth_token_authenticatable
116
+ licenses: []
117
+
118
+ post_install_message:
119
+ rdoc_options: []
120
+
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ requirements: []
142
+
143
+ rubyforge_project:
144
+ rubygems_version: 1.6.2
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: Allows Rails 3 + Devise to authenticate users via an OAuth Access Token from a 3rd party provider.
148
+ test_files: []
149
+