Packages
samly
0.9.2
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.1
retired
1.0.0-rc.0
retired
0.10.1
retired
0.10.0
retired
0.9.3
retired
0.9.2
retired
0.9.1
retired
0.9.0
retired
0.8.4
retired
0.8.3
retired
0.8.2
retired
0.8.1
retired
0.8.0
retired
0.7.2
retired
0.7.1
retired
0.7.0
retired
0.6.3
retired
0.6.2
retired
0.6.1
retired
0.6.0
retired
0.5.0
retired
0.4.0
retired
0.1.2
retired
SAML Single-Sign-On Authentication for Plug/Phoenix Applications
Retired package: Use versions >= 1.0.0
Current section
Files
Jump to
Current section
Files
lib/samly/auth_handler.ex
defmodule Samly.AuthHandler do
@moduledoc false
require Logger
import Plug.Conn
alias Samly.{Assertion, IdpData, Helper, State, Subject}
import Samly.RouterUtil, only: [ensure_sp_uris_set: 2, send_saml_request: 5, redirect: 3]
@sso_init_resp_template """
<body onload=\"document.forms[0].submit()\">
<noscript>
<p><strong>Note:</strong>
Since your browser does not support JavaScript, you must press
the button below once to proceed.
</p>
</noscript>
<form method=\"post\" action=\"<%= action %>\">
<%= if target_url do %>
<input type=\"hidden\" name=\"target_url\" value=\"<%= target_url %>\" />
<% end %>
<input type=\"hidden\" name=\"_csrf_token\" value=\"<%= csrf_token %>\" />
<noscript><input type=\"submit\" value=\"Submit\" /></noscript>
</form>
</body>
"""
def initiate_sso_req(conn) do
import Plug.CSRFProtection, only: [get_csrf_token: 0]
target_url =
case conn.params["target_url"] do
nil -> nil
url -> URI.decode_www_form(url)
end
opts = [
action: conn.request_path,
target_url: target_url,
csrf_token: get_csrf_token()
]
conn
|> put_resp_header("Content-Type", "text/html")
|> send_resp(200, EEx.eval_string(@sso_init_resp_template, opts))
end
def send_signin_req(conn) do
%IdpData{id: idp_id} = idp = conn.private[:samly_idp]
%IdpData{esaml_idp_rec: idp_rec, esaml_sp_rec: sp_rec} = idp
sp = ensure_sp_uris_set(sp_rec, conn)
target_url = (conn.params["target_url"] || "/") |> URI.decode_www_form()
nameid = get_session(conn, "samly_nameid")
case State.get_by_nameid(nameid) do
{^nameid, %Assertion{idp_id: ^idp_id}} ->
conn |> redirect(302, target_url)
_ ->
relay_state = State.gen_id()
{idp_signin_url, req_xml_frag} = Helper.gen_idp_signin_req(sp, idp_rec)
conn
|> configure_session(renew: true)
|> put_session("relay_state", relay_state)
|> put_session("idp_id", idp_id)
|> put_session("target_url", target_url)
|> send_saml_request(
idp_signin_url,
idp.use_redirect_for_req,
req_xml_frag,
relay_state |> URI.encode_www_form()
)
end
# rescue
# error ->
# Logger.error("#{inspect error}")
# conn |> send_resp(500, "request_failed")
end
def send_signout_req(conn) do
%IdpData{id: idp_id} = idp = conn.private[:samly_idp]
%IdpData{esaml_idp_rec: idp_rec, esaml_sp_rec: sp_rec} = idp
sp = ensure_sp_uris_set(sp_rec, conn)
target_url = (conn.params["target_url"] || "/") |> URI.decode_www_form()
nameid = get_session(conn, "samly_nameid")
case State.get_by_nameid(nameid) do
{^nameid, %Assertion{idp_id: ^idp_id, authn: authn, subject: subject}} ->
session_index = Map.get(authn, "session_index", "")
subject_rec = Subject.to_rec(subject)
{idp_signout_url, req_xml_frag} =
Helper.gen_idp_signout_req(sp, idp_rec, subject_rec, session_index)
State.delete(nameid)
relay_state = State.gen_id()
conn
|> put_session("target_url", target_url)
|> put_session("relay_state", relay_state)
|> put_session("idp_id", idp_id)
|> delete_session("samly_nameid")
|> send_saml_request(
idp_signout_url,
idp.use_redirect_for_req,
req_xml_frag,
relay_state |> URI.encode_www_form()
)
_ ->
conn |> send_resp(403, "access_denied")
end
# rescue
# error ->
# Logger.error("#{inspect error}")
# conn |> send_resp(500, "request_failed")
end
end