Samly - Add SAML SSO to your Phoenix application (now with multiple identity provider support)

Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application.

This library uses Erlang esaml to provide
plug enabled routes. Use of esaml provides the following capabilities:

  • SP initiated login
  • SP initiated logout
  • IdP initiated logout

This is tested with SimpleSAMLphp. There is a fairly detailed README.md file to help you get started. There are two companion Repos:

  • samly_simplesaml - Use this to create your own SAML IdP for development purpose when working with Samly
  • samly_howto - This is a sample Phoenix application that shows how to use Samly

The readme file has an FAQ section that covers the following:

  • How to setup a SAML 2.0 IdP for development purposes
  • Sample Phoenix application that shows how to use Samly
  • How to register the service provider with IdP
  • How to enable Samly in my application

Feedback and contributions are appreciated.

15 Likes

Samly library version v0.8.0 introduces support for working with multiple Identity Providers (earlier versions were limited to working with only one IdP).

There are two companion Repos:

  • samly_simplesaml - Docker based self-hosted SimpleSAMLPhp Identity provider for development purposes
  • samly_howto - A simple Phoenix application that demonstrates how to SAML enable your application. This will show the SAML assertions from the IdP upon success end-user authentication. It is mostly pre-configured to work with samly_simplesaml. But you can change the configuration to point to any SAML provider.

The Samly documentation has been updated as well.

3 Likes

Samly v0.8.4 and esaml v3.3.0

Now works with Shibboleth IdP (in addition to SimpleSAMLPhp). Here are some screenshots showing the login and consent flows interacting with a self-hosted Shibboleth IdP setup.

Login portion in a Samly enabled phoenix app

Shibboleth Login Page

Shibboleth Consent Page

Back to the Phoenix app - Showing the SAML assertion Attributes

3 Likes

@handnot2 Thank you for your work as part of the Samly library. I’m curious, how easy would it be for one to integrate this with Coherence or Guardian . Do you have any thoughts on that or any open source projects where you’ve tried out this kind of an integration ?

1 Like

Samly is used to enable SAML authentication in your application talking to a SAML provider. Such SAML providers typically would include the capabilities that Coherance provides. Lot of Universites use Shibboleth as their SAML provider and they already would have the user registration, password recovery, notification etc built-in or integrated. The same goes for cloud systems such as OneLogin, Azure and many internal corporate Identity systems. My understanding is that you would need Coherence when you want to manage these capabilities in your application itself. If that is your requirement, you probably don’t want SAML.

If your question is more about how easy it is to integrate Samly in your application, check out the README file. It is very easy. The usual hickups are related to making sure you have the correct SAML provider endpoint information, certificates, whether signing/encryption are used or not and making sure that the config settings are correct.

As a recommended practice, one should clone the following How-to repo, make config changes in that repo (dev.exs) pointing to the SAML provider you want to work with and make sure you are able to login/logout and get the SAML assertions correct. All this without making any other code changes.

The screenshots shown earlier were from this samly_howto Phoenix app. Once this works, make the minimal changes required in your own Phoenix app with the config settings you had success with. You will have a better integration experience if you follow this model.

Hope this helps.

1 Like

Hi @handnot2,

Thank you for your response.

I will look at Coherence in some more detail from my end to confirm if this is a SAML provider. I’m relatively new to SAML but will definitely explore further to understand it’s concepts better.

We’re trying to implement a Unified login service at our company currently. My requirement at a high level as part of this is to use the capabilities that Coherence provides, add Social sign in capabilities(planning to use - GitHub - danschultzer/coherence_assent: UNMAINTAINED - Add multi provider login to your Coherence Phoenix website for Google and FB login) and also implement SSO(via Samly) in addition to this.

One of the use cases where SSO can be useful is, if a user is logged in to one app, we want that user to be auto logged in to the other app(both apps should be somehow tied to the same unified login service). That’s basically why we need SSO. At a high level, when we implement Social Sign in, We want the call back URL from a Provider like FB or Google to go to our authentication service(the unified login) which would return a session token to help keep the user signed in across multiple apps.

This is basically why I wanted to get more clarity on what do you think could be the feasibility of having all of this together in one application. Would it be possible for you to throw more light on whether one can potentially have all these capabilities in one application from your knowledge and experience?

I will also work towards following up on the above from my end.

Thank you for your time!

1 Like

Does anyone need instructions on how to setup a self-hosted Shibboleth Identity Provider installation (development setup) for use with Samly?

This would be similar to the samly_simplesaml - but pointing to an LDAP store for user authentication.

2 Likes

Created a blog post on using samly. This includes instructions on setting up a self-hosted Shibboleth SAML Identity Provider that uses OpenLDAP.

4 Likes

Samly V0.9.0 - IDP Initiated Login (aka IDP-First flow)

The authentication flow can now be started from an IDP. For example, cloud hosted SAML authentication providers such as OneLogin allow your application to be registered and be shown on an application portal/dashboard. The end user can login to the application portal click on your application Icon and be redirected/taken to your application.

Samly already supports the SP initiated login flow. In this model, the end user visits your application pages first. The login flow is initiated in your application, user authenticates and gets redirected back to your app.

Based on how your IDP is setup, you can get the user profile information along with any assigned “roles” in the SAML assertion. These are made available for you in the Plug/Phoenix connection. You can use any plug based authorization system to perform authorizations.

FYI. Samly is known to work with SimpleSAMLPhp, Shibboleth, OneLogin and other SAML 2.0 providers. If you work with other SAML providers, use the documentation and the blog entry above to do your own integration. Please share if you are able to integrate Samly with other SAML providers.

Really impressed how you’re bringing Samly along. I have a project coming up that will absolutely benefit from your work. Part of that will be doing some work to integrate with SAP’s IDP. Thanks for the effort!

Hope this can evolve to work with more providers. When you are ready, let us know how it goes with SAP IDP. Thanks.

Please i’m having issue getting this to work in my phoenix app. I have installed as stated in the readme details.

Below is what my config looks like

config :samly, Samly.Provider,
  idp_id_from: :path_segment,
  service_providers: [
    %{
      id: "entsp",
      certfile: "priv/keys/samly.crt",
      keyfile: "priv/keys/samly.pem"
    }
  ],
  identity_providers: [
    %{
      id: "entidp",
      sp_id: "entsp",
      metadata_file: "idp_metadata.xml"
    }
  ]

While testing the login with the link http://localhost:4000/sso/auth/signin/entidp, i got Argument error

[error] #PID<0.704.0> running EnterpriseWeb.Endpoint (cowboy_protocol) terminated
Server: localhost:4000 (http)
Request: POST /sso/auth/signin/entidp
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.binary_to_list(nil)
        (xmerl) xmerl_lib.erl:69: :xmerl_lib.export_text/2
        (xmerl) xmerl.erl:191: :xmerl.export_content/2
        (xmerl) xmerl.erl:224: :xmerl.export_element/2
        (xmerl) xmerl.erl:199: :xmerl.export_content/2
        (xmerl) xmerl.erl:199: :xmerl.export_content/2
        (xmerl) xmerl.erl:224: :xmerl.export_element/2
        (xmerl) xmerl.erl:199: :xmerl.export_content/2
        (xmerl) xmerl.erl:168: :xmerl.export1/3
        (esaml) /Library/WebServer/Documents/enterprise/deps/esaml/src/esaml_binding.erl:77: :esaml_binding.encode_http_post/3
        (samly) lib/samly/router_util.ex:94: Samly.RouterUtil.send_saml_request/5
        (samly) lib/samly/auth_router.ex:1: Samly.AuthRouter.plug_builder_call/2
        (plug) lib/plug/router/utils.ex:92: Plug.Router.Utils.forward/4
        (samly) lib/samly/router.ex:1: Samly.Router.plug_builder_call/2
        (phoenix) lib/phoenix/router/route.ex:147: Phoenix.Router.Route.forward/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (enterprise) lib/enterprise_web/endpoint.ex:1: EnterpriseWeb.Endpoint.plug_builder_call/2
        (enterprise) lib/plug/debugger.ex:102: EnterpriseWeb.Endpoint."call (overridable 3)"/2
        (enterprise) lib/enterprise_web/endpoint.ex:1: EnterpriseWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4

Please how can i resolve this? Thanks.

Sounds to me like whatever SSO server you are using is missing a required field, should set the SSO server to export that field, or PR Saml to not require whatever that field is. :slight_smile:

/me built their own SSO client since our new work SSO server gives a username… and that’s it, nothing else…

1 Like

Thanks immensely @OvermindDL1. I don’t think the problem is from the SSO server because the request hasn’t been sent at the time this error shows up.

resp_body = :esaml_binding.encode_http_post(idp_url, signed_xml_payload, relay_state) from the function below throws that error.

def send_saml_request(conn, idp_url, use_redirect?, signed_xml_payload, relay_state) do
    if use_redirect? do
      url =
        :esaml_binding.encode_http_redirect(idp_url, signed_xml_payload, :undefined, relay_state)

      conn |> redirect(302, url)
    else
      resp_body = :esaml_binding.encode_http_post(idp_url, signed_xml_payload, relay_state)

      conn
      |> Conn.put_resp_header("Content-Type", "text/html")
      |> Conn.send_resp(200, resp_body)
    end
  end

samly/lib/samly/router_util

The error is gone after adding entity_id to the service provider confirguration.

1 Like

Please i observe the idp_metadata file linked in the configuration is not being used. Saml seems to be generating a separate idp_metadata content. I navigated to http://localhost:4000/sso/sp/metadata/entidp and the details of the metadata is different from the the linked idp_metadata.

Please is there a way to enforce the metadata that is used?

1 Like

@handnot2 please can you kindly update the name_format in the function below to use the nameid_format property provided in the idp configuration?

helper.ex

def gen_idp_signin_req(sp, idp_metadata) do
    idp_signin_url = Esaml.esaml_idp_metadata(idp_metadata, :login_location)
    # TODO: Expose an config
    name_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
    xml_frag = :esaml_sp.generate_authn_request(idp_signin_url, sp, name_format)
    {idp_signin_url, xml_frag}
  end

Hi,

Has anyone already integrated samly successfully with an ADFS IdP? I may need soon to support SSO with ADFS and just started evaluating the different solutions.

2 Likes

@handnot2 Attempting to follow the guide for: https://github.com/handnot2/samly_simplesaml

But when I run the docker container and hit http://localhost:8082/simplesaml/

I get:

**Parse error** : syntax error, unexpected end of file in  **/srv/simplesaml/config/config.php**  on line  **56**

**Fatal error** : Exception thrown without a stack frame in  **Unknown**  on line  **0**

Am I doing something terribly wrong or is an underlying package with the PHP setup not playing well maybe?

Thanks,
Scott

Let me check and get back in a day or two.