I'm trying to run the PHP example code documented here on the MUX website. I have just generated a brand new API key + secret and have stored them in a mux-credentials.php file:
define('MUX_TOKEN_ID', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
define('MUX_TOKEN_SECRET', '<base64-encoded-string-here>');
I used composer to install Firebase\JWT as instructed here and then ran the exact code specified in the MUX documentation:
// load libs
require_once 'vendor/autoload.php';
// load MUX credentials
require_once 'mux-credentials.php';
use \Firebase\JWT\JWT;
$myId = "<MY-ASSET-ID-HERE>"; // Enter the id for which you would like to get counts here
$myIdType = "asset_id"; // Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
$keyId = MUX_TOKEN_ID; // Enter your signing key id here
$keySecret = MUX_TOKEN_SECRET; // Enter your base64 encoded private key here
$payload = array(
"sub" => $myId,
"aud" => $myIdType,
"exp" => time() + 600, // Expiry time in epoch - in this case now + 10 mins
"kid" => $keyId
);
$jwt = JWT::encode($payload, base64_decode($keySecret), 'RS256');
print "$jwt\n";
This code barfs with a fatal error:
PHP Warning: openssl_sign(): supplied key param cannot be coerced into a private key in /home/example/vendor/firebase/php-jwt/src/JWT.php on line 225
PHP Fatal error: Uncaught DomainException: OpenSSL unable to sign data in /home/example/vendor/firebase/php-jwt/src/JWT.php:227
Stack trace:
#0 /home/example/vendor/firebase/php-jwt/src/JWT.php(195): Firebase\JWT\JWT::sign()
#1 /home/example/people-watching.php(30): Firebase\JWT\JWT::encode()
#2 {main}
thrown in /home/example/vendor/firebase/php-jwt/src/JWT.php on line 227
If I remove the last param in the JWT::encode call like so:
$jwt = JWT::encode($payload, base64_decode($keySecret));
Then the code successfully runs, and generates a long base64-encoded string. That JWT string, however, results in an error when I attempt to use it to contact the API:
curl 'https://stats.mux.com/counts?token=<JWT-HERE>'
The MUX api responds with:
{"error":{"type":"internal error","messages":["Could not get signing key."]}}
Can anyone help me fix this code so that I can contact the MUX API and retrieve the requested information about my asset id?
EDIT: I am grateful for the answers below pointing out that one should use a signing key rather than one's API credentials but, putting aside my confusion about why we need such a thing as a signing key, the curl request to create a signing key doesn't work either. This curl request (i've redacted my MUX_TOKEN_ID and MUX_TOKEN_SECRET):
curl -X POST \
-H "Content-Type: application/json" \
-u <MUX_TOKEN_KEY>:<MUX_TOKEN_SECRET> \
'https://api.mux.com/system/v1/signing-keys?product=data'
fails, returning this error:
{"error":{"type":"not_found","messages":["The requested resource either doesn't exist or you don't have access to it."]}}
Jared Smith here, one of the community engineers at Mux. Let me see if I can help clear this up a bit. The wording in that guide is a bit unclear, and I'll work internally to get that cleaned up, but I think I know where the confusion is here.
It looks to me like you're passing in your API token ID for $keyId and your API token secret for $keySecret.
Instead, you should first make a call to the /system/v1/signing-keys endpoint of the API (using your token ID and secret, as explained in step one of that guide) to create a signing key.
You then pass the signing key ID as $keyId, and the base 64 encoded signing key itself as $keySecret.
Another test you can use to make sure you've got the right signing key is to base 64 decode it, and make sure it begins with -----BEGIN RSA PRIVATE KEY----- and ends with -----END RSA PRIVATE KEY-----.
Hopefully that clears things up for you. If not, feel free to reach out for more help!
It looks like you're trying to use the API key and secret to sign the JWT. Mux instead is looking for separate a signing key
This signing key can be generated with the API key and secret you're currently using in a request to https://api.mux.com/system/v1/signing-keys?product=data
You can see an example of this request here: https://docs.mux.com/guides/data/see-how-many-people-are-watching#1-create-a-signing-key
The kid value of the JWT claims would then instead be set to the Key ID returned when signing key was created.
I'm a community engineer working with Mux – if you have any ideas on how you feel this process could be more clear, don't hesitate to provide any feedback!
Related
I have a NodeJS application generating JSON Web Tokens with the PS256 algorithm. I want to try and verify the signatures in these tokens in a PHP application.
So far I've got the following:
My JWT:
eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwMTBiYjllYS00YTg0LTQ1ZTMtOTg5My0wYzNhNDYxZmQzMGUiLCJpYXQiOjE2MDU4OTI5NjcsImV4cCI6MTYwNjQ5Nzc2NywiYXVkIjoiNzBiYzcxMTQ1MWM2NDBjOTVlZjgzYjdhNDliMWE0MWIiLCJpc3MiOiIyM2ZhYTRiNC0wNmVlLTRlNGEtYTVjZC05NjJmOTRhMjEzYmYiLCJqdGkiOiI1MTNiYjczZC0zOTY3LTQxYzUtODMwOS00Yjc1ZDI4ZGU3NTIifQ.kLtaSYKyhqzx7Dc7UIz7tqU8TsXabRLxGiaqw21lgCcuf_eBvpiLkFOuXpUs-V8XQunQg8jV-bKlKUIb0pLvipjhRP50IwKDClQgNtIwn4yyX5RyDNGJur0qHNnkHMLaF11NsXGPyhvh-6ogSZjWgyZnkQJkXpz4jggBetwqz1hnicapGfNb6C-UdRcOLyCaiMD4OmvniFVCY6YoKlC6eHdwxsgHAxOSgD1QKiiQX_yAe39ja_axZD2Ii3QaNgO0WXzfWMbqRg_yl0y3kjQFys9iXGvQ1JIKDMLffR3rKVL5PgKSU3e472xcPKf6PNSJzphPi1G_xH2gqg1VVXo3Lg
Decoded:
Header:
(
[alg] => PS256
[typ] => JWT
)
Body:
(
[sub] => 010bb9ea-4a84-45e3-9893-0c3a461fd30e
[iat] => 1605892967
[exp] => 1606497767
[aud] => 70bc711451c640c95ef83b7a49b1a41b
[iss] => 23faa4b4-06ee-4e4a-a5cd-962f94a213bf
[jti] => 513bb73d-3967-41c5-8309-4b75d28de752
)
sub is a GUID user ID (we utilize GUIDs so that if a user's ID is leaked no information can be extrapolated, like the number of users in our system or when a user signed up)
iat is the epoch time that the token was issued (UTC)
exp is the epoch time that the token will expire (UTC)
aud doesn't conform to the JWT spec. I abused this claim to mitigate the effects of stolen tokens. It's the MD5 hash of data sent with every client request that would be difficult for someone to guess. So if someone were to steal this token and use it without sending the appropriate passphrase, the token would be automatically revoked
iss also doesn't conform to the JWT spec. I abused this claim to list the ID of the key used for signing the JWT. This way I can rotate my public-private key pair and know which key to use when validating signatures
jti is a GUID uniquely identifying the JWT. Compared against an in-memory store of revoked tokens
I went with the PS256 algorithm over RS256 because I read on a blog post that it's more secure. Honestly I don't know the difference.
I went with the PS256 algorithm over ES256 because upon testing I found that while ES256 generated smaller signatures (and therefore smaller tokens), it took about 3x longer to compute. My goal is to make this app as scalable as possible, so long computation time is to be avoided.
My public key:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA0wO7By66n38BCOOPqxnj78gj8jMKACO5APe+M9wbwemOoipeC9DR
CGxC9q+n/Ki0lNKSujjCpZfnKe5xepL2klyKF7dGQwecgf3So7bve8vvb+vD8C6l
oqbCYEOHdLCDoC2UXEWIVRcV5H+Ahawym+OcE/0pzWlNV9asowIFWj/IXgDVKCFQ
nj164UFlxW1ITqLOQK1WlxqHAIoh20RzpeJTlX9PYx3DDja1Pw7TPomHChMeRNsw
Z7zJiavYrBCTvYE+tm7JrPfbIfc1a9fCY3LlwCTvaBkL2F5yeKdH7FMAlvsvBwCm
QhPE4jcDINUds8bHu2on5NU5VmwHjQ46xwIDAQAB
-----END RSA PUBLIC KEY-----
Using jsonwebtoken for NodeJS I can verify this token and authorize requests made using it. So all of the data seems good, the key works, and the math checks out.
However I've run into two problems when trying to verify the token in PHP:
1. The public key doesn't seem to be valid?
$key = openssl_pkey_get_public($pem);
print_r($key);
die();
This code prints out "false" - suggesting that the key could not be read from the PEM text posted above. Googling around I found this comment in the PHP manual which provided a solution. I did as instructed (removed new-lines from my key, prepended MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A, then wrapped to 64 characters) and for some reason openssl_pkey_get_public($pem) actually returned an OpenSSL Public Key now. I'm not really keen on using copy/paste solutions I don't understand, though, and the comment mentioned that this will only work for 2048-bit keys, which concerns me if we ever want to upgrade our security in the future.
After making the changes suggested to my key the new key looks like this:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0wO7By66n38BCOOPqxnj
78gj8jMKACO5APe+M9wbwemOoipeC9DRCGxC9q+n/Ki0lNKSujjCpZfnKe5xepL2
klyKF7dGQwecgf3So7bve8vvb+vD8C6loqbCYEOHdLCDoC2UXEWIVRcV5H+Ahawy
m+OcE/0pzWlNV9asowIFWj/IXgDVKCFQnj164UFlxW1ITqLOQK1WlxqHAIoh20Rz
peJTlX9PYx3DDja1Pw7TPomHChMeRNswZ7zJiavYrBCTvYE+tm7JrPfbIfc1a9fC
Y3LlwCTvaBkL2F5yeKdH7FMAlvsvBwCmQhPE4jcDINUds8bHu2on5NU5VmwHjQ46
xwIDAQAB
-----END PUBLIC KEY-----
(note that this is the same key, just with 32 magic bytes prepended to the beginning of it and "BEGIN RSA PUBLIC KEY" replaced with "BEGIN PUBLIC KEY")
2. The signature fails to verify (possibly because I'm using PS256 and not RS256)
Ignoring the issues with #1 for now and moving on to the next step, I tried to verify my signature like so:
$success = openssl_verify($jwtHeader . "." . $jwtBody, $jwtSignature, $key, OPENSSL_ALGO_SHA256);
This returned false. Meaning the signature was not valid. But I know the signature was valid because it worked fine in NodeJS. So I suspect the issue here revolves around my choice of algorithm.
How do I get PHP to properly verify this token?
Update 1
Here's the code that I'm using to verify my tokens in NodeJS. This is a HapiJS + TypeScript project, but you should be able to make sense of it. jwt is just defined as import * as jwt from "jsonwebtoken";:
jwt.verify(
token,
server.plugins["bf-jwtAuth"].publicKeys[tokenData.iss].key,
{
algorithms: [options.algorithm],
audience: userHash,
maxAge: options.tokenMaxAge
},
err =>
{
// We can disregard the "decoded" parameter
// because we already decoded it earlier
// We're just interested in the error
// (implying a bad signature)
if (err !== null)
{
request.log([], err);
return reject(Boom.unauthorized());
}
return resolve(h.authenticated({
credentials: {
user: {
id: tokenData.sub
}
}
}));
}
);
There's not too much to see here, because I just relied on a third-party tool to do all of the validation for me. jwt.verify(...) and it worked like magic.
Update 2
Assuming that my issue lie in the algorithm being used (PS256 vs RS256) I started searching around and found this StackOverflow post which pointed me at phpseclib
We actually coincidentally already had phpseclib installed via Composer as a dependency of Google's auth SDK, so I bumped it up to a top-level dependency and gave it a try. Unfortunately I still ran into an issue. Here's my code so far:
use phpseclib\Crypt\RSA;
// Setup:
$rsa = new RSA();
$rsa->setHash("sha256");
$rsa->setMGFHash("sha256");
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
// The variables I'm working with:
$jwt = explode(".", "..."); // [Header, Body, Signature]
$key = "..."; // This is my PEM-encoded string, from above
// Attempting to verify:
$rsa->loadKey($key);
$valid = $rsa->verify($jwt[0] . "." . $jwt[1], base64_decode($jwt[2]));
if ($valid) { die("Valid"); } else { die("Invalid"); }
Neither die() statement is reached as I hit an error on the $rsa->verify() line with the following:
ErrorException: Invalid signature
at
/app/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php(2693)
Looking at this line in the library, it looks like it's failing at the "length checking" step:
if (strlen($s) != $this->k) {
user_error("Invalid signature");
}
I'm not sure what length it's expecting, though. I passed the raw signature directly from the JWT
After messing with this all day I finally figured out the missing piece.
First, some quick notes on the original question (already touched on in the updates):
To do RSA signatures with PSS padding ("PS256") you will need a third-party library, because the OpenSSL functions in PHP don't support this. A common recommendation is phpseclib
The 32 magic bytes I had to add to the key were only a quirk of PHP's OpenSSL functions and don't need to be utilized with phpseclib
With that said, my final problem (with the signature being "invalid") was:
JWT signatures are base64URL encoded, not base64 encoded
I wasn't even aware there was an RFC specification for base64URL encoding. It turns out you just replace every + with a - and every / with an _. So instead of:
$signature = base64_decode($jwt[2]);
It should be:
$signature = base64_decode(strtr($jwt[2], "-_", "+/"));
This works and my signature finally validates!
As I understood, I have 2 ways of sending push notification to Apple APN : certificate-based, and token-based. I chose token-based
Apple guide says we need to create a token and refresh it every hour at least. So, i created a cron job that refreshes this token every hour and put it in a file on my server. Another cron job reads this token to send new pending push notification every second.
The problem comes in the refresh_token job, that I launch every hour. I use this librairy to create the JWT : https://web-token.spomky-labs.com/v/v2.x/components/signed-tokens-jws/jws-creation
Here is my code ( i just followed the guide I've just given as link) :
$algorithmManager = AlgorithmManager::create([
new ES256()
]);
// Our key.
$jwk = new JWK([
'kty' => 'EC', // *** PROBLEM HERE ***
'k' => $keyFile
]);
// The JSON Converter.
$jsonConverter = new StandardConverter();
// We instantiate our JWS Builder.
$jwsBuilder = new JWSBuilder(
$jsonConverter,
$algorithmManager
);
// The payload we want to sign. The payload MUST be a string hence we use our JSON Converter.
$payload = $jsonConverter->encode([
'iat' => time(),
'nbf' => time(),
'exp' => time() + 3600,
'iss' => APPLE_TEAM_ID
]);
$jws = $jwsBuilder
->create()
->withPayload($payload)
->addSignature($jwk, /* with header: */['kid' => APPLE_KEY_NAME, 'alg' => 'ES256'])
->build();
This code throws an exception at ->build(); function, at the end. It says that x, y and crv parameters are not specified in the key. These parameters seem to be related to the algorithm (ES256), because when I choose the alg provided in the JWT guide, they dont ask me for these parameters.
Though, Apple didn't provide any of these informations about the key they gave me on their website. Here is their guide : https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns
The way you load the key is not correct. The key in the guide corresponds to an Octet key, not an EC one. A JWK EC key should look like the example showed in the RFC7517 section 3 (with crv, x and y parameters).
You have to convert the key file you received from Apple services into a JWK EC key.
As you already have PHP on your platform, I recommend the use of the CLI tool:
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar.pubkey
chmod +x jose.phar
# Replace `/path/to/you/private/key/file.p8` with the actual path to your private key
./jose.phar key:load:key /path/to/you/private/key/file.p8
rm ./jose.phar
rm ./jose.phar.pubkey
You should get something like {"kty":"EC","crv":"P-256","d":"…","x":"…","y":"…"}.
The JWK can be loaded using the following line of code:
$jwk = JWK::createFromJson('{"kty":"EC","crv":"P-256","d":"…","x":"…","y":"…"}');
Hello and thank you for the help,
I am using the php-sdk for the aws lambda invoke method WITHOUT the use of an API.
I am following the docs per this https://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Lambda.LambdaClient.html#_invoke
What's weird is that I use the SNS class with my external credentials file located in ~/.aws/credentials and ~/.aws/config and it works fine so I don't think it's a problem with the credentials although I could be wrong.
My code is:
$client = LambdaClient::factory([
'version' => 'latest',
'key' => $f_key,
'secret' => $f_secret,
'region' => 'us-east-1'
]);
$result = $client->invoke(array(
'FunctionName' => 'MY_FUNC',
// NOT SET AWS SAYS IT DEFAULTS
// 'InvocationType' => 'string',
// 'LogType' => 'string',
// 'Qualifier' => 'string',
'ClientContext' => '
'ClientContext' => '{
"id": 1006410,
"title": "LAMBDA TEST"
}',
',
'Payload' => 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface',
));
Error I am getting:
PHP Fatal error: Uncaught exception 'Aws\Lambda\Exception\LambdaException' with message 'Error executing "Invoke" on "https://lambda.us-east-1.amazonaws.com/2015-03-31/ARN_REMOVED/invocations"; AWS HTTP error: Client error: `POST https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/ARN_REMOVED/invocations` resulted in a `403 Forbidden` response:
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access (truncated...)
InvalidSignatureException (client): The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. - {"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for in /home/USER/Documents/symphonic/SMS-v1/vendor/aws/aws-sdk-php/src/WrappedHttpHandler.php on line 191
This PHP SDK expects the optional ClientContext to be a base64-encoded JSON object, rather than an array.
ClientContext => (string)
Using the ClientContext you can pass client-specific information to the Lambda function you are invoking. You can then process the client information in your Lambda function as you choose through the context variable. For an example of a ClientContext JSON, go to PutEvents in the Amazon Mobile Analytics API Reference and User Guide.
The ClientContext JSON must be base64-encoded.
https://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Lambda.LambdaClient.html#_invoke
The signing error, presumably, results from a discrepancy between two parts of the internals: the way the SDK handles the incorrect argument type when generating the signature, and the way it handles it when generating the actual request. It seems like an SDK bug that the incorrect argument type is silently handled all the way past the point of sending a request to the API, which throws a vague error.
The service API isn't at fault, though.
When requests arrive at AWS, several things happen in very rapid succession, and always in the same order. The first failure to be encountered causes the entire request to fail.
The first thing checked is whether the signature has expired, either by an explicit expiration or by the Date or x-amz-date value being too skewed.
Next, the AWSAccessKeyId or X-Amz-Credential is checked for validity (and scope, in the latter case).
Then, the signature is validated, to ensure that it was signed with the correct matching secret key, and hasn't been tampered with since it was signed. Assuming the request was not corrupted in-flight or tampered with, there are two things that can cause this: an invalid access key secret, or a bug in the code that signed the request... hence, the error message:
Check your AWS Secret Access Key and signing method
The reason the message is vague is that one security mechanism that the request signing algorithms use (HMAC-SHA) makes it impossible to distinguish between the two conditions (invalid secret key vs. bug). Every Signature V4 request has exactly 1 possible valid signature, and 16^64 - 1 other possible signatures, all of which are equally wrong, and convey no information about the cause of the signature mismatch. (The number is smaller for Signature V2, but not meaningfully so, for this discussion.) The tiniest of errors throw the entire algorithm into unrecognizable chaos, which is an intrinsic part of the security.
It is only after these steps complete that the request is evaluated for syntax and structure, permissions are checked, and the rest of the request processing proceeds, which is why a more helpful error isn't all that practical to provide.
This is my first time posting here, so forgive me if I leave out something important. Anyway, I'm trying to connect to Etsy's API using PHP and OAuth. I've been following the guide here: https://www.etsy.com/developers/documentation/getting_started/oauth#section_obtaining_temporary_credentials
I already created an account and app on Etsy, so I have my consumer key and secret. I've copied their code for getting a request token 100% and defined the two variables using my unique key and secret. However, when I try to make the http request, I get an "ERR_EMPTY_RESPONSE". When I click to get more detail it says: "Unable to load the webpage because the server sent no data". Here is a screenshot of the error page: http://imgur.com/dgxAlci
I'm using MAMP to make a localhost on port 8888 in order to test any PHP code that I write (e.g. to get to this php file I enter this url: localhost:8888/Etsy/EtsyOAuth.php).
My code is below. I edited out my key and secret.
define('OAUTH_CONSUMER_KEY', "****");
define('OAUTH_CONSUMER_SECRET', "****");
// instantiate the OAuth object
// OAUTH_CONSUMER_KEY and OAUTH_CONSUMER_SECRET are constants holding your key and secret
// and are always used when instantiating the OAuth object
$oauth = new OAuth(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET);
// make an API request for your temporary credentials
$req_token = $oauth->getRequestToken("https://openapi.etsy.com/v2/oauth/request_token?scope=email_r%20listings_r", 'oob');
print $req_token['login_url']."\n";
I'm using pHP 5.6.7 and OAuth 1.2.3 says phpinfo().
Any help is much appreciated. Thanks in advance!
I had an implementation of OAuth working with Fitbit to pull data from fitbit's service. However they recently updated their service and now the request is failing whenever I try to get an access token.
They have made the following statement about the new requirement:
The solution is to OAuth sign the requests to <https://api.fitbit.com/oauth/request_token> and <https://api.fitbit.com/oauth/access_token> in a similar manner that all other calls to the Fitbit API are signed.
Requests to <https://api.fitbit.com/oauth/request_token> need to be signed with your application's consumer key and secret.
Requests to <https://api.fitbit.com/oauth/access_token> need to be signed with your application's consumer key and secret and the oauth_token and oauth_verifier received from the authorization callback.
I am using the PHP PECL OAuth library for OAuth requests. However I can't find a way to add additional parameters to the signature. I am trying the following but I'm not sure that this is the correct way to update the OAuth Signature:
$params['consumer_key'] = $this->consumer_key;
$params['consumer_secret'] = $this->consumer_secret;
$params['oauth_token'] = $this->oauth_token;
$params['oauth_verifier'] = $_REQUEST['oauth_verifier'];
$this->signature = $this->oauth->generateSignature('GET', $this->access_url, $params);
$this->access_token = $this->oauth->getAccessToken($this->access_url, $this->signature, $_REQUEST['oauth_verifier']);
The OAuth error I get is:
401
Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)
oauthoauth_signatureInvalid signature: FfvYDv5MSOfwcOwLZBJa0TlKS4Q=false
The signature which is stored from the code above shows that the proper signature should be:
[signature] => wlfzqPs4aEkTkHfqyaO65D/RW6o=
This is the "Headers Sent" piece of the debug information:
[headers_sent] => Authorization: OAuth oauth_session_handle="Frdnxw8oHe3BgNVi0Fy4jBXrZko%3D",
oauth_verifier="ss6nmke8elf3so66jg3auued49",
oauth_consumer_key="(my key)",
oauth_signature_method="HMAC-SHA1",
oauth_nonce="30463910852ea5cc2d04e60.71895372",
oauth_timestamp="1391090882",
oauth_version="1.0",
oauth_token="2cabd6beab341e332bdf8e522b6019ef",
oauth_signature="hULwWcQOl%2F8aYjh0YjR843iVXtA%3D"
I can't find anything in the documentation which explains how I can set the signature for OAuth to use with it's request. Any Help would be greatly appreciated!!!
Please let me know if you need more information!
I have found the issue.
It turns out I was not saving the oauth_token_secret being handed back and I was instead using the consumer secret.
Once I updated this, the process ran as expected.