DEV Community

Louis Liu
Louis Liu

Posted on

Streamlining Electronic Signatures with Single Sign-on Application

Paper trails are being replaced by electronic records, and the importance of maintaining data integrity and authenticity cannot be overstated. For industries such as pharmaceuticals, medical devices, and healthcare, adhering to regulatory standards is crucial. One such regulation is 21 CFR Part 11, which pertains to electronic records and signatures.

Background

The project I'm working on is a web-based application that uses SAML-based SSO to authenticate users. When users want to finalize the record in the system they need to provide their username/password again to sign the record to prove they're responsible for the content of the record.

Challenge

The application doesn't know the username/password, we only know whether the user is logged in. So we need to ask the identity provider (idP) to verify the user identity for us. Our application should sign the record if idP says the username/password combination is valid. But only a logged-in user can sign the record, the idP won't let user input their username/password again.

We plan to follow the SSO procedure to let users sign the record (just like what we do when user log into the application), they will be redirected to the idP site and then idP will redirect them back to the application (assuming the authentication is successful). If you are familiar with web application frameworks (such as express.js, Ruby on Rails) you probably find the problem. This kind of redirect will make the application lose the record signing context. When users redirect from the idP to the application that's a new request and it doesn't tell the application which record the user wants to sign.

Our application supports SAML-based SSO, customers can integrate any identity management service that supports SAML 2.0 into the application. So the solution must adapt SAML 2.0 specification.

Let me summarise the challenges we got so far:

  • How to re-authenticate users without letting them logout the application.
  • How to reserve the signature context when idP tells the application that the user passes the authentication.
  • Our solution must adapt to SAML 2.0 specifications.

Solution

Re-authenticate

The solution addresses the challenge of re-authenticating users without forcing them to log out of the application.

After reading the SAML 2.0 specification I've found an optional parameter we can pass in the authentication request called ForceAuthn.

ForceAuthn [Optional]: A Boolean value. If "true", the identity provider MUST authenticate the presenter directly rather than
rely on a previous security context. If a value is not provided, the default is "false". However, if both
ForceAuthn and IsPassive are "true", the identity provider MUST NOT freshly authenticate the
presenter unless the constraints of IsPassive can be met.

By utilizing the ForceAuthn parameter in the SAML authentication request, the identity provider is instructed to directly authenticate the user, verifying their username/password combination while keeping them logged in.

I'm using ruby-saml, add ForceAuthn to the SAML settings.

settings.force_authn = true
Enter fullscreen mode Exit fullscreen mode

Save the context

The App needs to know WHO sign WHAT record. The WHO is not a problem, SAML response includes all the information we need to identify who is singing the record. But how can we get the WHAT from the SAML response? What we need is a record ID so the application knows to sign which record. SAML is used to transfer the identified information and the record ID is obviously not a part of it.

By leveraging the RelayState parameter in the SAML specification, the record ID is passed as part of the SSO request to the identity provider.

Sometimes a binding-specific field called RelayState is used to coordinate messages and actions of IdPs and SPs, for example, to allow an IdP (with which SSO was initiated) to indicate the URL of a desired resource when communicating with an SP.

# initiate SSO request to idP
request    = OneLogin::RubySaml::Authrequest.new
relayState = "/record/[record_id]" 
redirect_to(request.create(saml_settings, :RelayState => relayState))
Enter fullscreen mode Exit fullscreen mode

Under the hood, it will send a request to your_sso_service_url?RelayState=/record/[record_id]. The identity provider includes the RelayState in the SAML response, allowing the application to extract the record ID at the consume endpoint:

# This is my consume endpoint to handle the SAML response
def consume
  response    = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :settings => saml_settings)
  relay_state = params[:RelayState]
  record_id   = get_record_id_from_relay_state(relay_state)
end
Enter fullscreen mode Exit fullscreen mode

You need to pay attention when using the record ID sent by the idP. RelayState is just a URL query string which means users can easily modify it in their browser. You must verify whether the user has the access and the authority to sign the record before you actually do the operation.

Summary

The solution involves enabling re-authentication by setting the ForceAuthn parameter to true in the SAML authentication request. The context of signing a record is preserved by passing the record ID as the RelayState value in the SSO request and extracting it from the SAML response at the application's consume endpoint. Proper authorization checks should be implemented to validate the user's access and authority to sign the record. Good luck!!

Top comments (0)