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.
- 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
|