sinatra-canvas_auth 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2de2f77ea5c253f39b90e8fa838a3da4fd332392
4
+ data.tar.gz: 404e2f5985d52d798ba61f4680205cf56ffbcd0e
5
+ SHA512:
6
+ metadata.gz: 1241210da5cc56803b178946eb18f4dbfba09b2f93239e641a01d20c86ca24963b4203792c2eb34f9c43d7e114bacd3bef3fc0071304d84c2d8766d5f4298575
7
+ data.tar.gz: afe435e636bbf4bbae985de9029d622b1da6ed93073f2509ca65f930da81e0b37581fde9b6d46fa5c25af32b57c763382e692a871638c2c870db019100dadc46
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Sinatra::CanvasAuth
2
+
3
+ CanvasAuth is a Sinatra extension that implements the [OAuth2 flow](https://canvas.instructure.com/doc/api/file.oauth.html) used for authenticating a user via [Canvas LMS](https://github.com/instructure/canvas-lms).
4
+
5
+ This gem handles redirection of unauthenticated/unauthorized users, as well as the routing & API calls necessary for logging in/obtaining an access token, logging out/deleting an access token.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sinatra-canvas_auth'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sinatra-canvas_auth
22
+
23
+
24
+ ## Usage
25
+ If you are developing a ["classic-style"](http://www.sinatrarb.com/intro.html#Modular%20vs.%20Classic%20Style) Sinatra app, require the files at the top of your app, enable sessions, and set the required configuration options.
26
+
27
+ ``` ruby
28
+ require sinatra
29
+ require sinatra/canvas_auth
30
+
31
+ # These settings are required
32
+ configure do
33
+ enable :sessions
34
+ set :canvas_url, 'https://ucdenver.instructure.com'
35
+ set :client_id, 10230000000000045
36
+ set :client_secret, '659df93f24affc25948ee437f8ac825edfa903d95e3a5ace0bb5ac4fb61686c6'
37
+ end
38
+
39
+ get '/' do
40
+ 'Hello World'
41
+ end
42
+ ```
43
+
44
+ For "modular-style" apps, you must also explicitly register the extension
45
+ ``` ruby
46
+ require sinatra/base
47
+ require sinatra/canvas_auth
48
+
49
+ class App < Sinatra::Base
50
+
51
+ # These settings are required
52
+ configure do
53
+ enable :sessions
54
+ set :canvas_url, 'https://ucdenver.instructure.com'
55
+ set :client_id, 10230000000000045
56
+ set :client_secret, '659df93f24affc25948ee437f8ac825edfa903d95e3a5ace0bb5ac4fb61686c6'
57
+ end
58
+
59
+ register Sinatra::CanvasAuth
60
+
61
+ get '/' do
62
+ 'Hello World'
63
+ end
64
+ end
65
+ ```
66
+
67
+ ## Configuration
68
+ CanvasAuth pulls settings from your Sinatra app's configuration, which can be set with [the built-in DSL](http://www.sinatrarb.com/configuration.html).
69
+
70
+ For simplicity, examples in this documentation hard-code configuration within the application, though it is often wise to use alternate methods when configuring sensitive data (e.g. API keys), especially when working with open source. Here are just a few options that can help you with this:
71
+ * [Sinatra::ConfigFile](http://www.sinatrarb.com/contrib/config_file.html)
72
+ * [Dotenv](https://github.com/bkeepers/dotenv)
73
+ * [Figaro](https://github.com/laserlemon/figaro)
74
+
75
+ CanvasAuth requires a baseline configuration to function, as Canvas API settings will differ between instances. Below is a full list of the available configuraiton options.
76
+
77
+ #### Required Settings
78
+
79
+ * **canvas_url** (String)
80
+
81
+ The full URL of the Canvas instance used for authentication.
82
+ ```ruby
83
+ set :canvas_url, 'https://ucdenver.instructure.com'
84
+ ```
85
+
86
+ * **client_id** (String)
87
+
88
+ The client id (AKA "ID") for the developer key associated with your app. Developer keys can be requested [here](http://goo.gl/yu4lT), or created directly by Canvas admins by clicking "Developer Keys" on the sidebar under your account's admin panel.
89
+ ```ruby
90
+ set :client_id, 10230000000000045
91
+ ```
92
+
93
+ * **client_secret** (String)
94
+
95
+ The 64 character client secret (AKA "Key") for the developer key associated with your app.
96
+ ```ruby
97
+ set :client_secret, '659df93f24affc25948ee437f8ac825edfa903d95e3a5ace0bb5ac4fb61686c6'
98
+ ```
99
+ &nbsp;
100
+
101
+
102
+ #### Optional Settings
103
+
104
+ * **auth_paths** (Array)
105
+ Default: [/.*/]
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
+ ```ruby
108
+ set :auth_paths, ['/admin', /^\/courses\/(\d)+$]
109
+ ```
110
+
111
+ Alternative syntax:
112
+ ```ruby
113
+ authenticate '/admin', /^\/courses\/(\d)+$
114
+ ```
115
+
116
+ * **unauthorized_redirect** (String)
117
+ Default: "/unauthorized"
118
+ If the above "authorized" setting is provided and returns falsy when called, the user will be redirected to this path.
119
+ ```ruby
120
+ set :unauthorized_redirect, '/not_allowed'
121
+ ```
122
+
123
+ * **logout_redirect** (String)
124
+ Default: "/logged-out"
125
+ After a user is logged out, they will be redirected to this path.
126
+ ```ruby
127
+ set :logout_redirect, '/goodbye'
128
+ ```
129
+ &nbsp;
130
+
131
+ #### Callbacks
132
+
133
+ The following are optional hooks called by CanvasAuth which allow you to customize certain behavior. They should be defined as [helper methods](http://www.sinatrarb.com/intro.html#Helpers) within your application.
134
+
135
+ * **oauth_callback(oauth_response)**
136
+
137
+ Once the OAuth authentication request has been made, this method is called with the API response from Canvas as an argument. This may be used to define a custom response handling action, such as saving the user's token in a database.
138
+ ```ruby
139
+ helpers do
140
+ def oauth_callback(oauth_response)
141
+ uid = oauth_response['user']['id']
142
+ token = oauth_response['access_token']
143
+ db_connection.execute("UPDATE users SET access_token = ? where uid = ?", token, uid)
144
+ end
145
+ end
146
+ ```
147
+
148
+ * **authorized()**
149
+
150
+ This method may be defined to check the authorization priveleges of a user once they have logged in. It should return truthy for authorized users, falsy otherwise. Since this is called without parameters, it generally makes use of session variables and/or settings which were set during the oauth_callback method or elsewhere in the app.
151
+ ```ruby
152
+ helpers do
153
+ def authorized
154
+ session[:allowed_roles].includes?(session[:user_roles])
155
+ end
156
+ end
157
+ ```
158
+
159
+ ## Miscellaneous Notes
160
+ * CanvasAuth automatically assigns `session['user_id']` and `session['access_token']` to the values returned by the OAuth response, so there is no need to do this manually in your oauth_callback proc.
161
+
162
+ * The following routes are defined by CanvasAuth for use in the OAuth flow and should not be overridden by your application:
163
+ * GET /canvas-auth-login
164
+ * GET /canvas-auth-logout
165
+ * POST /canvas-auth-token
166
+
167
+ * The following routes are also defined by CanvasAuth, but only as placeholders that may (and should) be overridden by your application. They do not include any functionality and serve only as landing pages to prevent 404ing on the default redirects.
168
+ * GET /unauthorized
169
+ * GET /logged-out
170
+
171
+ * All routes defined by CanvasAuth are permanently exempt from the requiring authentication, to avoid redirect loops.
172
+ ## Contributing
173
+
174
+ Feeback, bug reports, and pull requests are welcome and appreciated.
175
+
176
+ 1. Fork it ( https://github.com/CUOnline/sinatra-canvas_auth/fork )
177
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
178
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
179
+ 4. Push to the branch (`git push origin my-new-feature`)
180
+ 5. Create a new Pull Request
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module CanvasAuth
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,123 @@
1
+ require 'sinatra'
2
+ require 'rest-client'
3
+
4
+ module Sinatra
5
+ module CanvasAuth
6
+
7
+ DEFAULT_SETTINGS = {
8
+ :auth_paths => [/.*/],
9
+ :canvas_url => nil,
10
+ :client_id => nil,
11
+ :client_secret => nil,
12
+ :login_path => '/canvas-auth-login',
13
+ :token_path => '/canvas-auth-token',
14
+ :logout_path => '/canvas-auth-logout',
15
+ :logout_redirect => '/logged-out',
16
+ :unauthorized_redirect => '/unauthorized'
17
+ }.freeze
18
+
19
+ # Just a prettier syntax for setting auth_paths
20
+ def authenticate(*paths)
21
+ set :auth_paths, paths
22
+ end
23
+
24
+ def self.registered(app)
25
+ self.merge_defaults(app)
26
+
27
+ app.get app.login_path do
28
+ redirect_uri = "#{request.scheme}://#{request.host_with_port}" \
29
+ "#{request.env['SCRIPT_NAME']}#{app.token_path}"
30
+
31
+ redirect_params = "client_id=#{app.client_id}&" \
32
+ "response_type=code&" \
33
+ "state=#{CGI.escape(params['state'])}&" \
34
+ "redirect_uri=#{CGI.escape(redirect_uri)}"
35
+
36
+ ['scope', 'purpose', 'force_login', 'unique_id'].each do |optional_param|
37
+ if params[optional_param]
38
+ redirect_params += "&#{optional_param}=#{CGI.escape(params[optional_param])}"
39
+ end
40
+ end
41
+
42
+ redirect "#{app.canvas_url}/login/oauth2/auth?#{redirect_params}"
43
+ end
44
+
45
+ app.get app.token_path do
46
+ payload = {
47
+ :code => params['code'],
48
+ :client_id => settings.client_id,
49
+ :client_secret => settings.client_secret
50
+ }
51
+
52
+ response = RestClient.post("#{settings.canvas_url}/login/oauth2/token", payload)
53
+ response = JSON.parse(response)
54
+ session['user_id'] = response['user']['id']
55
+ session['access_token'] = response['access_token']
56
+
57
+ oauth_callback(response) if self.respond_to?(:oauth_callback)
58
+
59
+ redirect params['state']
60
+ end
61
+
62
+ app.get app.logout_path do
63
+ if session['access_token']
64
+ delete_url = "#{settings.canvas_url}/login/oauth2/token"
65
+ delete_url += "&expire_sessions=1" if params['expire_sessions']
66
+
67
+ RestClient::Request.execute({
68
+ :method => :delete,
69
+ :url => delete_url,
70
+ :headers => {
71
+ :authorization => "Bearer #{session['access_token']}"
72
+ }
73
+ })
74
+ end
75
+ session.clear
76
+ redirect to(settings.logout_redirect)
77
+ end
78
+
79
+ # These two routes exist to prevent 404'ing with default options, but
80
+ # ideally they should be overridden by the app, or alternate paths given
81
+ app.get '/unauthorized' do
82
+ 'Your canvas account unauthorized to view this resource'
83
+ end
84
+
85
+ app.get '/logged-out' do
86
+ "You have been logged out <a href='canvas-auth-login'>" \
87
+ "Click here</a> to log in again."
88
+ end
89
+
90
+
91
+ # Redirect unauthenticated/unauthorized users before hitting app routes
92
+ app.before do
93
+ current_path = "#{request.env['SCRIPT_NAME']}#{request.env['PATH_INFO']}"
94
+ if CanvasAuth.auth_path?(self.settings, current_path)
95
+ if session['user_id'].nil?
96
+ redirect "#{request.env['SCRIPT_NAME']}#{settings.login_path}?state=#{current_path}"
97
+ elsif self.respond_to?(:authorized) && !authorized
98
+ redirect "#{request.env['SCRIPT_NAME']}#{settings.unauthorized_redirect}"
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ # Should the current path ask for authentication or is it public?
105
+ def self.auth_path?(app, current_path)
106
+ exempt_paths = [ app.login_path, app.token_path, app.logout_path,
107
+ app.logout_redirect, app.unauthorized_redirect ]
108
+
109
+ app.auth_paths.select{ |p| current_path.match(p) }.any? &&
110
+ !exempt_paths.include?(current_path)
111
+ end
112
+
113
+ def self.merge_defaults(app)
114
+ DEFAULT_SETTINGS.each do |key, value|
115
+ if !app.respond_to?(key)
116
+ app.set key, value
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ register CanvasAuth
123
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-canvas_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Connor Ford
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sinatra
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.8'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.8'
69
+ description:
70
+ email: connor.ford@ucdenver.edu
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - README.md
76
+ - lib/sinatra/canvas_auth.rb
77
+ - lib/sinatra/canvas_auth/version.rb
78
+ homepage: https://github.com/cuonline
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.5.1
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Canvas LMS OAuth flow for Sinatra
102
+ test_files: []