diff options
| author | mo khan <mo@mokhan.ca> | 2025-03-26 14:46:20 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-03-26 14:46:20 -0600 |
| commit | e758496795f00094d1f9ebe4e5f12bf90da1651c (patch) | |
| tree | 10b28cc9d447a7d4e2394346fd074036a6838780 /bin | |
| parent | ead90215e0b08090cf5218a3dabbcf5506758477 (diff) | |
feat: exchange a SAML assertion for an access token
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/idp | 20 | ||||
| -rwxr-xr-x | bin/ui | 17 |
2 files changed, 19 insertions, 18 deletions
@@ -5,6 +5,7 @@ require "bundler/inline" gemfile do source "https://rubygems.org" + gem "base64", "~> 0.1" gem "bcrypt", "~> 3.0" gem "csv", "~> 3.0" gem "declarative_policy", "~> 1.0" @@ -14,7 +15,7 @@ gemfile do gem "rack", "~> 3.0" gem "rack-session", "~> 2.0" gem "rackup", "~> 2.0" - gem "saml-kit", "~> 1.0" + gem "saml-kit", "~> 1.0", git: "github.com:xlgmokha/saml-kit", branch: "main" gem "twirp", "~> 1.0" gem "warden", "~> 1.0" gem "webrick", "~> 1.0" @@ -444,7 +445,6 @@ module Authz @all ||= [] end - # TODO:: Look up saml_assertion def find_by(params) case params[:grant_type] when 'authorization_code' @@ -483,16 +483,11 @@ module Authz def saml_assertion_grant(encoded_saml_assertion) xml = Base64.decode64(encoded_saml_assertion) - saml_response = Saml::Kit::Document.to_saml_document(xml) - saml_assertion = saml_response.assertion # TODO:: Validate signature and prevent assertion reuse + saml_assertion = Saml::Kit::Document.to_saml_document(xml) - user = case saml_assertion.name_id_format - when Saml::Kit::Namespaces::EMAIL_ADDRESS - ::Authn::User.find_by_email(saml_assertion.name_id) - when Saml::Kit::Namespaces::PERSISTENT + user = ::Authn::User.find_by_email(saml_assertion.name_id) || ::Authn::User.find(saml_assertion.name_id) - end new(user, saml_assertion: saml_assertion) end @@ -544,6 +539,11 @@ module Authz body[:id_token] = user.create_id_token.to_jwt end end + rescue StandardError => error + { + error: error.message, + error_description: error.backtrace, + } end end @@ -582,7 +582,7 @@ module Authz params = request.content_type == "application/json" ? JSON.parse(request.body.read, symbolize_names: true) : Hash[URI.decode_www_form(request.body.read)].transform_keys(&:to_sym) grant = AuthorizationGrant.find_by(params) - return [404, { "Content-Type" => "application/json" }, [JSON.pretty_generate(error: 404, message: "Not Found")]] if grant.nil? || grant.inactive? + return [404, { "Content-Type" => "application/json" }, [JSON.pretty_generate(error: 404, error_description: "Not Found")]] if grant.nil? || grant.inactive? return [200, { "Content-Type" => "application/json" }, [JSON.pretty_generate(grant.exchange)]] when "/oauth/revoke" # RFC-7009 # TODO:: Revoke the JWT token and make it ineligible for usage @@ -123,7 +123,7 @@ end module HTTPHelpers def current_user?(request) - request.session[:id_token] + request.session[:access_token] end def not_found @@ -393,14 +393,14 @@ class UI saml_response = saml_binding.deserialize(request.params) raise saml_response.errors unless saml_response.valid? + assertion = Base64.strict_encode64(saml_response.assertion.to_xml) response = oauth_client.exchange( "urn:ietf:params:oauth:grant-type:saml2-bearer", - assertion: request.params["SAMLResponse"], + assertion: assertion, ) if response.code == "200" tokens = JSON.parse(response.body, symbolize_names: true) request.session[:access_token] = tokens[:access_token] - request.session[:id_token] = tokens[:id_token] request.session[:refresh_token] = tokens[:access_token] template = <<~ERB @@ -410,13 +410,14 @@ class UI <title></title> </head> <body style="background-color: pink;"> - <h2>Received SAML Response</h2> - <textarea readonly="readonly" disabled="disabled" cols=220 rows=40><%=- saml_response.to_xml(pretty: true) -%></textarea> - <pre id="raw-saml-response"><%= request.params["SAMLResponse"] %></pre> - <pre id="access-token"><%= JSON.pretty_generate(request.session[:access_token]) %></pre> - <a href="/index.html">Home</a> <a href="/groups.html">Groups</a> + + <h2>Received SAML Response</h2> + <textarea readonly="readonly" disabled="disabled" cols=220 rows=40><%=- saml_response.to_xml(pretty: true) -%></textarea> + <pre id="raw-saml-response" style="display: none;"><%= request.params["SAMLResponse"] %></pre> + <pre id="xml-saml-assertion" style="display: none;"><%= saml_response.assertion.to_xml(pretty: true) %></pre> + <pre id="access-token" style="display: none;"><%= JSON.pretty_generate(request.session[:access_token]) %></pre> </body> </html> ERB |
