Describes the PKCE Flow.

Constant Contact strongly suggests that you use the Proof Key for Code Exchange (PKCE) Flow for all public or untrusted clients. This includes native and single-page applications such as client-side JavaScript applications and mobile applications. PKCE is an OAuth2 security extension that builds upon the standard Authorization Code Flow. The PKCE Flow is used to prevent malicious attacks on applications that cannot securely store a client secret.

In order to use this flow, you must first create and configure a V3 API application. For more information, see the Create an Application Integration.

The PKCE Flow does not use the client secret key to authenticate. Instead, your application generates the following values to use:

  1. code_verifier - A random cryptographic value to include in the authorization request.
  2. code_challenge - A SHA-256 hash of the code_verifier value used in exchange for an access token (Bearer).

The Authorization Server stores the code_challenge and uses your application’s authorization request to redirect the user to log in to authenticate with Constant Contact and to grant your application permission to their data. If successful, the Authorization Server responds and sends your client the authorization code, which is valid for one use.

Your application sends a verification request that includes both the authorization code and the code_verifier to the Authorization Server. The Authorization Server checks that the authorization code and code_verifier are valid for the user, then returns the ID token, access token, and optionally the refresh token that your application requires to make V3 API calls to get the user’s Constant Contact data. Use the access token to make requests using the V3 API. Use refresh tokens to obtain new access and refresh tokens to ensure that your users only need to authenticate once.

To authenticate using the PKCE Flow, complete the steps that follow.

Step 1: Generate a Code Verifier and a Code Challenge

Using the PKCE Flow, you must create the cryptographically-random code_verifier value. Use SHA-256 to hash the code_verifier value as the code_challenge value.

Generate the Code Verifier

Generate a code_verifier value in your application and save it. The code_verifier should be some cryptographically random value. Later, you will use this value when:

  • Generating a code_challenge value.
  • Exchanging an authorization code for an access token.

For example:

   /**
     * This method generates a secure string used as the PKCE code verifier value.
     * In PKCE, you use the code verifier value:
     * 1. When you generate a code challenge value.
     * 2. When you exchange an authorization code for a bearer JWT token.
     * @return A random code verifier value.
     */
    public String generateCodeVerifier() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[36];
        secureRandom.nextBytes(bytes);

        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

Generate the Code Challenge

Generate a code_challenge value in your application and save it. The code_challenge value is derived from your code_verifier value. Generate the code_challenge value by using SHA-256 to hash the code_verifier value, then base64 URL encode the results.

For example: code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

For more details, see the Proof Key for Code Exchange by OAuth Public Clients sections of RFC 7636.

The following example shows how to create a code_challenge value using SHA-256 to base64 encode the code_verifier value:

    /**
     * This method generates a code challenge value by SHA-256 and base64 encoding the code verifier value.
     * In PKCE, you use the code challenge value when you construct the authorization request uri.
     * @param codeVerifierValue A random string value.
     * @return An encoded code challenge string value.
     * @throws NoSuchAlgorithmException
     */
    public String generateCodeChallengeHash(String codeVerifierValue) throws NoSuchAlgorithmException {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        byte[] codeVerifierBytes = codeVerifierValue.getBytes(StandardCharsets.US_ASCII);
        byte[] digest = messageDigest.digest(codeVerifierBytes);

        return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
    }

Step 2: Create the Authorization Request

Create the authorization request used to direct users to Constant Contact to authenticate their user account and to authorize your application to access their Constant Contact data.

To create an authorization request, use the authorization endpoint https://authz.constantcontact.com/oauth2/default/v1/authorize and include all required request query parameters.

For example:

https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id={your_client_id}&redirect_uri=https%3A%2F%2Flocalhost%3A8888&&response_type=code&code_challenge={generated code_challenge value}&code_challenge_method={S256}&state=235o250eddsdff&scope={contact_data+campaign_data+offline_access}

Request Parameters

  • code_challenge - Required. The code_challenge value generated by your application.

  • code_challenge_method - Required. The hashing algorithm you used to generate the code_challenge. This value should always be SHA-256.

  • client_idRequired. The API key for your application. You can view the API keys for all of your applications or create a new application on the My Applications page.

  • redirect_uriRequired. Specify the absolute URI that you want Constant Contact to use when redirecting a user to your application. After a user authorizes your application, Constant Contact redirects the user to your application and appends the authorization code and state (and optionally the scope) values to the URI as query parameters. Wildcards are not supported except at the domain root level. For more information, see the Authentication Overview page.

  • response_typeRequired. This flow uses code as the value to request an authorization code from Constant Contact.

  • state - Required. To prevent cross-site request forgery, specify the arbitrary state string value you want to use to uniquely identify a user’s session.

  • scopeOptional. A list of the scopes that your application requires. The V3 API currently supports the account_read, account_update, contact_data, offline_access, and campaign_data scopes. The offline_access scope is required for returning refresh tokens. For more information on scopes and the specific scopes required by each V3 API endpoint, see the Scopes Overview page.

  • nonceOptional. For the purpose of mitigating replay attacks, specify the string value to use to associate a client session with an id_token. The id_token includes information (claims) about the user and, if specified, the nonce value. The id_token is encoded as a JWT and returned in the response when you request an access token. To verify the nonce string value, decode the id_token.

Example Authorization Request

 /**
     * Use the URIBuilder from org.apache.http to construct an authorization request URI.
     * @param codeChallengeValue The code challenge value that your application encoded.
     * @param redirectUri URI used to redirect users to your application after they complete authorization.
     * @param clientId Your application's client id.
     * @param state A random string value used to help protect your application from cross-site request forgery.
     * @return The full authorization URI
     * @throws URISyntaxException
     */
    public String constructAuthorizationUri(String codeChallengeValue, String redirectUri, String clientId, String state) throws URISyntaxException {
        return new URIBuilder("https://authz.constantcontact.com/oauth2/default/v1/authorize")
                .addParameter("client_id", clientId)
                .addParameter("redirect_uri", redirectUri)
                .addParameter("scope", "offline_access openid")
                .addParameter("response_type", "code")
                .addParameter("state", state)
                .addParameter("code_challenge", codeChallengeValue)
                .addParameter("code_challenge_method", "S256")
                .build()
                .toString();
    }

Step 3: Get Authorization

When your application sends an authorization request to the Authorization Server, it directs the user to Constant Contact. Constant Contact prompts the user to sign in to authenticate their user account. Next, Constant Contact displays the Permission Request screen to allow the user to authorize your application access to their data.

User Permission Request Screen

If the user authorizes your application, Constant Contact appends the authorization code to the redirect_uri returned in the authorization response. The authorization code is used in exchange for an access token and can be used once.

Step 4: Get the Access and Refresh Tokens

To request an access_token and refresh_token, make a POST request to the Authorization Server using the https://authz.constantcontact.com/oauth2/default/v1/token endpoint and include the following required query parameters:

  • client_id - The ID that is associated with your application.
  • redirect_uri - The redirect URI that you specified as part of the authorization request.
  • code - The authorization code returned in the authorization response.
  • code_verifier - The code verifier value that you generated.

Example Request

 /**
     * This method exchanges an authorization code, the codeVerifier value you previous generated, for an access token and a refresh token.
     * @param clientId Your application's client id.
     * @param redirectUri The redirect uri that you used as part of the authorization request URL.
     * @param code The authorization code you received from the authorization request.
     * @param codeVerifier The random code verifier value you generated.
     * @return A JSON String containing an access token and a refresh token.
     * @throws IOException
     * @throws URISyntaxException
     */
    public String exchangeCodeAndVerifierForAuthResponse(String clientId, String redirectUri, String code, String codeVerifier) throws IOException, URISyntaxException {
        URI pkceTokenUri = new URIBuilder("https://authz.constantcontact.com/oauth2/default/v1/token")
                .addParameter("client_id", clientId)
                .addParameter("redirect_uri", redirectUri)
                .addParameter("code", code)
                .addParameter("code_verifier", codeVerifier)
                .addParameter("grant_type", "authorization_code")
                .build();

        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(pkceTokenUri)
                .POST(HttpRequest.BodyPublishers.ofString(""))
                .header("Accept", "application/json")
                .header("Content-Type", "application/x-www-form-urlencoded")
                .build();

        HttpResponse<String> httpResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        
        return httpResponse.body();
    }

Example Response

{
    "token_type": "Bearer",
    "expires_in": 28800,
    "access_token": "eyJraWQiOiIzSDdLVVpkSDlyNTh6RVVDSzNYMlR1b2o0SjV0eldWUF9FRUxQTDd6X3hjIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULkZ6YUUtTVFFb2dKQ1VzZlhSUGtBZldobDllckJndVlYRWphOGFXd1RyRzgiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmNvbnN0YW50Y29udGFjdC5jb20vb2F1dGgyL2F1czFsbTNyeTltRjd4MkphMGg4IiwiYXVkIjoiaHR0cHM6Ly9hcGkuY2MuZW1haWwvdjMiLCJpYXQiOjE2NDQ0Mjc3NzMsImV4cCI6MTY0NDQ1NjU3MywiY2lkIjoiY2M5NGMyZDYtNmU3MC00MjZjLWFmZDEtNzFjOGZhNWY0ODkwIiwidWlkIjoiMDB1MWlieDhsaGIzYW12SFIwaDgiLCJzY3AiOlsiYWNjb3VudF9yZWFkIiwiZW1haWwiLCJjYW1wYWlnbl9kYXRhIiwicHJvZmlsZSIsImFkZHJlc3MiLCJjb250YWN0X2RhdGEiLCJvcGVuaWQiXSwic3ViIjoiZ2Vla3NxdWFkIiwicGxhdGZvcm1fdXNlcl9pZCI6ImQzYTA2ZTYyLTJmMTQtNGQ5Ni05NDUzLTI2NTkxYWI1M2YwZCJ9.saJxgUYx1_OKUteH01KV71vzjaxqTKnZ3XfD4QKtsIkjrmcf4tUWEFSzjuF1kdxLvlMoCyr_MwO81ApwYX2UU0YRhkVsfw2VLR4KpO89ILul7ZJZyzzsVKak8BrgLhqR1AB7X116VRjsH74vD4D-1e4uKKUlK4yefsZE14kFgDZNi74_WPn5FBGeicwMsStdG3qms8X-6LqIxuZ5fXgSxK9wDEBe-xJfJSqAqo1vJKMqw2JT_NvBBLDXqoSg0_q_0IqbxXEMHdX2J_DHGKrY7w1QQGmeBU_BIlewQPUxUGX-dbRavxncBpItt1tsRLBIiPbPkQ_zDN8_J9lPIxJksA",
    "scope": "account_read offline_access",
    "refresh_token": "HFsqfgtZuGyffnD2qb-AQKWDURWAXM8hlgYIFEwaq4E",
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMDEyMzQ1ejRXMzhHS3BPMGg3IiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqZG9lQGFiYy5jb20iLCJ2ZXIiOjEsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHkuY29uc3RhbnRjb250YWN0LmNvbS9vYXV0aDIvYXVzengwNm81alJlZlE4RjQwaDciLCJhdWQiOiJmYThhMDZkYy1iMDAwLTQ3NWMtYWNlZC01NWNlYzVhZWUzMjUiLCJpYXQiOjE2NDQ4NTc4NDcsImV4cCI6MTY0NDg2MTQ0NywianRpIjoiSUQuRFhRQ2pxQmd3M28xMnVHdlN5R2ZpQTJ2QjVFcGNvSktsajBqbFhSc2lqayIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBvbnBtc2prYmdPc0wydlcwaDciLCJub25jZSI6Ijg2MDgyNTYwMyIsInByZWZlcnJlZF91c2VybmFtZSI6Impkb2UiLCJhdXRoX3RpbWUiOjE2NDQ4NTc4NDQsImF0X2hhc2giOiJuWk5sRjduWGo2bV9UNEFKcXl0c1RRIn0.ToO_Ye1YHRx4hWCMBnrT6yksWvkrpHfGmXcHTh7B75A"
}
  • token_type — This value is always set to Bearer.

  • expires_in — The number of seconds remaining until the access_token expires.

  • access_token — The new access_token value used to send requests with the V3 API. Access tokens automatically expire 1440 minutes (86,400 seconds). Making an API call with an expired access token returns a 401 unauthorized status code.

  • scope — The type of data in Constant Contact that the user is granting your application permission to use. The offline_access scope is required to get a refresh token.

  • refresh_token — Each refresh token corresponds to an access token. Use the refresh token to obtain a new access_token when the corresponding access_token expires.

  • id_token — The JWT that includes information about the user such as their name and email address, and if specified, the nonce value you passed in the authorization request. Decode the id_token to verify the nonce value and view user information.

Step 5: Validate the Access Token

For each new access token that you receive, follow best practices for security by validating the signature and claims of the access token.

Load the JSON web-key Set

Use the method that follows to load the Constant Contact public json web-key set used to validate access tokens.

 /**
     * This method uses the java11 http client to load Constant Contact's public json web key set.
     * In real use cases, this value should be cached.
     * @return Constant Contact's public json web key set for the v3 API.
     */
    public String getPublicJsonWebKeySet() throws IOException {
        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://identity.constantcontact.com/oauth2/aus1lm3ry9mF7x2Ja0h8/v1/keys"))
                .GET()
                .header("Accept","application/json")
                .header("Content-Type", "application/x-www-form-urlencoded")
                .build();
        HttpResponse<String> httpResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        return httpResponse.body();
    }

Verify the Access Token Claims

Use the method that follows to parse the access token, and then validate the signature and claims of the access token. The code examples in this step use the org.jose4j library.

Use the jwt parameter to pass in the access token. Use the jsonWebKeySetJson parameter to pass the JSON Web Key Set (JWKS) value that was returned in the previous step (Load the JSON web-key set to get the access token).

If the access token is not valid or if a required claim is missing or incorrect, an exception occurs.

    /**
     * This method uses the jose4j library to verify the claims on the JWT.
     * It throws an exception if it fails to parse the JWT, or the JWT does not include the expected claim.
     * @param jwt String containing a JSON web token.
     * @param jsonWebKeySetJson the set of keys available at https://identity.constantcontact.com/oauth2/aus1lm3ry9mF7x2Ja0h8/v1/keys
     * @throws JoseException
     * @throws InvalidJwtException
     */
    public void verifyJwtToken(String jwt, String jsonWebKeySetJson) throws JoseException, InvalidJwtException {

        JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jsonWebKeySetJson);
        VerificationJwkSelector jwkSelector = new VerificationJwkSelector();
        JsonWebSignature jws = new JsonWebSignature();
        jws.setCompactSerialization(jwt);

        JsonWebKey jwk = jwkSelector.select(jws, jsonWebKeySet.getJsonWebKeys());

        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()
                .setRequireSubject()
                .setExpectedIssuer("https://identity.constantcontact.com/oauth2/aus1lm3ry9mF7x2Ja0h8")
                .setExpectedAudience("https://api.cc.email/v3")
                .setVerificationKey(jwk.getKey())
                .setJwsAlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256)
                .build();

        jwtConsumer.processToClaims(jwt);
    }

Check the following claims:

  • Compare the cid (client identifier) value to ensure that it matches your application API key
  • The platform_user_id claim in the access token uniquely identifies a Constant Contact user.
JwtClaims claims = consumer.processToClaims(token);
     
     String cid = claims.get("cid")
     if (! apiKey.equals(cid)) {
     throw new InvalidJwtException("Api key in token incorrect");
     }
     String userId = claims.get("platform_user_id")
     
     if (userId == null) {
     throw new InvalidJwtException("Token is missing platform_user_id");
     }  

Step 6: Add the Access Token to the Authorization Request

After verifying the access token claims and signature, use the access_token to send requests in the V3 API by adding it to the Authorization request header in the format Authorization: Bearer {your_access_token}. When an access token expires, users must reauthorize your application with Constant Contact. Access tokens automatically expire after 1440 minutes (86,400 seconds).

Making an API call with an expired access token returns a 401 unauthorized status code. See the Authorization Request Errors table in the Authorization Overview for information on how to handle authorization request errors.

Step 7 (Optional): Check the Access Token Expiration Timestamp

You can quickly check to see if the access token is expired or not using the method that follows.

Use the jwt parameter to pass in the access token. This method returns True for an expired token if the expiration time is within 5 minutes (300 seconds) of the current time, or False if the token has not expired. If the access token expires, you can exchange it for a new access and refresh token using the Refresh the Access Token procedure.

  /**
     * This method checks if the current time is greater than or equal to the JWT expiration claim.
     * @param jwt A String JSON web token.
     * @return True if the jwt token is expired. False if the token is not expired.
     */
    public boolean checkIfTokenIsExpired(String jwt) throws InvalidJwtException, MalformedClaimException {

        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()
                .setSkipDefaultAudienceValidation()
                .setDisableRequireSignature()
                .setSkipSignatureVerification()
                .build();
         JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
         return NumericDate.now().getValue() >= jwtClaims.getExpirationTime().getValue();
    }

Example Response

{
  "token_type": "Bearer",
  "expires_in": "28800",
  "access_token": "eyJraWQiOiIzSDdLVVpkSDlyNTh6RVVDSzNYMlR1b2o0SjV0eldWUF9FRUxQTDd6X3hjIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULkZ6YUUtTVFFb2dKQ1VzZlhSUGtBZldobDllckJndVlYRWphOGFXd1RyRzgiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmNvbnN0YW50Y29udGFjdC5jb20vb2F1dGgyL2F1czFsbTNyeTltRjd4MkphMGg4IiwiYXVkIjoiaHR0cHM6Ly9hcGkuY2MuZW1haWwvdjMiLCJpYXQiOjE2NDQ0Mjc3NzMsImV4cCI6MTY0NDQ1NjU3MywiY2lkIjoiY2M5NGMyZDYtNmU3MC00MjZjLWFmZDEtNzFjOGZhNWY0ODkwIiwidWlkIjoiMDB1MWlieDhsaGIzYW12SFIwaDgiLCJzY3AiOlsiYWNjb3VudF9yZWFkIiwiZW1haWwiLCJjYW1wYWlnbl9kYXRhIiwicHJvZmlsZSIsImFkZHJlc3MiLCJjb250YWN0X2RhdGEiLCJvcGVuaWQiXSwic3ViIjoiZ2Vla3NxdWFkIiwicGxhdGZvcm1fdXNlcl9pZCI6ImQzYTA2ZTYyLTJmMTQtNGQ5Ni05NDUzLTI2NTkxYWI1M2YwZCJ9.saJxgUYx1_OKUteH01KV71vzjaxqTKnZ3XfD4QKtsIkjrmcf4tUWEFSzjuF1kdxLvlMoCyr_MwO81ApwYX2UU0YRhkVsfw2VLR4KpO89ILul7ZJZyzzsVKak8BrgLhqR1AB7X116VRjsH74vD4D-1e4uKKUlK4yefsZE14kFgDZNi74_WPn5FBGeicwMsStdG3qms8X-6LqIxuZ5fXgSxK9wDEBe-xJfJSqAqo1vJKMqw2JT_NvBBLDXqoSg0_q_0IqbxXEMHdX2J_DHGKrY7w1QQGmeBU_BIlewQPUxUGX-dbRavxncBpItt1tsRLBIiPbPkQ_zDN8_J9lPIxJksA",
  "scope": "account_read offline_access",
  "refresh_token": "s8Mu4hfiwmug7ru4Rcoo4hjkawiw4OUTH2ixvsy3b8"
}

Step 8 (Optional): Refresh the Access Token

To avoid having a user reauthenticate and reauthorize your application with Constant Contact, use the refresh token to get a new access token and a new refresh token. To refresh the access token, send a POST request to the https://authz.constantcontact.com/oauth2/default/v1/token authorization endpoint.

Constant Contact rate limits the token endpoint. A 429 response will likely be returned if you attempt to refresh an access token before every V3 API request. Constant Contact recommends that you only send a refresh token request to get a new access token if your existing access token is expired or about to expire.

Request parameters

  • refresh_tokenRequired. The refresh token that corresponds to the access token you are trying to refresh.

  • grant_typeRequired. The value is refresh_token. This specifies that the purpose of this request is to refresh an access token.

  • redirect_uriRequired. Enter the redirect URI that was used as part of the authorization request URL.

Example Request

/**
     * This method exchanges a refresh token for a new access token and a new bearer token.
     * @param clientId Your application's client id.
     * @param refreshToken A refresh token.
     * @param redirectUri URI used to redirect users to your application after they complete authorization.
     * @return A JSON object containing a new access token and a new bearer token
     * @throws URISyntaxException
     * @throws IOException
     */
    public String refreshPKCEAccessToken(String clientId, String refreshToken, String redirectUri) throws URISyntaxException, IOException {
        URI pkceTokenRefreshUri = new URIBuilder("https://authz.constantcontact.com/oauth2/default/v1/token")
                .addParameter("client_id", clientId)
                .addParameter("redirect_uri", redirectUri)
                .addParameter("refresh_token", refreshToken)
                .addParameter("grant_type", "refresh_token")
                .build();
        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(pkceTokenRefreshUri)
                .POST(HttpRequest.BodyPublishers.ofString(""))
                .header("Accept","application/json")
                .header("Content-Type", "application/x-www-form-urlencoded")
                .build();
        HttpResponse<String> httpResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
 
        return httpResponse.body();
    }

Example Response

{
  "token_type": "Bearer",
  "expires_in": "28800",
  "access_token": "eyJraWQiOiIzSDdLVVpkSDlyNTh6RVVDSzNYMlR1b2o0SjV0eldWUF9FRUxQTDd6X3hjIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULkZ6YUUtTVFFb2dKQ1VzZlhSUGtBZldobDllckJndVlYRWphOGFXd1RyRzgiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmNvbnN0YW50Y29udGFjdC5jb20vb2F1dGgyL2F1czFsbTNyeTltRjd4MkphMGg4IiwiYXVkIjoiaHR0cHM6Ly9hcGkuY2MuZW1haWwvdjMiLCJpYXQiOjE2NDQ0Mjc3NzMsImV4cCI6MTY0NDQ1NjU3MywiY2lkIjoiY2M5NGMyZDYtNmU3MC00MjZjLWFmZDEtNzFjOGZhNWY0ODkwIiwidWlkIjoiMDB1MWlieDhsaGIzYW12SFIwaDgiLCJzY3AiOlsiYWNjb3VudF9yZWFkIiwiZW1haWwiLCJjYW1wYWlnbl9kYXRhIiwicHJvZmlsZSIsImFkZHJlc3MiLCJjb250YWN0X2RhdGEiLCJvcGVuaWQiXSwic3ViIjoiZ2Vla3NxdWFkIiwicGxhdGZvcm1fdXNlcl9pZCI6ImQzYTA2ZTYyLTJmMTQtNGQ5Ni05NDUzLTI2NTkxYWI1M2YwZCJ9.saJxgUYx1_OKUteH01KV71vzjaxqTKnZ3XfD4QKtsIkjrmcf4tUWEFSzjuF1kdxLvlMoCyr_MwO81ApwYX2UU0YRhkVsfw2VLR4KpO89ILul7ZJZyzzsVKak8BrgLhqR1AB7X116VRjsH74vD4D-1e4uKKUlK4yefsZE14kFgDZNi74_WPn5FBGeicwMsStdG3qms8X-6LqIxuZ5fXgSxK9wDEBe-xJfJSqAqo1vJKMqw2JT_NvBBLDXqoSg0_q_0IqbxXEMHdX2J_DHGKrY7w1QQGmeBU_BIlewQPUxUGX-dbRavxncBpItt1tsRLBIiPbPkQ_zDN8_J9lPIxJksA",
  "scope": "account_read offline_access",
  "refresh_token": "s8Mu4hfiwmug7ru4Rcoo4hjkawiw4OUTH2ixvsy3b8"
}
  • token_type — The value is always set to Bearer.

  • expires_in - The access_token expiration timestamp, in seconds.

  • access_token — The response body returns a new access_token value.

  • refresh_token — The response body returns a new refresh_token value.