Commit f62507b

mo khan <mo@mokhan.ca>
2025-03-12 19:19:38
refactor: redirect to the standalone login page and continue saml transaction
1 parent d1562ce
Changed files (1)
bin
bin/idp
@@ -28,8 +28,12 @@ $port = ENV.fetch("PORT", 8282).to_i
 $host = ENV.fetch("HOST", "localhost:#{$port}")
 
 module HTTPHelpers
-  def logged_in?(request)
-    request.session[:user_id]
+  def current_user?(request)
+    current_user(request)
+  end
+
+  def current_user(request)
+    ::Authn::User.find(request.session[:user_id])
   end
 
   def default_headers
@@ -47,7 +51,7 @@ module HTTPHelpers
   end
 
   def http_redirect_to(location)
-    [302, { 'Location' => location }, []]
+    [302, { 'Location' => "http://idp.example.com:8080#{location}" }, []]
   end
 end
 
@@ -67,6 +71,12 @@ module Authn
         end
       end
 
+      def find(id)
+        all.find do |user|
+          user[:id] == id
+        end
+      end
+
       def find_by_username(username)
         all.find do |user|
           user[:username] == username
@@ -136,7 +146,8 @@ module Authn
         when '/sessions'
           if (user = User.find_by_credentials(request.params))
             request.session[:user_id] = user[:id]
-            return http_redirect_to('/')
+            path = request.params["redirect_back"] ? request.params["redirect_back"] : "/"
+            return http_redirect_to(path)
           else
             return http_redirect_to("/sessions/new")
           end
@@ -159,6 +170,7 @@ module Authn
           <body>
             <form id="login-form" action="/sessions" method="post">
               <input type="input" placeholder="Username" id="username" name="username" value="" />
+              <input type="hidden" name="redirect_back" value="<%= request.params["redirect_back"] %>" />
               <input type="password" placeholder="Password" id="password" name="password" value="" />
               <input type="submit" id="login-button" value="Login" />
             </form>
@@ -194,26 +206,33 @@ module Authn
     end
 
     def call(env)
-      path = env['PATH_INFO']
-      case env['REQUEST_METHOD']
-      when 'GET'
-        case path
-        when "/metadata.xml"
+      request = Rack::Request.new(env)
+      case request.request_method
+      when Rack::GET
+        case request.path
+        when "/saml/continue"
+          if current_user?(request)
+            saml_params = request.session[:saml_params]
+            return saml_post_back(request, current_user(request), saml_params)
+          else
+            return http_redirect_to("/sessions/new?redirect_back=/saml/continue")
+          end
+        when "/saml/metadata.xml"
           return http_ok(
             { 'Content-Type' => "application/samlmetadata+xml" },
             saml_metadata.to_xml(pretty: true)
           )
         end
-      when 'POST'
-        case path
-        when "/new"
-          return login_page(Rack::Request.new(env))
-        when "/login"
-          request = Rack::Request.new(env)
-          if (user = User.find_by_credentials(request.params))
-            return saml_post_back(request, user)
+      when Rack::POST
+        case request.path
+        when "/saml/new"
+          saml_params = saml_params_from(request)
+
+          if current_user?(request)
+            return saml_post_back(request, current_user(request), saml_params)
           else
-            return login_page(request)
+            request.session[:saml_params] = saml_params
+            return http_redirect_to("/sessions/new?redirect_back=/saml/continue")
           end
         end
       end
@@ -225,45 +244,14 @@ module Authn
 
     attr_reader :saml_metadata
 
-    def login_page(request)
-      saml_params = saml_params_from(request)
+    def saml_post_back(request, user, saml_params)
       saml_request = binding_for(request).deserialize(saml_params)
-      raise saml_request.errors unless saml_request.valid?
-
-      template = <<~ERB
-        <!DOCTYPE html>
-        <html>
-          <head><title></title></head>
-          <body>
-            <h2>Recieved SAML Request</h2>
-            <textarea readonly="readonly" disabled="disabled" cols=225 rows=6><%=- saml_request.to_xml(pretty: true) -%></textarea>
-
-            <form id="login-form" action="/saml/login" method="post">
-              <input type="input" placeholder="Username" id="username" name="username" value="" />
-              <input type="password" placeholder="Password" id="password" name="password" value="" />
-              <%- saml_params.each do |(key, value)| -%>
-                <input type="hidden" name="<%= key %>" value="<%= value %>" />
-              <%- end -%>
-              <input type="submit" id="login-button" value="Login" />
-            </form>
-          </body>
-        </html>
-      ERB
-      erb = ERB.new(template, trim_mode: '-')
-      html = erb.result(binding)
-      [200, { 'Content-Type' => "text/html" }, [html]]
-    end
-
-    def saml_post_back(request, user)
-      params = saml_params_from(request)
-      saml_request = binding_for(request).deserialize(params)
-      request.session[:user_id] = user[:id]
 
       @builder = nil
       url, saml_params = saml_request.response_for(
         user,
         binding: :http_post,
-        relay_state: params[:RelayState]
+        relay_state: saml_params[:RelayState]
       ) { |builder| @builder = builder }
       template = <<~ERB
         <!DOCTYPE html>
@@ -303,12 +291,7 @@ module Authn
     end
 
     def binding_for(request)
-      location = "#{$scheme}://#{$host}/saml/new"
-      if request.post?
-        Saml::Kit::Bindings::HttpPost.new(location: location)
-      else
-        Saml::Kit::Bindings::HttpRedirect.new(location: location)
-      end
+      Saml::Kit::Bindings::HttpPost.new(location: "#{$scheme}://#{$host}/saml/new")
     end
   end
 end
@@ -395,7 +378,7 @@ module Authz
       when Rack::GET
         case request.path_info
         when "/authorize" # RFC-6749
-          if logged_in?(request)
+          if current_user?(request)
             return get_authorize(request)
           else
             http_redirect_to("/saml/")
@@ -481,7 +464,7 @@ class IdentityProvider
     when Rack::GET
       case request.path
       when '/'
-        if logged_in?(request)
+        if current_user?(request)
           return get_dashboard(request)
         else
           return http_redirect_to("/sessions/new")
@@ -507,8 +490,7 @@ class IdentityProvider
       <html>
         <head><title></title></head>
         <body>
-          <h1>Dashboard</h1>
-          <pre><%= request.session[:access_token] %></pre>
+          <h1> Hello, <%= current_user(request)[:username] %></h1>
 
           <form action="/sessions/delete" method="post">
             <input type="submit" value="logout" />