grape_token_auth 0.0.0 → 0.1.0.rc1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.ruby-version +1 -0
  4. data/CONTRIBUTING.md +46 -0
  5. data/README.md +222 -12
  6. data/Rakefile +11 -1
  7. data/bin/rspec +16 -0
  8. data/circle.yml +6 -0
  9. data/config/database.yml +8 -0
  10. data/grape_token_auth.gemspec +15 -0
  11. data/lib/grape_token_auth/api_helpers.rb +30 -0
  12. data/lib/grape_token_auth/apis/confirmation_api.rb +49 -0
  13. data/lib/grape_token_auth/apis/omniauth_api.rb +149 -0
  14. data/lib/grape_token_auth/apis/password_api.rb +138 -0
  15. data/lib/grape_token_auth/apis/registration_api.rb +88 -0
  16. data/lib/grape_token_auth/apis/session_api.rb +60 -0
  17. data/lib/grape_token_auth/apis/token_validation_api.rb +29 -0
  18. data/lib/grape_token_auth/authentication_header.rb +52 -0
  19. data/lib/grape_token_auth/authorizer_data.rb +58 -0
  20. data/lib/grape_token_auth/configuration.rb +81 -0
  21. data/lib/grape_token_auth/exceptions.rb +29 -0
  22. data/lib/grape_token_auth/key_generator.rb +44 -0
  23. data/lib/grape_token_auth/lookup_token.rb +46 -0
  24. data/lib/grape_token_auth/mail/mail.rb +28 -0
  25. data/lib/grape_token_auth/mail/message_base.rb +34 -0
  26. data/lib/grape_token_auth/mail/messages/confirmation/confirmation.html.erb +16 -0
  27. data/lib/grape_token_auth/mail/messages/confirmation/confirmation.text.erb +8 -0
  28. data/lib/grape_token_auth/mail/messages/confirmation/confirmation_email.rb +27 -0
  29. data/lib/grape_token_auth/mail/messages/password_reset/password_reset.html.erb +18 -0
  30. data/lib/grape_token_auth/mail/messages/password_reset/password_reset.text.erb +9 -0
  31. data/lib/grape_token_auth/mail/messages/password_reset/password_reset_email.rb +27 -0
  32. data/lib/grape_token_auth/mail/smtp_mailer.rb +50 -0
  33. data/lib/grape_token_auth/middleware.rb +42 -0
  34. data/lib/grape_token_auth/mount_helpers.rb +80 -0
  35. data/lib/grape_token_auth/omniauth/omniauth_failure_html.rb +26 -0
  36. data/lib/grape_token_auth/omniauth/omniauth_html_base.rb +23 -0
  37. data/lib/grape_token_auth/omniauth/omniauth_resource.rb +109 -0
  38. data/lib/grape_token_auth/omniauth/omniauth_success_html.rb +61 -0
  39. data/lib/grape_token_auth/omniauth/response_template.html.erb +38 -0
  40. data/lib/grape_token_auth/orm_integrations/active_record_token_auth.rb +310 -0
  41. data/lib/grape_token_auth/resource/resource_creator.rb +48 -0
  42. data/lib/grape_token_auth/resource/resource_crud_base.rb +43 -0
  43. data/lib/grape_token_auth/resource/resource_finder.rb +53 -0
  44. data/lib/grape_token_auth/resource/resource_updater.rb +40 -0
  45. data/lib/grape_token_auth/token.rb +23 -0
  46. data/lib/grape_token_auth/token_authentication.rb +8 -0
  47. data/lib/grape_token_auth/token_authorizer.rb +60 -0
  48. data/lib/grape_token_auth/unauthorized_middleware.rb +20 -0
  49. data/lib/grape_token_auth/version.rb +1 -1
  50. data/lib/grape_token_auth.rb +65 -2
  51. metadata +266 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5f9b48e1bba210e29f6f544cb7c2c132fa4bc7d
4
- data.tar.gz: 717533ea3acd49d9a4e019bad61fa8efc7b76b17
3
+ metadata.gz: ddbd0d988786f926a723d58429d2b7dd79d558f9
4
+ data.tar.gz: b075458cd909456ea27c1c72b4dae44c4f788036
5
5
  SHA512:
6
- metadata.gz: 89f83a60e3502b534041c875201c45e5912edc464aa9a55d84b5f0dad316e7c67e030eee7244559593eed2b6f303272144f454adcb877fb96b73a9f89db649ab
7
- data.tar.gz: 9b894b55b83a04f34b1c9cb4707a120fe6341c8fd238574d7e6c56b130d9de278e61befac48b75c4c057814ebf7086a5c04b141a6c9603a65d8737eb07e9e795
6
+ metadata.gz: f7d689f290b5ff831bd32ff9eaffcdf6f8af0f409366d11031bfcd77abaf5fd8d567cbc0245c08450cc83991db925005d88c83feb2d25559922b85853cc06397
7
+ data.tar.gz: 9ed0e47470c0c50ecf0d4046ac6a36edd093fe101dc2ca298337027041e14fe5c75388dc3399120ef83d2cd2c2036fa2e355bf985a231db5793a3bd2e79d96fe
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
- --format documentation
2
1
  --color
2
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,46 @@
1
+ # Contributing
2
+
3
+ Fork, then clone the repo:
4
+
5
+ git clone git@github.com:your-username/grape_token_auth.git
6
+
7
+ Set up your machine:
8
+
9
+ ./bin/setup
10
+
11
+ Make sure the tests pass:
12
+
13
+ ./bin/rspec
14
+
15
+ Make your changes and add tests for your change. I use [rubocop][rc] with the default
16
+ style guide for consistency.
17
+
18
+ Make sure the tests pass before committing:
19
+
20
+ ./bin/rspec
21
+
22
+ ##Git committing
23
+
24
+ I prefer short, atomic commits. Obviously, this isn't a deal breaker but I
25
+ believe it makes for a better repo. When writing git messages, refer to this
26
+ [great post by Tim Pope][gc]. As a small addendum, I try to begin each message
27
+ with one of the following words:
28
+
29
+ - Add
30
+ - Modify
31
+ - Re-factor
32
+ - Fix
33
+ - Remove
34
+ - Tidy
35
+ - Update
36
+
37
+ I find this expresses the intent of the commit and also helps keep things
38
+ atomic. I picked this trick up from this
39
+ [post](https://github.com/rails/rails/blob/master/CONTRIBUTING.md).
40
+
41
+
42
+ Push to your fork and [submit a pull request][pr].
43
+
44
+ [gc]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
45
+ [rc]: https://github.com/bbatsov/rubocop
46
+ [pr]: https://github.com/mcordell/grape_token_auth/compare/
data/README.md CHANGED
@@ -1,8 +1,29 @@
1
1
  # GrapeTokenAuth
2
+ [![Code Climate GPA][11]][12] [![Test Coverage][13]][14] [![Circle CI][15]][16]
2
3
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/grape_token_auth`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+ > __This gem is in active development__ but approaching a 0.1.0 release. If you
5
+ are interested in helping out, please clone the release candidate and submit issues
6
+ and PRs to help get it into a production-ready state!
4
7
 
5
- TODO: Delete this and the text above, and describe your gem
8
+ GrapeTokenAuth is a token authentication solution for grape. It is compatible
9
+ with [ng-token-auth][1] (for _angular_) and [j-toker][2] (for _jQuery_), and is
10
+ meant as a [grape][4] (rather than _rails_) version of [devise_token_auth][3]. As
11
+ such, this project is built entirely upon _grape_ and [warden][9] and avoids the
12
+ need for _rails_. However, it has built in compatibility for [devise][devise] if
13
+ you are looking to mount a grape app within your rails app. Finally, If you are
14
+ placing a grape app within an existing _rails_ + _devise\_token\_auth_ app you might
15
+ be interested in [grape_devise_token_auth][5].
16
+
17
+ This gem is a port of [devise_token-auth][4] written by [Lyann Dylan Hurley][6]
18
+ and [the team of contributors][dta-contributors]. That team does great work and
19
+ the [conceptual section on that gem][7] is highly recommended reading.
20
+
21
+ _Philosophy_
22
+
23
+ This gem aims to maintain a small direct dependency footprint. As such, it
24
+ currently depends only on _grape_, _warden_, _mail_, and _bcrypt_. In the
25
+ future, the hope is to break this gem up into modules so that you can be even
26
+ more selective on the code and dependencies that are included.
6
27
 
7
28
  ## Installation
8
29
 
@@ -14,26 +35,215 @@ gem 'grape_token_auth'
14
35
 
15
36
  And then execute:
16
37
 
17
- $ bundle
38
+ $ bundle
18
39
 
19
40
  Or install it yourself as:
20
41
 
21
- $ gem install grape_token_auth
42
+ $ gem install grape_token_auth
43
+
44
+ ## Quick-Start Setup
45
+
46
+ This is the minimum setup to get _GrapeTokenAuth_ running. For a more
47
+ detailed walkthrough, you can refer to this [blog post][gta-setup], the [demo
48
+ repo][demo-repo], and the [wiki][gta-wiki]. Setup has 4 parts:
49
+
50
+ 1. [Middleware Setup](#middlewaresetup)
51
+ 2. [Model/ORM setup](#modelormsetup)
52
+ 3. [Grape Token Auth configuration](#grapetokenauthconfiguration)
53
+ 4. [Mounting Authentication APIs](#mountingauthenticationapis)
54
+
55
+ ###Middleware setup
56
+
57
+ _GrapeTokenAuth_ requires setting up _warden_ middleware in order to function
58
+ properly. In a simple _rack_ environment this is usually as easy as adding the
59
+ following to the `config.ru`:
60
+
61
+ ```ruby
62
+ # config.ru
63
+
64
+ require 'warden'
65
+ require 'grape_token_auth'
66
+
67
+ ## Setup session middleware (E.g. Rack::Session::Cookie)
68
+
69
+ GrapeTokenAuth.setup_warden!(self)
70
+
71
+ run YourGrapeAPI
72
+ ```
73
+
74
+ In _rails_, you will need to setup warden as so:
75
+
76
+ ```ruby
77
+ # application.rb
78
+
79
+ config.middleware.insert_after ActionDispatch::Flash, Warden::Manager do |manager|
80
+ manager.failure_app = GrapeTokenAuth::UnauthorizedMiddleware
81
+ manager.default_scope = :user
82
+ GrapeTokenAuth.configure_warden(manager)
83
+ end
84
+ ```
85
+
86
+ ### Model/ORM setup
87
+
88
+ Include the module for your ORM within the model classes. At the moment, only
89
+ ActiveRecord is supported but other ORMs are planned. Your model must
90
+ contain a text-type field called `tokens`.
91
+
92
+ ####ActiveRecord
93
+
94
+ ```ruby
95
+ class User < ActiveRecord::Base
96
+ include GrapeTokenAuth::ActiveRecord::TokenAuth
97
+ end
98
+ ```
99
+
100
+ ### Grape Token Auth Configuration
101
+
102
+ GTA does not make guesses about what scopes and user classes you are using, you
103
+ must define them before the Grape API is loaded. In _rails_ this could be in an
104
+ initializer, for a rack app run the setup before the API class definitions.
105
+
106
+ To define mappings, the scope is the key of the mapping hash, and the value is the
107
+ model to which the scope is mapped. For the above user class this would be:
108
+
109
+ ```ruby
110
+ GrapeTokenAuth.setup! do |config|
111
+ config.mappings = { user: User }
112
+ config.secret = 'THIS MUST BE A LONG HEX STRING'
113
+ end
114
+ ```
115
+
116
+ **Note on Secret**: generate a unique secret using `rake secret` in a rails app
117
+ or via [these directions][secret].
118
+
119
+
120
+ ### Mounting authentication APIs
121
+
122
+ In order to use a given feature of GrapeTokenAuth, the corresponding API must be
123
+ mounted. This can be accomplished in your grape app by first including the mount
124
+ helpers:
125
+
126
+
127
+ ```ruby
128
+ class TestApp < Grape::API
129
+ format :json
130
+
131
+ include GrapeTokenAuth::MountHelpers
132
+
133
+ #...
134
+ end
135
+ ```
136
+
137
+ Then you can use the individual helpers to mount a given GTA API:
138
+
139
+ ```ruby
140
+ class TestApp < Grape::API
141
+ # ...
142
+
143
+ mount_registration(to: '/auth', for: :user)
144
+ mount_session(to: '/auth', for: :user)
145
+ mount_token_validation(to: '/auth', for: :user)
146
+ mount_confirmation(to: '/auth', for: :user)
147
+
148
+ # ...
149
+ end
150
+ ```
151
+
152
+ The first line indicates the _GrapeTokenAuth_ registration API will be mounted
153
+ to '/auth' relative to the location where the TestApp is mounted. Presuming that
154
+ TestApp is being run at root, registration endpoints will be at `/auth`. Also,
155
+ we are defining the scope that these endpoints pertain to (user). **Important**
156
+ the scope must be defined in the [configuration
157
+ step](#grapetokenauthconfiguration).
158
+
159
+ A table of the various APIs and their associated helpers follows:
160
+
161
+ | API | helper | description |
162
+ | --- | --- | --- |
163
+ | Registration | `mount_registration` | used to register new 'email' type users |
164
+ | Session | `mount_sessions` | used to login 'email' type users |
165
+ | Confirmation | `mount_confirmation` | used to confirm 'email' users new emails |
166
+ | TokenValidation | `mount_token_validation` | used to tokens for all type users |
167
+ | OmniAuth | `mount_omniauth` | used to register/login omniauth users, requires the OmniAuthCallback API |
168
+ | OmniAuthCallback | `mount_omniauth_callbacks` | used to register/login omniauth users, requires the OmniAuth API|
169
+ | PasswordReset | `mount_password_reset` | used to issue password resets for forgotten passwords|
22
170
 
23
171
  ## Usage
24
172
 
25
- TODO: Write usage instructions here
173
+ First, include the `TokenAuthentication` module in the _grape_ API you want to
174
+ enforce authentication on.
175
+
176
+ ```ruby
177
+ class TestApp < Grape::API
178
+ # ...
179
+
180
+ include GrapeTokenAuth::TokenAuthentication
181
+
182
+ # ...
183
+ end
184
+ ```
185
+
186
+ ### Enforcing authentication on an endpoint
187
+
188
+ In any _grape_ endpoint you can call `authenticate_{SCOPE}!` to enforce
189
+ authentication on that endpoint. For instance, the following:
190
+
191
+ ```ruby
192
+ get '/' do
193
+ authenticate_user!
194
+ present Post.all
195
+ end
196
+ ```
197
+
198
+ will authenticate against the `:user` scope when trying to GET the `/` route.
199
+
200
+
201
+ ### Enforcing authentication on all endpoints
202
+
203
+ Alternatively, if you want to protect all of the endpoints in an API, place
204
+ the authentication call in a `before_filter`, like so:
205
+
206
+ ```ruby
207
+ class TestApp < Grape::API
208
+ before do
209
+ :authenticate_user!
210
+ end
211
+ end
212
+ ```
26
213
 
27
214
  ## Development
28
215
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
216
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
217
+ run `bin/console` for an interactive prompt that will allow you to experiment.
218
+
219
+ To run tests, you will need postgres to be setup and configured correctly. Run
220
+ `rake db:setup` to create the test db and `rake db:reset` to reset the db.
30
221
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
222
 
33
223
  ## Contributing
34
224
 
35
- 1. Fork it ( https://github.com/[my-github-username]/grape_token_auth/fork )
36
- 2. Create your feature branch (`git checkout -b my-new-feature`)
37
- 3. Commit your changes (`git commit -am 'Add some feature'`)
38
- 4. Push to the branch (`git push origin my-new-feature`)
39
- 5. Create a new Pull Request
225
+ [See CONTRIBUTING.md][contributing]
226
+
227
+ [1]: https://github.com/lynndylanhurley/ng-token-auth
228
+ [2]: https://github.com/lynndylanhurley/j-toker
229
+ [3]: https://github.com/lynndylanhurley/devise_token_auth
230
+ [4]: https://github.com/intridea/grape
231
+ [5]: https://github.com/mcordell/grape_devise_token_auth
232
+ [6]: https://github.com/lynndylanhurley
233
+ [7]: https://github.com/lynndylanhurley/devise_token_auth#conceptual
234
+ [8]: https://rubygems.org
235
+ [9]: https://github.com/hassox/warden
236
+ [10]: https://github.com/mcordell/grape_token_auth/milestones/Devise%20Token%20Auth%20Functional%20Parity
237
+ [11]: https://codeclimate.com/github/mcordell/grape_token_auth/badges/gpa.svg
238
+ [12]: https://codeclimate.com/github/mcordell/grape_token_auth
239
+ [13]: https://codeclimate.com/github/mcordell/grape_token_auth/badges/coverage.svg
240
+ [14]: https://codeclimate.com/github/mcordell/grape_token_auth/coverage
241
+ [15]: https://circleci.com/gh/mcordell/grape_token_auth.svg?style=svg
242
+ [16]: https://circleci.com/gh/mcordell/grape_token_auth
243
+ [contributing]: https://github.com/mcordell/grape_token_auth/blob/master/CONTRIBUTING.md
244
+ [gta-wiki]: https://github.com/mcordell/grape_token_auth/wiki
245
+ [demo-repo]: https://github.com/mcordell/grape_token_auth_demo
246
+ [gta-setup]: http://blog.mikecordell.com/grape-token-auth/2015/09/15/setting-up-authentication-on-a-grape-api-with-grapetokenauth.html
247
+ [secret]: http://www.jamesbadger.ca/2012/12/18/generate-new-secret-token/
248
+ [dta-contributors]: https://github.com/lynndylanhurley/devise_token_auth#callouts
249
+ [devise]: https://github.com/plataformatec/devise
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require_relative './spec/database'
2
3
 
4
+ namespace :db do
5
+ task :setup do
6
+ Database.setup
7
+ end
8
+
9
+ task :reset do
10
+ Database.reset
11
+ end
12
+ end
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/circle.yml ADDED
@@ -0,0 +1,6 @@
1
+ database:
2
+ override:
3
+ - bundle exec rake db:setup; bundle exec rake db:reset
4
+ test:
5
+ override:
6
+ - ./bin/rspec
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ database: gta_test
4
+ pool: 5
5
+ timeout: 5000
6
+ host: localhost
7
+ username: tester
8
+ password: password1
@@ -18,6 +18,21 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
+ spec.add_dependency 'grape', '~> 0.9.0'
22
+ spec.add_dependency 'warden', '~> 1.2.3'
23
+ spec.add_dependency 'bcrypt', '~> 3.0'
24
+ spec.add_dependency 'mail', '~> 2.0'
25
+ spec.add_development_dependency 'activerecord'
21
26
  spec.add_development_dependency 'bundler', '~> 1.8'
27
+ spec.add_development_dependency 'codeclimate-test-reporter'
28
+ spec.add_development_dependency 'database_cleaner'
29
+ spec.add_development_dependency 'factory_girl'
30
+ spec.add_development_dependency 'omniauth'
31
+ spec.add_development_dependency 'omniauth-facebook'
32
+ spec.add_development_dependency 'pry'
33
+ spec.add_development_dependency 'pg'
22
34
  spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rack-test'
36
+ spec.add_development_dependency 'rspec'
37
+ spec.add_development_dependency 'timecop'
23
38
  end
@@ -0,0 +1,30 @@
1
+ module GrapeTokenAuth
2
+ module ApiHelpers
3
+ def self.included(base)
4
+ GrapeTokenAuth.configuration.mappings.keys.each do |scope, _resource_class|
5
+ define_method("current_#{scope}") do
6
+ authorizer_data.fetch_stored_resource(scope)
7
+ end
8
+
9
+ define_method("authenticate_#{scope}!") do
10
+ token_authorizer = TokenAuthorizer.new(authorizer_data)
11
+ resource = token_authorizer.authenticate_from_token(scope)
12
+ fail Unauthorized unless resource
13
+ env['rack.session'] ||= {}
14
+ authorizer_data.store_resource(resource, scope)
15
+ resource
16
+ end
17
+ end
18
+ end
19
+
20
+ def authorizer_data
21
+ @authorizer_data ||= AuthorizerData.from_env(env)
22
+ end
23
+
24
+ def authenticated?(scope = :user)
25
+ user_type = "current_#{scope}"
26
+ return false unless respond_to?(user_type)
27
+ !!send(user_type)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module GrapeTokenAuth
2
+ # Module that contains the majority of the email confirming functionality.
3
+ # This module can be included in a Grape::API class that defines a
4
+ # resource_scope and therefore have all of the functionality with a given
5
+ # resource (mapping).
6
+ module ConfirmationAPICore
7
+ def self.included(base)
8
+ base.get do
9
+ resource_class = GrapeTokenAuth.configuration.scope_to_class(
10
+ base.resource_scope)
11
+ resource = resource_class.confirm_by_token(params[:confirmation_token])
12
+
13
+ if resource.persisted?
14
+ token = Token.new
15
+
16
+ resource.tokens[token.client_id] = {
17
+ token: token.to_password_hash,
18
+ expiry: token.expiry
19
+ }
20
+
21
+ resource.save!
22
+
23
+ redirect_url = resource.build_auth_url(
24
+ params[:redirect_url], token: token.to_s,
25
+ account_confirmation_success: true,
26
+ client_id: token.client_id,
27
+ config: params[:config])
28
+
29
+ redirect redirect_url
30
+ else
31
+ error!({ errors: 'Unable to find confirmation.',
32
+ status: 'error' }, 404)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ # "Empty" Confirmation API where OmniAuthAPICore is mounted, defaults to
39
+ # a :user resource class
40
+ class ConfirmationAPI < Grape::API
41
+ class << self
42
+ def resource_scope
43
+ :user
44
+ end
45
+ end
46
+
47
+ include ConfirmationAPICore
48
+ end
49
+ end
@@ -0,0 +1,149 @@
1
+ module GrapeTokenAuth
2
+ # Module that contains the majority of the OmniAuth functionality. This module
3
+ # can be included in a Grape::API class that defines a resource_scope and
4
+ # therefore have all of the functionality with a given resource (mapping).
5
+ module OmniAuthAPICore
6
+ def self.included(base)
7
+ base.helpers do
8
+ def auth_hash
9
+ @auth_hash ||= request.env['rack.session'].delete('gta.omniauth.auth')
10
+ end
11
+
12
+ def omniauth_params
13
+ @omniauth_params ||= request.env['rack.session']
14
+ .delete('gta.omniauth.params')
15
+ end
16
+
17
+ def render_html(html)
18
+ env['api.format'] = :html
19
+ content_type 'text/html; charset=utf-8'
20
+ html
21
+ end
22
+
23
+ def sign_in_resource(resource, scope)
24
+ request.env['warden'].session_serializer.store(resource, scope)
25
+ end
26
+
27
+ def redirect_or_render(success_html)
28
+ if %w(inAppBrowser newWindow).include?(success_html.window_type)
29
+ render_html(success_html.render_html)
30
+ elsif success_html.auth_origin_url
31
+ # default to same-window implementation, which forwards back to
32
+ # auth_origin_url build and redirect to destination url
33
+ redirect success_html.full_redirect_url
34
+ else
35
+ # there SHOULD always be an auth_origin_url, but if someone does
36
+ # something silly like coming straight to this url or refreshing the
37
+ # page at the wrong time, there may not be one. In that case, just
38
+ # render in plain text the error message if there is one or
39
+ # otherwisei a generic message.
40
+ fallback_render 'An error occurred'
41
+ end
42
+ end
43
+
44
+ def fallback_render(text)
45
+ render_html <<-EOD
46
+ <html>
47
+ <head></head>
48
+ <body>
49
+ #{text}
50
+ </body>
51
+ </html>
52
+ EOD
53
+ end
54
+ end
55
+
56
+ base.desc 'resource redirector for initial auth attempt' do
57
+ detail <<-EOD
58
+ Sets up the proper resource classes as a query parameter that is then
59
+ passed along to the proper OmniAuth provider app.
60
+ EOD
61
+ end
62
+ base.get ':provider' do
63
+ qs = CGI.parse(request.env['QUERY_STRING'])
64
+ qs['resource_class'] = [base.resource_scope]
65
+ query_params = qs.each_with_object({}) do |args, hsh|
66
+ hsh[args[0]] = args[1].first
67
+ end.to_param
68
+ omni_prefix = ::OmniAuth.config.path_prefix
69
+ path = "#{omni_prefix}/#{params[:provider]}?#{query_params}"
70
+ redirect path
71
+ end
72
+
73
+ base.desc 'OmniAuth success endpoint'
74
+ base.get ':provider/callback' do
75
+ fail unless omniauth_params
76
+ fail unless auth_hash
77
+ resource_class = GrapeTokenAuth.configuration
78
+ .scope_to_class(base.resource_scope)
79
+ success_html = OmniAuthSuccessHTML.build(resource_class,
80
+ auth_hash,
81
+ omniauth_params)
82
+ if success_html.persist_oauth_attributes!
83
+ sign_in_resource(success_html.resource, base.resource_scope)
84
+ redirect_or_render(success_html)
85
+ else
86
+ status 500
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # "Empty" OmniAuth API where OmniAuthAPICore is mounted, defaults to a :user
93
+ # resource class
94
+ class OmniAuthAPI < Grape::API
95
+ class << self
96
+ def resource_scope
97
+ :user
98
+ end
99
+ end
100
+
101
+ include OmniAuthAPICore
102
+ end
103
+
104
+ # Upon a callback from the OmniAuth provider this API (endpoint) provides
105
+ # routing to the indvidual resource class's OmniAuthAPI callback endpoint.
106
+ # This API eventually gets mounted at /OMNIAUTH_PREFIX/ where OMNIAUTH prefix
107
+ # is configured in GrapeTokenAuth
108
+ class OmniAuthCallBackRouterAPI < Grape::API
109
+ helpers do
110
+ def redirect_route_from_api(api, provider)
111
+ prefix = api.routes.find do |r|
112
+ %r{/:provider/callback}.match(r.route_path)
113
+ end.route_path.split(%r{/:provider})[0]
114
+ Pathname.new(prefix).join(provider, 'callback.json').to_s
115
+ end
116
+
117
+ def resource_class_from_auth
118
+ scope = request.env.fetch('omniauth.params', {})['resource_class']
119
+ return unless scope
120
+ GrapeTokenAuth.configuration.scope_to_class(scope.underscore.to_sym)
121
+ end
122
+
123
+ def session
124
+ request.env['rack.session']
125
+ end
126
+ end
127
+ desc 'Callback endpoint that redirects to individual resource callbacks'
128
+ get ':provider/callback' do
129
+ # derive target api from 'resource_class' param, which was set
130
+ # before authentication.
131
+ resource_class = resource_class_from_auth
132
+ api = GrapeTokenAuth.const_get("#{resource_class}OmniAuthAPI") ||
133
+ OmniAuthAPI
134
+
135
+ # preserve omniauth info for success route. ignore 'extra' in twitter
136
+ session['gta.omniauth.auth'] = request.env['omniauth.auth']
137
+ .except('extra')
138
+ session['gta.omniauth.params'] = request.env['omniauth.params']
139
+
140
+ redirect redirect_route_from_api(api, params[:provider])
141
+ end
142
+
143
+ get '/failure' do
144
+ env['api.format'] = :html
145
+ content_type 'text/html; charset=utf-8'
146
+ OmniAuthFailureHTML.new(params[:message] || params['message']).render_html
147
+ end
148
+ end
149
+ end