この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

Newer Version Available

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

カスタム認証プロバイダプラグインの作成

Apex を使用して、Salesforce へのシングルサインオン (SSO) 用に OAuth ベースのカスタム認証プロバイダプラグインを作成できます。

Salesforce は標準でシングルサインオン用の外部認証プロバイダとして Facebook や Google、LinkedIn などをサポートし、OpenID Connect プロトコルを実装するサービスプロバイダもサポートしています。Apex を使用してプラグインを作成することで、OAuth ベースの独自の認証プロバイダを追加できます。また、ユーザは、Salesforce 組織で Salesforce 以外のアプリケーションにすでに使用している SSO ログイン情報を使用できます。

Apex クラスを作成する前に、認証プロバイダのカスタムメタデータ型レコードを作成します。詳細は、「カスタム外部認証プロバイダの作成」を参照してください。

サンプルクラス

この例では、抽象クラス Auth.AuthProviderPluginClass を拡張して、Concur という外部認証プロバイダを設定します。次の順序で、サンプルクラスとサンプルテストクラスを作成します。
  1. Concur
  2. ConcurTestStaticVar
  3. MockHttpResponseGenerator
  4. ConcurTestClass
1global class Concur extends Auth.AuthProviderPluginClass {
2               
3               public String redirectUrl; // use this URL for the endpoint that the authentication provider calls back to for configuration
4               private String key;
5               private String secret;
6               private String authUrl;    // application redirection to the Concur website for authentication and authorization
7               private String accessTokenUrl; // uri to get the new access token from concur  using the GET verb
8               private String customMetadataTypeApiName; // api name for the custom metadata type created for this auth provider
9               private String userAPIUrl; // api url to access the user in concur
10               private String userAPIVersionUrl; // version of the user api url to access data from concur
11               
12       
13               global String getCustomMetadataType() {
14                   return customMetadataTypeApiName;
15               }
16       
17               global PageReference initiate(Map<string,string> authProviderConfiguration, String stateToPropagate) {
18                   authUrl = authProviderConfiguration.get('Auth_Url__c');
19                   key = authProviderConfiguration.get('Key__c');
20                   //Here the developer can build up a request of some sort
21                   //Ultimately they’ll return a URL where we will redirect the user
22                   String url = authUrl + '?client_id='+ key +'&scope=USER,EXPRPT,LIST&redirect_uri='+ redirectUrl + '&state=' + stateToPropagate;
23                   return new PageReference(url);
24                }
25        
26               global Auth.AuthProviderTokenResponse handleCallback(Map<string,string> authProviderConfiguration, Auth.AuthProviderCallbackState state ) {
27                   //Here, the developer will get the callback with actual protocol.  
28                   //Their responsibility is to return a new object called AuthProviderToken
29                   //This will contain an optional accessToken and refreshToken
30                   key = authProviderConfiguration.get('Key__c');
31                   secret = authProviderConfiguration.get('Secret__c');
32                   accessTokenUrl = authProviderConfiguration.get('Access_Token_Url__c');
33                   
34                   Map<String,String> queryParams = state.queryParameters;
35                   String code = queryParams.get('code');
36                   String sfdcState = queryParams.get('state');
37                   
38                   HttpRequest req = new HttpRequest();
39                   String url = accessTokenUrl+'?code=' + code + '&client_id=' + key + '&client_secret=' + secret;
40                   req.setEndpoint(url);
41                   req.setHeader('Content-Type','application/xml');
42                   req.setMethod('GET');
43                   
44                   Http http = new Http();
45                   HTTPResponse res = http.send(req);
46                   String responseBody = res.getBody();
47                   String accessToken = getTokenValueFromResponse(responseBody, 'AccessToken', null);
48                   //Parse access token value
49                   String refreshToken = getTokenValueFromResponse(responseBody, 'RefreshToken', null);
50                   //Parse refresh token value
51                   
52                   return new Auth.AuthProviderTokenResponse('Concur', accessToken, 'refreshToken', sfdcState);
53                   //don’t hard-code the refresh token value!
54                }
55    
56    
57                 global Auth.UserData  getUserInfo(Map<string,string> authProviderConfiguration, Auth.AuthProviderTokenResponse response) { 
58                     //Here the developer is responsible for constructing an Auth.UserData object
59                     String token = response.oauthToken;
60                     HttpRequest req = new HttpRequest();
61                     userAPIUrl = authProviderConfiguration.get('API_User_Url__c');
62                     userAPIVersionUrl = authProviderConfiguration.get('API_User_Version_Url__c');
63                     req.setHeader('Authorization', 'OAuth ' + token);
64                     req.setEndpoint(userAPIUrl);
65                     req.setHeader('Content-Type','application/xml');
66                     req.setMethod('GET');
67                     
68                     Http http = new Http();
69                     HTTPResponse res = http.send(req);
70                     String responseBody = res.getBody();
71                     String id = getTokenValueFromResponse(responseBody, 'LoginId',userAPIVersionUrl);
72                     String fname = getTokenValueFromResponse(responseBody, 'FirstName', userAPIVersionUrl);
73                     String lname = getTokenValueFromResponse(responseBody, 'LastName', userAPIVersionUrl);
74                     String flname = fname + ' ' + lname;
75                     String uname = getTokenValueFromResponse(responseBody, 'EmailAddress', userAPIVersionUrl);
76                     String locale = getTokenValueFromResponse(responseBody, 'LocaleName', userAPIVersionUrl);
77                     Map<String,String> provMap = new Map<String,String>();
78                     provMap.put('what1', 'noidea1');
79                     provMap.put('what2', 'noidea2');
80                     return new Auth.UserData(id, fname, lname, flname, uname,
81                          'what', locale, null, 'Concur', null, provMap);
82                }
83                
84                private String getTokenValueFromResponse(String response, String token, String ns) {
85                    Dom.Document docx = new Dom.Document();
86                    docx.load(response);
87                    String ret = null;
88
89                    dom.XmlNode xroot = docx.getrootelement() ;
90                    if(xroot != null){
91                       ret = xroot.getChildElement(token, ns).getText();
92                    }
93                    return ret;
94                }  
95    
96}

サンプルテストクラス

次の例には、Concur のテストクラスが含まれています。

1@IsTest
2public 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            Map<String,String> authProviderConfiguration = new Map<String,String>();
27           authProviderConfiguration.put('Key__c', KEY);
28           authProviderConfiguration.put('Auth_Url__c', AUTH_URL);
29           authProviderConfiguration.put('Secret__c', SECRET);
30           authProviderConfiguration.put('Access_Token_Url__c', ACCESS_TOKEN_URL);
31           authProviderConfiguration.put('API_User_Url__c',API_USER_URL);
32           authProviderConfiguration.put('API_User_Version_Url__c',API_USER_VERSION_URL);
33           authProviderConfiguration.put('Redirect_Url__c',REDIRECT_URL);
34           return authProviderConfiguration;
35          
36    }
37
38    static testMethod void testInitiateMethod() {
39           String stateToPropogate = 'mocktestState';
40           Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
41           Concur concurCls = new Concur();
42           concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url__c');
43           
44           PageReference expectedUrl =  new PageReference(authProviderConfiguration.get('Auth_Url__c') + '?client_id='+ 
45                                               authProviderConfiguration.get('Key__c') +'&scope=USER,EXPRPT,LIST&redirect_uri='+ 
46                                               authProviderConfiguration.get('Redirect_Url__c') + '&state=' + 
47                                               STATE_TO_PROPOGATE);
48           PageReference actualUrl = concurCls.initiate(authProviderConfiguration, STATE_TO_PROPOGATE);
49           System.assertEquals(expectedUrl.getUrl(), actualUrl.getUrl());
50       }
51    
52    static testMethod void testHandleCallback() {
53           Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
54           Concur concurCls = new Concur();
55           concurCls.redirectUrl = authProviderConfiguration.get('Redirect_Url_c');
56
57           Test.setMock(HttpCalloutMock.class, new ConcurMockHttpResponseGenerator());
58
59           Map<String,String> queryParams = new Map<String,String>();
60           queryParams.put('code','code');
61           queryParams.put('state',authProviderConfiguration.get('State_c'));
62           Auth.AuthProviderCallbackState cbState = new Auth.AuthProviderCallbackState(null,null,queryParams);
63           Auth.AuthProviderTokenResponse actualAuthProvResponse = concurCls.handleCallback(authProviderConfiguration, cbState);
64           Auth.AuthProviderTokenResponse expectedAuthProvResponse = new Auth.AuthProviderTokenResponse('Concur', OAUTH_TOKEN, REFRESH_TOKEN, null);
65           
66           System.assertEquals(expectedAuthProvResponse.provider, actualAuthProvResponse.provider);
67           System.assertEquals(expectedAuthProvResponse.oauthToken, actualAuthProvResponse.oauthToken);
68           System.assertEquals(expectedAuthProvResponse.oauthSecretOrRefreshToken, actualAuthProvResponse.oauthSecretOrRefreshToken);
69           System.assertEquals(expectedAuthProvResponse.state, actualAuthProvResponse.state);
70           
71
72    }
73    
74    
75    static testMethod void testGetUserInfo() {
76           Map<String,String> authProviderConfiguration = setupAuthProviderConfig();
77           Concur concurCls = new Concur();
78                      
79           Test.setMock(HttpCalloutMock.class, new ConcurMockHttpResponseGenerator());
80
81           Auth.AuthProviderTokenResponse response = new Auth.AuthProviderTokenResponse(PROVIDER, OAUTH_TOKEN ,'sampleOauthSecret', STATE);
82           Auth.UserData actualUserData = concurCls.getUserInfo(authProviderConfiguration, response) ;
83           
84           Map<String,String> provMap = new Map<String,String>();
85           provMap.put('key1', 'value1');
86           provMap.put('key2', 'value2');
87                     
88           Auth.UserData expectedUserData = new Auth.UserData(LOGIN_ID, FIRST_NAME, LAST_NAME, FULL_NAME, EMAIL_ADDRESS,
89                          null, LOCALE_NAME, null, PROVIDER, null, provMap);
90          
91           System.assertNotEquals(expectedUserData,null);
92           System.assertEquals(expectedUserData.firstName, actualUserData.firstName);
93           System.assertEquals(expectedUserData.lastName, actualUserData.lastName);
94           System.assertEquals(expectedUserData.fullName, actualUserData.fullName);
95           System.assertEquals(expectedUserData.email, actualUserData.email);
96           System.assertEquals(expectedUserData.username, actualUserData.username);
97           System.assertEquals(expectedUserData.locale, actualUserData.locale);
98           System.assertEquals(expectedUserData.provider, actualUserData.provider);
99           System.assertEquals(expectedUserData.siteLoginUrl, actualUserData.siteLoginUrl);
100    }
101    
102    
103   // implementing a mock http response generator for concur 
104   public  class ConcurMockHttpResponseGenerator implements HttpCalloutMock {
105     public HTTPResponse respond(HTTPRequest req) {
106        String namespace = API_USER_VERSION_URL;
107        String prefix = 'mockPrefix';
108
109        Dom.Document doc = new Dom.Document();
110        Dom.XmlNode xmlNode =  doc.createRootElement('mockRootNodeName', namespace, prefix);
111        xmlNode.addChildElement('LoginId', namespace, prefix).addTextNode(LOGIN_ID);
112        xmlNode.addChildElement('FirstName', namespace, prefix).addTextNode(FIRST_NAME);
113        xmlNode.addChildElement('LastName', namespace, prefix).addTextNode(LAST_NAME);
114        xmlNode.addChildElement('EmailAddress', namespace, prefix).addTextNode(EMAIL_ADDRESS);
115        xmlNode.addChildElement('LocaleName', namespace, prefix).addTextNode(LOCALE_NAME);            
116        xmlNode.addChildElement('AccessToken', null, null).addTextNode(OAUTH_TOKEN);
117        xmlNode.addChildElement('RefreshToken', null, null).addTextNode(REFRESH_TOKEN);
118        System.debug(doc.toXmlString());
119        // Create a fake response
120        HttpResponse res = new HttpResponse();
121        res.setHeader('Content-Type', 'application/xml');
122        res.setBody(doc.toXmlString());
123        res.setStatusCode(200);
124        return res;
125    }
126   
127  }
128}