Flux de porteur JWT OAuth 2.0 pour l'intégration serveur à serveur
Vous souhaitez parfois authentifier des serveurs pour accéder aux données sans consigner chaque fois interactivement les informations d'échange des serveurs. Dans ce cas, vous utilisez le flux de porteur du jeton Web JSON (JWT) OAuth 2.0. Ce flux utilise un certificat pour signer la requête JWT et ne nécessite pas l'interaction explicite de l'utilisateur. Ce flux nécessite toutefois l'approbation préalable de l'application cliente.
Éditions requises
| Disponible avec : Salesforce Classic et Lightning Experience |
| Disponible avec : Toutes les éditions |
Pour plus d'informations, consultez Nouvelles applications connectées ne peuvent plus être créées dans Spring ‘26.
Avec le flux de jeton du porteur JWT OAuth 2.0, le client publie un JWT au point de terminaison de jeton OAuth Salesforce. Salesforce traite le JWT, qui inclut une signature numérique, puis émet un jeton d'accès basé sur l'approbation préalable de l'application.
Cet exemple montre les étapes suivies par le flux.
- Un service de rapport commence par un rapport par lot généré de nuit.
- L'application connectée envoi le JWT au point de terminaison de jeton Salesforce. Le JWT permet de partager des informations d'identité et de sécurité entre des domaines de sécurité.
- Salesforce valide le JWT basé sur une signature en utilisant le certificat précédemment configuré et des paramètres supplémentaires.
- Considérant que le JWT est valide et que l'application connectée a déjà autorisée, Salesforce émet un jeton d'accès. L'approbation précédente est effectuée avec l'une des méthodes ci-dessous.
- Si votre stratégie d'application connectée est définie sur Les utilisateurs approuvés par l'administrateur sont pré-autorisés, vous pouvez utiliser des profils et des ensembles d'autorisations.
- Si votre stratégie d'application connectée est définie sur Tous les utilisateurs peuvent s'autoriser eux-mêmes, vous pouvez utiliser l'approbation de l'utilisateur et émettre un jeton d'actualisation. Cependant, il n'est pas nécessaire que le client dispose d'un jeton d'actualisation actuel ou stocké. Le client ne doit pas non plus transmettre de secret client au point de terminaison de jeton.
Remarque Pour les deux options, Salesforce émet un nouveau jeton d'accès uniquement lorsque le jeton d'accès d'origine inclut au moins une étendue standard différente de l'étenduerefresh_token. - L'application connectée utilise le jeton accès pour accéder aux données protégées dans le serveur Salesforce.
- Le service de rapport extrait les données autorisées dans son rapport généré de nuit.
Examinons chaque étape de ce flux d'autorisation.
Créer un JWT
Salesforce nécessite un JWT signé en utilisant le protocole RSA SHA256, qui utilise un certificat chargé en tant que secret de signature. Avant d'utiliser ce flux d'autorisation, assurez-vous de suivre les étapes ci-dessous.
- Chargez un certificat X509 dans un magasin de clés Java (JKS). La taille du certificat ne peut pas dépasser 4 Ko. En cas de dépassement, essayez d'utiliser un fichier codé en DER pour réduire la taille.
- Enregistrez le certificat X509 pour l'application connectée. Le certificat correspond à la clé privée de son application. Lorsque l'application connectée est enregistrée, le
client_idet leclient_secretsont générés et attribués à l'application. - Élaborez une application qui génère un JWT, signé avec la clé privée du certificat X509. L'application connectée associée utilise le certificat pour vérifier la signature. Le JWT doit respecter les règles de format générales spécifiées à l'adresse https://tools.ietf.org/html/rfc7519.
Remarque Salesforce ne nécessite pas de réclamations JWT ID (JTI) dans vos jetons de porteur JWT. Cependant, si vous transmettez une réclamation JTI à votre jeton de porteur JWT, Salesforce vérifie que la réclamation JTI n'a pas été envoyée avant. La validation empêche les attaques en relecture JWT.
Pour créer un JWT valide, procédez comme suit.
- Construisez un en-tête JWT sous le format suivant :
{"alg":"RS256"}. - Base64url encode l'en-tête JWT selon la définition spécifiée à l'adresse http://tools.ietf.org/html/rfc4648#page-7. Le résultat est similaire à
eyJhbGciOiJSUzI1NiJ9. - Construisez un ensemble de réclamations JSON pour le JWT avec les paramètres ci-dessous.
Voici un exemple d'ensemble de réclamations JSON pour le JWT.Paramètre Description issL'émetteur doit contenir le client_idOAuth ou l'application connectée pour laquelle le développeur a enregistré le certificat.audL'audience identifie le serveur d'autorisation en tant qu'audience ciblée. Le serveur d'autorisation doit vérifier qu'elle est une audience ciblée pour le jeton.
Utilisez l'URL du serveur d'autorisation pour la valeur d'audience : https://login.salesforce.com, https://test.salesforce.com, ou https://site.force.com/customers s'il est implémenté pour un site Experience Cloud.
subSi vous implémentez ce flux pour un site Experience Cloud, l'objet doit contenir le nom d'utilisateur.
Pour la rétro-compatibilité, vous pouvez utiliser le principal (
prn) au lieu de l'objet (sub). Si les deux sont spécifiés,prnest utilisé.expLa date et l'heure d'expiration du jeton, exprimées en nombre de secondes depuis 1970-01-01T0:0:0Z mesurée en heure UTC. Salesforce autorise un écart d'horloge de trois minutes. Par exemple, si l'heure d'expiration est définie sur 1,735,743,600 secondes ou le 1er janvier 2025 à 15:00:00 UTC, le jeton est toujours valide jusqu'à 15:03:00 UTC à cette date. {"iss": "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQ ukXnVjzRgSuQqGn75NL7yfkQcyy7", "sub": "my@email.com", "aud": "https://login.salesforce.com", "exp": "1333685628"} - Codez le JWT Claims Set en Base64url sans aucun saut de ligne. Par exemple :
eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz Njg1NjI4In0= - Créez une chaîne avec l'en-tête JWT Header codé et le JWT Claims Set codé, sous le format suivant :
Dans cet exemple, l'en-tête JWT codé est mis en évidence :encoded_JWT_Header + "." + encoded_JWT_Claims_SeteyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz Njg1NjI4In0= - Téléchargez le certificat X509 depuis le JKS.
- Signez la chaîne générée en utilisant RSA SHA256.
- Créez une chaîne à partir de la chaîne à cette étape, sous ce format.
Dans cet exemple, la signature codée en base64 est mise en évidence.existing_string + "." + base64_encoded_signatureeyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz Njg1NjI4In0=.iYCthqWCQucwi35yFs-nWNgpF5NA_a46fXDTNIY8ACko6BaEtQ9E6h4Hn1l_pcwcK I_GlmfUO2dJDg1A610t09TeoPagJsZDm_H83bsoZUoI8LpAA1s-2aj_Wbysqb1j4uDToz 480WtEbkwIv09sIeS_-QuWak2RXOl1Krnf72mpVGS4WWSULodgNzlKHHyjAMAHiBHIDNt 36y2L2Bh7M8TNWiKa_BNM6s1FNKDAwHEWQrNtAeReXgRy0MZgQY2rZtqT2FcDyjY3JVQb En_CSjH2WV7ZlUwsKHqGfI7hzeEvVdfOjH9NuaJozxvhPF489IgW6cntPuT2V647JWi7ng
Ce code Java est un simple exemple de création d'un jeton du porteur JWT.
import org.apache.commons.codec.binary.Base64;
import java.io.*;
import java.security.*;
import java.text.MessageFormat;
public class JWTExample {
public static void main(String[] args) {
String header = "{\"alg\":\"RS256\"}";
String claimTemplate = "'{'\"iss\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\", \"jti\": \"{4}\"'}'";
try {
StringBuffer token = new StringBuffer();
//Encode the JWT Header and add it to our string to sign
token.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8")));
//Separate with a period
token.append(".");
//Create the JWT Claims Object
String[] claimArray = new String[5];
claimArray[0] = "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQukXnVjzRgSuQqGn75NL7yfkQcyy7";
claimArray[1] = "my@email.com";
claimArray[2] = "https://login.salesforce.com";
claimArray[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
claimArray[4]=<JTI>
MessageFormat claims;
claims = new MessageFormat(claimTemplate);
String payload = claims.format(claimArray);
//Add the encoded claims object
token.append(Base64.encodeBase64URLSafeString(payload.getBytes("UTF-8")));
//Load the private key from a keystore
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream("./path/to/keystore.jks"), "keystorepassword".toCharArray());
PrivateKey privateKey = (PrivateKey) keystore.getKey("certalias", "privatekeypassword".toCharArray());
//Sign the JWT Header + "." + JWT Claims Object
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(token.toString().getBytes("UTF-8"));
String signedPayload = Base64.encodeBase64URLSafeString(signature.sign());
//Separate with a period
token.append(".");
//Add the encoded signature
token.append(signedPayload);
System.out.println(token.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}Demander un jeton d'accès
Pour demander un jeton d'accès, l'application connectée publie une demande de jeton au point de terminaison de jeton de l'instance Salesforce. Elle inclut le JWT dans la publication.
Cet exemple présente un exemple de demande de jeton.
POST /services/oauth2/token HTTP/1.1
Host: login.example.com
Content-Type: application/x-www-form-urlencoded
grant_type= urn:ietf:params:oauth:grant-type:jwt-bearer&
assertion=eyJpc3MiOiAiM01WRz...[omitted for brevity]...ZTInsérez les paramètres ci-dessous dans la publication.
| Paramètre | Description |
|---|---|
grant_type
|
Utilisez les valeurs suivantes pour le type d'autorisation d'accès : urn:ietf:params:oauth:grant-type:jwt-bearer. |
assertion
|
L'assertion est la valeur JWT complète. |
format
|
(Facultatif) Utilisez-le pour spécifier le format de retour attendu. Ce paramètre remplace l'en-tête de la requête. Les formats suivants sont pris en charge :
|
Paramètre d'étendue
Vous ne pouvez pas spécifier d'étendue dans un flux de jeton du porteur JWT. Les étendues sont émises en fonction de la stratégie Utilisateurs autorisés de l'application connectée, ou des paramètres de Contrôle d'accès API de votre organisation, comme indiqué dans le tableau ci-dessous.
| Paramètre | Résultat |
|---|---|
| Stratégie Utilisateurs autorisés : Tous les utilisateurs peuvent s’autoriser eux-mêmes | Lorsqu'une autorisation réussit, les étendues renvoyées avec le jeton d'accès sont dérivées à partir des étendues des approbations précédentes. |
| Stratégie Utilisateurs autorisés : Les utilisateurs approuvés par l'administrateur sont pré-autorisés | Les étendues standard et personnalisées attribuées à l'application connectée sont renvoyées avec le jeton d'accès. |
| Contrôle d'accès API : Les applications connectées de la liste d'autorisations de votre organisation | Les étendues standard et personnalisées attribuées à l'application connectée sont renvoyées avec le jeton d'accès. Si vous ajoutez des applications connectées à la liste d'autorisations de votre organisation et ne recevez pas les étendues attendues, procédez comme suit.
|
Salesforce accorde un jeton d'accès
Les requêtes de flux de porteur JWT et de porteur d'assertion SAML OAuth 2.0 examinent toutes les approbations précédentes de l'utilisateur qui contiennent un jeton d'actualisation. Si Salesforce renvoie des approbations correspondantes, les valeurs des étendues approuvées son combinées. Salesforce émet ensuite un jeton d'accès. Si Salesforce ne trouve aucune approbation précédente contenant un jeton d'actualisation ni d'étendues approuvées disponibles, la demande échoue et n'est pas autorisée.
Une fois la vérification effectuée, l'instance Salesforce envoie une réponse à l'application connectée. Une réponse de jeton pour le flux de jeton du porteur OAuth 2.0 JWT respecte le même format qu'un flux code d'autorisation, bien qu’aucun jeton d'actualisation ne soit émis.
Cet exemple présente une réponse de Salesforce.
{"access_token":"00Dxx0000001gPL!AR8AQJXg5oj8jXSgxJfA0lBog.
39AsX.LVpxezPwuX5VAIrrbbHMuol7GQxnMeYMN7cj8EoWr78nt1u44zU31
IbYNNJguseu",
"scope":"web openid api id","instance_url":"
https://yourInstance.salesforce.com","id":"
https://yourInstance.salesforce.com
/id/00Dxx0000001gPLEAY/005xx000001SwiUAAS","token_type":"Bearer"}Les paramètres suivants sont inclus dans le corps de la réponse.
| Paramètre | Description |
|---|---|
access_token |
Le jeton OAuth utilisé par une application connectée pour demander l'accès à une ressource protégée au nom de l'application cliente. Des autorisations supplémentaires sous forme d'étendues peuvent accompagner le jeton d'accès. |
token_type |
Un type de jeton Bearer, utilisé pour toutes les réponses qui incluent un jeton d'accès. |
scope |
Les étendues sont émises en fonction de la stratégie Utilisateurs autorisés de l'application connectée, ou des paramètres de Contrôle d'accès API de votre organisation. Consultez Paramètre d'étendue. |
instance_url |
Une URL indiquant l'instance de l'organisation de l'utilisateur. Par exemple : https://yourInstance.salesforce.com/. |
id |
Une URL d'identité qui peut être utilisée pour identifier l'utilisateur et pour demander plus d'informations sur l'utilisateur. Consultez URL d'identité. |
sfdc_site_url |
Si l'utilisateur est membre d'un site Experience Cloud, l'URL du site est fournie. |
sfdc_site_id |
Si l'utilisateur est membre d'un site Experience Cloud, l'ID du site de l'utilisateur est fourni. Pour des sites Experience Cloud, ce flux inclut la valeur "sfdc_site_id" dans le point de terminaison de jeton. Cet ID de site est toujours requis dans les requêtes de l'API REST Connect. |
Accéder aux données protégées
Lorsque l'application connectée reçoit le access_token, elle peut le transmettre en tant que jeton de porteur dans l'en-tête de la requête d'autorisation. Cet exemple présente un appel d'API REST à des sites Experience Cloud :
https://site.force.com/customers/services/data/v32.0/ -H
"Authorization: Bearer 00D50000000IehZ\!AQcAQH0dMHZfz972Szmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1E6 LYUfiDUkWe6H34r1AAwOR8B8fLEz6n04NPGRrq0FM"
