• Stars
    star
    383
  • Rank 111,995 (Top 3 %)
  • Language
  • Created about 10 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

SAML tutorial for web developers

SAML for Web Developers

Interested in adding SAML (Security Assertion Markup Language) support to your app? This post explains the basic single sign-on flows for web applications. If you have questions or feedback, please file an issue.

Benefits

SAML lets you manage user identities and authorizations across multiple apps. It's similar to how we use our Google identity to login to YouTube, or our Facebook identity for.... errr everything else.

Large companies love single sign-on. Instead of using OAuth, they use SAML. And instead of Farmville, it's Enterprise Softwareβ„’. Po-tay-toes, Po-tah-toes.

The SAML standard has been around for a long time, with the latest version (2.0) released in 2005. It's widely adopted by large companies, and there's a whole industry (identity access management, IAM) dedicated to supporting it.

How does it make your users' lives easier?

  • Single sign-on. Logged into Windows? Sweet. That means the user's also logged into your app. No additional username or password needed.
  • Automatic user creation (provisioning) based on your company identity.
  • User profiles pre-filled from their company profile (via LDAP or other directory service).
  • Single source of truth for managing users. Administrators can onboard and offboard users from one place. They can also manage app access for groups of users at a time, rather than individually.

Single sign on

There are many complex use cases for SAML, but let's start with a simple example to get familiar with the jargon.

Alice needs to schedule a work trip. Her company has two internal web applications; One for booking flights, and another for booking a rental car.

In SAML speak, Alice is our principal, the user that we're trying to authenticate and learn about. The two applications are the service providers.

She visits https://flights.acme-corp.biz/flights and is redirected to https://idp.acme-corp.biz?SAMLRequest=... to single sign-on.

When the principal, Alice on her browser, tries to access a protected resource (/flights), the service provider (https://flights.acme-corp.biz) doesn't present her with a login page. Instead, it redirects her to an identity provider (https://idp.acme-corp.biz) with an authentication request in the query parameter. The identity provider uses the authentication request to tell which service provider is making the request. Alice is now logged into the identity provider. In this context, we can also call the identity provider a session authority and say that Alice has a valid session with it. This becomes important later on when we talk about how she books a car rental.

Tip: I avoid abbreviations and acronyms in this guide, but you will commonly see service provider abbreviated as SP, and identity providers abbreviated as idP.

Alice types her username and password into https://idp.acme-corp.biz and is redirected back to https://flights.acme-corp.biz/flights.

After the identity provider successfully authenticates Alice, it sends a response back to the service provider saying authentication was successful, and releasing assertions about the principal. Assertions are statements about the principal. They typically include information like email, full name, and other contact information. The service provider can use these to update an existing user's profile, or to provision a new user. (Provisioning is a fancy term for "creating a new thing").

After booking her flight, she visits https://cars.acme-corp.biz/rentals. She is once again redirected to https://idp.acme-corp.biz?SAMLRequest=..., but she's immediately redirected back to https://cars.acme-corp.biz/rentals without having to type her password again.

Why didn't Alice need to re-authenticate? Remember that when she redirected to the identity provider for booking her flight, she established a session with the identity provider. When the other service provider (https://cars.acme-corp.biz) sent an authentication request, the identity provider saw that Alice had been previously authenticated because of her active session. There's no need for Alice to re-authenticate again, so the identity provider redirects her back to the site she wanted with a successful response.

Tip: Just as an identity provider can be called a session authority, the complement for service providers is to be called a session participant.

Bindings

In the single sign-on example, I glossed over how a user is "redirected" between the identity providers and service providers. In SAML, a binding describes how messages should be encoded, and the underlying transport protocol to carry them. For web single sign-on, two common bindings are the "HTTP Redirect Binding" and the "HTTP Post Binding". Their names hint at their function. For example, we can specify that initial authentication requests from a service provider to an identity provider should happen with the redirect binding; The response from identity provider to service provider should happen with the post binding.

This is a common configuration, but SAML also supports other bindings like SOAP, URI, and Artifact.

Security

SAML responses include conditions under which the response is valid. Responses are typically only good for a few minutes to prevent replay attacks (SAMLCore 2.5.1.2). They can be optionally signed and/or encrypted with a shared secret between relying parties (all identity providers and service providers) (SAMLCore 5 and 6). It's a good idea to do everything over SSL.

Metadata

Because SAML is such a general framework, there's a ton of choices for how to configure it. Which binding to use? Signed or unsigned assertions / responses? Fortunately, SAML also defines a standard for communicating these configurations between relying parties. By publishing metadata about your service, configuring your service could be as easy as a single click to import the metadata.

Putting it all together

Now that we know all the pieces involved, let's implement a simple single sign on flow for the Flights service provider. Here are our requirements:

  • HTTP Redirect Binding for initiating authentication requests (configured in identity provider)
  • HTTP Post Binding for receiving SAML responses (configured in service provider)
  • No signed assertions or signed responses

To keep the examples simple, I'm putting the minimum set of attributes in the XML messages. There are many more attributes and elements you can optionally specify to tweak it to your needs. Schema Central is a great reference for what's actually required and what's optional.

First, we need to define service provider metadata to let identity providers know how to talk to us.

<?xml version="1.0"?>
<md:EntityDescriptor entityID="flights.acme-corp.biz">
  <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://flights.acme-corp.biz/saml/consume" index="0"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

The AssertionConsumerService element tells identity providers to use the HTTP POST Binding to send responses to /saml/consume.

When Alice requests the page /flights, our app will redirect the user to the identity provider and pass along the following authentication request:

<?xml version="1.0"?>
<samlp:AuthnRequest ID="_abc123" IssueInstant="2014-09-17T20:53:21" Version="2.0">
</samlp:AuthnRequest>

The AuthnRequest must have a globally unique identifier (ID). This will show up again when we see the response from the identity provider.

Since we're using the HTTP Redirect binding, we want to Base64 encode this XML and append it to the url as a query parameter SAMLRequest. So the location we redirect Alice to will be:

http://idp.acme-corp.biz/sso?SAMLRequest=PD94bWwgdmVyc2lvbj0iMS4wIj8%2BCjxzYW1scDpBdXRoblJlcXVlc3QgeG1s%0AbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2Nv%0AbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFz%0Ac2VydGlvbiIgSUQ9Il9hYmNkZTEyMyIgSXNzdWVJbnN0YW50PSIyMDE0LTA5%0ALTE3VDIwOjUzOjIxIiBWZXJzaW9uPSIyLjAiPgo8L3NhbWxwOkF1dGhuUmVx%0AdWVzdD4%3D%0A

Tip: Be really careful about whitespace in your requests and responses. These can wreak havoc on digital signatures and cause errors when validating messages.

Alice sees the login page for the identity provider, types in the correct credentials, and establishes a session with the identity provider. The identity provider gathers the information it knows about Alice and builds a response with the assertions about Alice:

<?xml version="1.0"?>
<samlp:Response ID="_rst456" InResponseTo="_abc123" IssueInstant="2014-09-17T21:06:32" Version="2.0">
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion ID="_xyz888" IssueInstant="2014-09-17T21:06:32" Version="2.0">
    <saml:AttributeStatement>
      <saml:Attribute Name="email">
        <saml:AttributeValue>[email protected]</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

Again, this response is Base64 encoded. Since we've specified in our metadata to use HTTP Post binding for receiving messages, the identity provider will direct the principal to POST the encoded message back to https://flights.acme-corp.biz/saml/consume. The location was also defined in the metadata.

Tip: It's common to use the HTTP Post Binding for receiving SAML responses because browsers have url length limits that don't work with with large responses. However, if the service provider sets a session cookies with a SameSite=Lax attribute, it will not be included in the POST request. Popular browsers are increasingly treating cookies without a SameSite attribute to be similar to SameSite=Lax.

The Flights app sees that the authentication was successful from the StatusCode element, and can also learn more about the principal from the Assertion element; Here we see that Alice's email is [email protected]. Using this information, Flights can now establish a session for Alice.

Further reading

To keep the introduction gentle and focused for web developers, I intentionally left out a lot of cool details. What if you have native applications that doesn't go through a web browser? What about other bindings? What if you want to extend the spec?

The documentation is fantastic, but I recommend reading them in this order:

These are interesting to skim, but work better as reference docs rather than a guide:

Resources

More Repositories

1

personal-finance

Guide and notes for personal finance
225
star
2

greenfield

A minimal ruby web app skeleton. (deprecated)
Ruby
31
star
3

rack-stream

rack-stream is middleware for building multi-protocol streaming rack endpoints.
Ruby
20
star
4

em-irc

IRC client that uses EventMachine to handle connections to servers
Ruby
17
star
5

sinatra-sprockets

How to use Sprockets (aka the asset pipeline) with Sinatra or any Rack based framework
Ruby
17
star
6

chrome_spy

A Ruby script to help report about your Google Chrome usage
Ruby
11
star
7

jquery.inputHistory

jQuery plugin to make an input field act like a command prompt with history and keyboard shortcuts
JavaScript
11
star
8

activesupport-cascadestore

write through ActiveSupport cache implementation
Ruby
9
star
9

rack-firehose

Rack middleware for easy pubsub with Firehose.io
Ruby
7
star
10

cocoa-separate-nib-preferences

example separating out Preferences from main nib and using tabless view to switch between Preference sections
Objective-C
7
star
11

polling_request

Progress aware jQuery AJAX polling requests
CoffeeScript
6
star
12

as_callbacks_tutorial

This project demonstrates how to define and run custom callbacks on normal ruby objects with ActiveSupport::Callbacks
Ruby
6
star
13

cocoa-facebook-connection-example

example using webview to connect to Facebook Graph API
Objective-C
6
star
14

dotfiles

my dot files
Emacs Lisp
5
star
15

barn

Store lazily evaluated blocks for building test fixtures
Ruby
5
star
16

railsconf2012

4
star
17

custom-controller-renderer

example demonstrating how to subclass actionpack's AbstractController::Base
Ruby
4
star
18

env_config

Simple configuration management with environment variables
Ruby
4
star
19

scripts

Utilities
Shell
3
star
20

gzip_vs_minify

compare file sizes on different combinations of minification and gzip on javascript
JavaScript
3
star
21

jsg

Bash static site generator with GitHub flavored markdown support.
Shell
3
star
22

braintree_payment_processing

client to Braintree payment processing API
Ruby
3
star
23

glimpse-rails

A glimpse plugin for reporting on Rails.
Ruby
3
star
24

jquery.inputReset

reset the default value for input fields
JavaScript
2
star
25

gm-radio

Reverse engineering head unit to add a slave bluetooth receiver
Ruby
2
star
26

rockyroadblog

Two guys talking about cars, food, and everything in between
CSS
2
star
27

emacs-config

my emacs config files
Emacs Lisp
2
star
28

minitest-model

Test assertions for ActiveModel and ActiveRecord
Ruby
2
star
29

nokogiri-signatures

nokogiri enhancements to support XML digital signatures (xmldsig)
Ruby
2
star
30

fstest

File existence, and file contents test assertion methods
Ruby
2
star
31

NSStringURLUtilities

category that adds method to split query and fragment strings from URLs
Objective-C
2
star
32

github-deprecation

Create GitHub issues for ActiveSupport::Deprecation messages.
Ruby
1
star
33

mentos

the fresh maker (refreshes browser when files change)
1
star
34

git-example

tutorial through the basic workflow of git
1
star
35

beerpad

beer reviews!
1
star
36

git_filet

utility for finding commits that cause regressions without knowing a starting point or ending point
Ruby
1
star
37

textmate-jch

my textmate customizations
1
star
38

datasciencecoursera

Course notes
R
1
star
39

jquery.subnavigation

sub-navigation plugin that degrades gracefully when JS is unavailable
JavaScript
1
star
40

lumberjack

mac app for viewing rails logs
JavaScript
1
star
41

cl_spam

craigslist spam removal
Ruby
1
star
42

integration-testing-setup

example of how to setup headless integration tests for a rack app with rspec, capybara, and capybara-webkit
Ruby
1
star
43

presently-ios-sdk

Presently iOS SDK
Objective-C
1
star
44

styleguide

1
star
45

babysitter

babysit running commands
Ruby
1
star
46

caffeine-electron

Electron clone of caffeine. App to learn electron API's
JavaScript
1
star
47

test_faraday_rails

sample app shows how to use the Faraday Rack adapter for integration testing
Ruby
1
star
48

rails_templates

rails 3 application templates for common new project tasks
Ruby
1
star
49

rack-stream-example

Ruby
1
star