SalesforceのREST APIを叩く

前書き

JWTベアラーフローないの許してね(運用フェーズでの証明書更新が死ぬほど面倒なので、Webサーバフローの方が好みなんどす(´・ω・`)ノ

JWTベアラーフローはテラスカイさんの記事を見るのが吉

ユーザ名パスワードフロー(外部ソフトから叩く)

①接続アプリケーションを作成する

接続アプリケーション名:~ConnectApp
API参照名:~ConnectApp
取引先責任者メール:自分のメアド
OAuth認証の有効化:ON
コールバックURL:使われていないポート番号(例:https://localhost:8888)
選択したOAuth範囲:フルアクセス(※実際はもっとタイトにしてね)

②プロファイルのAPIを開放するorセキュリティトークンを発行する

③下記の情報をセットしてPOSTする

メソッド:
URL(本番環境の場合):https://login.salesforce.com/services/oauth2/token
URL(Sandboxの場合): https://test.salesforce.com/services/oauth2/token
Body
 └client_id:接続アプリケーションの「コンシューマ鍵」
 └client_secret: 接続アプリケーションの 「コンシューマの秘密」
 └username:ユーザ名
 └password(IP開放済の場合):パスワード
 └password(IP未開放の場合):パスワードセキュリティトークン
 └grant_type:password

curlから叩く場合↓

curl -X POST -d client_id=コンシューマ鍵 -d client_secret=コンシューマの秘密 -d "username=ユーザ名" -d "password=パスワード" -d "grant_type=password" https://salesforce.com/services/oauth2/token

Webサーバフロー(外部ソフトから叩く)

①接続アプリケーションを作成する

接続アプリケーション名:~ConnectApp
API参照名:~ConnectApp
取引先責任者メール:自分のメアド
OAuth認証の有効化:ON
コールバックURL:使われていないポート番号(例:https://localhost:8888)
選択したOAuth範囲:フルアクセス(※実際はもっとタイトにしてね)

②下記URLにアクセス&ログインして認証コードを取得する(※認証コードはリダイレクト後のURLの"code="以降に記載あり

・本番環境の場合

https://salesforce.com/services/oauth2/authorize?response_type=code&client_id=コンシューマ鍵&redirect_uri=リダイレクトURL

URL: https://salesforce.com/services/oauth2/authorize
response_type:code
client_id: 接続アプリケーションの「コンシューマ鍵」
redirect_uri:接続アプリケーションの 「リダイレクトURL」

・Sandboxの場合(URLが異なっていることに注意)

https://test.salesforce.com/services/oauth2/authorize?response_type=code&client_id=コンシューマ鍵&redirect_uri=リダイレクトURL

③下記の情報をセットしてPOSTする

メソッド:
URL(本番環境の場合):https://login.salesforce.com/services/oauth2/token
URL(Sandboxの場合): https://test.salesforce.com/services/oauth2/token
Body
 └code:②で取得した認可コード(※末尾の%3D%3Dを==に変換すること)
 └client_id:接続アプリケーションの「コンシューマ鍵」
 └client_secret: 接続アプリケーションの 「コンシューマの秘密」
 └redirect_uri: 接続アプリケーションの 「リダイレクトURL」
 └grant_type:authorization_code

curlから叩く場合↓

curl -X POST -d code=認可コード -d client_id=コンシューマ鍵 -d client_secret=コンシューマの秘密 -d redirect_uri=リダイレクトURL -d grant_type=authorization_code https://salesforce.com/services/oauth2/token

ユーザ名パスワードフロー(操作ユーザとしてApex Onlyで叩く)

①:下記のようなコードを利用してエンドポイントを叩く。

String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(baseUrl+'/services/data/v52.0/~');
request.setMethod('POST');
request.setHeader('Content-Type','application/json');
request.setHeader('Authorization', 'OAuth ' + Userinfo.getsessionID());
request.setHeader('Authorization', 'Bearer ' + Userinfo.getsessionID());
request.setBody(自分で置き換えてね);
HttpResponse response = http.send(request);

ユーザ名パスワードフロー(別のユーザとしてApex Onlyで叩く)

①接続アプリケーションを作成する

接続アプリケーション名:~ConnectApp
API参照名:~ConnectApp
取引先責任者メール:自分のメアド
OAuth認証の有効化:ON
コールバックURL:使われていないポート番号(例:https://localhost:8888)
選択したOAuth範囲:フルアクセス(※実際はもっとタイトにしてね)

②プロファイルのAPIを開放する(※これによりセキュリティトークンの発行を不要にする)

③カスタム設定にユーザ名とパスワードを登録する(※カスタムメタデータでもOK)

④CalloutクラスからHTTPリクエストを投げる

④-①:アクセストークンを取得する

public static String getAccessToken(){
    String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
    String CLIENT_ID = 'コンシューマ鍵';
    String CLIENT_SECRET = 'コンシューマの秘密';
    test_customsetting__c testcs = test_customsetting__c.getOrgDefaults();
    String USER_ID = testcs.username__c;
    String PASSWORD = testcs.password__c;
    String requestBody1 = 'grant_type=password'+'&client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&username='+USER_ID+'&password='+PASSWORD;
    String AccessToken;
    
    Http http1 = new Http();
    HttpRequest request1 = new HttpRequest();
    request1.setEndpoint(baseUrl+'/services/oauth2/token');
    request1.setMethod('POST');
    request1.setHeader('Content-Type','application/x-www-form-urlencoded');
    request1.setBody(requestBody1);
    HttpResponse response1 = http1.send(request1);
    if (response1.getStatusCode() == 200 || response1.getStatusCode() == 201) {
        System.debug('API linkage was successful: ' +
                     response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
        Map<String, Object> result1 = (Map<String, Object>)JSON.deserializeUntyped(response1.getBody());
        accessToken = (String)result1.get('access_token'); 
        
    } else {
        System.debug('The status code returned was not expected: ' +
                     response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
    }
    return accessToken;        
}

④-②:アクセストークンを利用して、エンドポイントを叩く

public static Id createSomething(String returnedAccessToken, String jsonS){
    String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
    Id createdOrderSummaryId;
    
    Http http2 = new Http();
    HttpRequest request2 = new HttpRequest();
    request2.setEndpoint(baseUrl+'/services/data/v52.0/~');
    request2.setMethod('POST');
    request2.setHeader('Content-Type','application/json');
    request2.setHeader('Authorization', 'Bearer ' + returnedAccessToken);
    request2.setBody(jsonS);
    HttpResponse response2 = http2.send(request2);
    system.debug(response2);
    if (response2.getStatusCode() == 200 || response2.getStatusCode() == 201) {
        System.debug('API linkage was successful: ' +
                     response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
        system.debug(response2.getBody());
        Map<String, Object> result2 = (Map<String, Object>)JSON.deserializeUntyped(response2.getBody());
        createdRecordId = Id.valueOf((String)result2.get('~')); 
    } else {
        System.debug('The status code returned was not expected: ' +
                     response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
    }
    return createdRecordId;
}

フルコードサンプル

public class CallRestApi {
    @future(callout=true)
    public static void makeApiPost(String jsonS){
        String returnedAccessToken = getAccessToken();
        Id returnedId = createSomething(returnedAccessToken,jsonS);
        }        
    }
    
    public static String getAccessToken(){
        String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
        String CLIENT_ID = 'コンシューマ鍵';
        String CLIENT_SECRET = 'コンシューマの秘密';
        test_customsetting__c testcs = test_customsetting__c.getOrgDefaults();
        String USER_ID = testcs.username__c;
        String PASSWORD = testcs.password__c;
        String requestBody1 = 'grant_type=password'+'&client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&username='+USER_ID+'&password='+PASSWORD;
        String AccessToken;
        
        Http http1 = new Http();
        HttpRequest request1 = new HttpRequest();
        request1.setEndpoint(baseUrl+'/services/oauth2/token');
        request1.setMethod('POST');
        request1.setHeader('Content-Type','application/x-www-form-urlencoded');
        request1.setBody(requestBody1);
        HttpResponse response1 = http1.send(request1);
        if (response1.getStatusCode() == 200 || response1.getStatusCode() == 201) {
            System.debug('API linkage was successful: ' +
                         response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
            Map<String, Object> result1 = (Map<String, Object>)JSON.deserializeUntyped(response1.getBody());
            accessToken = (String)result1.get('access_token'); 
            
        } else {
            System.debug('The status code returned was not expected: ' +
                         response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
        }
        return accessToken;        
    }
    
    public static Id createSomething(String returnedAccessToken, String jsonS){
        String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
        Id createdOrderSummaryId;
        
        Http http2 = new Http();
        HttpRequest request2 = new HttpRequest();
        request2.setEndpoint(baseUrl+'/services/data/v52.0/~');
        request2.setMethod('POST');
        request2.setHeader('Content-Type','application/json');
        request2.setHeader('Authorization', 'Bearer ' + returnedAccessToken);
        request2.setBody(jsonS);
        HttpResponse response2 = http2.send(request2);
        system.debug(response2);
        if (response2.getStatusCode() == 200 || response2.getStatusCode() == 201) {
            System.debug('API linkage was successful: ' +
                         response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
            system.debug(response2.getBody());
            Map<String, Object> result2 = (Map<String, Object>)JSON.deserializeUntyped(response2.getBody());
            createdRecordId = Id.valueOf((String)result2.get('~')); 
        } else {
            System.debug('The status code returned was not expected: ' +
                         response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
        }
        return createdRecordId;
    }
    
    
    
}

Webサーバフロー(Apex Onlyで叩く)

認可コードの自動取得ができないため、Apex内部で完結させることができません。

↓なぜできないかを把握するためのサンプルコード

String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
String CLIENT_ID = 'コンシューマ鍵';
String CLIENT_SECRET = 'コンシューマの秘密';
String REDIRET_URI = 'https://localhost:33333';
String OAUTH_CODE = '~';//ここをApex Onlyだと動的に取得することができない
String requestBody1 = 'grant_type=authorization_code&code='+OAUTH_CODE+'&client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&redirect_uri='+REDIRET_URI;
String AccessToken = '';

Http http1 = new Http();
HttpRequest request1 = new HttpRequest();
request1.setEndpoint(baseUrl+'/services/oauth2/token');
request1.setMethod('POST');
request1.setHeader('Content-Type','application/x-www-form-urlencoded');
request1.setBody(requestBody1);
HttpResponse response1 = http1.send(request1);
if (response1.getStatusCode() == 200 || response1.getStatusCode() == 201) {
    System.debug('API linkage was successful: ' +
                    response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
    Map<String, Object> result1 = (Map<String, Object>)JSON.deserializeUntyped(response1.getBody());
    accessToken = (String)result1.get('access_token'); 
} else {
    System.debug('The status code returned was not expected: ' +
                    response1.getStatusCode() + ' ' + response1.getStatus()+ ' ' + response1.getBody());
}

Http http2 = new Http();
HttpRequest request2 = new HttpRequest();
request2.setEndpoint(baseUrl+'/services/data/v52.0/~');
request2.setMethod('POST');
request2.setHeader('Content-Type','application/json');
request2.setHeader('Authorization', 'Bearer ' + accessToken);
request2.setBody(自分で置き換えてね);
HttpResponse response2 = http2.send(request2);
if (response2.getStatusCode() == 200 || response2.getStatusCode() == 201) {
    System.debug('API linkage was successful: ' +
                    response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
} else {
    System.debug('The status code returned was not expected: ' +
                    response2.getStatusCode() + ' ' + response2.getStatus()+ ' ' + response2.getBody());
}

Webサーバフロー(指定ログイン情報を利用する)

①接続アプリケーションを作成する

接続アプリケーション名:~ConnectApp
API参照名:~ConnectApp
取引先責任者メール:自分のメアド
OAuth認証の有効化:ON
コールバックURL:https://ドメイン名.salesforce.com/services/authcallback/認証プロバイダ名(=指定ログイン情報での初回認証時に返ってきたコールバックURL)
選択したOAuth範囲:フルアクセス(※実際はもっとタイトにしてね)

②認証プロパイダを設定する

プロバイダタイプ:Salesforce
名前:Salesforce ~ REST API
URL接尾辞:Salesforce_~_REST_API
コンシューマ鍵:接続アプリケーションの「コンシューマ鍵」
コンシューマの秘密:接続アプリケーションの 「コンシューマの秘密」
承認エンドポイントURL(本番環境の場合):https://login.salesforce.com/services/oauth2/authorize
承認エンドポイントURL(Sandbox環境の場合):https://test.salesforce.com/services/oauth2/authorize
トークンエンドポイントURL (本番環境の場合) :https://login.salesforce.com/services/oauth2/token
トークンエンドポイントURL (Sandbox環境の場合) :https://test.salesforce.com/services/oauth2/token

③指定ログイン情報を設定する

表示ラベル: Salesforce ~ REST API Credential
名前: Salesforce_~_REST_API_ Credential
URL: https://ドメイン名.salesforce.com/services/oauth2/authorize(=String.valueOf(Url.getOrgDomainUrl().toExternalForm());メソッドの戻り値)
※↑でURL no longer existsエラーが出る場合は、 https://ドメイン名.salesforce.comを試すとよい。
※tring.valueOf(Url.getOrgDomainUrl().toExternalForm());メソッドはOpen Execute Anonymous Windowなどで実行すると楽
ID種別:指定ユーザ
認証プロトコル:OAuth 2.0
認証プロバイダ: Salesforce ~ REST API (※②で作成した認証プロバイダ)
範囲:full refresh_token
認証ヘッダーを生成:TRUE
HTTPヘッダーの差し込み項目を許可:TRUE
HTTP本文の差し込み項目を許可:TRUE

④指定ログイン情報を利用してREST APIを叩く

String baseUrl = String.valueOf(Url.getOrgDomainUrl().toExternalForm());
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Salesforce_~_REST_API_ Credential/services/data/v52.0/~');
request.setMethod('POST');
request.setHeader('Content-Type','application/json');
request.setBody(自分で置き換えてね);
HttpResponse response = http.send(request);

最後に

個人的に、実務で使われる認証は以下の三種類だと思ってますノ

ユーザ名パスワードフロー(操作ユーザとしてApex Onlyで叩く)
Webサーバフロー(指定ログイン情報を利用する)
JWTベアラーフロー

ここに登場していないSAMLについては私自身そもそも何かすら理解できてないので誰か教えてください(´・ω・`)←

Salesforce

Posted by regardie