The SAML Inbound Security module can be used to provide user authentication and authorization to interactive endpoints in Smile CDR, such as the Web Admin Console or FHIRWeb Console.
The SAML Specification covers many use cases and patterns. Not all of these patterns are relevant to Smile CDR however. The supported parts of the SAML specification are described here.
The SAML 2.0 Authentication Request Protocol is used to authenticate and authorize users to an interactive application (i.e. not a system making an API call but rather a human user signing into an application of some sort). For example, SAML Authentication Request Protocol can be used to secure access to the Smile CDR Web Admin Console module, but it cannot be used to secure access to a FHIR Endpoint.
If you are already familiar with how SAML works, you may skip this section and skip to Enabling SAML Auth.
This flow begins with the user launching the module using a special URL. Suppose the Web Admin Console module is listening on a server on port 9100, the following URL can be used to request a SAML authentication request. Note the string SMILECDR
which is the registration ID, a shared ID agreed upon by the module being authenticated (the Service Provider in SAML terminology) and the SAML authentication service (the Identity Provider).
https://example.com:9100/saml2/authenticate/SMILECDR
The module will immediately redirect the user's browser session to the IDP server using a URL similar to the following. Note that parameter values have been truncated for readability.
http://some.idp/websso
The SAMLRequest
parameter contains a SAML Request XML document that has been serialized, deflated, and base64 encoded. Decoding this parameter yields a document similar to the following:
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://example.com:9100/login/saml2/sso/SMILECDR"
Destination="http://some.idp/websso"
ForceAuthn="false"
ID="ARQb35c288-3888-43ee-990b-2d0418f231fa"
IsPassive="false"
IssueInstant="2020-08-03T00:46:56.121Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://example.com:9100/saml2/service-provider-metadata/SMILECDR</saml2:Issuer>
</saml2p:AuthnRequest>
At this point, the user has been redirected to the IDP server. It may ask for credentials, or it may use an existing SSO session. Assuming the IDP server wishes to grant the user access to the requested Service Provider, it will generate and sign a SAML XML document called a Response.
Next, the IDP will direct the user back to the Service Provider and will include a SAML Response document as a part of the redirect. This is typically performed using an HTML FORM with a POST action that is automatically submitted. The following is an example of a SAML Response document:
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Version="2.0" ID="12345" IssueInstant="2020-08-03T00:46:56.397Z">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://example.com/idp</saml2:Issuer>
<saml2p:Status><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="assertion-id-1234" IssueInstant="2020-08-03T00:46:56.397Z" Version="2.0">
<saml2:Issuer>http://example.com/idp</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#assertion-id-1234">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xsd"/></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>4+090Sm0RWBVi82T92jqco4rQyyaDnWYdxcWfObQuAs=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
LHiJn4R6GmjuImJHbO69eLUHGSZqYHDwMmDn1tUYltAHa6Y956VK5CzN/IJT6UpZ0QkzUVRpNCs9
 rwKbrNwPuuG4f9n0xdl06vJ/wsT6VS77UMDCz8OUOzKv7CIKWfWdtrRKoqiO8Q8ELkByRAXL6bOS
 s8/n6cWKmLYebLfV3X5glgVilzFEpa173s+EU7RjBVJ1z96Aqqcmsc1+BOfb1v2ZFIAUFX3Jl5jy
LLnB/pUqQV5RNLCryyB+JY4Nv/MVKBcVI1ym5hWTp8ISg6Y7fqdF7V1VKXD29GfEMIDxHYmma4Xz
 VjUCoXgAa92S5X2xwxHFULqpnVwRxROyEdMAMYw6zlaGw7oeRjD+ham/38KB2GBO456eeGsj4Gh/
 RjlvE4maDBHyuW0dDZRK6wuKUoAN/FD3nFDiygzZFG0XIixKoHGq/OQTdLqUNvVW/8qZ0enEmxfk
Uq6MJvujytd2igyZcEpj67fp4ZCNK/BUdGAom/HSIilav+1B3Y/upzx+txyzAxQzF+0etvNZ+1MP
 QVkd9T+dfsJFZ6x9INuBsji31KBZ/QCPkV0CJlWrB7xTDRfnX/wXi7xe39Ra7DSj3JRllNGGfRJ5
 BdjouLHsBS/xGSuGNjCJdgFFkGiH70nbxIaEEc44wmcC9s2CQftr0TWUja0i6oKAs3eseWFgvHY=
</ds:SignatureValue>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="My Website">UserJohnSmith</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotBefore="2020-08-03T00:46:56.397Z" NotOnOrAfter="2020-08-03T00:48:56.397Z"/></saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:AuthnStatement AuthnInstant="2020-08-03T00:46:56.399Z" SessionIndex="abcdedf1234567" SessionNotOnOrAfter="2020-08-03T00:46:56.404Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute Name="FirstName">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">John</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="LastName">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">Smith</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="http://example.com/roles">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">AdminUser</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
The SAML Response document contains (among other things) the following:
A digital signature in the <ds:Signature>
tag, which allows Smile CDR to verify that it was actually issued by the IDP.
A subject in the <saml2:Subject>
tag, which identifies the user on whose behalf this response was created.
A set of attributes in the <saml2:AttributeStatement>
tag, which contain arbitrary information about the logged in user. The attributes shown above are examples only, different IDPs will provide different sets of attributes. These attributes will typically be translated into Smile CDR user session attributes and permissions via a script.
To enable SAML Authentication for a Smile CDR module such as the Web Admin Console, follow the following steps.
In your IDP system, create a new registration (your IDP may call this something else, such as an Application, or an Application Profile) for authenticating users to your system. You will need to come up with an ID (called a Registration ID by Smile CDR) that uniquely identifies the integration between Smile CDR and your IDP. By default the string SMILECDR
will be used.
The IDP will likely need to know the SSO URL for the application being authenticated. This will be the following, where the host and port (and possibly also an additional context root) will correspond to the address for the Smile CDR endpoint module being authenticated. For example, if you have a Web Admin Console module on server example.com at port 9100, your base URL will be:
https://example.com:9100/login/saml2/sso/REGISTRATION_ID
Next, you will need to create a Keystore file. This file should contain the certificate used to verify the signed assertions. If you are using signed Authentication Requests, it should also contain the key and certificate that will be used to perform this signature.
In the Web Admin Console, create a new module of type SAML Inbound Security. Enter the appropriate settings for your IDP.
Issuer ID – This should contain the ID for the IDP, and must match the value configured in that system. This is also sometimes called an Entity ID (and specifically refers to the Entity ID for the IDP/SAML Server itself). By convention this is generally a URL to the IDP server, but Smile CDR will not attempt to resolve it.
Web SSO URL – This is the URL to the IDP SSO endpoint. Smile CDR will redirect users to this URL when a login is needed.
Authentication Callback Script – This is the callback script that will be used to map the SAML response and its various attributes to Smile CDR permissions in order to create a user session. See Authentication Scripts below for more information.
Entity ID Template – This value controls the Entity ID for the application being authenticated with this security module. See Entity ID Template below for more information.
In the module that is being SAML-enabled:
In your endpoint module configuration (i.e. the actual module you wish to allow a SAML-secured login to, such as the Web Admin Console), enable SAML Authentication Enabled.
Specify a module dependency from the endpoint module to your new SAML Inbound Security module.
Restart the endpoint module
Smile CDR supports IDP-initiated launch, so if your IDP supports it you can try launching directly from the IDP.
You can also initiate an Authentication Request from Smile CDR by accessing the URL:
https://example.com:9100/saml2/authenticate/REGISTRATION_ID
The Entity ID Template controls the SAML Entity ID assigned to the SAML Relying Party, which refers to the application being authenticated. For example, if you are using a SAML IDP to authenticate access to the Smile CDR Web Admin Console module, this setting controls the Entity ID that the SAML Server will know the Web Admin Console by.
This setting is important, because a well configured SAML Server should include an Audience identifier in assertions that it issues, and the Entity ID that Smile CDR uses must match the Audience declaration in the SAML Assertion.
This setting may include the following substitution variables:
{baseUrl} | Refers to the base URL for the web application being authenticated. For example, if the Web Admin Console module is secured using the SAML Inbound Security module, this variable will be replaced with the base URL for the Web Admin Console module. This value will be different for each module that is secured using the same SAML Inbound Security module, which is helpful since it allows an Entity ID to be unique per authenticated module. |
{registrationId} | The SAML Registration ID assigned to this SAML configuration. |
{baseScheme} | The protocol (typically https ) used in the base URL. |
{baseHost} | The host name used in the base URL. |
{basePort} | The port number used (or implied by the protocol) in the base URL. |
The default value for this setting is the following:
{baseUrl}/saml2/service-provider-metadata/{registrationId}
For example, when using a default registration ID and authenticating a Web Admin Console module that is deployed to https://smilecdr.example.com:9100, the default value will be translated to the following URL (which should be configured exactly as such in the SAML Provider settings):
https://smilecdr.example.com:9100/saml2/service-provider-metadata/SMILECDR
If you prefer, you can simply hard code a value in this field with no substitution variables. By convention this value should be a URL, but it does not have to be.
The authentication script provides a method called authenticate
that is called automatically when a SAML token has been received and verified. Smile CDR will automatically validate signatures, timestamps, etc. in order to ensure that the token itself is valid for use. The script should then translate the data in the token into a session object containing the appropriate user demographics, permissions, etc.
This function will be invoked once for each token received, and should return a session or an error.
theRequest – Contains details about the request. This parameter will be of type AuthenticationRequest. Generally this will be used as shown in the example below to access the SAML Response document and extract attributes from it using XPath JavaScript Document Operations.
theOutcomeFactory – This parameter is of type ScriptAuthenticationOutcomeFactory. This parameter can be used to create a failure object if you wish to abort the login from your script.
The following example script expects custom attributes named "FirstName", "LastName", and "http://example.com/roles". See the example SAML Response above to see how these attributes might look.
/*
* This is an authentication script for the saml-inbound-security
* module. It is required to contain a function called "authenticate".
*
* This function takes two parameters:
* - theRequest : This object contains the incoming credentials.
* - theOutcomeFactory: This is a factory for the success/failure responses
* to be returned by this function.
*/
function authenticate(theRequest, theOutcomeFactory) {
let samlResponse = theRequest.samlResponseDocument;
// Create a successful login response
var success = theOutcomeFactory.newSuccess();
success.username = theRequest.username;
success.familyName = samlResponse.getXPathValue("/saml2p:Response/saml2:Assertion/saml2:AttributeStatement/saml2:Attribute[@Name='LastName']/saml2:AttributeValue/text()");
success.givenName = samlResponse.getXPathValue("/saml2p:Response/saml2:Assertion/saml2:AttributeStatement/saml2:Attribute[@Name='FirstName']/saml2:AttributeValue/text()");
// Extract the custom role attribute
let roles = samlResponse.getXPathValues("/saml2p:Response/saml2:Assertion/saml2:AttributeStatement/saml2:Attribute[@Name='http://example.com/roles']/saml2:AttributeValue/text()");
// We'll log a few attributes - In a real system you might reduce these to debug level
Log.info("GivenName: " + success.givenName);
Log.info("User has roles: " + roles);
// If the user has an admin role, we'll make them a superuser
if (roles.includes('AdminUser')) {
success.addAuthority('ROLE_SUPERUSER');
}
// You can also reject the session based on specific criteria if needed
if (roles.includes('BannedUser')) {
var failure = theOutcomeFactory.newFailure();
failure.message = 'User is banned';
return failure;
}
return success;
}
See SAML to SMART Bridging for a description of how to use this module to provide authentication and authorization for SMART on FHIR apps.