Loading
Salesforce now sends email only from verified domains. Read More
Identify Your Users and Manage Access
Table of Contents
Select Filters

          No results
          No results
          Here are some search tips

          Check the spelling of your keywords.
          Use more general search terms.
          Select fewer filters to broaden your search.

          Search all of Salesforce Help
          OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration

          OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration

          Sometimes you want to authorize servers to access data without interactively logging in each time the servers exchange information. For these cases, you can use the OAuth 2.0 JSON Web Token (JWT) bearer flow. This flow uses a certificate to sign the JWT request and doesn’t require explicit user interaction. However, this flow does require prior approval of the client app.

          Required Editions

          Available in: both Salesforce Classic and Lightning Experience
          Available in: All Editions
          Note
          Note Connected apps creation is restricted as of Spring ‘26. You can continue to use existing connected apps during and after Spring ‘26. However, we recommend using external client apps instead. If you must continue creating connected apps, contact Salesforce Support.

          See New connected apps can no longer be created in Spring ‘26 for more details.

          With the OAuth 2.0 JWT bearer token flow, the client posts a JWT to the Salesforce OAuth token endpoint. Salesforce processes the JWT, which includes a digital signature, and issues an access token based on prior approval of the app.

          This example shows the steps taken in the flow.

          1. A report service begins its nightly batch report.
          2. The connected app sends the JWT to the Salesforce token endpoint. The JWT enables identity and security information to be shared across security domains.
          3. Salesforce validates the JWT based on a signature using a previously configured certificate and additional parameters.
          4. Assuming that the JWT is valid and that the connected app has prior approval, Salesforce issues an access token. Prior approval happens in one of these ways:
            • If your connected app policy is set to Admin approved users are pre-authorized, you can use profiles and permission sets.
            • If your connected app policy is set to All users may self-authorize, you can use end-user approval and issuance of a refresh token. However, the client isn’t required to have a current or stored refresh token. The client also isn’t required to pass a client secret to the token endpoint.
            Note
            Note For both options, Salesforce issues a new access token only when the original access token includes at least one standard scope other than the refresh_token scope.
          5. The connected app uses the access token to access the protected data on the Salesforce server.
          6. The report service pulls the authorized data into its nightly report.
          Note
          Note This flow never issues a refresh token.

          Let’s go over each step of this authorization flow.

          Create a JWT

          Salesforce requires that a JWT is signed using RSA SHA256, which uses an uploaded certificate as the signing secret. Before using this authorization flow, make sure that you complete these steps.

          • Upload an X509 Certificate to a Java Key Store (JKS). The certificate size can’t exceed 4 KB. If it does, try using a DER encoded file to reduce the size.
          • Register the X509 Certificate for the connected app. The certificate corresponds to the private key of the app. When the connected app is saved, the client_id and client_secret are generated and assigned to the app.
          • Build an app that generates a JWT, which is signed with the X509 Certificate’s private key. The associated connected app uses the certificate to verify the signature. The JWT must conform with the general format rules specified in https://tools.ietf.org/html/rfc7519.
            Note
            Note Salesforce doesn’t require JWT ID (JTI) claims in your JWT bearer tokens. However, if you pass a JTI claim in your JWT bearer token, Salesforce validates that the JTI claim hasn’t been sent before. This validation prevents JWT replay attacks.

          To create a valid JWT, take these steps.

          1. Construct a JWT header with this format: {"alg":"RS256"}.
          2. Base64url encode the JWT header as defined in http://tools.ietf.org/html/rfc4648#page-7. The result is similar to eyJhbGciOiJSUzI1NiJ9.
          3. Construct a JSON Claims Set for the JWT with these parameters.
            Parameter Description
            iss The issuer must contain the OAuth client_id or the connected app for which you registered the certificate.
            aud

            The audience identifies the authorization server as an intended audience. The authorization server must verify that it’s an intended audience for the token.

            Use the authorization server’s URL for the audience value: https://login.salesforce.com, https://test.salesforce.com, or https://site.force.com/customers if implementing for an Experience Cloud site.

            sub

            If you’re implementing this flow for an Experience Cloud site, the subject must contain the user’s username.

            For backward compatibility, you can use principal (prn) instead of subject (sub). If both are specified, prn is used.

            exp The date and time at which the token expires, expressed as the number of seconds from 1970-01-01T0:0:0Z measured in UTC. Salesforce allows a 3-minute buffer for clock skew. For example, if the expiration time is set to 1,735,743,600 seconds or January 1, 2025 at 15:00:00 UTC, the token is still valid until 15:03:00 UTC on this date.
            Here’s an example JSON Claim Set for the JWT.
            {"iss": "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQ
            ukXnVjzRgSuQqGn75NL7yfkQcyy7", 
            "sub": "my@email.com", 
            "aud": "https://login.salesforce.com", 
            "exp": "1333685628"}
          4. Base64url encode the JWT Claims Set without any line breaks. For example:
            eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su
            Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu
            Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz
            Njg1NjI4In0=
          5. Create a string for the encoded JWT Header and the encoded JWT Claims Set in this format.
            encoded_JWT_Header + "." + encoded_JWT_Claims_Set
            In this example, the encoded JWT Header is highlighted.
            eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su
            Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu
            Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz
            Njg1NjI4In0=
          6. Download the X509 Certificate from the JKS.
          7. Sign the resulting string using RSA SHA256.
          8. Create a string of the string from this step in this format.
            existing_string + "." + base64_encoded_signature
            In this example, the base64 encoded signature is highlighted.
            eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiAiM01WRzk5T3hUeUVNQ1EzZ05wMlBqa3FlWkt4bm1BaUcxeFY0b0hoOUFLTF9yU0su
            Qm9TVlBHWkhRdWtYblZqelJnU3VRcUduNzVOTDd5ZmtRY3l5NyIsICJwcm4iOiAibXlAZW1haWwu
            Y29tIiwgImF1ZCI6ICJodHRwczovL2xvZ2luLnNhbGVzZm9yY2UuY29tIiwgImV4cCI6ICIxMzMz
            Njg1NjI4In0=.iYCthqWCQucwi35yFs-nWNgpF5NA_a46fXDTNIY8ACko6BaEtQ9E6h4Hn1l_pcwcK​
            I_GlmfUO2dJDg1A610t09TeoPagJsZDm_H83bsoZUoI8LpAA1s-2aj_Wbysqb1j4uDToz​
            480WtEbkwIv09sIeS_-QuWak2RXOl1Krnf72mpVGS4WWSULodgNzlKHHyjAMAHiBHIDNt​
            36y2L2Bh7M8TNWiKa_BNM6s1FNKDAwHEWQrNtAeReXgRy0MZgQY2rZtqT2FcDyjY3JVQb​
            En_CSjH2WV7ZlUwsKHqGfI7hzeEvVdfOjH9NuaJozxvhPF489IgW6cntPuT2V647JWi7ng

          This Java code is a simple example of constructing a JWT bearer token.

          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();
              }
            }
          }

          Request Access Token

          To request an access token, the connected app posts a token request to the Salesforce instance’s token endpoint. It includes the JWT in the post.

          This example shows a sample token request.

          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]...ZT
          Important
          Important When developing OAuth integrations, always pass sensitive information in the body of a POST request or in a request header. Don't use GET parameters in the URL query string to pass sensitive information. Sensitive information includes but isn't limited to usernames, passwords, OAuth tokens, client secrets, and any personally identifiable information. For more information on security best practices, see Storing Sensitive Data in the Secure Coding Guide.

          Include these parameters in the post.

          Parameter Description
          grant_type Use these values for the grant type: urn:ietf:params:oauth:grant-type:jwt-bearer.
          assertion The assertion is the entire JWT value.
          format

          (Optional) Use to specify the expected return format. This parameter overrides the request’s header. These formats are supported.

          • urlencoded
          • json (default)
          • xml

          Scope Parameter

          You can’t specify scopes in a JWT bearer token flow. Scopes are issued according to the connected app’s Permitted Users policy or your org’s API Access Control settings, as shown in this table.

          Setting Result
          Permitted Users policy: All users may self-authorize With a successful authorization, the scopes returned with the access token are derived from the scopes of prior approvals.
          Permitted Users policy: Admin approved users are pre-authorized Standard and custom scopes assigned to the connected app are returned with the access token.
          API Access Control: Allowlist connected apps in your org

          Standard and custom scopes assigned to the connected app are returned with the access token. If you allowlist connected apps in your org and don’t receive the expected scopes, take these steps.

          • From Setup, in the Quick Find box, enter OAuth, and then select Connected Apps OAuth Usage.
          • For the allowlisted connected app, click Block.
          • For the allowlisted connected app, click Unblock.

          Salesforce Grants Access Token

          The OAuth 2.0 JWT bearer and SAML assertion bearer flow requests look at all previous approvals for the user that include a refresh token. If Salesforce finds matching approvals, it combines the values of the approved scopes. Salesforce then issues an access token. If Salesforce doesn’t find previous approvals that included a refresh token or any available approved scopes, the request fails as unauthorized.

          After a successful verification, the Salesforce instance sends a response to the connected app. A token response for the OAuth 2.0 JWT bearer token flow follows the same format as an authorization code flow, although a refresh token is never issued.

          This example shows a response from 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"}

          These parameters are in the body of the response.

          Parameter Description
          access_token OAuth token that a connected app uses to request access to a protected resource on behalf of the client application. Additional permissions in the form of scopes can accompany the access token.
          token_type A Bearer token type, which is used for all responses that include an access token.
          scope Scopes are issued according to the connected app’s Permitted Users policy or your org’s API Access Control settings. See Scope Parameter.
          instance_url A URL indicating the instance of the user’s org. For example: https://yourInstance.salesforce.com/.
          id An identity URL that can be used to identify the user and to query for more information about the user. See Identity URLs.
          sfdc_site_url If the user is a member of an Experience Cloud site, the site URL is provided.
          sfdc_site_id If the user is a member of an Experience Cloud site, the user’s site ID is provided. For Experience Cloud sites, this flow includes the "sfdc_site_id" value in the token endpoint. This site ID is potentially required in Connect REST API requests.

          Access Protected Data

          After the connected app receives the access_token, it can pass it as a bearer token in the Authorization header request. This example shows a REST API call to Experience Cloud sites:

          
                    https://site.force.com/customers/services/data/v32.0/ -H 
          "Authorization: Bearer 00D50000000IehZ\!AQcAQH0dMHZfz972Szmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1E6 LYUfiDUkWe6H34r1AAwOR8B8fLEz6n04NPGRrq0FM"
                  
           
          Loading
          Salesforce Help | Article