Five things I learned about SAML from studying a realistic example
Tags: security, architecture, ruby, vscode
Published: 2021-02-03 21:07:38

I recently dug into execution traces of a [saml-rails example app](https://github.com/enterprise-oss/saml-rails) using the [AppMap extension for VSCode](http://bit.ly/appmap-vscode), looking for "points of interest" in the code. Here are five interesting things that I learned about SAML and how to integrate SAML into a realistic application.

Note: The code snippets in this post are edited a little bit for brevity. For full source, click through to the links.

## 1) The SAML auth request is complex

When a web site wants to login a user with SAML, it sends an HTTP request that contains a specially encoded auth parameter. The auth parameter is a [base64 encoding of an XML document](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=296). It's built using code that looks like this: 

```ruby
  request_doc = create_authentication_xml_doc(settings)
  base64_request = encode(request)
  request_params = {"SAMLRequest" => base64_request}
```

The XML document contains [a few required parameters, and many optional ones](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=298). 

In a real-world scenario, learning more about all these parameters as you review the app requirements, SAML vendor doc, and the SAML spec is going to be important. [Other resources confirm](https://github.com/jch/saml) my intuition about the complexity of SAML configuration.

## 2. The user's HTTP login request is [redirected to the SAML provider](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=313)

Once the base64-ed auth request is created in the previous step, the request is redirected to the SAML provider.

The encoded data is passed to the SAML provider as the `SAMLRequest` parameter. An example URL looks like this:

```
https://idp.samlprovider.com/saml-login?SAMLRequest=fZAxb8MgEIX%2FijcmbIwdKTnFlqxmiZQuSduhS0QwjVExUA5X7
```

Needless to say, SSL is essential throughout the process.

## 3. The SAML response is [verified using a certificate](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=532)

The [`saml_callback` response](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=409) contains a fingerprint and digital signature. Your application (a "service provider" in SAML-speak) uses the certificate public key of the SAML identity provider to verify the login response.

## 4. Applications can dynamically create user records in response to SAML login

When a SAML response is received and verified, it's expected that the application ("service provider", remember?) will  [find-or-create a `user` record](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=559)

## 5. The `user` id is stored in the session, as normal

Once the user record is created, the user id can be [stored the in the session](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=609), like any other login flow. It's good to know that this doesn't change with SAML as compared to other, less complex forms of authentication.

On the user's next request, the user id can be [retrieved from the session](https://app.land/scenarios/96fc2cce-544b-4b4e-8e79-b3d77bfd525e#filterShow=sql_select%2Csql_insert%2Csql_update%2Ctrivial_functions%2Cstatic_asset_requests%2Cstack_depth_less%2Cexclude_function&filterShowParams=%7B%22stack_depth_less%22%3A%227%22%2C%22exclude_function%22%3A%22%23encrypt%2C%23decrypt%2C%23generate%2C%23digest%22%7D&event=622). The user id is used to find the user record in the database, and the request continues.

**While you're here**

We are conducting a survey: [State of Software Architecture Quality](https://www.surveymonkey.com/r/archsurveygeneral). We are aiming for 300 responses, and once we meet our goal we will be donating $1,000 to Girls Who Code. Please contribute to our understanding of software architecture quality by filling out the survey! Of course, we will be summarizing and publishing the results once they are available.

If you don't want to fill out the survey, but you want to be notified when the results are available, you can fill out this form: https://forms.gle/u8CPS3GGD6A7WHsG7.

## Resources

* A good writeup of [SAML for web developers](https://github.com/jch/saml).

* [enterprise-oss/saml-rails repo](https://github.com/enterprise-oss/saml-rails).

* The [AppMap extension for VSCode](http://bit.ly/appmap-vscode), and [installation instructions for Ruby](https://github.com/applandinc/appmap-ruby/#installation) and [Java](https://github.com/applandinc/appmap-java/blob/master/README.md#configuration).

* My blog post about [how to setup AppMap recording for the saml-rails](https://dev.to/kgilpin/unpacking-saml-with-appmaps-and-vscode-1i98/edit) repo, so that you can explore the execution traces using AppMaps.