handnot2 / samly

Elixir Plug library to enable SAML 2.0 SP SSO in Phoenix/Plug applications.
MIT License
125 stars 90 forks source link

Configuration for multiple idps #4

Closed natewallis closed 6 years ago

natewallis commented 6 years ago

With the current code, it's not possible to use more than one identity provider in your application.

ccutch commented 6 years ago

bump

handnot2 commented 6 years ago

Migration information is available at:

https://github.com/handnot2/samly/wiki/Migrate-Samly-v0.7.x-to-v0.8.0

This is not merged to main. Would like to get feedback on how this works for you (follow the migration instructions to try it out). Will merge to main and publish to hex.pm once any issues are ironed out.

natewallis commented 6 years ago

I have installed an started testing, however when I go to my sign in URI, I get the following response in the browser - "invalid_request". After having a quick look at the samly code, it appears to be failing on valid_referer? call in auth_handler.ex...

I have inspected a few of my variables (conn, referer and request_authority), I haven't had a chance to see when these variables should be set... Here is the output

conn

[info] GET /sso/federation/auth/signin %Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{}, before_send: [#Function<0.96292254/1 in Plug.CSRFProtection.call/2>,

Function<0.112984571/1 in Plug.Session.before_send/2>,

Function<6.99240232/1 in Samly.Router.secure_samly/2>,

Function<1.99561661/1 in Plug.Logger.call/2>,

Function<0.28837570/1 in Phoenix.LiveReloader.before_send_inject_reloader/2>],

body_params: %{}, cookies: %{"_mannequin_key" => "SFMyNTY.g3QAAAAEbQAAAAtfY3NyZl90b2tlbm0AAAAYMVJUZTU4L052TGhVSThyWXMyaFZFZz09bQAAAAZpZHBfaWRtAAAACmZlZGVyYXRpb25tAAAAC3JlbGF5X3N0YXRlbQAAACA3QnZkZlphTG5hYUZyTW8xcUV4d1RGQVNaT2dmTzAwVm0AAAAKdGFyZ2V0X3VybG0AAAABLw.HmbJ92lF84i-93VItnK4uhIoz88Obv4U_DiR55pXCH0"}, halted: false, host: "localhost", method: "GET", owner: #PID<0.546.0>, params: %{"glob" => ["signin"], "idp_id" => "federation"}, path_info: ["signin"], path_params: %{"glob" => ["signin"], "idp_id" => "federation"}, peer: {{127, 0, 0, 1}, 62421}, port: 4000, private: %{MannequinWeb.Router => {[], %{Samly.Router => []}}, :phoenix_endpoint => MannequinWeb.Endpoint, :phoenix_pipelines => [], :phoenix_router => MannequinWeb.Router, :plug_route => #Function<5.72521736/1 in Samly.AuthRouter.do_match/4>, :plug_session => %{"_csrf_token" => "1RTe58/NvLhUI8rYs2hVEg==", "idp_id" => "federation", "relay_state" => "7BvdfZaLnaaFrMo1qExwTFASZOgfO00V", "target_url" => "/"}, :plug_session_fetch => :done}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, req_cookies: %{"_mannequin_key" => "SFMyNTY.g3QAAAAEbQAAAAtfY3NyZl90b2tlbm0AAAAYMVJUZTU4L052TGhVSThyWXMyaFZFZz09bQAAAAZpZHBfaWRtAAAACmZlZGVyYXRpb25tAAAAC3JlbGF5X3N0YXRlbQAAACA3QnZkZlphTG5hYUZyTW8xcUV4d1RGQVNaT2dmTzAwVm0AAAAKdGFyZ2V0X3VybG0AAAABLw.HmbJ92lF84i-93VItnK4uhIoz88Obv4U_DiR55pXCH0"}, req_headers: [{"host", "localhost:4000"}, {"connection", "keep-alive"}, {"user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"}, {"upgrade-insecure-requests", "1"}, {"accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8"}, {"accept-encoding", "gzip, deflate, br"}, {"accept-language", "en-US,en;q=0.8"}, {"cookie", "_mannequin_key=SFMyNTY.g3QAAAAEbQAAAAtfY3NyZl90b2tlbm0AAAAYMVJUZTU4L052TGhVSThyWXMyaFZFZz09bQAAAAZpZHBfaWRtAAAACmZlZGVyYXRpb25tAAAAC3JlbGF5X3N0YXRlbQAAACA3QnZkZlphTG5hYUZyTW8xcUV4d1RGQVNaT2dmTzAwVm0AAAAKdGFyZ2V0X3VybG0AAAABLw.HmbJ92lF84i-93VItnK4uhIoz88Obv4U_DiR55pXCH0"}], request_path: "/sso/federation/auth/signin", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "c900uu3onvpb423hherk3a3of1e95fag"}], scheme: :http, script_name: ["sso", "federation", "auth"], secret_key_base: "d+rlZ7blKgUE5RF9g1TRF/PAeRFjNPIiIlgKYWEGI1WtBCcmRFU5PnfJPzRJtlST", state: :unset, status: nil}

referer

%URI{authority: nil, fragment: nil, host: nil, path: nil, port: nil, query: nil, scheme: nil, userinfo: nil}

request authority

localhost:4000 [info] Sent 403 in 13ms

I generated a new certificate and private key for this new config that had localhost as the auth, here is my config in /config.dev.exs - did I need to fill out the entity_id?

config :samly, Samly.Provider,
  base_url: "http://localhost:4000/sso", # use your info
  service_providers: [
    %{
      id: "mannequin",
      #entity_id: "urn:my-host:my-id",
      certfile: "auth/mannequin.crt",
      keyfile: "auth/mannequin.pem"
    }
  ],
  identity_providers: [
    %{
      id: "federation",
      sp_id: "mannequin",
      metadata_file: "auth/idp_metadata.xml", # use correct file name
      #pre_session_create_pipeline: SamlyHowtoWeb.Plugs.SamlyPipeline, # optional - your pipeline module here
      use_redirect_for_req: false,
      sign_requests: true,
      sign_metadata: true,
      signed_assertion_in_resp: true,
      signed_envelopes_in_resp: true
    }
  ]
handnot2 commented 6 years ago

Are you manually entering this URL in the browser directly? If that is the case the "referer" header is not set and will result in failure. Can you make the sigin-in / sign-out links in some page in your web app and click on that link? The browser will automatically set the referer header and it should work.

Here is what is included in the index.html.eex file of samly_howto application:

        <%= if @uid do %>
          <a href="/sso/<%= @idp_id %>/auth/signout?target_url=<%= @target_url %>"
            class="btn btn-sm btn-primary navbar-btn"
            role="button">
            Sign out from <%= @idp_id %>
          </a>
        <% else %>
          <a href="/sso/<%= @idp_id %>/auth/signin?target_url=<%= @target_url %>"
             class="btn btn-sm btn-primary navbar-btn"
             role="button">
             Sign in with <%= @idp_id %>
           </a>
        <% end %>

Theidp_id in your case is federation.

The entity_id attribute is optional. If you don't set it, the service provider metadata URL will be used as entity id. Just make sure that it is consistent with how your service provider is registered with the IdP.

natewallis commented 6 years ago

Silly me - yes..

ccutch commented 6 years ago

Thank you!

natewallis commented 6 years ago

Ditto on the "Thank you" - I thought I had in our private thread on elixirforum.com, but it appears I forgot my manners... cheers.. :)

natewallis commented 6 years ago

Works well, thanks... will do some more testing and let you know if I hit any snags..

natewallis commented 6 years ago

@handnot2 - Is there anyway with the current multi idp setup that I could run my app from a subdomain and use the subdomain to determine the idp to use? I wonder if moving the base_url per SP would allow this flexibility (with minor code changes)... .

I still feel that running my app from a subdomain is a better alternative to white label my application rather than doing it via the URL... e.g

federation.myapp.com is preferred to myapp.com/federation

I don't think I can do it like that and use multi idps with the base_url sitting at the top level in the config.

handnot2 commented 6 years ago

Just need to simplify the config a bit.

handnot2 commented 6 years ago

Please checkout multi_idp branch and try this out again. Here is the link to the instructions:

https://github.com/handnot2/samly/wiki/Migrate-Samly-v0.7.x-to-v0.8.0

Feedback appreciated.