AuthProviderPluginClass Class
Namespace
Usage
To create a custom authentication provider for single sign-on, create a class that extends Auth.AuthProviderPluginClass. 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.
As of API version 39.0, use the abstract class AuthProviderPluginClass to create a custom external authentication provider. This class replaces the AuthProviderPlugin interface. If you’ve already implemented a custom authentication provider plug-in using the interface, it still works. However, use AuthProviderPluginClass to extend your plug-in. If you haven’t created an interface, create a custom authentication provider plug-in by extending this abstract class. For more information, see Create a Custom Authentication Provider Plug-in.
AuthProviderPluginClass Methods
The AuthProviderPluginClass methods don’t support DML options.
This class doesn't include a method for single logout. You can easily configure single logout in Setup. For steps, see Configure OpenID Connect Single Logout with Salesforce as the Relying Party in Salesforce Help. Alternatively, create custom methods for single logout.
getCustomMetadataType()
Signature
public String getCustomMetadataType()
Usage
The getCustomMetatadaType() method returns only custom metadata type names. It does not return custom metadata record names. As of API version 39.0, use this method when extending Auth.AuthProviderPluginClass to create a custom external authentication provider.
getUserInfo(authProviderConfiguration, response)
Signature
public Auth.UserData getUserInfo(Map<String,String> authProviderConfiguration, Auth.AuthProviderTokenResponse response)
Parameters
-
authProviderConfiguration:
The configuration for the custom authentication provider. When you create a custom metadata type in Salesforce, the configuration populates it with the custom metadata type default values. Or you can set the configuration with values that 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.
Usage
As of API version 39.0, use this method when extending Auth.AuthProviderPluginClass to create a custom authentication provider.
handleCallback(authProviderConfiguration, callbackState)
Signature
public Auth.AuthProviderTokenResponse handleCallback(Map<String,String> authProviderConfiguration, Auth.AuthProviderCallbackState callbackState)
Parameters
-
authProviderConfiguration:
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.
Usage
As of API version 39.0, use this method when extending Auth.AuthProviderPluginClass to create a custom authentication provider.
initiate(authProviderConfiguration, stateToPropagate)
Signature
public System.PageReference initiate(Map<String,String> authProviderConfiguration, String stateToPropagate)
Parameters
-
authProviderConfiguration:
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.
Usage
As of API version 39.0, use this method when extending Auth.AuthProviderPluginClass to create a custom authentication provider.
refresh(authProviderConfiguration, refreshToken)
Signature
public Auth.OAuthRefreshResult refresh(Map<String,String> authProviderConfiguration, String refreshToken)
Parameters
-
authProviderConfiguration:
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.
-
refreshToken:
Type: String
The refresh token for the user who is logged in.
Return Value
Type: Auth.OAuthRefreshResult
Returns the new access token, or an error message if an error occurs.
Usage
A successful request returns a Auth.OAuthRefreshResult with the access token and refresh token in the response. If you receive an error, make sure that you set the error string to the error message. A NULL error string indicates no error.
The refresh method works only with named credentials; it doesn’t respect the standard OAuth refresh flow. The refresh method with named credentials works only if the earlier request returns a 401.
AuthProviderPluginClass Code Example
The following example demonstrates how to implement a custom Auth. provider plug-in using the abstract class, Auth.AuthProviderPluginClass.
1public with sharing class Concur extends Auth.AuthProviderPluginClass {
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 public String getCustomMetadataType() {
26 return customMetadataTypeApiName;
27 }
28
29 public PageReference initiate(Map<string,string>
30 authProviderConfiguration, String stateToPropagate) {
31 authUrl = authProviderConfiguration.get('Auth_Url__c');
32 key = authProviderConfiguration.get('Key__c');
33
34 // Here the developer can build up a request of some sort.
35 // Ultimately, they 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 public Auth.AuthProviderTokenResponse handleCallback(Map<string,string>
41 authProviderConfiguration, Auth.AuthProviderCallbackState state ) {
42 // Here, the developer will get the callback with actual protocol.
43 // Their responsibility is to return a new object called
44 // AuthProviderTokenResponse.
45 // This will contain an optional accessToken and refreshToken
46 key = authProviderConfiguration.get('Key__c');
47 secret = authProviderConfiguration.get('Secret__c');
48 accessTokenUrl = authProviderConfiguration.get('Access_Token_Url__c');
49
50 Map<String,String> queryParams = state.queryParameters;
51 String code = queryParams.get('code');
52 String sfdcState = queryParams.get('state');
53
54 HttpRequest req = new HttpRequest();
55 String url = accessTokenUrl+'?code=' + code + '&client_id=' + key +
56 '&client_secret=' + secret;
57 req.setEndpoint(url);
58 req.setHeader('Content-Type','application/xml');
59 req.setMethod('GET');
60
61 Http http = new Http();
62 HTTPResponse res = http.send(req);
63 String responseBody = res.getBody();
64 String token = getTokenValueFromResponse(responseBody, 'Token', null);
65
66 return new Auth.AuthProviderTokenResponse('Concur', token,
67 'refreshToken', sfdcState);
68 }
69
70 public Auth.UserData getUserInfo(Map<string,string>
71 authProviderConfiguration,
72 Auth.AuthProviderTokenResponse response) {
73 //Here the developer is responsible for constructing an
74 //Auth.UserData object
75 String token = response.oauthToken;
76 HttpRequest req = new HttpRequest();
77 userAPIUrl = authProviderConfiguration.get('API_User_Url__c');
78 userAPIVersionUrl = authProviderConfiguration.get
79 ('API_User_Version_Url__c');
80 req.setHeader('Authorization', 'OAuth ' + token);
81 req.setEndpoint(userAPIUrl);
82 req.setHeader('Content-Type','application/xml');
83 req.setMethod('GET');
84
85 Http http = new Http();
86 HTTPResponse res = http.send(req);
87 String responseBody = res.getBody();
88 String id = getTokenValueFromResponse(responseBody,
89 'LoginId',userAPIVersionUrl);
90 String fname = getTokenValueFromResponse(responseBody,
91 'FirstName', userAPIVersionUrl);
92 String lname = getTokenValueFromResponse(responseBody,
93 'LastName', userAPIVersionUrl);
94 String flname = fname + ' ' + lname;
95 String uname = getTokenValueFromResponse(responseBody,
96 'EmailAddress', userAPIVersionUrl);
97 String locale = getTokenValueFromResponse(responseBody,
98 'LocaleName', userAPIVersionUrl);
99 Map<String,String> provMap = new Map<String,String>();
100 provMap.put('what1', 'noidea1');
101 provMap.put('what2', 'noidea2');
102 return new Auth.UserData(id, fname, lname, flname,
103 uname, 'what', locale, null, 'Concur', null, provMap);
104 }
105
106 private String getTokenValueFromResponse(String response,
107 String token, String ns) {
108 Dom.Document docx = new Dom.Document();
109 docx.load(response);
110 String ret = null;
111
112 dom.XmlNode xroot = docx.getrootelement() ;
113 if(xroot != null){
114 ret = xroot.getChildElement(token, ns).getText();
115 }
116 return ret;
117 }
118
119}Sample Test Classes
The following example contains test classes for the Concur class.
1@IsTest
2public with sharing 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 Map<String,String> authProviderConfiguration = new Map<String,String>();
33 authProviderConfiguration.put('Key__c', KEY);
34 authProviderConfiguration.put('Auth_Url__c', AUTH_URL);
35 authProviderConfiguration.put('Secret__c', SECRET);
36 authProviderConfiguration.put('Access_Token_Url__c', ACCESS_TOKEN_URL);
37 authProviderConfiguration.put('API_User_Url__c',API_USER_URL);
38 authProviderConfiguration.put('API_User_Version_Url__c',
39 API_USER_VERSION_URL);
40 authProviderConfiguration.put('Redirect_Url__c',REDIRECT_URL);
41 return authProviderConfiguration;
42 }
43
44 @IsTest
45 static void testInitiateMethod() {
46 String stateToPropogate = 'mocktestState';
47 Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
48 Concur concurCls = new Concur();
49 concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url__c');
50 PageReference expectedUrl = new PageReference(authProviderConfiguration.get('Auth_Url__c') + '?client_id='+
51 authProviderConfiguration.get('Key__c') +'&scope=USER,EXPRPT,LIST&redirect_uri='+
52 authProviderConfiguration.get('Redirect_Url__c') + '&state=' +
53 STATE_TO_PROPOGATE);
54 PageReference actualUrl = concurCls.initiate(authProviderConfiguration, STATE_TO_PROPOGATE);
55 Assert.areEqual(expectedUrl.getUrl(), actualUrl.getUrl());
56 }
57
58 @IsTest
59 static void testHandleCallback() {
60 Map<String,String> authProviderConfiguration =
61 setupAuthProviderConfig();
62 Concur concurCls = new Concur();
63 concurCls.redirectUrl = authProviderConfiguration.get
64 ('Redirect_Url_c');
65
66 Test.setMock(HttpCalloutMock.class, new
67 ConcurMockHttpResponseGenerator());
68
69 Map<String,String> queryParams = new Map<String,String>();
70 queryParams.put('code','code');
71 queryParams.put('state',authProviderConfiguration.get('State_c'));
72 Auth.AuthProviderCallbackState cbState =
73 new Auth.AuthProviderCallbackState(null,null,queryParams);
74 Auth.AuthProviderTokenResponse actualAuthProvResponse =
75 concurCls.handleCallback(authProviderConfiguration, cbState);
76 Auth.AuthProviderTokenResponse expectedAuthProvResponse =
77 new Auth.AuthProviderTokenResponse(
78 'Concur', OAUTH_TOKEN, REFRESH_TOKEN, null);
79
80 Assert.areEqual(expectedAuthProvResponse.provider,
81 actualAuthProvResponse.provider);
82 Assert.areEqual(expectedAuthProvResponse.oauthToken,
83 actualAuthProvResponse.oauthToken);
84 Assert.areEqual(expectedAuthProvResponse.oauthSecretOrRefreshToken,
85 actualAuthProvResponse.oauthSecretOrRefreshToken);
86 Assert.areEqual(expectedAuthProvResponse.state,
87 actualAuthProvResponse.state);
88 }
89
90 @IsTest
91 static void testGetUserInfo() {
92 Map<String,String> authProviderConfiguration =
93 setupAuthProviderConfig();
94 Concur concurCls = new Concur();
95
96 Test.setMock(HttpCalloutMock.class, new
97 ConcurMockHttpResponseGenerator());
98
99 Auth.AuthProviderTokenResponse response =
100 new Auth.AuthProviderTokenResponse(
101 PROVIDER, OAUTH_TOKEN ,'sampleOauthSecret', STATE);
102 Auth.UserData actualUserData = concurCls.getUserInfo(
103 authProviderConfiguration, response) ;
104
105 Map<String,String> provMap = new Map<String,String>();
106 provMap.put('key1', 'value1');
107 provMap.put('key2', 'value2');
108
109 Auth.UserData expectedUserData = new Auth.UserData(LOGIN_ID,
110 FIRST_NAME, LAST_NAME, FULL_NAME, EMAIL_ADDRESS,
111 null, LOCALE_NAME, null, PROVIDER, null, provMap);
112
113 Assert.isNotNull(expectedUserData);
114 Assert.areEqual(expectedUserData.firstName,
115 actualUserData.firstName);
116 Assert.areEqual(expectedUserData.lastName,
117 actualUserData.lastName);
118 Assert.areEqual(expectedUserData.fullName,
119 actualUserData.fullName);
120 Assert.areEqual(expectedUserData.email,
121 actualUserData.email);
122 Assert.areEqual(expectedUserData.username,
123 actualUserData.username);
124 Assert.areEqual(expectedUserData.locale,
125 actualUserData.locale);
126 Assert.areEqual(expectedUserData.provider,
127 actualUserData.provider);
128 Assert.areEqual(expectedUserData.siteLoginUrl,
129 actualUserData.siteLoginUrl);
130 }
131
132 // Implement a mock http response generator for Concur.
133 public with sharing class ConcurMockHttpResponseGenerator implements HttpCalloutMock {
134 public HTTPResponse respond(HTTPRequest req) {
135 String namespace = API_USER_VERSION_URL;
136 String prefix = 'mockPrefix';
137
138 Dom.Document doc = new Dom.Document();
139 Dom.XmlNode xmlNode = doc.createRootElement(
140 'mockRootNodeName', namespace, prefix);
141 xmlNode.addChildElement('LoginId', namespace, prefix)
142 .addTextNode(LOGIN_ID);
143 xmlNode.addChildElement('FirstName', namespace, prefix)
144 .addTextNode(FIRST_NAME);
145 xmlNode.addChildElement('LastName', namespace, prefix)
146 .addTextNode(LAST_NAME);
147 xmlNode.addChildElement('EmailAddress', namespace, prefix)
148 .addTextNode(EMAIL_ADDRESS);
149 xmlNode.addChildElement('LocaleName', namespace, prefix)
150 .addTextNode(LOCALE_NAME);
151 xmlNode.addChildElement('Token', null, null)
152 .addTextNode(OAUTH_TOKEN);
153 System.debug(doc.toXmlString());
154 // Create a fake response
155 HttpResponse res = new HttpResponse();
156 res.setHeader('Content-Type', 'application/xml');
157 res.setBody(doc.toXmlString());
158 res.setStatusCode(200);
159 return res;
160 }
161 }
162}