SkillAgentSearch skills...

Samly

Elixir Plug library to enable SAML 2.0 SP SSO in Phoenix/Plug applications.

Install / Use

/learn @handnot2/Samly
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Samly

A SAML 2.0 Service Provider Single-Sign-On Authentication library. This Plug library can be used to SAML enable a Plug/Phoenix application.

This has been used in the wild with the following Identity Providers:

  • Okta
  • Ping Identity
  • OneLogin
  • ADFS
  • Nexus GO
  • Shibboleth
  • SimpleSAMLphp

Please send a note by DM if you have successfully used Samly with other Identity Providers.

Inline docs

This library uses Erlang esaml to provide plug enabled routes.

Setup

# mix.exs

# v1.0.0 uses esaml v4.2 which in turn relies on cowboy 2.x
# If you need to work with cowboy 1.x, you need the following override:
# {:esaml, "~> 3.7", override: true}

defp deps() do
  [
    # ...
    {:samly, "~> 1.0.0"},
  ]
end

Supervision Tree

Add Samly.Provider to your application supervision tree.

# application.ex

children = [
  # ...
  {Samly.Provider, []},
]

Router Change

Make the following change in your application router.

# router.ex

# Add the following scope ahead of other routes
# Keep this as a top-level scope and **do not** add
# any plugs or pipelines explicitly to this scope.
scope "/sso" do
  forward "/", Samly.Router
end

Certificate and Key for Samly

Samly needs a private key and a corresponding certificate. These are used to sign the SAML requests when communicating with the Identity Provider. This certificate should be made available to Samly via config settings. It should also be made available to the Identity Provider so it can verify the SAML signed requests.

You can create a self-signed certificate for this purpose. You can use phx.gen.cert mix task that is available as part of Phoenix 1.4 or use openssl directly to generate the key and corresponding certificate. (Check out samly_howto README.md for this.)

Identity Provider Metadata

Samly expects information about the Identity Provider including information about its SAML endpoints in an XML file. Most Identity Providers have some way of exporting the IdP metadata in XML form. Some may provide a web UI to export/save the XML locally. Others may provide a URL that can be used to fetch the metadata.

For example, SimpleSAMLPhp IdP provides a URL for the metadata. You can fetch it using wget.

wget --no-check-certificate -O idp1_metadata.xml https://idp1.samly:9091/simplesaml/saml2/idp/metadata.php

If you are using the SimpleSAMLPhp administrative Web UI, login with you admin credentials (https://idp1.samly:9091/simplesaml). Go to the Federation tab. At the top there will be a section titled "SAML 2.0 IdP Metadata". Click on the Show metadata link. Copy the metadata XML from this page and save it in a local file (idp1_metadata.xml for example).

Make sure to save this XML file and provide the path to the saved file in Samly configuration.

Identity Provider ID in Samly

Samly has the ability to support multiple Identity Providers. All IdPs that Samly needs to talk to must have an identifier (idp_id). This IdP id will be used in the service provider URLs. This is how Samly figures out which SAML request corresponds to what IdP so that it can perform relevant validation checks and process the requests/responses.

There are two options when it comes to how the idp_id is represented in the Service Provider SAML URLs.

URL Path Segment

In this model, the idp_id is present as a URL path segment. Here is an example URL: https://do-good.org/sso/auth/signin/affiliates. The idp_id in this URL is "affiliates". If you have more than one IdP, only this last part changes. The URLs for this model are:

| Description | URL | |:----|:----| | Sign-in button/link in Web UI | /sso/auth/signin/affiliates | | Sign-out button/link in Web UI | /sso/auth/signout/affiliates | | SP Metadata URL | https://do-good.org/sso/sp/metadata/affiliates | | SAML Assertion Consumer Service | https://do-good.org/sso/sp/consume/affiliates | | SAML SingleLogout Service | https://do-good.org/sso/sp/logout/affiliates |

The path segment model is the default one in Samly. If there is only one Identity Provider, use this mode.

These URL routes are automatically created based on the configuration information and the above mentioned router scope definition.

Use the Sign-in and Sign-out URLs shown above in your application's Web UI buttons/links. When the end-user clicks on these buttons/links, the HTTP GET request is handled by Samly which internally does a POST that in turn sends the appropriate SAML request to the IdP.

Subdomain in Host Name

In this model, the subdomain name is used as the idp_id. Here is an example URL: https://ngo.do-good.org/sso/auth/signin. Here ngo is the idp_id. The URLs supported by Samly in this model look different.

| Description | URL | |:----|:----| | Sign-in button/link in Web UI | /sso/auth/signin | | Sign-out button/link in Web UI | /sso/auth/signout | | SP Metadata URL | https://ngo.do-good.org/sso/sp/metadata | | SAML Assertion Consumer Service | https://ngo.do-good.org/sso/sp/consume | | SAML SingleLogout Service | https://ngo.do-good.org/sso/sp/logout |

Take a look at samly_howto - a reference/demo application on how to use this library.

Make sure to use HTTPS URLs in production deployments.

Target URL for Sign-In and Sign-Out Actions

The sign-in and sign-out URLs (HTTP GET) mentioned above optionally take a target_url query parameter. Samly will redirect the browser to these URLs upon successfuly completing the sign-in/sign-out operations initiated from your application.

This target_url query parameter value must be x-www-form-urlencoded.

Samly Configuration

# config/dev.exs

config :samly, Samly.Provider,
  idp_id_from: :path_segment,
  service_providers: [
    %{
      id: "do-good-affiliates-sp",
      entity_id: "urn:do-good.org:affiliates-app",
      certfile: "path/to/samly/certfile.pem",
      keyfile: "path/to/samly/keyfile.pem",
      #contact_name: "Affiliates Admin",
      #contact_email: "affiliates-admin@do-good.org",
      #org_name: "Do Good",
      #org_displayname: "Goodly, No evil!",
      #org_url: "https://do-good.org"
    }
  ],
  identity_providers: [
    %{
      id: "affiliates",
      sp_id: "do-good-affiliates-sp",
      base_url: "https://do-good.org/sso",
      metadata_file: "idp1_metadata.xml",
      #pre_session_create_pipeline: MySamlyPipeline,
      #use_redirect_for_req: false,
      #sign_requests: true,
      #sign_metadata: true,
      #signed_assertion_in_resp: true,
      #signed_envelopes_in_resp: true,
      #allow_idp_initiated_flow: false,
      #allowed_target_urls: ["https://do-good.org"],
      #nameid_format: :transient
    }
  ]

| Parameters | Description | |:------------|:-----------| | idp_id_from | (optional):path_segment or :subdomain. Default is :path_segment. | | Service Provider Parameters | | | id | (mandatory) | | identity_id | (optional) If omitted, the metadata URL will be used | | certfile | (optional) This is needed when SAML requests/responses from Samly need to be signed. Make sure to set this in a production deployment. Could be omitted during development if your IDP is setup to not require signing. If that is the case, the following Identity Provider Parameters must be explicitly set to false: sign_requests, sign_metadata| | keyfile | (optional) Similar to certfile | | contact_name | (optional) Technical contact name for the Service Provider | | contact_email | (optional) Technical contact email address | | org_name | (optional) SAML Service Provider (your app) Organization name | | org_displayname | (optional) SAML SP Organization displayname | | org_url | (optional) Service Provider Organization web site URL | | Identity Provider Parameters | | | id | (mandatory) This will be the idp_id in the URLs | | sp_id | (mandatory) The service provider definition to be used with this Identity Provider definition | | base_url | (optional) If missing Samly will use the current URL to derive this. It is better to define this in production deployment. | | metadata_file | (mandatory) Path to the IdP metadata XML file obtained from the Identity Provider. | | pre_session_create_pipeline | (optional) Check the customization section. | | use_redirect_for_req | (optional) Default is false. When this is false, Samly will POST to the IdP SAML endpoints. | | sign_requests, sign_metadata | (optional) Default is true. | | signed_assertion_in_resp, signed_envelopes_in_resp | (optional) Default is true. When true, Samly expects the requests and responses from IdP to be signed. | | allow_idp_initiated_flow | (optional) Default is false. IDP initiated SSO is allowed only when this is set to true. | | allowed_target_urls | (optional) Default is []. Samly uses this only when allow_idp_initiated_flow parameter is set to true. Make sure to set this to one or more exact URLs you want to allow (whitelist). The URL to redirect the user after completing the SSO flow is sent from IDP in auth response as relay_state. This relay_state target URL is matched against this URL list. Set the value to nil if you do not want this whitelist capability. | | nameid_format | (optional) When specified, Samly includes the value as the NameIDPolicy element's Format attribute in the login request. Value must either be a string or one of the following atoms: :email, :x509, :windows, :krb, :persistent, :transient. Use the string value when you need to specify a non-standard/custom nameid format supported by your IdP. |

Authenticated S

View on GitHub
GitHub Stars135
CategoryDevelopment
Updated8d ago
Forks103

Languages

Elixir

Security Score

100/100

Audited on Mar 23, 2026

No findings