class Redd::Middleware
Rack middleware.
Public Class Methods
@param opts [Hash] the options to create the object with @option opts [String] :user_agent your app's unique and descriptive user agent @option opts [String] :client_id the client id of your app @option opts [String] :redirect_uri the provided redirect URI @option opts [String] :secret ('') the app secret (for the web type) @option opts [Array<String>] :scope (['identity']) a list of scopes to request @option opts ['temporary', 'permanent'] :duration ('permanent') the duration to request the
code for.
@option opts [Boolean] :auto_refresh (true) allow refreshing a permanent access automatically
(only if duration is 'permanent')
@option opts [String] :via ('/auth/reddit') the relative path in the application that
redirects a user to reddit
# File lib/redd/middleware.rb, line 23 def initialize(app, opts = {}) @app = app strategy_opts = opts.select { |k| %i(user_agent client_id secret redirect_uri).include?(k) } @strategy = Redd::AuthStrategies::Web.new(strategy_opts) @user_agent = opts.fetch(:user_agent, "Redd:Web Application:v#{Redd::VERSION} (by unknown)") @client_id = opts.fetch(:client_id) @redirect_uri = opts.fetch(:redirect_uri) @scope = opts.fetch(:scope, ['identity']) @duration = opts.fetch(:duration, 'permanent') @auto_refresh = opts.fetch(:auto_refresh, true) && @duration == 'permanent' @via = opts.fetch(:via, '/auth/reddit') end
Public Instance Methods
# File lib/redd/middleware.rb, line 37 def call(env) # This is done for thread safety so that each thread has its own copy # of the middleware logic. dup._call(env) end
Protected Instance Methods
# File lib/redd/middleware.rb, line 45 def _call(env) @request = Rack::Request.new(env) return redirect_to_reddit! if @request.path == @via before_call response = @app.call(env) after_call response end
Private Instance Methods
Do any cleanup or changes after calling the application.
# File lib/redd/middleware.rb, line 82 def after_call env_session = @request.env['redd.session'] if env_session && env_session.client.access # Make sure to flush any changes made to the Session client to the browser. @request.session[:redd_session] = env_session.client.access.to_h else # Clear the session if the app explicitly set 'redd.session' to nil. @request.session.delete(:redd_session) end end
Do any setup before calling the rest of the application.
# File lib/redd/middleware.rb, line 72 def before_call # Convert the code to an access token if returning from authentication. create_session! if @request.base_url + @request.path == @redirect_uri # Clear the state for any other request. @request.session.delete(:redd_state) # Load a Session model from the access token in the user's cookies. @request.env['redd.session'] = (@request.session[:redd_session] ? parse_session : nil) end
Store the access token and other details in the user's browser, assigning any errors to the 'redd.error' env variable.
# File lib/redd/middleware.rb, line 103 def create_session! # Skip authorizing if there was an error from the authorization. handle_token_error # Try to get a code (the rescue block will also prevent crazy crashes) access = @strategy.authenticate(@request.GET['code']) @request.session[:redd_session] = access.to_h rescue Redd::TokenRetrievalError, Redd::ResponseError => error @request.env['redd.error'] = error end
Assigns a single string representing a reddit authentication errors.
# File lib/redd/middleware.rb, line 94 def handle_token_error message = nil message = 'invalid_state' if @request.GET['state'] != @request.session[:redd_state] message = @request.GET['error'] if @request.GET['error'] raise Redd::TokenRetrievalError, message if message end
Return a {Redd::Models::Session} based on the hash saved into the browser's session.
# File lib/redd/middleware.rb, line 114 def parse_session client = Redd::APIClient.new( @strategy, user_agent: @user_agent, limit_time: 0, auto_refresh: @auto_refresh ) client.access = Redd::Models::Access.new(@strategy, @request.session[:redd_session]) Redd::Models::Session.new(client) end
Creates a unique state and redirects the user to reddit for authentication.
# File lib/redd/middleware.rb, line 58 def redirect_to_reddit! state = SecureRandom.urlsafe_base64 url = Redd.url( client_id: @client_id, redirect_uri: @redirect_uri, scope: @scope, duration: @duration, state: state ) @request.session[:redd_state] = state [302, { 'Location' => url }, []] end