grape_token_auth 0.0.0 → 0.1.0.rc1

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