Tag Archives: SSO

Salesforce SSO SAML JIT Handler

For regular standard User creation SAML JIT Handler:

//This class provides logic for inbound just-in-time provisioning of single sign-on users in your Salesforce organization.
global class SSOUserHandler implements Auth.SamlJitHandler {

    //JIT Handler Exception
    private class JitException extends Exception {}

    //Handle User
    private void handleUser(boolean create, User u, Map < String, String > attributes, String federationIdentifier, boolean isStandard) {

        if (create && attributes.containsKey('User.Username')) {
            u.Username = attributes.get('User.Username');
        }
        if (create) {
            if (attributes.containsKey('User.FederationIdentifier')) {
                u.FederationIdentifier = attributes.get('User.FederationIdentifier');
            } else {
                u.FederationIdentifier = federationIdentifier;
            }
        }
        if (attributes.containsKey('User.ProfileId')) {
            String profileId = attributes.get('User.ProfileId');
            Profile p = [SELECT Id FROM Profile WHERE Id =: profileId];
            u.ProfileId = p.Id;
        }
        if (attributes.containsKey('User.UserRoleId')) {
            String userRole = attributes.get('User.UserRoleId');
            UserRole r = [SELECT Id FROM UserRole WHERE Id =: userRole];
            u.UserRoleId = r.Id;
        }
        if (attributes.containsKey('User.Phone')) {
            u.Phone = attributes.get('User.Phone');
        }
        if (attributes.containsKey('User.Email')) {
            u.Email = attributes.get('User.Email');
        }
        if (attributes.containsKey('User.FirstName')) {
            u.FirstName = attributes.get('User.FirstName');
        }
        if (attributes.containsKey('FirstName')) {
            u.FirstName = attributes.get('FirstName');
        }
        if (attributes.containsKey('User.LastName')) {
            u.LastName = attributes.get('User.LastName');
        }
        if (attributes.containsKey('User.Title')) {
            u.Title = attributes.get('User.Title');
        }
        if (attributes.containsKey('User.CompanyName')) {
            u.CompanyName = attributes.get('User.CompanyName');
        }
        if (attributes.containsKey('User.AboutMe')) {
            u.AboutMe = attributes.get('User.AboutMe');
        }
        if (attributes.containsKey('User.Street')) {
            u.Street = attributes.get('User.Street');
        }
        if (attributes.containsKey('User.State')) {
            u.State = attributes.get('User.State');
        }
        if (attributes.containsKey('User.City')) {
            u.City = attributes.get('User.City');
        }
        if (attributes.containsKey('User.Zip')) {
            u.PostalCode = attributes.get('User.Zip');
        }
        if (attributes.containsKey('User.Country')) {
            u.Country = attributes.get('User.Country');
        }
        if (attributes.containsKey('User.CallCenter')) {
            u.CallCenterId = attributes.get('User.CallCenter');
        }
        if (attributes.containsKey('User.Manager')) {
            u.ManagerId = attributes.get('User.Manager');
        }
        if (attributes.containsKey('User.MobilePhone')) {
            u.MobilePhone = attributes.get('User.MobilePhone');
        }
        if (attributes.containsKey('User.DelegatedApproverId')) {
            u.DelegatedApproverId = attributes.get('User.DelegatedApproverId');
        }
        if (attributes.containsKey('User.Department')) {
            u.Department = attributes.get('User.Department');
        }
        if (attributes.containsKey('User.Division')) {
            u.Division = attributes.get('User.Division');
        }
        if (attributes.containsKey('User.EmployeeNumber')) {
            u.EmployeeNumber = attributes.get('User.EmployeeNumber');
        }
        if (attributes.containsKey('User.Extension')) {
            u.Extension = attributes.get('User.Extension');
        }
        if (attributes.containsKey('User.Fax')) {
            u.Fax = attributes.get('User.Fax');
        }
        if (attributes.containsKey('User.CommunityNickname')) {
            u.CommunityNickname = attributes.get('User.CommunityNickname');
        }
        if (attributes.containsKey('User.IsActive')) {
            String IsActiveVal = attributes.get('User.IsActive');
            u.IsActive = '1'.equals(IsActiveVal) || Boolean.valueOf(IsActiveVal);
        }
        if (attributes.containsKey('User.ReceivesAdminInfoEmails')) {
            String ReceivesAdminInfoEmailsVal = attributes.get('User.ReceivesAdminInfoEmails');
            u.ReceivesAdminInfoEmails = '1'.equals(ReceivesAdminInfoEmailsVal) || Boolean.valueOf(ReceivesAdminInfoEmailsVal);
        }
        if (attributes.containsKey('User.ReceivesInfoEmails')) {
            String ReceivesInfoEmailsVal = attributes.get('User.ReceivesInfoEmails');
            u.ReceivesInfoEmails = '1'.equals(ReceivesInfoEmailsVal) || Boolean.valueOf(ReceivesInfoEmailsVal);
        }
        if (attributes.containsKey('User.ForecastEnabled')) {
            String ForecastEnabledVal = attributes.get('User.ForecastEnabled');
            u.ForecastEnabled = '1'.equals(ForecastEnabledVal) || Boolean.valueOf(ForecastEnabledVal);
        }
        String uid = UserInfo.getUserId();
        User currentUser = [SELECT LocaleSidKey, LanguageLocaleKey, TimeZoneSidKey, EmailEncodingKey FROM User WHERE Id =: uid];
        if (attributes.containsKey('User.LocaleSidKey')) {
            u.LocaleSidKey = attributes.get('User.LocaleSidKey');
        } else if (create) {
            u.LocaleSidKey = currentUser.LocaleSidKey;
        }
        if (attributes.containsKey('User.LanguageLocaleKey')) {
            u.LanguageLocaleKey = attributes.get('User.LanguageLocaleKey');
        } else if (create) {
            u.LanguageLocaleKey = currentUser.LanguageLocaleKey;
        }
        if (attributes.containsKey('User.Alias')) {
            u.Alias = attributes.get('User.Alias');
        } else if (create) {
            String alias = '';
            if (u.FirstName == null) {
                alias = u.LastName;
            } else {
                alias = u.FirstName.charAt(0) + u.LastName;
            }
            if (alias.length() > 5) {
                alias = alias.substring(0, 5);
            }
            u.Alias = alias;
        }
        if (attributes.containsKey('User.TimeZoneSidKey')) {
            u.TimeZoneSidKey = attributes.get('User.TimeZoneSidKey');
        } else if (create) {
            u.TimeZoneSidKey = currentUser.TimeZoneSidKey;
        }
        if (attributes.containsKey('User.EmailEncodingKey')) {
            u.EmailEncodingKey = attributes.get('User.EmailEncodingKey');
        } else if (create) {
            u.EmailEncodingKey = currentUser.EmailEncodingKey;
        }

        if (!create) {
            update(u);
        } else {
            Insert u;
        }
    }

    //Handle JIT
    private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId, String federationIdentifier, Map < String, String > attributes, String assertion) {
        handleUser(create, u, attributes, federationIdentifier);
    }

    //For New User
    global User createUser(Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = new User();
        handleJit(true, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
        return u;
    }

    //For Existing User
    global void updateUser(Id userId, Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = [SELECT Id, FirstName, ContactId FROM User WHERE Id =: userId];
        handleJit(false, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
    }
}

For Community User, Account and Contact Creation SAML JIT Handler:

//This class provides logic for inbound just-in-time provisioning of single sign-on users in your Salesforce organization.
global class SSOUserHandler implements Auth.SamlJitHandler {

    //JIT Handler Exception
    private class JitException extends Exception {}

    //Handle User
    private void handleUser(boolean create, User u, Map < String, String > attributes, String federationIdentifier, boolean isStandard) {

        if (create && attributes.containsKey('User.Username')) {
            u.Username = attributes.get('User.Username');
        }
        if (create) {
            if (attributes.containsKey('User.FederationIdentifier')) {
                u.FederationIdentifier = attributes.get('User.FederationIdentifier');
            } else {
                u.FederationIdentifier = federationIdentifier;
            }
        }
        if (attributes.containsKey('User.ProfileId')) {
            String profileId = attributes.get('User.ProfileId');
            Profile p = [SELECT Id FROM Profile WHERE Id =: profileId];
            u.ProfileId = p.Id;
        }
        if (attributes.containsKey('User.UserRoleId')) {
            String userRole = attributes.get('User.UserRoleId');
            UserRole r = [SELECT Id FROM UserRole WHERE Id =: userRole];
            u.UserRoleId = r.Id;
        }
        if (attributes.containsKey('User.Phone')) {
            u.Phone = attributes.get('User.Phone');
        }
        if (attributes.containsKey('User.Email')) {
            u.Email = attributes.get('User.Email');
        }
        if (attributes.containsKey('User.FirstName')) {
            u.FirstName = attributes.get('User.FirstName');
        }
        if (attributes.containsKey('FirstName')) {
            u.FirstName = attributes.get('FirstName');
        }
        if (attributes.containsKey('User.LastName')) {
            u.LastName = attributes.get('User.LastName');
        }
        if (attributes.containsKey('User.Title')) {
            u.Title = attributes.get('User.Title');
        }
        if (attributes.containsKey('User.CompanyName')) {
            u.CompanyName = attributes.get('User.CompanyName');
        }
        if (attributes.containsKey('User.AboutMe')) {
            u.AboutMe = attributes.get('User.AboutMe');
        }
        if (attributes.containsKey('User.Street')) {
            u.Street = attributes.get('User.Street');
        }
        if (attributes.containsKey('User.State')) {
            u.State = attributes.get('User.State');
        }
        if (attributes.containsKey('User.City')) {
            u.City = attributes.get('User.City');
        }
        if (attributes.containsKey('User.Zip')) {
            u.PostalCode = attributes.get('User.Zip');
        }
        if (attributes.containsKey('User.Country')) {
            u.Country = attributes.get('User.Country');
        }
        if (attributes.containsKey('User.CallCenter')) {
            u.CallCenterId = attributes.get('User.CallCenter');
        }
        if (attributes.containsKey('User.Manager')) {
            u.ManagerId = attributes.get('User.Manager');
        }
        if (attributes.containsKey('User.MobilePhone')) {
            u.MobilePhone = attributes.get('User.MobilePhone');
        }
        if (attributes.containsKey('User.DelegatedApproverId')) {
            u.DelegatedApproverId = attributes.get('User.DelegatedApproverId');
        }
        if (attributes.containsKey('User.Department')) {
            u.Department = attributes.get('User.Department');
        }
        if (attributes.containsKey('User.Division')) {
            u.Division = attributes.get('User.Division');
        }
        if (attributes.containsKey('User.EmployeeNumber')) {
            u.EmployeeNumber = attributes.get('User.EmployeeNumber');
        }
        if (attributes.containsKey('User.Extension')) {
            u.Extension = attributes.get('User.Extension');
        }
        if (attributes.containsKey('User.Fax')) {
            u.Fax = attributes.get('User.Fax');
        }
        if (attributes.containsKey('User.CommunityNickname')) {
            u.CommunityNickname = attributes.get('User.CommunityNickname');
        }
        if (attributes.containsKey('User.IsActive')) {
            String IsActiveVal = attributes.get('User.IsActive');
            u.IsActive = '1'.equals(IsActiveVal) || Boolean.valueOf(IsActiveVal);
        }
        if (attributes.containsKey('User.ReceivesAdminInfoEmails')) {
            String ReceivesAdminInfoEmailsVal = attributes.get('User.ReceivesAdminInfoEmails');
            u.ReceivesAdminInfoEmails = '1'.equals(ReceivesAdminInfoEmailsVal) || Boolean.valueOf(ReceivesAdminInfoEmailsVal);
        }
        if (attributes.containsKey('User.ReceivesInfoEmails')) {
            String ReceivesInfoEmailsVal = attributes.get('User.ReceivesInfoEmails');
            u.ReceivesInfoEmails = '1'.equals(ReceivesInfoEmailsVal) || Boolean.valueOf(ReceivesInfoEmailsVal);
        }
        if (attributes.containsKey('User.ForecastEnabled')) {
            String ForecastEnabledVal = attributes.get('User.ForecastEnabled');
            u.ForecastEnabled = '1'.equals(ForecastEnabledVal) || Boolean.valueOf(ForecastEnabledVal);
        }
        String uid = UserInfo.getUserId();
        User currentUser = [SELECT LocaleSidKey, LanguageLocaleKey, TimeZoneSidKey, EmailEncodingKey FROM User WHERE Id =: uid];
        if (attributes.containsKey('User.LocaleSidKey')) {
            u.LocaleSidKey = attributes.get('User.LocaleSidKey');
        } else if (create) {
            u.LocaleSidKey = currentUser.LocaleSidKey;
        }
        if (attributes.containsKey('User.LanguageLocaleKey')) {
            u.LanguageLocaleKey = attributes.get('User.LanguageLocaleKey');
        } else if (create) {
            u.LanguageLocaleKey = currentUser.LanguageLocaleKey;
        }
        if (attributes.containsKey('User.Alias')) {
            u.Alias = attributes.get('User.Alias');
        } else if (create) {
            String alias = '';
            if (u.FirstName == null) {
                alias = u.LastName;
            } else {
                alias = u.FirstName.charAt(0) + u.LastName;
            }
            if (alias.length() > 5) {
                alias = alias.substring(0, 5);
            }
            u.Alias = alias;
        }
        if (attributes.containsKey('User.TimeZoneSidKey')) {
            u.TimeZoneSidKey = attributes.get('User.TimeZoneSidKey');
        } else if (create) {
            u.TimeZoneSidKey = currentUser.TimeZoneSidKey;
        }
        if (attributes.containsKey('User.EmailEncodingKey')) {
            u.EmailEncodingKey = attributes.get('User.EmailEncodingKey');
        } else if (create) {
            u.EmailEncodingKey = currentUser.EmailEncodingKey;
        }

        if (!create) {
            update(u);
        } else {
            Insert u;
        }
    }

    //Handle Contact
    private void handleContact(boolean create, String accountId, User u, Map < String, String > attributes) {

        Contact c;
        boolean newContact = false;

        List < Contact > lstContact = new List < Contact > ();
        Set < String > setEmailIds = new Set < String > ();
        Map < String, Contact > mapContactDetails = new Map < String, Contact > ();
        String communityID = '';
        String invitationId = '';
        String firstName = '';

        lstContact = [SELECT ID, Name, FirstName, Email, Community_Id__c, InvitaionId__c FROM Contact WHERE Community_Id__c != null AND InvitaionId__c != null];

        for (Contact cn: lstContact) {
            setEmailIds.add(cn.Email);
            mapContactDetails.put(cn.Email, cn);
        }

        if (setEmailIds.contains(attributes.get('User.Email'))) {
            communityID = mapContactDetails.get(attributes.get('User.Email')).Community_Id__c;
            invitationId = mapContactDetails.get(attributes.get('User.Email')).InvitaionId__c;
            firstName = mapContactDetails.get(attributes.get('User.Email')).FirstName;
        }

        if (create) {
            if (attributes.containsKey('User.Contact')) {
                String contact = attributes.get('User.Contact');
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
                u.ContactId = contact;
            } else {
                c = new Contact();
                newContact = true;
            }
        } else {
            if (attributes.containsKey('User.Contact')) {
                String contact = attributes.get('User.Contact');
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
                if (u.ContactId != c.Id) {
                    throw new JitException('Cannot change User.ContactId');
                }
            } else {
                String contact = u.ContactId;
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
            }
        }
        if (!newContact && c.AccountId != accountId) {
            throw new JitException('Mismatched account: ' + c.AccountId + ', ' + accountId);
        }

        if (String.isNotBlank(communityID)) {
            c.Community_Id__c = communityID;
            c.IsAddedInPublicGroup__c = true;
        }

        if (String.isNotBlank(invitationId)) {
            c.InvitaionId__c = invitationId;
        }

        if (attributes.containsKey('Contact.Email')) {
            c.Email = attributes.get('Contact.Email');
        }

        if (attributes.containsKey('FirstName')) {
            c.FirstName = attributes.get('FirstName');
        } else if (String.isNotBlank(firstName)) {
            c.FirstName = firstName;
        }
        if (attributes.containsKey('Contact.LastName')) {
            c.LastName = attributes.get('Contact.LastName');
        }
        if (attributes.containsKey('Contact.Phone')) {
            c.Phone = attributes.get('Contact.Phone');
        }
        if (attributes.containsKey('Contact.MailingStreet')) {
            c.MailingStreet = attributes.get('Contact.MailingStreet');
        }
        if (attributes.containsKey('Contact.MailingCity')) {
            c.MailingCity = attributes.get('Contact.MailingCity');
        }
        if (attributes.containsKey('Contact.MailingState')) {
            c.MailingState = attributes.get('Contact.MailingState');
        }
        if (attributes.containsKey('Contact.MailingCountry')) {
            c.MailingCountry = attributes.get('Contact.MailingCountry');
        }
        if (attributes.containsKey('Contact.MailingPostalCode')) {
            c.MailingPostalCode = attributes.get('Contact.MailingPostalCode');
        }
        if (attributes.containsKey('Contact.OtherStreet')) {
            c.OtherStreet = attributes.get('Contact.OtherStreet');
        }
        if (attributes.containsKey('Contact.OtherCity')) {
            c.OtherCity = attributes.get('Contact.OtherCity');
        }
        if (attributes.containsKey('Contact.OtherState')) {
            c.OtherState = attributes.get('Contact.OtherState');
        }
        if (attributes.containsKey('Contact.OtherCountry')) {
            c.OtherCountry = attributes.get('Contact.OtherCountry');
        }
        if (attributes.containsKey('Contact.OtherPostalCode')) {
            c.OtherPostalCode = attributes.get('Contact.OtherPostalCode');
        }
        if (attributes.containsKey('Contact.AssistantPhone')) {
            c.AssistantPhone = attributes.get('Contact.AssistantPhone');
        }
        if (attributes.containsKey('Contact.Department')) {
            c.Department = attributes.get('Contact.Department');
        }
        if (attributes.containsKey('Contact.Description')) {
            c.Description = attributes.get('Contact.Description');
        }
        if (attributes.containsKey('Contact.Fax')) {
            c.Fax = attributes.get('Contact.Fax');
        }
        if (attributes.containsKey('Contact.HomePhone')) {
            c.HomePhone = attributes.get('Contact.HomePhone');
        }
        if (attributes.containsKey('Contact.MobilePhone')) {
            c.MobilePhone = attributes.get('Contact.MobilePhone');
        }
        if (attributes.containsKey('Contact.OtherPhone')) {
            c.OtherPhone = attributes.get('Contact.OtherPhone');
        }
        if (attributes.containsKey('Contact.Title')) {
            c.Title = attributes.get('Contact.Title');
        }
        if (attributes.containsKey('Contact.Salutation')) {
            c.Salutation = attributes.get('Contact.Salutation');
        }
        if (attributes.containsKey('Contact.LeadSource')) {
            c.LeadSource = attributes.get('Contact.LeadSource');
        }
        if (attributes.containsKey('Contact.DoNotCall')) {
            String DoNotCallVal = attributes.get('Contact.DoNotCall');
            c.DoNotCall = '1'.equals(DoNotCallVal) || Boolean.valueOf(DoNotCallVal);
        }
        if (attributes.containsKey('Contact.HasOptedOutOfEmail')) {
            String HasOptedOutOfEmailVal = attributes.get('Contact.HasOptedOutOfEmail');
            c.HasOptedOutOfEmail = '1'.equals(HasOptedOutOfEmailVal) || Boolean.valueOf(HasOptedOutOfEmailVal);
        }
        if (attributes.containsKey('Contact.HasOptedOutOfFax')) {
            String HasOptedOutOfFaxVal = attributes.get('Contact.HasOptedOutOfFax');
            c.HasOptedOutOfFax = '1'.equals(HasOptedOutOfFaxVal) || Boolean.valueOf(HasOptedOutOfFaxVal);
        }
        if (attributes.containsKey('Contact.Owner')) {
            c.OwnerId = attributes.get('Contact.Owner');
        }
        if (attributes.containsKey('Contact.AssistantName')) {
            c.AssistantName = attributes.get('Contact.AssistantName');
        }
        if (attributes.containsKey('Contact.Birthdate')) {
            c.Birthdate = Date.valueOf(attributes.get('Contact.Birthdate'));
        }
        if (newContact) {
            c.AccountId = accountId;
            insert(c);
            u.ContactId = c.Id;
        } else {
            update(c);
        }

        if (setEmailIds.contains(attributes.get('User.Email'))) {
            delete mapContactDetails.get(attributes.get('User.Email'));
        }
    }

    //Handle Account
    private String handleAccount(boolean create, User u, Map < String, String > attributes) {

        Account a;
        boolean newAccount = false;
        if (create) {

            List < Contact > lstContact = new List < Contact > ();
            Set < String > setEmailIds = new Set < String > ();
            Map < String, Contact > mapContactDetails = new Map < String, Contact > ();
            String communityID = '';

            lstContact = [SELECT ID, Name, Email, Community_Id__c, AccountId FROM Contact WHERE Community_Id__c != null];

            for (Contact cn: lstContact) {
                setEmailIds.add(cn.Email);
                mapContactDetails.put(cn.Email, cn);
            }

            if (setEmailIds.contains(attributes.get('User.Email')) && mapContactDetails.get(attributes.get('User.Email')).AccountId != null) {

                a = [SELECT Id FROM Account WHERE Id =: mapContactDetails.get(attributes.get('User.Email')).AccountId];
            } else if (attributes.containsKey('Contact.Account')) {
                String account = attributes.get('Contact.Account');
                a = [SELECT Id FROM Account WHERE Id =: account];
            } else {
                if (attributes.containsKey('User.Contact')) {
                    String contact = attributes.get('User.Contact');
                    Contact c = [SELECT AccountId FROM Contact WHERE Id =: contact];
                    String account = c.AccountId;
                    a = [SELECT Id FROM Account WHERE Id =: account];
                } else {
                    a = new Account();
                    newAccount = true;
                }
            }
        } else {
            if (attributes.containsKey('User.Account')) {
                String account = attributes.get('User.Account');
                a = [SELECT Id FROM Account WHERE Id =: account];
            } else {
                if (attributes.containsKey('User.Contact')) {
                    String contact = attributes.get('User.Contact');
                    Contact c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
                    if (u.ContactId != c.Id) {
                        throw new JitException('Cannot change User.ContactId');
                    }
                    String account = c.AccountId;
                    a = [SELECT Id FROM Account WHERE Id =: account];
                } else {
                    throw new JitException('Could not find account');
                }
            }
        }
        if (attributes.containsKey('Account.Name')) {
            a.Name = attributes.get('Account.Name');
        }
        if (attributes.containsKey('Account.AccountNumber')) {
            a.AccountNumber = attributes.get('Account.AccountNumber');
        }
        if (attributes.containsKey('Account.Owner')) {
            a.OwnerId = attributes.get('Account.Owner');
        }
        if (attributes.containsKey('Account.BillingStreet')) {
            a.BillingStreet = attributes.get('Account.BillingStreet');
        }
        if (attributes.containsKey('Account.BillingCity')) {
            a.BillingCity = attributes.get('Account.BillingCity');
        }
        if (attributes.containsKey('Account.BillingState')) {
            a.BillingState = attributes.get('Account.BillingState');
        }
        if (attributes.containsKey('Account.BillingCountry')) {
            a.BillingCountry = attributes.get('Account.BillingCountry');
        }
        if (attributes.containsKey('Account.BillingPostalCode')) {
            a.BillingPostalCode = attributes.get('Account.BillingPostalCode');
        }
        if (attributes.containsKey('Account.AnnualRevenue')) {
            a.AnnualRevenue = Integer.valueOf(attributes.get('Account.AnnualRevenue'));
        }
        if (attributes.containsKey('Account.Description')) {
            a.Description = attributes.get('Account.Description');
        }
        if (attributes.containsKey('Account.Fax')) {
            a.Fax = attributes.get('Account.Fax');
        }
        if (attributes.containsKey('Account.NumberOfEmployees')) {
            a.NumberOfEmployees = Integer.valueOf(attributes.get('Account.NumberOfEmployees'));
        }
        if (attributes.containsKey('Account.Phone')) {
            a.Phone = attributes.get('Account.Phone');
        }
        if (attributes.containsKey('Account.ShippingStreet')) {
            a.ShippingStreet = attributes.get('Account.ShippingStreet');
        }
        if (attributes.containsKey('Account.ShippingCity')) {
            a.ShippingCity = attributes.get('Account.ShippingCity');
        }
        if (attributes.containsKey('Account.ShippingState')) {
            a.ShippingState = attributes.get('Account.ShippingState');
        }
        if (attributes.containsKey('Account.ShippingCountry')) {
            a.ShippingCountry = attributes.get('Account.ShippingCountry');
        }
        if (attributes.containsKey('Account.ShippingPostalCode')) {
            a.ShippingPostalCode = attributes.get('Account.ShippingPostalCode');
        }
        if (attributes.containsKey('Account.Sic')) {
            a.Sic = attributes.get('Account.Sic');
        }
        if (attributes.containsKey('Account.TickerSymbol')) {
            a.TickerSymbol = attributes.get('Account.TickerSymbol');
        }
        if (attributes.containsKey('Account.Website')) {
            a.Website = attributes.get('Account.Website');
        }
        if (attributes.containsKey('Account.Industry')) {
            a.Industry = attributes.get('Account.Industry');
        }
        if (attributes.containsKey('Account.Ownership')) {
            a.Ownership = attributes.get('Account.Ownership');
        }
        if (attributes.containsKey('Account.Rating')) {
            a.Rating = attributes.get('Account.Rating');
        }
        if (newAccount) {
            insert(a);
        } else {
            update(a);
        }
        return a.Id;
    }

    //Handle JIT
    private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId, String federationIdentifier, Map < String, String > attributes, String assertion) {
        if (communityId != null || portalId != null) {
            String account = handleAccount(create, u, attributes);
            handleContact(create, account, u, attributes);
            handleUser(create, u, attributes, federationIdentifier, false);
        } else {
            handleUser(create, u, attributes, federationIdentifier, true);
        }
    }

    //For New User
    global User createUser(Id samlSsoProviderId, Id communityId, Id portalId, String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = new User();
        handleJit(true, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
        return u;
    }

    //For Existing User
    global void updateUser(Id userId, Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = [SELECT Id, FirstName, ContactId FROM User WHERE Id =: userId];
        handleJit(false, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
    }
}

Salesforce Single Sign-On Implementation

What is Single Sign-On?
Single Sign-On is a process that allows network users to access all authorized network resources without having to separately log in to each resource. Single Sign-On also enables your organization to integrate with an external identity management system or perform web-based single sign-on to Force.com. Single Sign-On enables multiple types of authentication integration, but the most common are:

  • Use an existing directory of user names and passwords, such as Active Directory or LDAP, for Salesforce users.
  • Allow seamless sign-on to Force.com applications, eliminating the need for explicit user log on actions

Advantage of Single Sign-On
Single Sign-On produces benefits in three main areas – reduction in administrative costs, increased ease of use and better implementation of security schemes.

  • Reduced Administrative Costs: With Single Sign-On, all user authentication information resides in a central directory, which reduces the need to maintain, monitor and potentially synchronized multiple stores, as well as reducing user support requests around passwords.
  • Increased ease of use: Each user only has a single username and password which grants them access to corporate resources and Salesforce. Reduced complexity means an easier to use environment that provides seamless access to all resources. Single Sign-On also saves users time, since each individual sign-on process can take 5 to 20 seconds to complete. And removing the extra step of logging into Salesforce can increase user adoption of your Salesforce applications by lowering the barrier to use.
  • Increased Security: Any password policies that you have established for your corporate network will also be in effect for Salesforce. In addition, sending an authentication credential that is only valid for a single use can increase security for users who have access to sensitive data.

How Single Sign-On Works?
The high-level process for authenticating users via Single Sign-On is as follows:

  • When a user tries to log in—either online or using the API—Salesforce validates the username and checks the user’s profile settings.
  • If the user’s profile has the “Uses Single Sign-on” user permission, then Salesforce does not authenticate the username with the password. Instead, a Web Services call is made to the user’s single sign-on service, asking it to validate the username and password.
  • The Web Services call passes the username, password, and sourceIp to a Web Service defined for your organization. (sourceIp is the IP address that originated the login request). You must create and deploy an implementation of the Web Service that can be accessed by Salesforce.com servers.
  • Your implementation of the Web Service validates the passed information and returns either “true” or “false.”
  • If the response is “true,” then the login process continues, a new session is generated, and the user proceeds to the application. If “false” is returned, then the user is informed that his or her username and password combination was invalid.

Single Sign-On Configuration

Step 1:

  • From Setup, click “Security Controls | Single Sign-On Settings”, then click Federated Single Sign-On Using SAML Edit.
  • Select the SAML Enabled check box.

Step 2:

  • Click ‘New’ to fill out Single Sign On settings.
  • Use the following settings:
    1. Name: Put the name of SSO
    2. SAML Version: 2.0
    3. API Name: Will auto populate as per the Name field
    4. Issuer: (The issuer can be found in SSO form provided by External System team) Issuer is nothing but domain URL of Identity provider Org.
    5. Entity Id: It would be the URL of the target org.
    6. Identity Provider Certificate: Upload the Identity provider certificate from the Auth. Single Sign On server.
    7. Request Signing Certificate: Default Certificate
    8. Request Signature Method: RSA-SHA1
    9. SAML Identity Type: Select – Assertion contains the Federation ID from the User object.
    10. SAML Identity Location: Identity is in the NameIdentifier element of the Subject statement.
    11. Service Provider Initiated Request Binding: HTTP Redirect
    12. Identity Provider Login URL: (External system login page) Parameter of your application)
    13. Identity Provider Logout URL: (External system site logout page) Parameter of your application.

The input fields in the image should be entered as described in above.

SSO1

Step 3:

  • From Setup, click Domain Management | My Domain – Enter a new subdomain name, and click Check Availability. If the name is available, click the Terms and Conditions check box, then click Register Domain.

SSO2

Step 4:

  • Test App sign on option should be visible on the login page of the target org. So, we need to enable it in Authentication configuration. From Setup, click Domain Management | My Domain | Edit – Authentication Configuration | Enable Test App. After enable it we can see the Test App link in Salesforce Login page. Click it and once Test App portal shows up, login using Test App Credentials.

SSO3

Step 5:

  • To tie External System to SFDC User account, the system admin needs to know the unique id associated with External System ID that gets passed by SAML assertion. As of now, only way to get this is via reading SAML assertion as noted above and this can cause a bad user experience. The best way to approach this issue is via creating new SFDC user account whenever a use wants to setup sync between his or her External System ID and SFDC account. On the context of on-boarding, this means that the user would need to get External System ID first prior to getting SFDC account. Once the user creates External System ID, the user can use login using External System ID feature to create a new SFDC account. If the user go through this route, the federation ID field of newly created user will be auto-populated with the entity from SAML message that is specified in SAML single sign on setting. SFDC provides such option under SAML Single Sign-On Settings. It can be found at the bottom of the page.

SSO4

  • To automate sync the External System user to Salesforce we need to write an apex class. And that class must be executed by a user, which user has the permission to create/edit of users.

Sample JIT Handler Class

//This class provides logic for inbound just-in-time provisioning of single sign-on users in your Salesforce organization.
global class SSOUserHandler implements Auth.SamlJitHandler {
    private class JitException extends Exception{}
    
    private void handleUser(boolean create, User u, Map<String, String> attributes,
        String federationIdentifier, boolean isStandard) {
        
        if(create && attributes.containsKey('User.Username')) {
            u.Username = attributes.get('User.Username');
        }
        if(create) {
            if(attributes.containsKey('User.FederationIdentifier')) {
                u.FederationIdentifier = attributes.get('User.FederationIdentifier');
            } else {
                u.FederationIdentifier = federationIdentifier;
            }
        }
        if(attributes.containsKey('User.ProfileId')) {
            String profileId = attributes.get('User.ProfileId');
            Profile p = [SELECT Id FROM Profile WHERE Id=:profileId];
            u.ProfileId = p.Id;
        }
        if(attributes.containsKey('User.UserRoleId')) {
            String userRole = attributes.get('User.UserRoleId');
            UserRole r = [SELECT Id FROM UserRole WHERE Id=:userRole];
            u.UserRoleId = r.Id;
        }
        if(attributes.containsKey('User.Phone')) {
            u.Phone = attributes.get('User.Phone');
        }
        if(attributes.containsKey('User.Email')) {
          u.Email = attributes.get('User.Email');            
        }
        if(attributes.containsKey('User.FirstName')) {
            u.FirstName = attributes.get('User.FirstName');
        }
        if(attributes.containsKey('FirstName')) {
            u.FirstName = attributes.get('FirstName');
        }
        if(attributes.containsKey('User.LastName')) {
            u.LastName = attributes.get('User.LastName');
        }
        if(attributes.containsKey('User.Title')) {
            u.Title = attributes.get('User.Title');
        }
        if(attributes.containsKey('User.CompanyName')) {
            u.CompanyName = attributes.get('User.CompanyName');
        }
        if(attributes.containsKey('User.AboutMe')) {
            u.AboutMe = attributes.get('User.AboutMe');
        }
        if(attributes.containsKey('User.Street')) {
            u.Street = attributes.get('User.Street');
        }
        if(attributes.containsKey('User.State')) {
            u.State = attributes.get('User.State');
        }
        if(attributes.containsKey('User.City')) {
            u.City = attributes.get('User.City');
        }
        if(attributes.containsKey('User.Zip')) {
            u.PostalCode = attributes.get('User.Zip');
        }
        if(attributes.containsKey('User.Country')) {
            u.Country = attributes.get('User.Country');
        }
        if(attributes.containsKey('User.CallCenter')) {
            u.CallCenterId = attributes.get('User.CallCenter');
        }
        if(attributes.containsKey('User.Manager')) {
            u.ManagerId = attributes.get('User.Manager');
        }
        if(attributes.containsKey('User.MobilePhone')) {
            u.MobilePhone = attributes.get('User.MobilePhone');
        }
        if(attributes.containsKey('User.DelegatedApproverId')) {
            u.DelegatedApproverId = attributes.get('User.DelegatedApproverId');
        }
        if(attributes.containsKey('User.Department')) {
            u.Department = attributes.get('User.Department');
        }
        if(attributes.containsKey('User.Division')) {
            u.Division = attributes.get('User.Division');
        }
        if(attributes.containsKey('User.EmployeeNumber')) {
            u.EmployeeNumber = attributes.get('User.EmployeeNumber');
        }
        if(attributes.containsKey('User.Extension')) {
            u.Extension = attributes.get('User.Extension');
        }
        if(attributes.containsKey('User.Fax')) {
            u.Fax = attributes.get('User.Fax');
        }
        if(attributes.containsKey('User.CommunityNickname')) {
            u.CommunityNickname = attributes.get('User.CommunityNickname');
        }
        if(attributes.containsKey('User.IsActive')) {
            String IsActiveVal = attributes.get('User.IsActive');
            u.IsActive = '1'.equals(IsActiveVal) || Boolean.valueOf(IsActiveVal);
        }
        if(attributes.containsKey('User.ReceivesAdminInfoEmails')) {
            String ReceivesAdminInfoEmailsVal = attributes.get('User.ReceivesAdminInfoEmails');
            u.ReceivesAdminInfoEmails = '1'.equals(ReceivesAdminInfoEmailsVal) || Boolean.valueOf(ReceivesAdminInfoEmailsVal);
        }
        if(attributes.containsKey('User.ReceivesInfoEmails')) {
            String ReceivesInfoEmailsVal = attributes.get('User.ReceivesInfoEmails');
            u.ReceivesInfoEmails = '1'.equals(ReceivesInfoEmailsVal) || Boolean.valueOf(ReceivesInfoEmailsVal);
        }
        if(attributes.containsKey('User.ForecastEnabled')) {
            String ForecastEnabledVal = attributes.get('User.ForecastEnabled');
            u.ForecastEnabled = '1'.equals(ForecastEnabledVal) || Boolean.valueOf(ForecastEnabledVal);
        }
        String uid = UserInfo.getUserId();
        User currentUser = 
            [SELECT LocaleSidKey, LanguageLocaleKey, TimeZoneSidKey, EmailEncodingKey FROM User WHERE Id=:uid];
        if(attributes.containsKey('User.LocaleSidKey')) {
            u.LocaleSidKey = attributes.get('User.LocaleSidKey');
        } else if(create) {
            u.LocaleSidKey = currentUser.LocaleSidKey;
        }
        if(attributes.containsKey('User.LanguageLocaleKey')) {
            u.LanguageLocaleKey = attributes.get('User.LanguageLocaleKey');
        } else if(create) {
            u.LanguageLocaleKey = currentUser.LanguageLocaleKey;
        }
        if(attributes.containsKey('User.Alias')) {
            u.Alias = attributes.get('User.Alias');
        } else if(create) {
            String alias = '';
            if(u.FirstName == null) {
                alias = u.LastName;
            } else {
                alias = u.FirstName.charAt(0) + u.LastName;
            }
            if(alias.length() > 5) {
                alias = alias.substring(0, 5);
            }
            u.Alias = alias;
        }
        if(attributes.containsKey('User.TimeZoneSidKey')) {
            u.TimeZoneSidKey = attributes.get('User.TimeZoneSidKey');
        } else if(create) {
            u.TimeZoneSidKey = currentUser.TimeZoneSidKey;
        }
        if(attributes.containsKey('User.EmailEncodingKey')) {
            u.EmailEncodingKey = attributes.get('User.EmailEncodingKey');
        } else if(create) {
            u.EmailEncodingKey = currentUser.EmailEncodingKey;
        }
		
		if(!create) {
            update(u);
        }
        else{
            Insert u;
        }
    }
    
    private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId, String federationIdentifier, Map<String, String> attributes, String assertion) {
        handleUser(create, u, attributes, federationIdentifier);
    }
    
    global User createUser(Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map<String, String> attributes, String assertion) {
        User u = new User();
        handleJit(true, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
        return u;
    }

    global void updateUser(Id userId, Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map<String, String> attributes, String assertion) {
        User u = [SELECT Id, FirstName, ContactId FROM User WHERE Id=:userId];
		handleJit(false, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
    }
}

Sample SOAP Message
As part of the Single Sign-On process, a Salesforce.com server makes a SOAP 1.1 request to authenticate the user who is passing in the credentials. Below is an example of this type of request. Your Single Sign-On service needs to accept this request, process it, and return a “true” or “false” response.

Sample Request

<?xml version="1.0" encoding="UTF-8" ?> 
<soapenv:Envelope 
xmlns:soapenv="<a rel="nofollow" class="external free" href="http://schemas.xmlsoap.org/soap/envelope/">http://schemas.xmlsoap.org/soap/envelope/</a>"> 
 <soapenv:Body> 
   <Authenticate xmlns="urn:authentication.soap.sforce.com"> 
     <username>testuser@testorg.com</username>  
     <password>test12345</password>  
     <sourceIp>1.2.3.4</sourceIp>  
   </Authenticate> 
 </soapenv:Body> 
</soapenv:Envelope> 

Sample Response Message

<?xml version="1.0" encoding="UTF-8"?> 
<soapenv:Envelope 
xmlns:soapenv="<a rel="nofollow" class="external free" href="http://schemas.xmlsoap.org/soap/envelope/">http://schemas.xmlsoap.org/soap/envelope/</a>"> 
<soapenv:Body> 
 <AuthenticateResponse 
xmlns="urn:authentication.soap.sforce.com"> 
  <Authenticated>true</Authenticated> 
 </AuthenticateResponse> 
</soapenv:Body> 
</soapenv:Envelope>

Sample SAML Assertion for Just-In-Time Provisioning

<saml:AttributeStatement>

   <saml:Attribute Name="User.Username" 
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">testuser@testorg.com
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.Phone" 
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">123-456-7890
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.FirstName"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">Biswajeet
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.LanguageLocaleKey"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">en_US
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.CompanyName"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">ABC 
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.Alias"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">Kunal
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.CommunityNickname"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">Kunal
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.UserRoleId"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">000000000000000
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.Title"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">Mr.
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.LocaleSidKey"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">en_CA
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.Email"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">testuser@testorg.com
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name=" User.FederationIdentifier"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">123542
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.TimeZoneSidKey"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">America/Los_Angeles
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.LastName"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">Samal
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.ProfileId"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">00ex0000001pDNP
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.IsActive"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">1
      </saml:AttributeValue>
   </saml:Attribute>

   <saml:Attribute Name="User.EmailEncodingKey"
      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml:AttributeValue xsi:type="xs:anyType">UTF-8
      </saml:AttributeValue>
   </saml:Attribute>

</saml:AttributeStatement>