module Sinatra::CanvasAuth
Constants
- DEFAULT_SETTINGS
- VERSION
Public Class Methods
auth_path?(app, current_path, script_name = '')
click to toggle source
Should the current path ask for authentication or is it public?
# File lib/sinatra/canvas_auth.rb, line 145 def self.auth_path?(app, current_path, script_name = '') exempt_paths = [ app.login_path, app.token_path, app.logout_path, app.logout_redirect, app.unauthorized_redirect, app.failure_redirect ] app.auth_paths.select{ |p| current_path.match(p) }.any? && !app.public_paths.select{ |p| current_path.match(p) }.any? && !exempt_paths.map{|p| File.join(script_name, p)}.include?(current_path) end
merge_defaults(app)
click to toggle source
# File lib/sinatra/canvas_auth.rb, line 155 def self.merge_defaults(app) DEFAULT_SETTINGS.each do |key, value| if !app.respond_to?(key) app.set key, value end end end
registered(app)
click to toggle source
# File lib/sinatra/canvas_auth.rb, line 29 def self.registered(app) self.merge_defaults(app) app.helpers do def login_url(state = nil) return false if request.nil? path_elements = [request.env['SCRIPT_NAME'], settings.login_path] path_elements << state if state File.join(path_elements) end def render_view(header='', message='') render(:erb, :canvas_auth, { :views => File.expand_path(File.dirname(__FILE__)), :locals => { :header => header, :message => message } }) end end app.get app.login_path do session['oauth_redirect'] ||= request.env['SCRIPT_NAME'] session['oauth_state'] = SecureRandom.urlsafe_base64(24) redirect_uri = "#{request.scheme}://#{request.host_with_port}" \ "#{request.env['SCRIPT_NAME']}#{settings.token_path}" redirect_params = "client_id=#{settings.client_id}&" \ "response_type=code&" \ "state=#{session['oauth_state']}&" \ "redirect_uri=#{CGI.escape(redirect_uri)}" ['scope', 'purpose', 'force_login', 'unique_id'].each do |optional_param| if params[optional_param] redirect_params += "&#{optional_param}=#{CGI.escape(params[optional_param])}" end end redirect "#{settings.canvas_url}/login/oauth2/auth?#{redirect_params}" end app.get app.token_path do payload = { :code => params['code'], :client_id => settings.client_id, :client_secret => settings.client_secret } begin CanvasAuth.verify_oauth_state(session, params) response = RestClient.post("#{settings.canvas_url}/login/oauth2/token", payload) rescue RestClient::Exception, CanvasAuth::StateError => e failure_url = File.join(request.env['SCRIPT_NAME'], settings.failure_redirect) redirect (failure_url + "?error=#{params[:error] || CGI.escape(e.to_s)}") end response = JSON.parse(response) session['user_id'] = response['user']['id'] session['access_token'] = response['access_token'] oauth_callback(response) if self.respond_to?(:oauth_callback) redirect session['oauth_redirect'] end app.get app.logout_path do if session['access_token'] delete_url = "#{settings.canvas_url}/login/oauth2/token" delete_url += "&expire_sessions=1" if params['expire_sessions'] RestClient::Request.execute({ :method => :delete, :url => delete_url, :headers => { :authorization => "Bearer #{session['access_token']}" } }) end session.clear redirect to(settings.logout_redirect) end app.get app.logout_redirect do render_view('Logged out', 'You have been successfully logged out') end app.get app.unauthorized_redirect do render_view('Authentication Failed', 'Your canvas account is unauthorized to view this resource') end app.get app.failure_redirect do message = "Login could not be completed." if params[:error] && !params[:error].empty? message += " (#{CGI.unescape(params[:error])})" end render_view("Authentication Failed", message) end # Redirect unauthenticated/unauthorized users before hitting app routes app.before do current_path = "#{request.env['SCRIPT_NAME']}#{request.env['PATH_INFO']}" if CanvasAuth.auth_path?(self.settings, current_path, request.env['SCRIPT_NAME']) if session['user_id'].nil? session['oauth_redirect'] = current_path redirect "#{request.env['SCRIPT_NAME']}#{settings.login_path}" elsif self.respond_to?(:authorized) && !authorized redirect "#{request.env['SCRIPT_NAME']}#{settings.unauthorized_redirect}" end end end end
verify_oauth_state(params, session)
click to toggle source
Verify state param from Canvas is the same one originally sent. Otherwise, unauthorized requests can be made by intercepting the redirect from Canvas to app token_path and tricking an authorized user into accessing the link. homakov.blogspot.com/2012/07/saferweb-most-common-oauth2.html
# File lib/sinatra/canvas_auth.rb, line 167 def self.verify_oauth_state(params, session) saved_state = session['oauth_state'] if saved_state != params['state'] || (saved_state && saved_state.empty?) raise CanvasAuth::StateError, 'Invalid OAuth state token provided' end end
Public Instance Methods
authenticate(*paths)
click to toggle source
Just a prettier syntax for setting auth_paths
# File lib/sinatra/canvas_auth.rb, line 25 def authenticate(*paths) set :auth_paths, paths end
login_url(state = nil)
click to toggle source
# File lib/sinatra/canvas_auth.rb, line 33 def login_url(state = nil) return false if request.nil? path_elements = [request.env['SCRIPT_NAME'], settings.login_path] path_elements << state if state File.join(path_elements) end
render_view(header='', message='')
click to toggle source
# File lib/sinatra/canvas_auth.rb, line 40 def render_view(header='', message='') render(:erb, :canvas_auth, { :views => File.expand_path(File.dirname(__FILE__)), :locals => { :header => header, :message => message } }) end