For all public clients (native and single-page applications), such as client-side JavaScript applications and mobile applications, rather than using the OAuth2 Implicit Flow to authorize your application, Constant Contact strongly recommends using the more secure PKCE flow. However, you may choose to use the Implicit Flow if you are unable to use the PKCE Flow.
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.
To authorize your application to access a user’s Constant Contact data, you must create an authorization request and add it to your application’s code. When a user logs in to your application, the authorization request is used to direct the user to Constant Contact to authenticate their user account and to authorize your application to use their Constant Contact data.
After the user successfully grants your application access to their data, Constant Contact redirects the user to your chosen redirect URI and appends the access token. Your application uses the access token to make V3 API requests to get the user’s Constant Contact data. Your V3 API requests must include the access token in the Authorization
request header. The OAuth2 Implicit Flow does not use your application secret.
Unlike the Authorization Code and PKCE Flows, the Implicit Flow does not use refresh tokens. When an access token expires, users must reauthorize your application with Constant Contact. Access tokens automatically expire after 480 minutes (28,800 seconds). Access tokens have a maximum lifetime of 8 hours.
To use the Implicit Flow for authorization, complete the steps that follow.
Step 1: Create an 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, make a GET request to the authorization endpoint and include all required request query parameters: https://authz.constantcontact.com/oauth2/default/v1/authorize
.
For example, the following shows an encoded URL authorization request:
https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id={your_client_id}&redirect_uri={variable}&response_type=token&state={encoded string value(s)}&nonce={encoded string value}&scope={contact_data%20campaign_data}
Request Parameters
-
client_id
— Required. 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_uri
— Required. The absolute URI that Constant Contact redirects the user to after they grant access to your application. Wildcards are not supported except at the domain root level. For more information about wildcard restrictions, see the Create an Application Integration page. -
response_type
— Required. This flow uses thetoken
value to return a bearer token value in the response. -
state
— Required. For security, developers usestate
to encode and securely pass arbitrary string value(s) in the authorization request URL to uniquely identify a user’s session and to prevent cross-site request forgery. You decode the encoded value to verify it is authentic when validating the JWT token. -
nonce
— Required. Specify an arbitrary value. -
scope
— Optional. A list of the scopes that your application requires. The V3 API currently supports theaccount_read
,account_update
,contact_data
, andcampaign_data
scopes. Scope names must be space-delimited. For example: {contact_data%20campaign_data%20offline_access}.For more information on scopes and the specific scopes required by each V3 API endpoint, see the Scopes Overview page.
Step 2: Get Authorization
Add the authorization request to your application to direct users of your application to Constant Constant where they are prompted to sign in to authenticate their user account. Next, Constant Contact displays the Permission Request screen allowing the user to authorize your application to access the Constant Contact data that your application requests.
Step 3: Get the Access Token
After a user successfully grants your application access to their data, Constant Contact redirects the user to your chosen redirect URI and appends the access token value (access_token
) and type (token_type=Bearer
) as URL fragments in the authorization response.
For example:
http://localhost:8080/authorization-code/callback#access_token=eyJraWQiOiIzSDdLVVpkSDlyNTh6RVVDSzNYMlR1b2o0SjV0eldWUF9FRUxQTDd6X3hjIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULkZ6YUUtTVFFb2dKQ1VzZlhSUGtBZldobDllckJndVlYRWphOGFXd1RyRzgiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmNvbnN0YW50Y29udGFjdC5jb20vb2F1dGgyL2F1czFsbTNyeTltRjd4MkphMGg4IiwiYXVkIjoiaHR0cHM6Ly9hcGkuY2MuZW1haWwvdjMiLCJpYXQiOjE2NDQ0Mjc3NzMsImV4cCI6MTY0NDQ1NjU3MywiY2lkIjoiY2M5NGMyZDYtNmU3MC00MjZjLWFmZDEtNzFjOGZhNWY0ODkwIiwidWlkIjoiMDB1MWlieDhsaGIzYW12SFIwaDgiLCJzY3AiOlsiYWNjb3VudF9yZWFkIiwiZW1haWwiLCJjYW1wYWlnbl9kYXRhIiwicHJvZmlsZSIsImFkZHJlc3MiLCJjb250YWN0X2RhdGEiLCJvcGVuaWQiXSwic3ViIjoiZ2Vla3NxdWFkIiwicGxhdGZvcm1fdXNlcl9pZCI6ImQzYTA2ZTYyLTJmMTQtNGQ5Ni05NDUzLTI2NTkxYWI1M2YwZCJ9.saJxgUYx1_OKUteH01KV71vzjaxqTKnZ3XfD4QKtsIkjrmcf4tUWEFSzjuF1kdxLvlMoCyr_MwO81ApwYX2UU0YRhkVsfw2VLR4KpO89ILul7ZJZyzzsVKak8BrgLhqR1AB7X116VRjsH74vD4D-1e4uKKUlK4yefsZE14kFgDZNi74_WPn5FBGeicwMsStdG3qms8X-6LqIxuZ5fXgSxK9wDEBe-xJfJSqAqo1vJKMqw2JT_NvBBLDXqoSg0_q_0IqbxXEMHdX2J_DHGKrY7w1QQGmeBU_BIlewQPUxUGX-dbRavxncBpItt1tsRLBIiPbPkQ_zDN8_J9lPIxJksA&token_type=Bearer&expires_in=7200&scope=account_read&state=foo
For more information about authorization request errors see Create an Application Integration.
Step 4: Validate the Access Token
For each new access token that you receive, follow best security practices by validating the signature and claims for the access token by completing the procedures that follow.
Load the JSON web-key Set
Use the method that follows to load the Constant Contact public JSON web-key set used to get the access token.
/**
* 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 (this is the value that was returned in the previous Load the JSON web-key set step).
/**
* 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);
}
If the access token is not valid or if a required claim is missing or incorrect, an exception occurs.
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.
To check the claims, use the previous code example, except replace jwtConsumer.processToClaims(jwt);
with the following:
JwtClaims claims = consumer.processToClaims(token);
String cid = claims.get("cid")
if (! apiKey.equals(cid)) {
throw new InvalidJwtException("Api key in token incorrect"); in t
}
String userId = claims.get("platform_user_id")
if (userId == null) {
throw new InvalidJwtException("Token is missing platform_user_id");
}
Step 5: 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}
.
If the access token expires, users must reauthenticate and reauthorize your application through Constant Contact. Making an API call with an expired access token returns a 401 unauthorized status code.
Step 6 (Optional): Check the Access Token Expiration Timestamp
You can quickly check to see if the access token is expired by 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();
}