Configuring SAML SSO

Integrate SAML SSO to your brainCloud web application

Jason Liang avatar
Written by Jason Liang
Updated over a week ago

brainCloud has been enhanced to support SAML for end-user authentication. With SAML SSO configured to your application, it will increase your app's security by only sending credentials to one identity provider (IdP) directly, and allow for a faster authentication process and less expectation of the end-user to remember multiple login credentials for every application.

In this tutorial, we will walk you through the steps to integrate a SAML Identity Provider with brainCloud.

There are many SAML IdPs your company can choose from, such as Salesforce, Okta, OneLogin, etc., Okta will be used in our example.

The following sequence diagram shows the workflow of end-user authentication between client app, Idp, and server provider (brainCloud).

(Note: In the above diagram, "username" is really "userId" in the SAML redirect)

Step1: Create an application from your IdP console

  • Log in to your Okta developer admin console, select the Application tab from the top menus.

  • Click the Create New App button and select SAML 2.0 as the Sign-on method, then click Create.

  • Specify a name and logo for your app and leave App visibility as default, then click Next.

  • Put brainCloud SAML endpoint -- https://sharedprod.braincloudservers.com/saml -- as your app ACS.

  • Leave Default RelaySate blank. brainCloud integration is an SP initiated flow. (For the instance of the IdP initiated flows, this Relaystate will be sent to brainCloud when a user clicks an application button from the Okata dashboard. For that case, you may enter the RelayState field with your appId and the redirect appName. { "appId" : "23828", "appName" : "redirectapp" } -- Note: this appName is not the name your set for your app in the brainCloud development portal, it is the name will be configured at the brainCloud SAML integration redirect section.)

  • You can optionally set the Single log out by selecting the Enable Single Logout checkbox and put relevant info in the required fields (Specify https://sharedprod.braincloudservers.com/samllogout for Single Logout URL and https://sharedprod.braincloudservers.com as SP Issuer. You can use OpenSSL to generate your .crt file and upload it to Signature Certificate field. Note: the cert you enter here is the public cert of a public/private key pair, the private key will be required when configuring brainCloud SAML integration later).

  • Download Okta Certificate from the Download button beside SAML Settings form.

  • Continue filling out the other fields as below. then click Next.

  • Click Finish to save your general settings and redirect you to the next tab.

  • From this Sign On tab, click View Setup Instructions button or Identity Provider metadata link.

  • You will need these pieces of information to fill into brianCloud SAML integration settings.

  • Click Assignments tab, make sure to add end-user individuals or groups from your team to this app.

  • Go back to the Okta initial home page, you will see your newly added application shows up on your workspace.

Step2: Configuring SAML IdP settings into brainCloud

  • Navigate to brainCloud development portal Design | Integrations | Manage Integrations page, scroll down to SAML Integration section, select the checkbox of Enable SAML Integration.

  • Fill in the IdP info you got from the above steps into the corresponding fields respectively. The Identity Provider Issuer, x 509 Cert and Identity Provider Logout URL which can be found from your application SAML Settings section of Okta (By clicking View Setup Instructions button or Identity Provider metadata link on SAML Setting tab section).

(Note: The other two fields -- Service Provider Public Key Cert and Service Provider Private key -- are the public/private key pair that you generated through OpenSSL or other tools in step 1. )

  • Specify http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 as Security Sig Algorithm.

  • Put RelayState appName into Redirect URLs section Page Name field, that's "redirectapp".

  • Click Save to finish your settings.

  • brainCloud SAML authentication builds upon the External Authentication feature, so we need to create a cloud code script and attach this script to external authentication. Check this link for more information about implementing external authentication.

  • Your samlAuth cloud code script should be similar to the script below (The parameters for the AUTHENTICATE API call are "externalId" and "authenticationToken"):

var friendProxy = bridge.getFriendServiceProxy();
var EXTERNALAUTHTYPE = "ssoSaml";
var PREREGISTRATION_SUPPORT = true;
function findPreregisteredUserByEmail( email ) {
var emailLookupResult = friendProxy.getProfileInfoForCredential(email, "Email")
// Check to see if a user with this email has been precreated
// Note this searches by email authentication type, not contact email...
if (emailLookupResult.status == 200) {
bridge.logInfoJson("getProfileInfoForCredential (" + email + ")", emailLookupResult.data );
// Note that they MUST NOT have an External identity
// We would try to exclude only EXTERNALAUTHTYPE identities, but that isn't directly supported...
var emailSession = bridge.getSessionForProfile( emailLookupResult.data.playerId );
var identitiesProxy = bridge.getIdentityServiceProxy( emailSession );
var identitiesLookupResult = identitiesProxy.getIdentities();
bridge.logInfoJson("getIdentities (" + email + ")", identitiesLookupResult.data );
if ( identitiesLookupResult.status == 200 ) {
/*
if (identitiesLookupResult.data.identities.hasOwnProperty('External') ) {
bridge.logInfoJson("User " + email + " rejected because already has an external identity", identitiesLookupResult.data );
return null;
} else {
bridge.logInfoJson("User " + email + " accepted/identified because has no external identity", identitiesLookupResult.data );
return emailLookupResult.data.playerId; // Email matches and doesn't have external auth (yet)
}
*/
return emailLookupResult.data.playerId;
}
} else {
bridge.logInfo("Could not find user with " + email + " email identity", null );
return null;
}
}
function findUserBySAMLEmail( samlEmail ) {
var extLookupResult = friendProxy.getProfileInfoForExternalAuthId(samlEmail, EXTERNALAUTHTYPE);
if (extLookupResult.status == 200) {
return extLookupResult.data.playerId; // Success!
} else {
return null;
}
}
function main() {
bridge.logInfoJson("Script inputs", data);
// load service proxy
var proxy = bridge.getSamlServiceProxy();
// retrieve response attributes
var attributesResp = proxy.sysGetResponseAttributes(data.authenticationToken);
bridge.logInfoJson("Call result", attributesResp);
if (attributesResp.status == 200) {
var attributes = attributesResp.data.responseAttributes;
bridge.logInfoJson("Attributes", attributes);
bridge.logInfoJson("Data", attributesResp.data);
// get email list
var emailList = attributes.Email;
// email is first item in list
var email = emailList[0];
var response = {};
response.valid = false;
if (email === data.externalId)
{
var saml = {};
saml.token = data.authenticationToken;
saml.email = attributes.Email;
saml.name = attributes.FirstName + " " + attributes.LastName;
var authPayload = getAuthPayload();
response.valid = true;
response.authPayload = authPayload;
bridge.setSessionCacheObject( "saml", saml );
if (PREREGISTRATION_SUPPORT)
{
var samlLinkedProfile = findUserBySAMLEmail( saml.email);
// user doens't exist
if ( samlLinkedProfile === null )
{
var preRegistered = findPreregisteredUserByEmail( saml.email );
if ( preRegistered !== null )
{
// Tell the system to link to this user profile
response.useProfileId = preRegistered;
}
}
}
}
return response;
}
return false;
}
function getAuthPayload() {
// load service proxy
var proxy = bridge.getSamlServiceProxy();
// retrieve response attributes
var logoutResp = proxy.sysGetLogoutRequestUrl(data.authenticationToken);
if (logoutResp.status == 200) {
var logoutRequestUrl = logoutResp.data.logoutRequestUrl;
var authPayload = {};
authPayload.logoutRequestUrl = logoutRequestUrl;
return authPayload;
}
return null;
}
main();

  • Your app login page should set a link, when the end-users click it from browse, will redirect them to the Okta login page, the redirect link was created from Step1, which is Identity Provider Single Sign-On URL, https://gmailsamltest.okta.com/app/gmailorg1027922_productsamltest_1/exkleono5zYtpuX015d5/sso/saml. And request send along with SAML issuer -- https://gmailsamltest.okta.com/auth2/default. This is an SAML POST binding where the SAMLRequest and RelayState are sent in the POST body as per the spec.

  • Once end-users logged in to your app, you can set your app to optionally perform auto-logout us the Identity Provider Single Logout URL: https://gmailsamltest.okta.com/app/gmailorg1027922_productsamltest_1/exkleono5zYtpuX015d5/slo/saml

Did this answer your question?