Newer Version Available

This content describes an older version of this product. View Latest

AuthProviderPlugin Interface

Use this interface to create a custom OAuth-based authentication provider plug-in for single sign-on in to Salesforce.

Namespace

Auth

Usage

To create a custom authentication provider for single sign-on, create a class that implements Auth.AuthProviderPlugin. This class allows you to store the custom configuration for your authentication provider and handle authentication protocols when users log in to Salesforce with their login credentials for an external service provider. In Salesforce, the class that implements this interface appears in the Provider Type drop-down list in Auth. Providers in Setup. Make sure that the user you specify to run the class has “Customize Application” and “Manage Auth. Providers” permissions.

AuthProviderPlugin Methods

The following are methods for AuthProviderPlugin.

getCustomMetadataType()

Returns the custom metadata type API name for a custom OAuth-based authentication provider for single sign-on to Salesforce.

Signature

public String getCustomMetadataType()

Return Value

Type: String

The custom metadata type API name for the authentication provider.

Usage

The getCustomMetatadaType() method returns only custom metadata type names. It does not return custom metadata record names.

getUserInfo(authProviderConfiguration, response)

Returns information from the custom authentication provider about the current user. This information is used by the registration handler and in other authentication provider flows.

Signature

public Auth.UserData getUserInfo(Map<String,String> authProviderConfiguration, Auth.AuthProviderTokenResponse response)

Parameters

authProviderConfiguration
Type: Map<String,String>
The configuration for the custom authentication provider. When you create a custom metadata type in Salesforce, the configuration populates with the custom metadata type default values. Or you can set the configuration with values you enter when you create the custom provider in Auth. Providers in Setup.
response
Type: Auth.AuthProviderTokenResponse

The OAuth access token, OAuth secret or refresh token, and state provided by the authentication provider to authenticate the current user.

Return Value

Type: Auth.UserData

Creates a new instance of the Auth.UserData class.

handleCallback(authProviderConfiguration, callbackState)

Uses the authentication provider’s supported authentication protocol to return an OAuth access token, OAuth secret or refresh token, and the state passed in when the request for the current user was initiated.

Signature

public Auth.AuthProviderTokenResponse handleCallback(Map<String,String> authProviderConfiguration, Auth.AuthProviderCallbackState callbackState)

Parameters

authProviderConfiguration
Type: Map<StringString>
The configuration for the custom authentication provider. When you create a custom metadata type in Salesforce, the configuration populates with the custom metadata type default values. Or you can set the configuration with values you enter when you create the custom provider in Auth. Providers in Setup.
callbackState
Type: Auth.AuthProviderCallbackState
The class that contains the HTTP headers, body, and queryParams of the authentication request.

Return Value

Type: Auth.AuthProviderTokenResponse

Creates an instance of the AuthProviderTokenResponse class.

initiate(authProviderConfiguration, stateToPropagate)

Returns the URL where the user is redirected for authentication.

Signature

public System.PageReference initiate(Map<String,String> authProviderConfiguration, String stateToPropagate)

Parameters

authProviderConfiguration
Type: Map<StringString>
The configuration for the custom authentication provider. When you create a custom metadata type in Salesforce, the configuration populates with the custom metadata type default values. Or you can set the configuration with values you enter when you create the custom provider in Auth. Providers in Setup.
stateToPropagate
Type: String
The state passed in to initiate the authentication request for the user.

Return Value

Type: System.PageReference

The URL of the page where the user is redirected for authentication.

AuthProviderPlugin Example Implementation

This is an example implementation of the Auth.AuthProviderPlugin interface.

The following example tests the implementation:

1global class Concur implements Auth.AuthProviderPlugin {
2          
3          // Use this URL for the endpoint that the 
4          // authentication provider calls back to for configuration
5          public String redirectUrl; 
6          private String key;
7          private String secret;
8          
9          // Application redirection to the Concur website for 
10          // authentication and authorization
11          private String authUrl;  
12          
13          // URI to get the new access token from concur  using the GET verb
14          private String accessTokenUrl; 
15          
16          // Api name for the custom metadata type created for this auth provider
17          private String customMetadataTypeApiName; 
18          
19          // Api url to access the user in concur
20          private String userAPIUrl; 
21          
22          // Version of the user api url to access data from concur
23          private String userAPIVersionUrl; 
24          
25          global String getCustomMetadataType() {
26              return customMetadataTypeApiName;
27          } 
28            
29            global PageReference initiate(Map<string,string> 
30            authProviderConfiguration, String stateToPropagate) 
31            { 
32                authUrl = authProviderConfiguration.get('Auth_Url__c'); 
33                key = authProviderConfiguration.get('Key__c'); 
34                //Here the developer can build up a request of some sort 
35                //Ultimately they’ll return a URL where we will redirect the user 
36                String url = authUrl + '?client_id='+ key +'&scope=USER,EXPRPT,LIST&redirect_uri='+ redirectUrl + '&state=' + stateToPropagate; 
37                return new PageReference(url); 
38            } 
39            
40            global Auth.AuthProviderTokenResponse handleCallback(Map<string,string> 
41            authProviderConfiguration, Auth.AuthProviderCallbackState state ) 
42            { 
43                //Here, the developer will get the callback with actual protocol. 
44                //Their responsibility is to return a new object called 
45                //AuthProviderToken 
46                //This will contain an optional accessToken and refreshToken 
47                key = authProviderConfiguration.get('Key__c'); 
48                secret = authProviderConfiguration.get('Secret__c'); 
49                accessTokenUrl = authProviderConfiguration.get('Access_Token_Url__c'); 
50            
51                Map<String,String> queryParams = state.queryParameters; 
52                String code = queryParams.get('code'); 
53                String sfdcState = queryParams.get('state'); 
54            
55                HttpRequest req = new HttpRequest(); 
56                String url = accessTokenUrl+'?code=' + code + '&client_id=' + key + 
57                '&client_secret=' + secret; 
58                req.setEndpoint(url); 
59                req.setHeader('Content-Type','application/xml'); 
60                req.setMethod('GET'); 
61            
62                Http http = new Http(); 
63                HTTPResponse res = http.send(req); 
64                String responseBody = res.getBody(); 
65                String token = getTokenValueFromResponse(responseBody, 'Token', null); 
66            
67                return new Auth.AuthProviderTokenResponse('Concur', token, 
68                'refreshToken', sfdcState); 
69            } 
70            
71            
72            global Auth.UserData getUserInfo(Map<string,string> 
73            authProviderConfiguration, 
74            Auth.AuthProviderTokenResponse response) 
75            { 
76                //Here the developer is responsible for constructing an 
77                //Auth.UserData object 
78                String token = response.oauthToken; 
79                HttpRequest req = new HttpRequest(); 
80                userAPIUrl = authProviderConfiguration.get('API_User_Url__c');
81                userAPIVersionUrl = authProviderConfiguration.get
82                ('API_User_Version_Url__c'); 
83                req.setHeader('Authorization', 'OAuth ' + token); 
84                req.setEndpoint(userAPIUrl); 
85                req.setHeader('Content-Type','application/xml'); 
86                req.setMethod('GET'); 
87            
88                Http http = new Http(); 
89                HTTPResponse res = http.send(req); 
90                String responseBody = res.getBody(); 
91                String id = getTokenValueFromResponse(responseBody, 
92                'LoginId',userAPIVersionUrl); 
93                String fname = getTokenValueFromResponse(responseBody, 
94                'FirstName', userAPIVersionUrl); 
95                String lname = getTokenValueFromResponse(responseBody, 
96                'LastName', userAPIVersionUrl); 
97                String flname = fname + ' ' + lname; 
98                String uname = getTokenValueFromResponse(responseBody, 
99                'EmailAddress', userAPIVersionUrl); 
100                String locale = getTokenValueFromResponse(responseBody, 
101                'LocaleName', userAPIVersionUrl); 
102                Map<String,String> provMap = new Map<String,String>(); 
103                provMap.put('what1', 'noidea1'); 
104                provMap.put('what2', 'noidea2'); 
105                return new Auth.UserData(id, fname, lname, flname, 
106                uname, 'what', locale, null, 'Concur', null, provMap); 
107            } 
108            
109            private String getTokenValueFromResponse(String response, 
110            String token, String ns) 
111            { 
112                Dom.Document docx = new Dom.Document(); 
113                docx.load(response); 
114                String ret = null; 
115            
116                dom.XmlNode xroot = docx.getrootelement() ; 
117                if(xroot != null){ 
118                ret = xroot.getChildElement(token, ns).getText(); 
119            } 
120            return ret; 
121            } 
122            
123        }

Sample Test Classes

The following example contains test classes for the Concur class.

1@IsTest 
2            public class ConcurTestClass { 
3            
4                private static final String OAUTH_TOKEN = 'testToken'; 
5                private static final String STATE = 'mocktestState'; 
6                private static final String REFRESH_TOKEN = 'refreshToken'; 
7                private static final String LOGIN_ID = 'testLoginId'; 
8                private static final String USERNAME = 'testUsername'; 
9                private static final String FIRST_NAME = 'testFirstName'; 
10                private static final String LAST_NAME = 'testLastName'; 
11                private static final String EMAIL_ADDRESS = 'testEmailAddress'; 
12                private static final String LOCALE_NAME = 'testLocalName'; 
13                private static final String FULL_NAME = FIRST_NAME + ' ' + LAST_NAME; 
14                private static final String PROVIDER = 'Concur'; 
15                private static final String REDIRECT_URL = 
16                'http://localhost/services/authcallback/orgId/Concur'; 
17                private static final String KEY = 'testKey'; 
18                private static final String SECRET = 'testSecret'; 
19                private static final String STATE_TO_PROPOGATE = 'testState'; 
20                private static final String ACCESS_TOKEN_URL = 
21                'http://www.dummyhost.com/accessTokenUri'; 
22                private static final String API_USER_VERSION_URL = 
23                'http://www.dummyhost.com/user/20/1'; 
24                private static final String AUTH_URL = 
25                'http://www.dummy.com/authurl'; 
26                private static final String API_USER_URL = 
27                'www.concursolutions.com/user/api'; 
28            
29            // In the real world scenario, the key and value would be read 
30            // from the (custom fields in) custom metadata type record 
31            private static Map<String,String> setupAuthProviderConfig () 
32            { 
33                Map<String,String> authProviderConfiguration = new Map<String,String>(); 
34                authProviderConfiguration.put('Key__c', KEY); 
35                authProviderConfiguration.put('Auth_Url__c', AUTH_URL); 
36                authProviderConfiguration.put('Secret__c', SECRET); 
37                authProviderConfiguration.put('Access_Token_Url__c', ACCESS_TOKEN_URL); 
38                authProviderConfiguration.put('API_User_Url__c',API_USER_URL); 
39                authProviderConfiguration.put('API_User_Version_Url__c',
40                API_USER_VERSION_URL); 
41                authProviderConfiguration.put('Redirect_Url__c',REDIRECT_URL); 
42                return authProviderConfiguration; 
43            
44            } 
45            
46            static testMethod void testInitiateMethod() 
47            { 
48                String stateToPropogate = 'mocktestState'; 
49                Map<String,String> authProviderConfiguration = setupAuthProviderConfig(); 
50                Concur concurCls = new Concur(); 
51                concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url__c'); 
52            PageReference expectedUrl = new PageReference(authProviderConfiguration.get('Auth_Url__c') + '?client_id='+ 
53            authProviderConfiguration.get('Key__c') +'&scope=USER,EXPRPT,LIST&redirect_uri='+ 
54            authProviderConfiguration.get('Redirect_Url__c') + '&state=' + 
55            STATE_TO_PROPOGATE); 
56                PageReference actualUrl = concurCls.initiate(authProviderConfiguration, STATE_TO_PROPOGATE); 
57                System.assertEquals(expectedUrl.getUrl(), actualUrl.getUrl());
58            } 
59            
60            static testMethod void testHandleCallback() 
61            { 
62                Map<String,String> authProviderConfiguration = 
63                setupAuthProviderConfig(); 
64                Concur concurCls = new Concur(); 
65                concurCls.redirectUrl = authProviderConfiguration.get
66                ('Redirect_Url_c'); 
67            
68                Test.setMock(HttpCalloutMock.class, new 
69                ConcurMockHttpResponseGenerator()); 
70            
71                Map<String,String> queryParams = new Map<String,String>(); 
72                queryParams.put('code','code'); 
73                queryParams.put('state',authProviderConfiguration.get('State_c')); 
74                Auth.AuthProviderCallbackState cbState = 
75                new Auth.AuthProviderCallbackState(null,null,queryParams); 
76                Auth.AuthProviderTokenResponse actualAuthProvResponse = 
77                concurCls.handleCallback(authProviderConfiguration, cbState); 
78                Auth.AuthProviderTokenResponse expectedAuthProvResponse = 
79                new Auth.AuthProviderTokenResponse(
80                'Concur', OAUTH_TOKEN, REFRESH_TOKEN, null); 
81            
82                System.assertEquals(expectedAuthProvResponse.provider, 
83                actualAuthProvResponse.provider); 
84                System.assertEquals(expectedAuthProvResponse.oauthToken, 
85                actualAuthProvResponse.oauthToken); 
86                System.assertEquals(expectedAuthProvResponse.oauthSecretOrRefreshToken, 
87                actualAuthProvResponse.oauthSecretOrRefreshToken); 
88                System.assertEquals(expectedAuthProvResponse.state, 
89                actualAuthProvResponse.state); 
90            
91            
92            } 
93            
94            
95            static testMethod void testGetUserInfo() 
96            { 
97                Map<String,String> authProviderConfiguration = 
98                setupAuthProviderConfig(); 
99                Concur concurCls = new Concur(); 
100            
101                Test.setMock(HttpCalloutMock.class, new 
102                ConcurMockHttpResponseGenerator()); 
103            
104                Auth.AuthProviderTokenResponse response = 
105                new Auth.AuthProviderTokenResponse(
106                PROVIDER, OAUTH_TOKEN ,'sampleOauthSecret', STATE); 
107                Auth.UserData actualUserData = concurCls.getUserInfo(
108                authProviderConfiguration, response) ; 
109            
110                Map<String,String> provMap = new Map<String,String>(); 
111                provMap.put('key1', 'value1'); 
112                provMap.put('key2', 'value2'); 
113            
114                Auth.UserData expectedUserData = new Auth.UserData(LOGIN_ID, 
115                FIRST_NAME, LAST_NAME, FULL_NAME, EMAIL_ADDRESS, 
116                null, LOCALE_NAME, null, PROVIDER, null, provMap); 
117            
118                System.assertNotEquals(expectedUserData,null); 
119                System.assertEquals(expectedUserData.firstName, 
120                actualUserData.firstName); 
121                System.assertEquals(expectedUserData.lastName, 
122                actualUserData.lastName); 
123                System.assertEquals(expectedUserData.fullName, 
124                actualUserData.fullName); 
125                System.assertEquals(expectedUserData.email, 
126                actualUserData.email); 
127                System.assertEquals(expectedUserData.username, 
128                actualUserData.username); 
129                System.assertEquals(expectedUserData.locale, 
130                actualUserData.locale); 
131                System.assertEquals(expectedUserData.provider, 
132                actualUserData.provider); 
133                System.assertEquals(expectedUserData.siteLoginUrl, 
134                actualUserData.siteLoginUrl); 
135            } 
136            
137            
138            // Implement a mock http response generator for concur 
139            public class ConcurMockHttpResponseGenerator implements HttpCalloutMock 
140            { 
141                public HTTPResponse respond(HTTPRequest req) 
142                { 
143                    String namespace = API_USER_VERSION_URL; 
144                    String prefix = 'mockPrefix'; 
145            
146                    Dom.Document doc = new Dom.Document(); 
147                    Dom.XmlNode xmlNode = doc.createRootElement(
148                    'mockRootNodeName', namespace, prefix); 
149                    xmlNode.addChildElement('LoginId', namespace, prefix)
150                    .addTextNode(LOGIN_ID); 
151                    xmlNode.addChildElement('FirstName', namespace, prefix)
152                    .addTextNode(FIRST_NAME); 
153                    xmlNode.addChildElement('LastName', namespace, prefix)
154                    .addTextNode(LAST_NAME); 
155                    xmlNode.addChildElement('EmailAddress', namespace, prefix)
156                    .addTextNode(EMAIL_ADDRESS); 
157                    xmlNode.addChildElement('LocaleName', namespace, prefix)
158                    .addTextNode(LOCALE_NAME); 
159                    xmlNode.addChildElement('Token', null, null)
160                    .addTextNode(OAUTH_TOKEN); 
161                    System.debug(doc.toXmlString()); 
162                    // Create a fake response 
163                    HttpResponse res = new HttpResponse(); 
164                    res.setHeader('Content-Type', 'application/xml'); 
165                    res.setBody(doc.toXmlString()); 
166                    res.setStatusCode(200); 
167                    return res; 
168                } 
169            
170            } 
171        }