Monday, April 11, 2016

[Salesforce / Heroku] Salesforce as Identity Provider and Heroku as Service Provider

One year from my last SSO blog post (Implementing Delegated SSO) I'm still here playing with Single Sign On, this time using SAML 2.0.

For a quick overview of the how SAML works, read SAML FEDERATION FOR DUMMIES and SAML for dummies.

We have 3 actors:
  • Client: this is your browser
  • Service Provider: this is the remote Site where you need to access your private data/services without having to log in in this portal (this is Heroku in our demo implementation)
  • Identity provider: this is the server that hosts your authentication details, which authenticates your private sessions on the Service Provider (this is Salesforce)

These actors speak to eachother to allow the client to be authenticated both in the Service Provider and the Identity Provider.

The aim of this article is to configure Salesforce as an Identity Provider using a NodeJS app hosted on Heroku as Service Provier.

Let's say we have a Customer Community and every user should access a remote Service Provider without the need to log in again.


First let's configure a new Community


From Setup > All Communities click on New Community, select the Salesforce Tabs + Visualforce template, select a name:


Click on the Administration tab and then to Members. Leave the System Administrator and add a customer community profile:


Click on Branding to change header image and color theme.

Click on Login & Registration to change header logo of login page and other login/logout settings.

You have setup a new community:



Setup Salesforce as Identity Provider


The next step is to setup Salesforce as Identity Provider.

Enable My Domain from Setup > Domain Managerment > My Domain.

Remember that once the My Domain subdomain is setup you cannot change it anymore.

By deploying the domain you enable it for all your ORG's users:
  • Users will be able to login from https://yourdomain.my.salesforce.com/login
  • if you don't change the base settings, users will still be able lo login through https:/login.salesforce.com
  • Integrations will continue to work without any problem

You can even change login brand:



Now go to Setup > Security Controls > Identity Provider click Enable Identity Provider.


Single Sign On is almost here!


For every community and the base Salesforce Identity you are provided with the SAML descriptor to configure Single Sign On on the remote Service Provider.

There is one more step to properly configure the authentication channel.


Create a connected app


From Setup > Create > Apps click on New button of the Connected Apps section.


Fill in the mandatory fields:
  • Connected App Name: name of your app
  • API Name: dev name of the app (cannot be changed once set)
  • Enable SAML: true
  • Entity Id: this is the unique identifier of your Service Provider. This field is unique across Connected Apps (any other connected app should have a different value)
  • ACS URL: this is the callback URL on your Service Provider (we'll be configuring the app in the next chapter)
  • Subject Type: set to User ID (your user will be identified by its Salesforce ID)
  • Name ID Format: this is the format of the unique id (urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified)
  • Issuer: leave the default value

Another cool feature is the Custom Connected App Handler: you can specify an Apex class which is called when the authentication is done.

The class must extend the Auth.ConnectedAppPlugin class:

global class SamlConnectedAppPlugin extends Auth.ConnectedAppPlugin{

    global override boolean authorize(Id userId, Id connectedAppId, boolean isAdminApproved) {
        User u = [select id, First_SAML_Login__c from User where id =: userId].get(0);
        if(u.First_SAML_Login__c == null){
            u.First_SAML_Login__c = System.now();
            try{
                update u;
            }catch(exception e){
                System.debug('Exception:' + e.getMessage());
            }
        }
        return true;
    }


    global override Map customAttributes(Id userId, Map formulaDefinedAttributes) {  
        User u = [select id, Name, CommunityNickname from User where id =: userId];
        formulaDefinedAttributes.put('nickname',u.CommunityNickname);
        formulaDefinedAttributes.put('fullname',u.Name);
        return formulaDefinedAttributes;
    }
}

Where:
  • authorize(): authorized user based on custom login (e.g. you can query for a given User's field)
  • customAttributes(): allow to specify a set of custom attributes to be sent to the Service Provider. In this example the send the user's full name and nickname
  • refresh(): (not implemented here) is called upon token refresh (OAuth 2.0)


Configure the Service Provier


The service provider is the external App that provides a service which can be accessed only if the user if authenticated.

It will be using Salesforce as the Identity Provider (don't you say??).

To simulate the Service Provider, here is a NodeJS app hosted on Heroku:





By clicking the Heroku Button you are redirected to the Heroku app creation:


In the Config Variables lays down the magic:

  • SAML_ENTRYPOINT: set with the value of the node md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" of the SAML descriptor for the community
  • SAML_ISSUER: set with the name you have setup in the Entity Id of your connected app
  • SAML_CERT: copy the whole certificate string in the node ds:X509Certificate of the SAML descriptor of your community
  • SAML_IDENTIFIER_FORMAT: leave with the default value urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
  • APP_NAME: set a cool name for your service provider
  • SF_HOME: this is the base URL for your community


Then click the Deploy For Free button and wait for the magic to take place!

Your Heroku app is up and running at https://heroku_app_name.herokuapp.com



Link the Service Provider in your community


Let's add a simple link to the community sidebar.

Click to Setup > Customize > Home > Custom links:


Create a new Home Page Component:


And add it to a new Home Page Layout tailored to your community:



Let's test it out!