How to implement ADFS/SAML integration for frontend applications

Robert van Boesschoten

Published: 30-10-2019

Last updated: 27-08-2020

For this article it is important that you understand the basics of SAML connections and that you can set-up a SAML provider service.

Note: This tutorial is based on a specific ADFS environment. It serves as an example and doesn't set the standard for all ADFS implementations. Make sure to fully understand the ADFS environment you're working with and adjust the values in your Betty Blocks application to the values required. 

Throughout this article we use SAML as an example and will be described what needs to configured to successfully create the connection. Note: choosing a different authentication service might deviate from the settings described in this article. 

First we will explain which Data Models you need.


Data Models

WebUser & SamlSetting

These Data Models are needed to successfully use the connection. The model "WebUser" is the center model that is used to authenticate the user within the Betty Blocks application. In the next step we will create an authentication profile for this model.

The model "SamlSetting" is separated from other models so it doesn't have any relations to other models. 

Below you'll see an overview of the models and properties. The order of the column is: Property name --> Label --> Property type.

Create an authentication profile to authenticate the user in Betty Blocks

Don't know how to use authentication profiles? Read this article first.

Name: SAML
Kind: Custom authentication
Login variable: online_user

Settings
Login model: web_user
Expire: 86400 (standaard)
Login redirect to endpoint: (Eigen endpoint)


In your backoffice create a record in the SamlSetting model and fill out the details of your SAML provider. 

Example:

Create Web endpoints to go through the authentication flow

The 2 following pages connect it all together with the action that will be explained later in this article.Now you create the first page. This is a custom HTML page with the GET Method. In this example we call it "initiate". This page will redirect the user to the ADFS/SAML provider of your choice to authenticate the user via the SSO service. After creating this endpoint you can set it as the redirect endpoint in your authentication profile as well. When a user is not authenticated in Betty Blocks but the endpoint requires an authenticated user, the user will then be automatically redirected to the ADFS/SAML provider.

At this page you create a couple variables add an action that is triggered when this page is used.

The following variables are required:

  • auth_request_url:
    Text Expression (enable liquid parse)
'{{saml_settings.idp_sso_target_url}}?SAMLRequest={{escaped_request}}'
  • deflation_level:  
    Number
9
  • encoded_request:
    Text Expression
replace(base64_encode(substring(zlib_deflate(var:xml_auth_request, var:deflation_level), 2, -4)), newline, '')
  • escaped_request:
    Text Expression (enable liquid parse)
'{{encoded_request | url_encode}}' 
  • now:
    Date Time Expression
no
  • saml_settings - Object: Model: SamlSetting , Filter: Id exists
  • uuid:
    Text Expression
generate_uuid()
  • xml_auth_request:
    Text Expression (enable liquid parse)
"<samlp:AuthnRequest Destination='{{saml_settings.idp_sso_target_url}}' ID='_{{uuid}}' IssueInstant='{{now | localize: '%Y-%m-%dT%H:%M:%SZ'}}' Version='2.0' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'><saml:Issuer>{{saml_settings.issuer}}</saml:Issuer><samlp:NameIDPolicy AllowCreate='true' Format='{{saml_settings.name_identifier_format}}'/></samlp:AuthnRequest>

Action

The action that is triggered at the "initiate" page will be called "Redirect to SAML Provider". In this action you add a "Redirect web page event" with response code 302. The redirection URL is the variable you created earlier: var:auth_request_url.

For the 2nd and last page you make a page with XML format and POST method. At this page you create a couple more variables.

This page has 2 input variables:


the following variables are required:

  • decoded_response:
    Text Expression
base64_decode(var:samlresponse, 'string'
  • name_id:
    Text Expression
str(xpath(var:decoded_response, '//NameID/text()', true))
  • status:
    Text Expression
str(xpath(var:decoded_response, '//Status/StatusCode/@Value', true))
  • user_group:
    Text Expression
str(xpath(var:decoded_response, '//Attribute[@Name=" ADFSschema "]/AttributeValue/text()', true))

The template that you use for this page is very simple

<STATUS>{{status}}/</STATUS>
<NAMEID>{{name_id}}</NAMEID>

Make sure to remove any layout that your page might use here. Since we're building a XML page, we don't need a layout.

Action Login via SAML
Section 1

Condition
Use Expression:

var:status == 'urn:oasis:names:tc:SAML:2.0:status:Success'
Section 2 

Redirect web page 302
Path: 

'/saml/auth-failed?status=' + var:status

Condition:
use expression:

var:existing_web_user != nil

Variabel

Kind: Object
Name: existing_web_user
Model: web_user
Filter: Username is equal to var:name_id

Section 3

Create
Kind: create
Model: web_user
Assign: (property) username (value) name_id
As: web_user

Authenticate & login user
Kind: Authenticate & Login user
Authentication profile: SAML (Custom authentication)
User variable: var:existing_web_user

Section 4

Authenticate & login user
Kind: Authenticate & login user
Authentication profile: SAML (Custom authentication)
User variable: var:web_user

Section 5

Redirect web page 302
Redirect URL:
Path: '/' (this is the page where you want the user to be redirected to after a succesful authentication.

Your connection should be all set up and ready to test.

In this tutorial