AuthProviderPlugin インターフェース
名前空間
使用方法
シングルサインオンのカスタム認証プロバイダを作成するには、Auth.AuthProviderPlugin を実装するクラスを作成します。このクラスにより、認証プロバイダのカスタム設定を保存し、ユーザが外部サービスプロバイダのログイン情報を使用して Salesforce にログインしたときに認証プロトコルを処理できます。Salesforce で、このインターフェースを実装するクラスは、[設定] の [認証プロバイダ] にある [プロバイダタイプ] ドロップダウンリストに表示されます。クラスを実行するよう指定するユーザに「アプリケーションのカスタマイズ」権限と「認証プロバイダの管理」権限があることを確認します。
AuthProviderPlugin メソッド
AuthProviderPlugin のメソッドは次のとおりです。
getCustomMetadataType()
署名
public String getCustomMetadataType()
使用方法
getCustomMetatadaType() メソッドは、カスタムメタデータ型の名前のみを返します。カスタムメタデータレコード名は返しません。
getUserInfo(authProviderConfiguration, response)
署名
public Auth.UserData getUserInfo(Map<String,String> authProviderConfiguration, Auth.AuthProviderTokenResponse response)
パラメータ
- authProviderConfiguration
- 型: Map<String, String>
- カスタム認証プロバイダの設定。Salesforce にカスタムメタデータ型を作成すると、設定にカスタムメタデータ型のデフォルト値が入力されます。または、[設定] の [承認プロバイダ] でカスタムプロバイダを作成したときに入力した値を設定で指定することもできます。
- response
- 型: Auth.AuthProviderTokenResponse
-
OAuth アクセストークン、OAuth の秘密または更新トークン、および現在のユーザを認証するために認証プロバイダによって指定された状態。
handleCallback(authProviderConfiguration, callbackState)
署名
public Auth.AuthProviderTokenResponse handleCallback(Map<String,String> authProviderConfiguration, Auth.AuthProviderCallbackState callbackState)
パラメータ
- authProviderConfiguration
- 型: Map<StringString>
- カスタム認証プロバイダの設定。Salesforce にカスタムメタデータ型を作成すると、設定にカスタムメタデータ型のデフォルト値が入力されます。または、[設定] の [承認プロバイダ] でカスタムプロバイダを作成したときに入力した値を設定で指定することもできます。
- callbackState
- 型: Auth.AuthProviderCallbackState
- 認証要求の HTTP ヘッダー、本文、および queryParams が含まれるクラス。
AuthProviderPlugin の実装例
これは、Auth.AuthProviderPlugin インターフェースの実装例です。
次の例では、実装をテストしています。
1@TestClass
2 // Add test class
3 global class Concur implements Auth.AuthProviderPlugin {
4
5 // Use this URL for the endpoint that the authentication provider calls back to for configuration
6 public String redirectUrl;
7 private String key;
8 private String secret;
9
10 // Application redirection to the Concur website for 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
26 global String getCustomMetadataType() {
27 return customMetadataTypeApiName;
28 }
29
30 global PageReference initiate(Map<string,string> 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> authProviderConfiguration, Auth.AuthProviderCallbackState state )
41 {
42 //Here, the developer will get the callback with actual protocol.
43 //Their responsibility is to return a new object called AuthProviderToken
44 //This will contain an optional accessToken and refreshToken
45 key = authProviderConfiguration.get('Key__c');
46 secret = authProviderConfiguration.get('Secret__c');
47 accessTokenUrl = authProviderConfiguration.get('Access_Token_Url__c');
48
49 Map<String,String> queryParams = state.queryParameters;
50 String code = queryParams.get('code');
51 String sfdcState = queryParams.get('state');
52
53 HttpRequest req = new HttpRequest();
54 String url = accessTokenUrl+'?code=' + code + '&client_id=' + key + '&client_secret=' + secret;
55 req.setEndpoint(url);
56 req.setHeader('Content-Type','application/xml');
57 req.setMethod('GET');
58
59 Http http = new Http();
60 HTTPResponse res = http.send(req);
61 String responseBody = res.getBody();
62 String token = getTokenValueFromResponse(responseBody, 'Token', null);
63
64 return new Auth.AuthProviderTokenResponse('Concur', token, 'refreshToken', sfdcState);
65 }
66
67
68 global Auth.UserData getUserInfo(Map<string,string> authProviderConfiguration, Auth.AuthProviderTokenResponse response)
69 {
70 //Here the developer is responsible for constructing an Auth.UserData object
71 String token = response.oauthToken;
72 HttpRequest req = new HttpRequest();
73 userAPIUrl = authProviderConfiguration.get('API_User_Url__c');
74 userAPIVersionUrl = authProviderConfiguration.get('API_User_Version_Url__c');
75 req.setHeader('Authorization', 'OAuth ' + token);
76 req.setEndpoint(userAPIUrl);
77 req.setHeader('Content-Type','application/xml');
78 req.setMethod('GET');
79
80 Http http = new Http();
81 HTTPResponse res = http.send(req);
82 String responseBody = res.getBody();
83 String id = getTokenValueFromResponse(responseBody, 'LoginId',userAPIVersionUrl);
84 String fname = getTokenValueFromResponse(responseBody, 'FirstName', userAPIVersionUrl);
85 String lname = getTokenValueFromResponse(responseBody, 'LastName', userAPIVersionUrl);
86 String flname = fname + ' ' + lname;
87 String uname = getTokenValueFromResponse(responseBody, 'EmailAddress', userAPIVersionUrl);
88 String locale = getTokenValueFromResponse(responseBody, 'LocaleName', userAPIVersionUrl);
89 Map<String,String> provMap = new Map<String,String>();
90 provMap.put('what1', 'noidea1');
91 provMap.put('what2', 'noidea2');
92 return new Auth.UserData(id, fname, lname, flname, uname,
93 'what', locale, null, 'Concur', null, provMap);
94 }
95
96 private String getTokenValueFromResponse(String response, String token, String ns)
97 {
98 Dom.Document docx = new Dom.Document();
99 docx.load(response);
100 String ret = null;
101
102 dom.XmlNode xroot = docx.getrootelement() ;
103 if(xroot != null){
104 ret = xroot.getChildElement(token, ns).getText();
105 }
106 return ret;
107 }
108
109 }サンプルテストクラス
次の例には、Concur のテストクラスが含まれています。
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 = 'http://localhost/services/authcallback/orgId/Concur';
16 private static final String KEY = 'testKey';
17 private static final String SECRET = 'testSecret';
18 private static final String STATE_TO_PROPOGATE = 'testState';
19 private static final String ACCESS_TOKEN_URL = 'http://www.dummyhost.com/accessTokenUri';
20 private static final String API_USER_VERSION_URL = 'http://www.dummyhost.com/user/20/1';
21 private static final String AUTH_URL = 'http://www.dummy.com/authurl';
22 private static final String API_USER_URL = 'www.concursolutions.com/user/api';
23
24 // In the real world scenario, the key and value would be read from the (custom fields in) custom metadata type record
25 private static Map<String,String> setupAuthProviderConfig ()
26 {
27 Map<String,String> authProviderConfiguration = new Map<String,String>();
28 authProviderConfiguration.put('Key__c', KEY);
29 authProviderConfiguration.put('Auth_Url__c', AUTH_URL);
30 authProviderConfiguration.put('Secret__c', SECRET);
31 authProviderConfiguration.put('Access_Token_Url__c', ACCESS_TOKEN_URL);
32 authProviderConfiguration.put('API_User_Url__c',API_USER_URL);
33 authProviderConfiguration.put('API_User_Version_Url__c',API_USER_VERSION_URL);
34 authProviderConfiguration.put('Redirect_Url__c',REDIRECT_URL);
35 return authProviderConfiguration;
36
37 }
38
39 static testMethod void testInitiateMethod()
40 {
41 String stateToPropogate = 'mocktestState';
42 Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
43 Concur concurCls = new Concur();
44 concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url__c');
45
46 PageReference expectedUrl = new PageReference(authProviderConfiguration.get('Auth_Url__c') + '?client_id='+
47 authProviderConfiguration.get('Key__c') +'&scope=USER,EXPRPT,LIST&redirect_uri='+
48 authProviderConfiguration.get('Redirect_Url__c') + '&state=' +
49 STATE_TO_PROPOGATE);
50 PageReference actualUrl = concurCls.initiate(authProviderConfiguration, STATE_TO_PROPOGATE);
51 System.assertEquals(expectedUrl.getUrl(), actualUrl.getUrl());
52 }
53
54 static testMethod void testHandleCallback()
55 {
56 Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
57 Concur concurCls = new Concur();
58 concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url_c');
59
60 Test.setMock(HttpCalloutMock.class, new ConcurMockHttpResponseGenerator());
61
62 Map<String,String> queryParams = new Map<String,String>();
63 queryParams.put('code','code');
64 queryParams.put('state',authProviderConfiguration.get('State_c'));
65 Auth.AuthProviderCallbackState cbState = new Auth.AuthProviderCallbackState(null,null,queryParams);
66 Auth.AuthProviderTokenResponse actualAuthProvResponse = concurCls.handleCallback(authProviderConfiguration, cbState);
67 Auth.AuthProviderTokenResponse expectedAuthProvResponse = new Auth.AuthProviderTokenResponse('Concur', OAUTH_TOKEN, REFRESH_TOKEN, null);
68
69 System.assertEquals(expectedAuthProvResponse.provider, actualAuthProvResponse.provider);
70 System.assertEquals(expectedAuthProvResponse.oauthToken, actualAuthProvResponse.oauthToken);
71 System.assertEquals(expectedAuthProvResponse.oauthSecretOrRefreshToken, actualAuthProvResponse.oauthSecretOrRefreshToken);
72 System.assertEquals(expectedAuthProvResponse.state, actualAuthProvResponse.state);
73
74
75 }
76
77
78 static testMethod void testGetUserInfo()
79 {
80 Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
81 Concur concurCls = new Concur();
82
83 Test.setMock(HttpCalloutMock.class, new ConcurMockHttpResponseGenerator());
84
85 Auth.AuthProviderTokenResponse response = new Auth.AuthProviderTokenResponse(PROVIDER, OAUTH_TOKEN ,'sampleOauthSecret', STATE);
86 Auth.UserData actualUserData = concurCls.getUserInfo(authProviderConfiguration, response) ;
87
88 Map<String,String> provMap = new Map<String,String>();
89 provMap.put('key1', 'value1');
90 provMap.put('key2', 'value2');
91
92 Auth.UserData expectedUserData = new Auth.UserData(LOGIN_ID, FIRST_NAME, LAST_NAME, FULL_NAME, EMAIL_ADDRESS,
93 null, LOCALE_NAME, null, PROVIDER, null, provMap);
94
95 System.assertNotEquals(expectedUserData,null);
96 System.assertEquals(expectedUserData.firstName, actualUserData.firstName);
97 System.assertEquals(expectedUserData.lastName, actualUserData.lastName);
98 System.assertEquals(expectedUserData.fullName, actualUserData.fullName);
99 System.assertEquals(expectedUserData.email, actualUserData.email);
100 System.assertEquals(expectedUserData.username, actualUserData.username);
101 System.assertEquals(expectedUserData.locale, actualUserData.locale);
102 System.assertEquals(expectedUserData.provider, actualUserData.provider);
103 System.assertEquals(expectedUserData.siteLoginUrl, actualUserData.siteLoginUrl);
104 }
105
106
107 // Implement a mock http response generator for concur
108 public class ConcurMockHttpResponseGenerator implements HttpCalloutMock
109 {
110 public HTTPResponse respond(HTTPRequest req)
111 {
112 String namespace = API_USER_VERSION_URL;
113 String prefix = 'mockPrefix';
114
115 Dom.Document doc = new Dom.Document();
116 Dom.XmlNode xmlNode = doc.createRootElement('mockRootNodeName', namespace, prefix);
117 xmlNode.addChildElement('LoginId', namespace, prefix).addTextNode(LOGIN_ID);
118 xmlNode.addChildElement('FirstName', namespace, prefix).addTextNode(FIRST_NAME);
119 xmlNode.addChildElement('LastName', namespace, prefix).addTextNode(LAST_NAME);
120 xmlNode.addChildElement('EmailAddress', namespace, prefix).addTextNode(EMAIL_ADDRESS);
121 xmlNode.addChildElement('LocaleName', namespace, prefix).addTextNode(LOCALE_NAME);
122 xmlNode.addChildElement('Token', null, null).addTextNode(OAUTH_TOKEN);
123 System.debug(doc.toXmlString());
124 // Create a fake response
125 HttpResponse res = new HttpResponse();
126 res.setHeader('Content-Type', 'application/xml');
127 res.setBody(doc.toXmlString());
128 res.setStatusCode(200);
129 return res;
130 }
131
132 }
133 }