Immediate Access Token Expiration?

MasonA93
Campaign Collaborator
0 Votes

So I've hit a wall in the last 24 hours on the Constant Contact implementation. I'm using the Oauth flow and I have obtained an access token and refresh token and am now trying to Post a contact. The issue is in I'm hitting the Unauthorized/Unauthorized message when doing so. 

 

Going off what I have read in other posts, I then created an automatic refresh mechanic so that if a request comes back with an httpCode of 401, we do a quick refresh of the access tokens and try the request over with that new access token. No matter what I do, it's always coming back as Unauthorized/Unauthorized. 

 

Since every single thing I've seen in this forum states that "Unauthorized/Unauthorized" messages mean that the access token has expired, how is that occurring if I'm refreshing the access token (and seeing that new access token update and within the request) and then making the same request again (w/ newly updated access token) only to get the same error message?

 

I've tried every solution I can come up within in the last 6 hours and at this point I need a little help. Here is the class I'm working off of, follow the logic of the createContact() function.

 

 

<?php  

class Constant_Contact extends Email
{	
	private $clientKey;
	private $clientSecret;
	private $connection;
	private $refreshAttempts = 0;

	const BASIC = 1;
	const BEARER = 2;
	
	function __construct()
	{
		$this->clientKey = $GLOBALS['fw']['config']['constantContact']['clientKey'];
		$this->clientSecret = $GLOBALS['fw']['config']['constantContact']['clientSecret'];
	}

	function storeInit(Store $store)
	{
		$connection = StoreManager::singleton()->WHERE(array('store_id' => $store->id, 'type_id' => Connection_To_Store::ConstantContact))->getObject('Connection_To_Store');
		if (isset($connection->id) && $connection->id) {
			$this->connection = $connection;
		}
		return false;
	}

	// creates a URL that the browser will redirect to obtain a code for authorization
	public function getAuthorizationURL()
	{

		$baseUrl = 'https://authz.constantcontact.com/oauth2/default/v1/authorize';
		$scope = '+contact_data+offline_access';
		$responseType = 'code';
		$state = 'asdf08a8dfljsfs1';
		$redirectUri = 'http://devadmin.xxxx.com/admin/connections/ConstantContactGetCode';

		return $baseUrl . '?client_id=' . $this->clientKey . '&scope=' . $scope . '&response_type=' . $responseType . '&state=' . $state . '&redirect_uri=' . urlencode($redirectUri);
	}


	// Using the ?code from authorization, we will grab the tokens for using Constant Contact
	public function getTokens($code)
	{
		if (!$code)
			return;

		$redirectUri = 'http://devadmin.xxxx.com/admin/connections/ConstantContactGetCode';
		$baseUrl = 'https://authz.constantcontact.com/oauth2/default/v1/token?code=' . $code . '&redirect_uri=' . $redirectUri . '&grant_type=authorization_code';

		return $this->request(Constant_Contact::BASIC, FWHTTPRequest::POST, $baseUrl, $data);

	}

	public function createContact(User $user)
	{
		if (!$user->id)
			return;

		if (!$this->connection->id)
			return;

        $data['api_key'] = $this->clientKey;
        $data['first_name'] = $user->first_name;
        $data['last_name'] = $user->last_name;
        $data['email_address']['address'] = $user->email;
        $data['email_address']['permission_to_send'] = 'implicit';

		return $this->request(Constant_Contact::BEARER, FWHTTPRequest::POST, 'https://api.cc.email/v3/contacts', $data);
	}

	private function refreshToken()
	{

		$baseUrl = 'https://authz.constantcontact.com/oauth2/default/v1/token?refresh_token=' . $this->connection->key_3 . '&grant_type=refresh_token';
		$data = $this->request(Constant_Contact::BASIC, FWHTTPRequest::POST, $baseUrl);

		$connection = new Connection_To_Store($this->connection->id);
		$connection->access_token = $data->access_token;
		$connection->account_index = $data->expires_in;
		$connection->key_3 = $data->refresh_token;
		$connection->save();

		$this->connection = $connection;
	}

	private function request($type, $mode = FWHTTPRequest::POST, $endpoint, $data = array(), $refresh = false)
	{
		if (!$type)
			return;

		if (!$this->clientKey)
			return;

		if ($refresh && $this->refreshAttempts == 1)		
        	$this->refreshToken();

        $fwoptions = new FWHTTPRequestOptions();
        $fwoptions->action = $mode;
        $fwoptions->contentType = 'json';
        $fwoptions->returnResult = true;
        $fwoptions->timeout = 300;
        $fwoptions->connectionTimeout = 30;
        $fwoptions->maxRedirects = -1;

        if ($type == Constant_Contact::BEARER) {

			$authorization = 'Authorization: Bearer ' . $this->connection->access_token;
	        $fwoptions->headers = array($authorization, 'Content-Type: application/json');

        } else if ($type == Constant_Contact::BASIC) {       	

			$credentials = base64_encode($this->clientKey . ':' . $this->clientSecret);
			$authorization = 'Authorization: Basic ' . $credentials;
	        $fwoptions->headers = array($authorization, 'Content-Type: application/x-www-form-urlencoded');
        }

        if (is_array($data) && count($data)) {
        	foreach($data as $key => $value) {
        		$fwoptions->postFields[$key] = $value;
        	}
        }

        try {
                LogManager::info(__METHOD__ . ' making call to ' . $endpoint);

                if ($request = new FWHTTPRequest($endpoint, $fwoptions, true)) {

                		// if we got here, the access token needs to be renewed
                		if ($request->httpCode == '401') {

                			$this->refreshAttempts++;
                			$this->request($type, $mode, $endpoint, $data, true);

                		} else if ($request->httpCode != '404') {

                                if ($request->responseBody == 'false') {
                                        exit;
                                }
                                $content = $request->responseBody;
                                $content = json_decode($content);

                                if (isset($content->error_key) && $content->error_key)
        							Log_Error::create($_SESSION['store']->id, Log_Error::Connections, $content->error_message, $content->error_key, $endpoint);

                                if (isset($content->error) && $content->error)
        							Log_Error::create($_SESSION['store']->id, Log_Error::Connections, $content->error_description, $content->error, $endpoint);

                                return $content;
                        }   
                        print_r($request);                                                   
                }

        } catch(exception $e) {
                LogManager::error(__METHOD__ . ' endpoint: ' . $endpoint . ', exception: ' . $e);
                throw $e;
        }
	}

}

?>

 

8 REPLIES 8
MasonA93
Campaign Collaborator
0 Votes

It's been 4 days without a response. Can I please get some guidance as this is stalled out development.

MasonA93
Campaign Collaborator
0 Votes

It's now been 8 days without a reply. I'm basically going to have to suggest my customer switches to a different email provider.

 

If you're going to go Oauth2 for authorization you should have the support to handle the issues that comes with it. So far I've integrated Mailchimp, Emma and SendinBlue and none of them use Oauth2 and they all have had better support (some of which I did not actually have to contact due to the resources being so thorough).

Courtney_E
Employee
0 Votes

Hello MasonA93,

 

Thank you for reaching out to Constant Contact API Developer Support. My team is here to assist outside software developers with questions about building into Constant Contact's API.

 

We appreciate your patience and apologize for our delay in response, as our team answers inquiries in the order that they are received.

 

While you mentioned that you were receiving a 401 response, after taking a look in our logs, it looks like the responses for your key's endpoint calls are returning a 400 response on our end with the error "create_source is missing, create_source does not have a valid value".

 

In your example, you provided the following code: 

        $data['api_key'] = $this->clientKey;

        $data['first_name'] = $user->first_name;

        $data['last_name'] = $user->last_name;

        $data['email_address']['address'] = $user->email;

        $data['email_address']['permission_to_send'] = 'implicit';

 

Which logged the request body in our system as:

{
"api_key":"-----------,
"first_name":"-----------",
"last_name":"-----------",
"email_address":{"address":"-----------","permission_to_send":"implicit"
}

 

When making a POST call to /v3/contacts, you'll want to format the request body like so:

{

"email_address": {

"address": "-----------",

"permission_to_send":"implicit"

},

"first_name":"-----------",

"last_name":"-----------",

"create_source": "Contact",

 "list_memberships": ["-----------"]

}

 

So, the three things you'll want to update are:

  • In V3, you won't include the api_key/client_id as a parameter of the call, as this is already specified within the bearer token of your authorization header and can cause issues with the system's ability to read some endpoint calls.
  • Make sure to include the "create_source", which can be "Contact" or "Account".
  • While not including a list ID will not cause an error with this endpoint (as it does with some others), adding a contact without specifying which list to add it to will essentially leave it floating in the account. Without a list, a contact can't be sent to.

 

Please have a look and let us know if you have any other questions!


Regards,

Courtney E.
Tier II API Support Engineer

Did I answer your question?
If so, please mark my post as an "Accepted Solution" by clicking the Accept as Solution button in the bottom right hand corner of this post.
MasonA93
Campaign Collaborator
0 Votes

I'm actually getting Unauthorized/Unauthorized going through the Getting Started tutorials using build in "TRY" form (and yes, I'm signed in on my developer account).

 

example.png

 

Unauthorized/Unauthorized certainly sounds like a credentials or perms issue but it's starting to feel the general catch-all error that I get for anything (which isn't very helpful).

Courtney_E
Employee
0 Votes

Hello MasonA93,

 

Thank you for reaching out to Constant Contact API Developer Support. My team is here to assist outside software developers with questions about building into Constant Contact's API.

 

After doing some testing, I was able to replicate the error message that you are getting. 

 

Before clicking "Try" in the API Reference tester, you must first click the "OAuth" button to the right in order to grant the tester access to your account.

 

If you continue to experience this issue even after granting access, please feel free to email us at webservices@constantcontact.com and reference case #30595753 for further assistance.

 

Please have a look and let us know if you have any other questions!


Regards,

Courtney E.
Tier II API Support Engineer

Did I answer your question?
If so, please mark my post as an "Accepted Solution" by clicking the Accept as Solution button in the bottom right hand corner of this post.
MasonA93
Campaign Collaborator
0 Votes

While it's helpful for you to give me details on the creation of the contact, we're not getting to the point where that's a problem in the above call. It's still the Unauthorized / Unauthorized issue 

 

 

FWHTTPRequest Object
(
    [url] => https://api.cc.email/v3/contacts
    [action] => POST
    [options] => FWHTTPRequestOptions Object
        (
            [action] => POST
            [connectionTimeout] => 30
            [timeout] => 300
            [maxRedirects] => -1
            [headers] => Array
                (
                    [0] => Authorization: Bearer NOT-PASTING-IT                    [1] => Content-Type: application/json
                )

            [username] => 
            [password] => 
            [returnResult] => 1
            [postFields] => Array
                (
                    [first_name] => aaaa
                    [last_name] => aaaa
                    [create_source] => Contact
                    [email_address] => Array
                        (
                            [address] => nogame77@gmail.com
                            [permission_to_send] => implicit
                        )

                    [list_memberships] => Array
                        (
                            [0] => 6bcf3306-4c7d-11ed-89b8-fa163e9af8f6
                        )

                )

            [putData] => 
            [contentType] => json
        )

    [errorCode] => 0
    [httpCode] => 401
    [responseInfo] => Array
        (
            [url] => https://api.cc.email/v3/contacts
            [content_type] => application/json
            [http_code] => 401
            [header_size] => 713
            [request_size] => 606
            [filetime] => -1
            [ssl_verify_result] => 0
            [redirect_count] => 0
            [total_time] => 0.067426
            [namelookup_time] => 0.005209
            [connect_time] => 0.006597
            [pretransfer_time] => 0.037343
            [size_upload] => 211
            [size_download] => 59
            [speed_download] => 880
            [speed_upload] => 3149
            [download_content_length] => 59
            [upload_content_length] => 211
            [starttransfer_time] => 0.067386
            [redirect_time] => 0
            [redirect_url] => 
            [primary_ip] => 13.32.208.99
            [certinfo] => Array
                (
                )

            [primary_port] => 443
            [local_ip] => 172.26.8.231
            [local_port] => 54752
            [http_version] => 2
            [protocol] => 2
            [ssl_verifyresult] => 0
            [scheme] => HTTPS
            [appconnect_time_us] => 37306
            [connect_time_us] => 6597
            [namelookup_time_us] => 5209
            [pretransfer_time_us] => 37343
            [redirect_time_us] => 0
            [starttransfer_time_us] => 67386
            [total_time_us] => 67426
        )

    [responseHeaders] => Array
        (
            [Content-Type] => application/json
            [Content-Length] => 59
            [Connection] => keep-alive
            [Date] => Wed, 07 Dec 2022 21:57:03 GMT
            [x-amzn-RequestId] => 46ae8af4-b495-4149-91b2-9c93e58c0014
            [Access-Control-Allow-Origin] => *
            [Access-Control-Allow-Headers] => Content-Type,X-Amz-Date,Authorization,X-Api-Key
            [x-amzn-ErrorType] => UnauthorizedException
            [WWW-Authenticate] => Bearer realm=api.cc.email
            [x-amz-apigw-id] => cy7XaHsnIAMFebg=
            [x-eig-tracking-id] => 
            [Access-Control-Allow-Methods] => GET,POST,PUT,DELETE,PATCH,OPTIONS
            [X-Cache] => Error from cloudfront
            [Via] => 1.1 7b24ef2363096031b08114f59c8248f2.cloudfront.net (CloudFront)
            [X-Amz-Cf-Pop] => IAD66-C1
            [X-Amz-Cf-Id] => QqjbS72fX88n5_2t4XolT3ixqwBRDmuf45r-bvGqcrS4svjv21PPuA==
        )

    [responseBody] => {"error_key":"unauthorized","error_message":"Unauthorized"}
    [errorMessage] => 
)

 

Courtney_E
Employee
0 Votes

Hello MasonA93,

 

Thank you for reaching out to Constant Contact API Developer Support. 

 

Based on your examples in comparison with our logs, it looks like when sending the request, the access token is being truncated and the full bearer token is not being sent in the request.

 

Because it does appear to be a valid portion of a JWT token, our best guess is that there may be an issue with how the token is being parsed and/or stored as a variable once it is returned, causing only a portion of the full value to be stored, which would cause the error that you are seeing.

 

You can also reach out to our team directly if desired, by emailing us at webservices@constantcontact.com and referencing case #30600138.

 

Please have a look and let us know if you have any other questions!


Regards,

Courtney E.
Tier II API Support Engineer

Did I answer your question?
If so, please mark my post as an "Accepted Solution" by clicking the Accept as Solution button in the bottom right hand corner of this post.
MasonA93
Campaign Collaborator
0 Votes

Coming back to this topic after a bit. You helped me format the request a bit better and that's very helpful (although it's odd that "create_source" isn't identified as REQUIRED on the API documentation). But my issue was more so on the topic of UNAUTHORIZED/UNAUTHORIZED. Due to that fact, I cannot actually make the request at all. After a few minor edits (adding what you told me to add on the above code, this is now my response):

 

FWHTTPRequest Object
(
    [url] => https://api.cc.email/v3/contacts
    [action] => POST
    [options] => FWHTTPRequestOptions Object
        (
            [action] => POST
            [connectionTimeout] => 30
            [timeout] => 300
            [maxRedirects] => -1
            [headers] => Array
                (
                    [0] => Authorization: Bearer eyJraWQiOiJpSVFyRVVHX2tlWkhPY2VTVEZMdW1mTGZGTWNLNHZFSmotdXZEelZtZlNFIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULnloang0OWtLN1pBOEQ5TXQwMnYtQlVCZ2I1VGM1MUNYUjlFY1FjUU5CUkEub2FydTZsYjdqMGJZSDFJNVgwaDYiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmNvbnN0YW50Y29udGFj
                    [1] => Content-Type: application/json
                )

            [username] => 
            [password] => 
            [returnResult] => 1
            [postFields] => Array
                (
                    [first_name] => mason
                    [last_name] => ambrose
                    [create_source] => Contact
                    [email_address] => Array
                        (
                            [address] => nogame77@gmail.com
                            [permission_to_send] => implicit
                        )

                    [list_memberships] => Array
                        (
                            [0] => 6bcf3306-4c7d-11ed-89b8-fa163e9af8f6
                        )

                )

            [putData] => 
            [contentType] => json
        )

    [errorCode] => 0
    [httpCode] => 401
    [responseInfo] => Array
        (
            [url] => https://api.cc.email/v3/contacts
            [content_type] => application/json
            [http_code] => 401
            [header_size] => 713
            [request_size] => 606
            [filetime] => -1
            [ssl_verify_result] => 0
            [redirect_count] => 0
            [total_time] => 0.031846
            [namelookup_time] => 0.001038
            [connect_time] => 0.001355
            [pretransfer_time] => 0.010297
            [size_upload] => 211
            [size_download] => 59
            [speed_download] => 1903
            [speed_upload] => 6806
            [download_content_length] => 59
            [upload_content_length] => 211
            [starttransfer_time] => 0.031818
            [redirect_time] => 0
            [redirect_url] => 
            [primary_ip] => 108.138.85.109
            [certinfo] => Array
                (
                )

            [primary_port] => 443
            [local_ip] => 172.26.8.231
            [local_port] => 59022
            [http_version] => 2
            [protocol] => 2
            [ssl_verifyresult] => 0
            [scheme] => HTTPS
            [appconnect_time_us] => 10253
            [connect_time_us] => 1355
            [namelookup_time_us] => 1038
            [pretransfer_time_us] => 10297
            [redirect_time_us] => 0
            [starttransfer_time_us] => 31818
            [total_time_us] => 31846
        )

    [responseHeaders] => Array
        (
            [Content-Type] => application/json
            [Content-Length] => 59
            [Connection] => keep-alive
            [Date] => Wed, 07 Dec 2022 17:49:52 GMT
            [x-amzn-RequestId] => 37062998-78db-41e9-9c2e-a392a4c951a4
            [Access-Control-Allow-Origin] => *
            [Access-Control-Allow-Headers] => Content-Type,X-Amz-Date,Authorization,X-Api-Key
            [x-amzn-ErrorType] => UnauthorizedException
            [WWW-Authenticate] => Bearer realm=api.cc.email
            [x-amz-apigw-id] => cyXKDFESIAMFXYA=
            [x-eig-tracking-id] => 
            [Access-Control-Allow-Methods] => GET,POST,PUT,DELETE,PATCH,OPTIONS
            [X-Cache] => Error from cloudfront
            [Via] => 1.1 4685cae701bd588fa0176a1c8b1e52f4.cloudfront.net (CloudFront)
            [X-Amz-Cf-Pop] => IAD12-P2
            [X-Amz-Cf-Id] => 1mjFnK-XAj1CZf_2JFkZuaAR4ClA-F9R9D8eKe-dPcQAFebW9JxG4A==
        )

    [responseBody] => {"error_key":"unauthorized","error_message":"Unauthorized"}
    [errorMessage] => 
)

 

As to my original question, why am I getting Unauthorized / Unauthorized ?

Resources
Developer Portal

View API documentation, code samples, get your API key.

Visit Page

Announcements

API Updates

Join our list to be notified of new features and updates to our V3 API.

Sign Up