devise-rownd 1.0.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +164 -0
- data/Rakefile +18 -0
- data/app/assets/config/devise_rownd_manifest.js +1 -0
- data/app/assets/stylesheets/devise/rownd/application.css +15 -0
- data/app/controllers/devise/rownd/application_controller.rb +6 -0
- data/app/controllers/devise/rownd/auth_controller.rb +59 -0
- data/app/helpers/devise/rownd/application_helper.rb +16 -0
- data/app/helpers/devise/rownd/auth_helper.rb +4 -0
- data/app/jobs/devise/rownd/application_job.rb +6 -0
- data/app/mailers/devise/rownd/application_mailer.rb +8 -0
- data/app/models/devise/rownd/application_record.rb +7 -0
- data/app/views/devise/rownd/_signin.html.erb +1 -0
- data/app/views/devise/sessions/new.html.erb +2 -0
- data/config/config.rb +17 -0
- data/config/initializers/app_config.rb +21 -0
- data/config/initializers/app_creds.rb +21 -0
- data/config/routes.rb +8 -0
- data/lib/devise/rownd/api.rb +27 -0
- data/lib/devise/rownd/custom_failure.rb +27 -0
- data/lib/devise/rownd/engine.rb +36 -0
- data/lib/devise/rownd/models/rownd_authenticatable.rb +35 -0
- data/lib/devise/rownd/models.rb +9 -0
- data/lib/devise/rownd/strategies/rownd_authenticatable.rb +95 -0
- data/lib/devise/rownd/strategies.rb +1 -0
- data/lib/devise/rownd/user.rb +16 -0
- data/lib/devise/rownd/version.rb +5 -0
- data/lib/devise/rownd.rb +16 -0
- data/lib/tasks/devise/rownd_tasks.rake +4 -0
- metadata +229 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 472c2fc42ba778e0a7b888b3855a26f625ec83c15b5420cedb6441549f8e9336
|
4
|
+
data.tar.gz: 81fd0a737715b7be796acb202b7d267a40663128e2fb300d025ccd7f307a09ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8aea286cffa63aed1791e8599eb211c9043448dd2ceddf147d489bb09f633dc6c1c134e7ba30220cc52fda5c8eaaeabc650596121dbec73af5d3c750d624147f
|
7
|
+
data.tar.gz: 59a104ec432e8fbbc4e2814aff6713da1caf2d330c07e565a353f2c61240d5516e2fd7e93a84c00931592376a443445d39d4c7d3f2015d1dcd41ba9fb37b8704
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Bobby Radford
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# Devise::Rownd
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/devise-jwt)
|
4
|
+
|
5
|
+
`devise-rownd` is a [devise](https://github.com/heartcombo/devise) extension that auuthenticates users with Rownd's passwordless authentication strategies. It works in-tandem with the Rownd Hub, a javascript snippet embedded in your website. With this Gem installed, Rownd handles all aspects of user authentication and gives you the tools to customize the user experience on your site.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'devise-rownd'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install devise-rownd
|
22
|
+
```
|
23
|
+
|
24
|
+
### Mount the Engine
|
25
|
+
|
26
|
+
Add this to your `config/routes.rb` file
|
27
|
+
|
28
|
+
```rb
|
29
|
+
mount Devise::Rownd::Engine, at: '/api/auth/rownd'
|
30
|
+
```
|
31
|
+
|
32
|
+
### Rownd Hub
|
33
|
+
Follow [these instructions](https://docs.rownd.io/rownd/sdk-reference/web/javascript-browser) to install the Rownd Hub. You'll want to ensure it runs on every page of your application, so be sure to add it as a common in your Rails JS packs. Here's the easiest way to do that:
|
34
|
+
|
35
|
+
1. Create a new file in your JS packs director called `rph.js` and paste the JS snippet that you obtained from the instructions listed above.
|
36
|
+
|
37
|
+
3. Add the following API callbacks to your Javascript:
|
38
|
+
```javascript
|
39
|
+
_rphConfig.push(['setPostAuthenticationApi', {
|
40
|
+
method: 'post',
|
41
|
+
url: '/api/auth/rownd/authenticate'
|
42
|
+
}]);
|
43
|
+
|
44
|
+
_rphConfig.push(['setPostSignOutApi', {
|
45
|
+
method: 'POST',
|
46
|
+
url: '/api/auth/rownd/sign_out'
|
47
|
+
}]);
|
48
|
+
|
49
|
+
_rphConfig.push(['setPostUserDataUpdateApi', {
|
50
|
+
method: 'POST',
|
51
|
+
url: '/api/auth/rownd/update_data'
|
52
|
+
}]);
|
53
|
+
```
|
54
|
+
|
55
|
+
> NOTE: The path prefix `/api/auth/rownd` must match the `Devise::Rownd::Engine` mount path that you specified in your Rails routes
|
56
|
+
|
57
|
+
3. Finally, include the Javascript pack in your application layout.
|
58
|
+
```html
|
59
|
+
<body>
|
60
|
+
<%= show_rownd_signin_if_required %>
|
61
|
+
<%= yield %>
|
62
|
+
<%= javascript_pack_tag 'rph', 'data-turbolinks-track': 'reload' %>
|
63
|
+
</body>
|
64
|
+
```
|
65
|
+
There are two key pieces that you musut include in the layout:
|
66
|
+
|
67
|
+
`<%= show_rownd_signin_if_required %>`
|
68
|
+
This renders the Rownd sign in modal to prompt the user for authentication when your app explicitly requires it in a controller
|
69
|
+
|
70
|
+
`<%= javascript_pack_tag 'rph', 'data-turbolinks-track': 'reload' %>`
|
71
|
+
Tells Rails to include the rph Javascript pack. We also tell Turbolinks to include the script on page reloads
|
72
|
+
|
73
|
+
## Usage
|
74
|
+
|
75
|
+
For this to work, you need to define these key environment variables:
|
76
|
+
|
77
|
+
* `ROWND_APP_ID` - This is the ID of your Rownd application
|
78
|
+
* `ROWND_APP_KEY` - Your Rownd application key
|
79
|
+
* `ROWND_APP_SECRET` - Your Rownd application secret
|
80
|
+
|
81
|
+
You can get all of these values from the [Rownd Platform](https://app.rownd.io)
|
82
|
+
|
83
|
+
### Users
|
84
|
+
|
85
|
+
This gem provides a new Devise module named `:rownd_authenticatable`. In your `user` model, you can tell Devise to use it like this:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
class User < ApplicationRecord
|
89
|
+
devise :rownd_authenticatable
|
90
|
+
|
91
|
+
...
|
92
|
+
end
|
93
|
+
|
94
|
+
```
|
95
|
+
|
96
|
+
Now, in your `config/routes.rb` file, add the following:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Rails.application.routes.draw do
|
100
|
+
devise_for :users
|
101
|
+
...
|
102
|
+
|
103
|
+
mount Devise::Rownd::Engine, at: '/api/auth/rownd'
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
### Require Authentication
|
108
|
+
|
109
|
+
You can require authentication on a controller's actions the same way you uwould for any Devise strategies.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class MyController < ApplicationController
|
113
|
+
before_action :authenticate_user!
|
114
|
+
|
115
|
+
...
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Now, whenever a user navigates to a route that requires authentication, if a user is not already signed in, Devise will prompt the user to sign into Rownd.
|
120
|
+
|
121
|
+
### Customizing the Page using the `current_user`
|
122
|
+
|
123
|
+
In any of your controllers, views, or helpers, you have access to the currently authenticated user via the `current_user` variable. You can use it to customize your page content like this:
|
124
|
+
|
125
|
+
```html
|
126
|
+
<h1>Hello, <%= current_user.first_name %>!</h1>
|
127
|
+
```
|
128
|
+
|
129
|
+
The `current_user` object has all of the fields specified in your Rownd application's schema. If the user doesn't have a value for a particular field, it will be `nil`
|
130
|
+
|
131
|
+
### Extending the `current_user` model
|
132
|
+
|
133
|
+
You can extend the `current_user` object by modifying the `Devise::Rownd::User` class. This can be very helpful if you want to have additionanl functions that aggregate data accross multiple fields, or perform some logic and return the result.
|
134
|
+
|
135
|
+
For instance, you might want a function called `admin?` that will return if the current user is has an `admin` role. To extend the `current_user` object, add a new initializer in `config/initializers` called `devise_rownd.rb`. In there you can modify the `Devise::Rownd::User` like this:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
Devise::Rownd::User.class_eval do
|
139
|
+
def admin?
|
140
|
+
roles&.include?('admin')
|
141
|
+
end
|
142
|
+
|
143
|
+
def display_name
|
144
|
+
fullname = "#{first_name} #{last_name}"
|
145
|
+
fullname.present? ? fullname.strip.upcase : email&.upcase
|
146
|
+
end
|
147
|
+
|
148
|
+
...
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
Now, you can call things like `current_user.admin?` and `current_user.display_name`
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
### Further Customization
|
157
|
+
|
158
|
+
All of the other Rownd HTML attributes work as well. You can see a full list of them [here](). This means you have the ability to customize the page with pure HTML, rather than Ruby code, if you prefer
|
159
|
+
|
160
|
+
## Contributing
|
161
|
+
Please feel free to open up a pull request with any improvements, features, or bug fixes. This is a community effort!
|
162
|
+
|
163
|
+
## License
|
164
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
require "rake/testtask"
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :test
|
@@ -0,0 +1 @@
|
|
1
|
+
//= link_directory ../stylesheets/devise/rownd .css
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# # ONLY NEEDED IN `classic` MODE.
|
4
|
+
# require_dependency 'devise/rownd/application_controller'
|
5
|
+
|
6
|
+
module Devise::Rownd
|
7
|
+
class AuthController < ApplicationController
|
8
|
+
# Skip authenticity token verification
|
9
|
+
skip_before_action :verify_authenticity_token
|
10
|
+
|
11
|
+
def authenticate
|
12
|
+
@access_token = params[:access_token]
|
13
|
+
|
14
|
+
new_access_token = session[:rownd_user_access_token] != @access_token
|
15
|
+
|
16
|
+
session[:rownd_user_access_token] = @access_token if new_access_token
|
17
|
+
|
18
|
+
if new_access_token || session[:rownd_stale_data] == true
|
19
|
+
warden.logout(:user)
|
20
|
+
warden.authenticate!(scope: :user)
|
21
|
+
end
|
22
|
+
|
23
|
+
render json: {
|
24
|
+
message: 'Successfully authenticated user',
|
25
|
+
should_refresh_page: new_access_token || session[:rownd_stale_data] == true,
|
26
|
+
}, status: :ok
|
27
|
+
end
|
28
|
+
|
29
|
+
def sign_out
|
30
|
+
session.delete(:rownd_user_access_token)
|
31
|
+
warden.logout(:user)
|
32
|
+
render json: {
|
33
|
+
message: 'Successfully signed out user',
|
34
|
+
return_to: return_to_after_sign_out
|
35
|
+
}, status: :ok
|
36
|
+
end
|
37
|
+
|
38
|
+
def update_data
|
39
|
+
session[:rownd_stale_data] = true
|
40
|
+
warden.logout(:user)
|
41
|
+
warden.authenticate!(scope: :user)
|
42
|
+
render json: {
|
43
|
+
# should_refresh_page: true
|
44
|
+
}, status: :ok
|
45
|
+
end
|
46
|
+
|
47
|
+
def healthz
|
48
|
+
render json: {
|
49
|
+
message: 'Healthy'
|
50
|
+
}, status: :ok
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def return_to_after_sign_out
|
56
|
+
'/'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'devise/rownd/user'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Rownd
|
5
|
+
module ApplicationHelper
|
6
|
+
def show_rownd_signin_if_required
|
7
|
+
unless flash[:rownd_alert]&.to_sym == :rownd_authentication_required
|
8
|
+
flash.keep(:rownd_alert)
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
render partial: 'devise/rownd/signin', locals: { rownd_return_to: session[:user_return_to] }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<div data-rownd-request-sign-in data-rownd-return-to="<%= rownd_return_to %>" />
|
data/config/config.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Define the Passages namespace
|
2
|
+
module Devise::Rownd
|
3
|
+
# Central configuration class
|
4
|
+
class Config
|
5
|
+
attr_accessor :automount
|
6
|
+
end
|
7
|
+
|
8
|
+
def config
|
9
|
+
@config ||= Config.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure
|
13
|
+
yield config
|
14
|
+
end
|
15
|
+
|
16
|
+
module_function :config, :configure
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'devise/rownd/api'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Rownd
|
5
|
+
include API
|
6
|
+
|
7
|
+
def app_config
|
8
|
+
fetch_app_config
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.fetch_app_config
|
12
|
+
Rails.cache.fetch("rownd_app_config", expires_in: 15.minutes) do
|
13
|
+
response = API.make_api_call('/hub/app-config', { method: 'GET',
|
14
|
+
headers: { 'x-rownd-app-key' => app_key } })
|
15
|
+
response.body['app'] if response.success?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module_function :app_config
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Devise
|
2
|
+
module Rownd
|
3
|
+
def app_id
|
4
|
+
app_config['id']
|
5
|
+
end
|
6
|
+
|
7
|
+
def app_schema
|
8
|
+
app_config['schema']
|
9
|
+
end
|
10
|
+
|
11
|
+
def app_key
|
12
|
+
ENV['rownd_app_key'] || ENV['ROWND_APP_KEY'] || ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def app_secret
|
16
|
+
ENV['rownd_app_secret'] || ENV['ROWND_APP_SECRET'] || ''
|
17
|
+
end
|
18
|
+
|
19
|
+
module_function :app_id, :app_schema, :app_key, :app_secret
|
20
|
+
end
|
21
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday/net_http'
|
5
|
+
require 'faraday/retry'
|
6
|
+
|
7
|
+
module Devise::Rownd
|
8
|
+
module API
|
9
|
+
def conn
|
10
|
+
@conn ||= Faraday.new('https://api.rownd.io') do |f|
|
11
|
+
f.request :json
|
12
|
+
f.request :retry
|
13
|
+
f.response :json
|
14
|
+
f.adapter :net_http
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_api_call(path, options = { method: 'GET' })
|
19
|
+
conn.send(options[:method].downcase, path, options[:params]) do |request|
|
20
|
+
request.body = options[:body].to_json if options[:body]
|
21
|
+
request.headers = options[:headers] if options[:headers]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function :conn, :make_api_call
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Devise
|
2
|
+
module Rownd
|
3
|
+
class CustomAuthFailure < Devise::FailureApp
|
4
|
+
def redirect
|
5
|
+
store_location!
|
6
|
+
if is_flashing_format?
|
7
|
+
if flash[:timedout] && flash[:alert]
|
8
|
+
flash.keep(:timedout)
|
9
|
+
flash.keep(:alert)
|
10
|
+
else
|
11
|
+
key = i18n_message == :rownd_authentication_required ? :rownd_alert : :alert
|
12
|
+
flash[key] = i18n_message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
redirect_to redirect_url
|
16
|
+
end
|
17
|
+
|
18
|
+
def i18n_message(default = nil)
|
19
|
+
message = warden_message || default || :unauthenticated
|
20
|
+
|
21
|
+
return :rownd_authentication_required if message.to_sym == :unauthenticated
|
22
|
+
|
23
|
+
super(default)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Devise
|
2
|
+
module Rownd
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace Devise::Rownd
|
5
|
+
|
6
|
+
# TODO: Add this to the devise config
|
7
|
+
# Look into rails generate devise:install which creates the
|
8
|
+
# initializers/devise.rb file
|
9
|
+
# config.skip_session_storage = [:http_auth]
|
10
|
+
|
11
|
+
# mount devise-rownd to the application
|
12
|
+
initializer('devise-rownd.mount', after: :load_config_initializers) do |_app|
|
13
|
+
if Devise::Rownd.config.automount
|
14
|
+
Rails.application.routes.prepend do
|
15
|
+
mount Devise::Rownd::Engine, at: '/api/auth/rownd'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# add the custom failure app to warden config
|
21
|
+
initializer('devise-rownd.devise_failure_app', after: :load_config_initializers) do |_app|
|
22
|
+
Devise.setup do |config|
|
23
|
+
require 'devise/rownd/custom_failure'
|
24
|
+
config.warden do |manager|
|
25
|
+
manager.failure_app = Devise::Rownd::CustomAuthFailure
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# load devise-rownd helpers into the application
|
31
|
+
config.to_prepare do
|
32
|
+
::ApplicationController.helper Devise::Rownd::ApplicationHelper
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
module RowndAuthenticatable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# TODO: What if we don't interact with the database at all?
|
9
|
+
class_methods do
|
10
|
+
def find_or_create_with_authentication_profile(profile)
|
11
|
+
result = where(user_id: profile['user_id']).first_or_create({ email: profile['email'] })
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize_from_session(data)
|
16
|
+
result = Devise::Rownd::User.new(data)
|
17
|
+
# result = find_by_user_id_or_email(user_id, email)
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize_into_session(resource)
|
22
|
+
# result = [resource.data]
|
23
|
+
result = [resource]
|
24
|
+
# result = [resource['user_id'], resource['email']]
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
# def find_by_user_id_or_email(user_id, email)
|
29
|
+
# result = find_by('user_id = ? OR email = ?', user_id, email)
|
30
|
+
# result
|
31
|
+
# end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'devise'
|
2
|
+
require 'devise/strategies/authenticatable'
|
3
|
+
require 'devise/rownd/user'
|
4
|
+
require 'devise/rownd/api'
|
5
|
+
require 'jose'
|
6
|
+
|
7
|
+
require_relative '../../../../config/initializers/app_creds'
|
8
|
+
|
9
|
+
module Devise
|
10
|
+
module Strategies
|
11
|
+
include ::Devise::Rownd::API
|
12
|
+
|
13
|
+
class RowndAuthenticatable < Authenticatable
|
14
|
+
def valid?
|
15
|
+
session[:rownd_user_access_token].present?
|
16
|
+
end
|
17
|
+
|
18
|
+
# All Strategies must define this method.
|
19
|
+
def authenticate!
|
20
|
+
@access_token = session[:rownd_user_access_token]
|
21
|
+
return fail!('No Access Token') unless @access_token
|
22
|
+
|
23
|
+
decoded_jwt = verify_token(@access_token)
|
24
|
+
@app_id = decoded_jwt['aud'].find(/^app:.+/).first.split(':').last
|
25
|
+
|
26
|
+
configured_app_id = Devise::Rownd.app_id
|
27
|
+
ok = @app_id == configured_app_id
|
28
|
+
return fail!('JWT not authorized for app') unless ok
|
29
|
+
|
30
|
+
rownd_user = Devise::Rownd::User.new(fetch_user)
|
31
|
+
|
32
|
+
return fail!(:unable_to_authenticate) unless rownd_user
|
33
|
+
|
34
|
+
success!(rownd_user)
|
35
|
+
end
|
36
|
+
|
37
|
+
def return_to_after_sign_out
|
38
|
+
'/'
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch_user
|
42
|
+
if session[:rownd_stale_data] == true
|
43
|
+
data = fetch_user_from_api
|
44
|
+
Rails.cache.write('rownd_user', data, expires_in: 1.minute)
|
45
|
+
session.delete(:rownd_stale_data) if session[:rownd_stale_data]
|
46
|
+
return data
|
47
|
+
end
|
48
|
+
|
49
|
+
Rails.cache.fetch('rownd_user', expires_in: 1.minute) do
|
50
|
+
fetch_user_from_api
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch_user_from_api
|
55
|
+
response = ::Devise::Rownd::API.make_api_call(
|
56
|
+
"/me/applications/#{@app_id}/data",
|
57
|
+
{
|
58
|
+
method: 'GET',
|
59
|
+
headers: { 'Authorization' => "Bearer #{@access_token}" }
|
60
|
+
}
|
61
|
+
)
|
62
|
+
return response.body['data'] if response.success?
|
63
|
+
|
64
|
+
raise StandardError, response.body['message']
|
65
|
+
end
|
66
|
+
|
67
|
+
def verify_token(access_token)
|
68
|
+
for jwk in jwks
|
69
|
+
begin
|
70
|
+
response = JOSE::JWT.verify_strict(jwk, ['EdDSA'], access_token)
|
71
|
+
return response[1].fields if response[0]
|
72
|
+
rescue StandardError => e
|
73
|
+
puts "Error: #{e}"
|
74
|
+
next
|
75
|
+
end
|
76
|
+
raise StandardError
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def jwks
|
81
|
+
Rails.cache.fetch('rownd_jwks', expires_in: 15.minutes) do
|
82
|
+
fetch_jwks
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def fetch_jwks
|
87
|
+
response = ::Devise::Rownd::API.make_api_call('/hub/auth/keys')
|
88
|
+
response.body['keys']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Warden::Strategies.add(:rownd_authenticatable, Devise::Strategies::RowndAuthenticatable)
|
95
|
+
Devise.add_module :rownd_authenticatable, :strategy => true
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'devise/rownd/strategies/rownd_authenticatable'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Devise
|
2
|
+
module Rownd
|
3
|
+
class User
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@data = data
|
8
|
+
Devise::Rownd.app_schema.each do |key, _value|
|
9
|
+
self.class.send :attr_accessor, key
|
10
|
+
instance_variable_value = data.is_a?(Hash) && data.key?(key) ? data[key] : nil
|
11
|
+
instance_variable_set("@#{key}", instance_variable_value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/devise/rownd.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'devise/rownd/version'
|
2
|
+
require 'devise/rownd/engine'
|
3
|
+
require 'devise/rownd/strategies'
|
4
|
+
require 'devise/rownd/models'
|
5
|
+
require 'devise/rownd/api'
|
6
|
+
require 'devise/rownd/user'
|
7
|
+
|
8
|
+
module Devise
|
9
|
+
module Rownd
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative '../../config/config'
|
14
|
+
require_relative '../../config/initializers/app_creds'
|
15
|
+
require_relative '../../config/initializers/app_config'
|
16
|
+
# require_relative '../../config/initializers/devise'
|
metadata
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devise-rownd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bobby Radford
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: devise
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faraday_middleware
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rbnacl
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 7.1.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 7.1.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jose
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.1.3
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.1.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 6.1.5
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 6.1.5
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 13.0.6
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 13.0.6
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.26.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.26.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-performance
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.13.3
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.13.3
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop-rake
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.6.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.6.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-rspec
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 2.9.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 2.9.0
|
167
|
+
description: A Devise extension for authenticating with Rownd
|
168
|
+
email:
|
169
|
+
- bobby@rownd.io
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- MIT-LICENSE
|
175
|
+
- README.md
|
176
|
+
- Rakefile
|
177
|
+
- app/assets/config/devise_rownd_manifest.js
|
178
|
+
- app/assets/stylesheets/devise/rownd/application.css
|
179
|
+
- app/controllers/devise/rownd/application_controller.rb
|
180
|
+
- app/controllers/devise/rownd/auth_controller.rb
|
181
|
+
- app/helpers/devise/rownd/application_helper.rb
|
182
|
+
- app/helpers/devise/rownd/auth_helper.rb
|
183
|
+
- app/jobs/devise/rownd/application_job.rb
|
184
|
+
- app/mailers/devise/rownd/application_mailer.rb
|
185
|
+
- app/models/devise/rownd/application_record.rb
|
186
|
+
- app/views/devise/rownd/_signin.html.erb
|
187
|
+
- app/views/devise/sessions/new.html.erb
|
188
|
+
- config/config.rb
|
189
|
+
- config/initializers/app_config.rb
|
190
|
+
- config/initializers/app_creds.rb
|
191
|
+
- config/routes.rb
|
192
|
+
- lib/devise/rownd.rb
|
193
|
+
- lib/devise/rownd/api.rb
|
194
|
+
- lib/devise/rownd/custom_failure.rb
|
195
|
+
- lib/devise/rownd/engine.rb
|
196
|
+
- lib/devise/rownd/models.rb
|
197
|
+
- lib/devise/rownd/models/rownd_authenticatable.rb
|
198
|
+
- lib/devise/rownd/strategies.rb
|
199
|
+
- lib/devise/rownd/strategies/rownd_authenticatable.rb
|
200
|
+
- lib/devise/rownd/user.rb
|
201
|
+
- lib/devise/rownd/version.rb
|
202
|
+
- lib/tasks/devise/rownd_tasks.rake
|
203
|
+
homepage: https://github.com/rownd/devise-rownd
|
204
|
+
licenses:
|
205
|
+
- MIT
|
206
|
+
metadata:
|
207
|
+
homepage_uri: https://github.com/rownd/devise-rownd
|
208
|
+
source_code_uri: https://github.com/rownd/devise-rownd
|
209
|
+
changelog_uri: https://github.com/rownd/devise-rownd/CHANGELOG.md
|
210
|
+
post_install_message:
|
211
|
+
rdoc_options: []
|
212
|
+
require_paths:
|
213
|
+
- lib
|
214
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
215
|
+
requirements:
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: 2.6.5
|
219
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: 3.0.3
|
224
|
+
requirements: []
|
225
|
+
rubygems_version: 3.0.3
|
226
|
+
signing_key:
|
227
|
+
specification_version: 4
|
228
|
+
summary: Rownd Authentication for Devise
|
229
|
+
test_files: []
|