sinatra-canvas_auth 0.0.3 → 0.1.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 +4 -4
- data/README.md +18 -3
- data/lib/sinatra/canvas_auth.erb +89 -0
- data/lib/sinatra/canvas_auth.rb +69 -17
- data/lib/sinatra/canvas_auth/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 829a0ea15cee0cbe68d6e29af2ffb52a2f839903
|
4
|
+
data.tar.gz: 651e5961b52eba8b9423c6acecd0b2f34ecaa0fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f92f0c1a68083d4b58e907b0d0d811037619223e29fbfb40c6c684e005303803b1058552a30b0e8437f83359412c45e5222aa464b2b72e2a008318660d737887
|
7
|
+
data.tar.gz: 4d4635ca2e3c66edd45d06dc8cfa211f9f8909ad8c9c58781ebb6054bb707c703edc66a2361aa46a170c7225757b5b007c87730dba60981c14f103b1eb439b3d
|
data/README.md
CHANGED
@@ -105,12 +105,19 @@ CanvasAuth requires a baseline configuration to function, as Canvas API settings
|
|
105
105
|
Default: [/.*/]
|
106
106
|
To only require authentication for certain routes, they may be explicitly specified here with either strings or regular expression. By default, all app routes will require authentication.
|
107
107
|
```ruby
|
108
|
-
set :auth_paths, ['/admin', /^\/courses\/(\d)
|
108
|
+
set :auth_paths, ['/admin', /^\/courses\/(\d)+$/]
|
109
109
|
```
|
110
110
|
|
111
111
|
Alternative syntax:
|
112
112
|
```ruby
|
113
|
-
authenticate '/admin', /^\/courses\/(\d)
|
113
|
+
authenticate '/admin', /^\/courses\/(\d)+$/
|
114
|
+
```
|
115
|
+
|
116
|
+
* **public_paths** (Array)
|
117
|
+
Default: []
|
118
|
+
The inverse of auth_paths, routes matching strings or regexps in this array will not require authentication
|
119
|
+
```ruby
|
120
|
+
set :public_paths, ['/homepage', /^\/assets\/.+$/]
|
114
121
|
```
|
115
122
|
|
116
123
|
* **unauthorized_redirect** (String)
|
@@ -126,6 +133,13 @@ CanvasAuth requires a baseline configuration to function, as Canvas API settings
|
|
126
133
|
```ruby
|
127
134
|
set :logout_redirect, '/goodbye'
|
128
135
|
```
|
136
|
+
|
137
|
+
* **failure_redirect** (String)
|
138
|
+
Default: "/login-failure"
|
139
|
+
If the user declines to grant the app access to their Canvas account, or the API request for a Canvas token raises an unexpected error, the user will be redirected to this path.
|
140
|
+
```ruby
|
141
|
+
set :error_path, '/auth-error'
|
142
|
+
```
|
129
143
|
|
130
144
|
|
131
145
|
#### Callbacks
|
@@ -164,9 +178,10 @@ The following are optional hooks called by CanvasAuth which allow you to customi
|
|
164
178
|
* GET /canvas-auth-logout
|
165
179
|
* POST /canvas-auth-token
|
166
180
|
|
167
|
-
* The following routes are also defined by CanvasAuth,
|
181
|
+
* The following routes are also defined by CanvasAuth, and may be overridden by your application, should you wish to replace the default view/behavior provided:
|
168
182
|
* GET /unauthorized
|
169
183
|
* GET /logged-out
|
184
|
+
* GET /login-failure
|
170
185
|
|
171
186
|
* All routes defined by CanvasAuth are permanently exempt from the requiring authentication, to avoid redirect loops.
|
172
187
|
## Contributing
|
@@ -0,0 +1,89 @@
|
|
1
|
+
<% params ||= {} %>
|
2
|
+
|
3
|
+
<!DOCTYPE html>
|
4
|
+
<html>
|
5
|
+
<head>
|
6
|
+
<style>
|
7
|
+
#box-content {
|
8
|
+
background-color: #FFF;
|
9
|
+
padding: 24px;
|
10
|
+
}
|
11
|
+
|
12
|
+
#modal-box {
|
13
|
+
border-radius: 3px;
|
14
|
+
box-shadow: 0 1px 4px 1px rgba(0,0,0,0.4);
|
15
|
+
box-sizing: border-box;
|
16
|
+
margin: 36px auto 0;
|
17
|
+
width: 696px;
|
18
|
+
}
|
19
|
+
|
20
|
+
.btn {
|
21
|
+
background-color: #CFB87C;
|
22
|
+
color: #2D3B45;
|
23
|
+
border: 1px solid;
|
24
|
+
border-color: #C7CDD1;
|
25
|
+
border-radius: 3px;
|
26
|
+
padding: 8px 14px;
|
27
|
+
font-size: 14px;
|
28
|
+
font-size: .875rem;
|
29
|
+
line-height: 20px;
|
30
|
+
text-align: center;
|
31
|
+
cursor: pointer;
|
32
|
+
font-weight: 300;
|
33
|
+
}
|
34
|
+
|
35
|
+
.btn:hover {
|
36
|
+
background-color: #c8ae69;
|
37
|
+
}
|
38
|
+
|
39
|
+
body {
|
40
|
+
background-color: #2e3c46;
|
41
|
+
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
42
|
+
font-weight: 300;
|
43
|
+
}
|
44
|
+
|
45
|
+
form {
|
46
|
+
margin-top: 30px;
|
47
|
+
}
|
48
|
+
|
49
|
+
header {
|
50
|
+
background-color: #34444f;
|
51
|
+
padding: 18px;
|
52
|
+
}
|
53
|
+
|
54
|
+
h2 {
|
55
|
+
margin-top: 0;
|
56
|
+
font-weight: 300;
|
57
|
+
}
|
58
|
+
|
59
|
+
img {
|
60
|
+
width: 140px;
|
61
|
+
height: 57px;
|
62
|
+
}
|
63
|
+
</style>
|
64
|
+
</head>
|
65
|
+
|
66
|
+
<body>
|
67
|
+
<div id="modal-box">
|
68
|
+
<header>
|
69
|
+
<img alt="Canvas by Instructure" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAByCAYAAACIls4EAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODIxNkEzODM0OUI4MTFFNUIwMTFCREYzMTYyNDQwM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODIxNkEzODQ0OUI4MTFFNUIwMTFCREYzMTYyNDQwM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4MjE2QTM4MTQ5QjgxMUU1QjAxMUJERjMxNjI0NDAzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4MjE2QTM4MjQ5QjgxMUU1QjAxMUJERjMxNjI0NDAzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pruwz2EAABbFSURBVHja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" />
|
70
|
+
</header>
|
71
|
+
|
72
|
+
<div id="box-content">
|
73
|
+
<h2>
|
74
|
+
<%= header %>
|
75
|
+
</h2>
|
76
|
+
|
77
|
+
<p>
|
78
|
+
<%= message %>
|
79
|
+
</p>
|
80
|
+
|
81
|
+
<% if login_url(params['state']) %>
|
82
|
+
<form method='GET' action='<%= login_url(params['state']) %>'>
|
83
|
+
<input class='btn' type='submit' value='Return to login'/>
|
84
|
+
</form>
|
85
|
+
<% end %>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
</body>
|
89
|
+
</html>
|
data/lib/sinatra/canvas_auth.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
require 'sinatra'
|
2
2
|
require 'rest-client'
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
5
|
module Sinatra
|
5
6
|
module CanvasAuth
|
6
7
|
|
8
|
+
class StateError < ::StandardError; end
|
9
|
+
|
7
10
|
DEFAULT_SETTINGS = {
|
8
11
|
:auth_paths => [/.*/],
|
9
12
|
:canvas_url => nil,
|
10
13
|
:client_id => nil,
|
11
14
|
:client_secret => nil,
|
15
|
+
:failure_redirect => '/login-failure',
|
12
16
|
:login_path => '/canvas-auth-login',
|
13
17
|
:token_path => '/canvas-auth-token',
|
14
18
|
:logout_path => '/canvas-auth-logout',
|
15
19
|
:logout_redirect => '/logged-out',
|
20
|
+
:public_paths => [],
|
16
21
|
:unauthorized_redirect => '/unauthorized'
|
17
22
|
}.freeze
|
18
23
|
|
@@ -24,14 +29,35 @@ module Sinatra
|
|
24
29
|
def self.registered(app)
|
25
30
|
self.merge_defaults(app)
|
26
31
|
|
32
|
+
app.helpers do
|
33
|
+
def login_url(state = nil)
|
34
|
+
return false if request.nil?
|
35
|
+
path_elements = [request.env['SCRIPT_NAME'], settings.login_path]
|
36
|
+
path_elements << state if state
|
37
|
+
File.join(path_elements)
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_view(header='', message='')
|
41
|
+
render(:erb, :canvas_auth, {
|
42
|
+
:views => File.expand_path(File.dirname(__FILE__)),
|
43
|
+
:locals => {
|
44
|
+
:header => header,
|
45
|
+
:message => message
|
46
|
+
}
|
47
|
+
})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
27
51
|
app.get app.login_path do
|
28
|
-
|
52
|
+
session['oauth_redirect'] ||= request.env['SCRIPT_NAME']
|
53
|
+
session['oauth_state'] = SecureRandom.urlsafe_base64(24)
|
54
|
+
|
29
55
|
redirect_uri = "#{request.scheme}://#{request.host_with_port}" \
|
30
|
-
"#{request.env['SCRIPT_NAME']}#{
|
56
|
+
"#{request.env['SCRIPT_NAME']}#{settings.token_path}"
|
31
57
|
|
32
|
-
redirect_params = "client_id=#{
|
58
|
+
redirect_params = "client_id=#{settings.client_id}&" \
|
33
59
|
"response_type=code&" \
|
34
|
-
"state=#{
|
60
|
+
"state=#{session['oauth_state']}&" \
|
35
61
|
"redirect_uri=#{CGI.escape(redirect_uri)}"
|
36
62
|
|
37
63
|
['scope', 'purpose', 'force_login', 'unique_id'].each do |optional_param|
|
@@ -40,7 +66,7 @@ module Sinatra
|
|
40
66
|
end
|
41
67
|
end
|
42
68
|
|
43
|
-
redirect "#{
|
69
|
+
redirect "#{settings.canvas_url}/login/oauth2/auth?#{redirect_params}"
|
44
70
|
end
|
45
71
|
|
46
72
|
app.get app.token_path do
|
@@ -50,14 +76,20 @@ module Sinatra
|
|
50
76
|
:client_secret => settings.client_secret
|
51
77
|
}
|
52
78
|
|
53
|
-
|
79
|
+
begin
|
80
|
+
CanvasAuth.verify_oauth_state(session, params)
|
81
|
+
response = RestClient.post("#{settings.canvas_url}/login/oauth2/token", payload)
|
82
|
+
rescue RestClient::Exception, CanvasAuth::StateError => e
|
83
|
+
failure_url = File.join(request.env['SCRIPT_NAME'], settings.failure_redirect)
|
84
|
+
redirect (failure_url + "?error=#{params[:error] || CGI.escape(e.to_s)}")
|
85
|
+
end
|
86
|
+
|
54
87
|
response = JSON.parse(response)
|
55
88
|
session['user_id'] = response['user']['id']
|
56
89
|
session['access_token'] = response['access_token']
|
57
|
-
|
58
90
|
oauth_callback(response) if self.respond_to?(:oauth_callback)
|
59
91
|
|
60
|
-
redirect
|
92
|
+
redirect session['oauth_redirect']
|
61
93
|
end
|
62
94
|
|
63
95
|
app.get app.logout_path do
|
@@ -77,24 +109,31 @@ module Sinatra
|
|
77
109
|
redirect to(settings.logout_redirect)
|
78
110
|
end
|
79
111
|
|
80
|
-
|
81
|
-
|
82
|
-
app.get '/unauthorized' do
|
83
|
-
'Your canvas account unauthorized to view this resource'
|
112
|
+
app.get app.logout_redirect do
|
113
|
+
render_view('Logged out', 'You have been successfully logged out')
|
84
114
|
end
|
85
115
|
|
86
|
-
app.get
|
87
|
-
|
88
|
-
|
116
|
+
app.get app.unauthorized_redirect do
|
117
|
+
render_view('Authentication Failed',
|
118
|
+
'Your canvas account is unauthorized to view this resource')
|
89
119
|
end
|
90
120
|
|
121
|
+
app.get app.failure_redirect do
|
122
|
+
message = "Login could not be completed."
|
123
|
+
if params[:error] && !params[:error].empty?
|
124
|
+
message += " (#{CGI.unescape(params[:error])})"
|
125
|
+
end
|
126
|
+
|
127
|
+
render_view("Authentication Failed", message)
|
128
|
+
end
|
91
129
|
|
92
130
|
# Redirect unauthenticated/unauthorized users before hitting app routes
|
93
131
|
app.before do
|
94
132
|
current_path = "#{request.env['SCRIPT_NAME']}#{request.env['PATH_INFO']}"
|
95
133
|
if CanvasAuth.auth_path?(self.settings, current_path, request.env['SCRIPT_NAME'])
|
96
134
|
if session['user_id'].nil?
|
97
|
-
|
135
|
+
session['oauth_redirect'] = current_path
|
136
|
+
redirect "#{request.env['SCRIPT_NAME']}#{settings.login_path}"
|
98
137
|
elsif self.respond_to?(:authorized) && !authorized
|
99
138
|
redirect "#{request.env['SCRIPT_NAME']}#{settings.unauthorized_redirect}"
|
100
139
|
end
|
@@ -105,9 +144,11 @@ module Sinatra
|
|
105
144
|
# Should the current path ask for authentication or is it public?
|
106
145
|
def self.auth_path?(app, current_path, script_name = '')
|
107
146
|
exempt_paths = [ app.login_path, app.token_path, app.logout_path,
|
108
|
-
app.logout_redirect, app.unauthorized_redirect
|
147
|
+
app.logout_redirect, app.unauthorized_redirect,
|
148
|
+
app.failure_redirect ]
|
109
149
|
|
110
150
|
app.auth_paths.select{ |p| current_path.match(p) }.any? &&
|
151
|
+
!app.public_paths.select{ |p| current_path.match(p) }.any? &&
|
111
152
|
!exempt_paths.map{|p| File.join(script_name, p)}.include?(current_path)
|
112
153
|
end
|
113
154
|
|
@@ -118,6 +159,17 @@ module Sinatra
|
|
118
159
|
end
|
119
160
|
end
|
120
161
|
end
|
162
|
+
|
163
|
+
# Verify state param from Canvas is the same one originally sent. Otherwise,
|
164
|
+
# unauthorized requests can be made by intercepting the redirect from Canvas
|
165
|
+
# to app token_path and tricking an authorized user into accessing the link.
|
166
|
+
# http://homakov.blogspot.com/2012/07/saferweb-most-common-oauth2.html
|
167
|
+
def self.verify_oauth_state(params, session)
|
168
|
+
saved_state = session['oauth_state']
|
169
|
+
if saved_state != params['state'] || (saved_state && saved_state.empty?)
|
170
|
+
raise CanvasAuth::StateError, 'Invalid OAuth state token provided'
|
171
|
+
end
|
172
|
+
end
|
121
173
|
end
|
122
174
|
|
123
175
|
register CanvasAuth
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-canvas_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Connor Ford
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,6 +73,7 @@ extensions: []
|
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
75
|
- README.md
|
76
|
+
- lib/sinatra/canvas_auth.erb
|
76
77
|
- lib/sinatra/canvas_auth.rb
|
77
78
|
- lib/sinatra/canvas_auth/version.rb
|
78
79
|
homepage: https://github.com/cuonline
|