Reputation: 143
Why does the Rack middleware fail to redirect when coupled with default GET and POST login routes and 401 handling in the Sinatra app?
Relevant Shield middleware extract :
module Shield
class Middleware
attr :url
def initialize(app, url = "/login")
@app = app
@url = url
end
def call(env)
tuple = @app.call(env)
if tuple[0] == 401
[302, headers(env["SCRIPT_NAME"] + env["PATH_INFO"]), []]
else
tuple
end
end
private
def headers(path)
{ "Location" => "%s?return=%s" % [url, encode(path)],
"Content-Type" => "text/html",
"Content-Length" => "0"
}
end
def encode(str)
URI.encode_www_form_component(str)
end
end
end
View full source code (104 lines/2.8kb).
Here a relevant extract of the Sinatra app:
# application_controller.rb
class ApplicationController < Sinatra::Base
helpers Shield::Helpers
use Shield::Middleware, "/login"
...
get '/noway' do
error(401) unless authenticated(User)
erb :app_noway
end
get '/login' do
erb :login
end
post "/login" do
if login(User, params[:login], params[:password])
remember(authenticated(User)) if params[:remember_me]
redirect(params[:return] || "/")
else
redirect "/login"
end
end
end
Full source code (basic app displaying the problem behavior), for easy and immediate perusal: https://github.com/shieldtest/shieldtest
The repository is ready for a "clone and rackup" with database, env and all. Login credentials; email: [email protected], password: shield.
Problem
When accessing a protected route (/noway), the middleware injects a authentication process, as intended. But after the successful autentication, the subsequent redirect always defaults to root, instead of the return URL for the protected page (/noway).
Solution needed
The protected page (/noway) should be redirected to automatically after authenticating successfully via Shield.
Step 1 (below): At the Sinatra main page. Click link to protected page (/noway)
Step 2 (below): Redirected to /login correctly, as no user is authenticated. Enter correct login credentials correctly.
PROBLEM BEHAVIOR - redirected to main instead of the protected page
Step 3A (below): After entering correct login credentials: sent back to main page (again)
TESTING LOGIN - protected page is accessible now (manually, by clicking page again)
Step 4 (below): At the main page. Click the protected page (/noway) again => Access granted
Upvotes: 1
Views: 547
Reputation: 143
The params[:return]
was never forwarded to the POST request, it seems.
So, a 'dirty fix' would be to grab the return params and pass it via the login form to the POST request. This yields the desired behavior:
#login.rb
...
<% if params[:return] %>
<input type='hidden' name='redirect' value="<%= params[:return] %>">
<% end %>
...
And then redirecting to the redirect params from the login form:
#application_controller.rb
post "/login" do
if login(User, params[:login], params[:password])
...
redirect to params[:redirect] || "/"
...
end
end
Still, I would have preferred to understand why the middleware didn't perform as expected and how to fix/store this return params via the Rack middleware.
Upvotes: 0