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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +46 -0
- data/README.md +222 -12
- data/Rakefile +11 -1
- data/bin/rspec +16 -0
- data/circle.yml +6 -0
- data/config/database.yml +8 -0
- data/grape_token_auth.gemspec +15 -0
- data/lib/grape_token_auth/api_helpers.rb +30 -0
- data/lib/grape_token_auth/apis/confirmation_api.rb +49 -0
- data/lib/grape_token_auth/apis/omniauth_api.rb +149 -0
- data/lib/grape_token_auth/apis/password_api.rb +138 -0
- data/lib/grape_token_auth/apis/registration_api.rb +88 -0
- data/lib/grape_token_auth/apis/session_api.rb +60 -0
- data/lib/grape_token_auth/apis/token_validation_api.rb +29 -0
- data/lib/grape_token_auth/authentication_header.rb +52 -0
- data/lib/grape_token_auth/authorizer_data.rb +58 -0
- data/lib/grape_token_auth/configuration.rb +81 -0
- data/lib/grape_token_auth/exceptions.rb +29 -0
- data/lib/grape_token_auth/key_generator.rb +44 -0
- data/lib/grape_token_auth/lookup_token.rb +46 -0
- data/lib/grape_token_auth/mail/mail.rb +28 -0
- data/lib/grape_token_auth/mail/message_base.rb +34 -0
- data/lib/grape_token_auth/mail/messages/confirmation/confirmation.html.erb +16 -0
- data/lib/grape_token_auth/mail/messages/confirmation/confirmation.text.erb +8 -0
- data/lib/grape_token_auth/mail/messages/confirmation/confirmation_email.rb +27 -0
- data/lib/grape_token_auth/mail/messages/password_reset/password_reset.html.erb +18 -0
- data/lib/grape_token_auth/mail/messages/password_reset/password_reset.text.erb +9 -0
- data/lib/grape_token_auth/mail/messages/password_reset/password_reset_email.rb +27 -0
- data/lib/grape_token_auth/mail/smtp_mailer.rb +50 -0
- data/lib/grape_token_auth/middleware.rb +42 -0
- data/lib/grape_token_auth/mount_helpers.rb +80 -0
- data/lib/grape_token_auth/omniauth/omniauth_failure_html.rb +26 -0
- data/lib/grape_token_auth/omniauth/omniauth_html_base.rb +23 -0
- data/lib/grape_token_auth/omniauth/omniauth_resource.rb +109 -0
- data/lib/grape_token_auth/omniauth/omniauth_success_html.rb +61 -0
- data/lib/grape_token_auth/omniauth/response_template.html.erb +38 -0
- data/lib/grape_token_auth/orm_integrations/active_record_token_auth.rb +310 -0
- data/lib/grape_token_auth/resource/resource_creator.rb +48 -0
- data/lib/grape_token_auth/resource/resource_crud_base.rb +43 -0
- data/lib/grape_token_auth/resource/resource_finder.rb +53 -0
- data/lib/grape_token_auth/resource/resource_updater.rb +40 -0
- data/lib/grape_token_auth/token.rb +23 -0
- data/lib/grape_token_auth/token_authentication.rb +8 -0
- data/lib/grape_token_auth/token_authorizer.rb +60 -0
- data/lib/grape_token_auth/unauthorized_middleware.rb +20 -0
- data/lib/grape_token_auth/version.rb +1 -1
- data/lib/grape_token_auth.rb +65 -2
- metadata +266 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddbd0d988786f926a723d58429d2b7dd79d558f9
|
4
|
+
data.tar.gz: b075458cd909456ea27c1c72b4dae44c4f788036
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7d689f290b5ff831bd32ff9eaffcdf6f8af0f409366d11031bfcd77abaf5fd8d567cbc0245c08450cc83991db925005d88c83feb2d25559922b85853cc06397
|
7
|
+
data.tar.gz: 9ed0e47470c0c50ecf0d4046ac6a36edd093fe101dc2ca298337027041e14fe5c75388dc3399120ef83d2cd2c2036fa2e355bf985a231db5793a3bd2e79d96fe
|
data/.rspec
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
38
|
+
$ bundle
|
18
39
|
|
19
40
|
Or install it yourself as:
|
20
41
|
|
21
|
-
|
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
|
-
|
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,
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
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
data/config/database.yml
ADDED
data/grape_token_auth.gemspec
CHANGED
@@ -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
|