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.
Related
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!
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.
I'm trying to use the AWS SDK for PHP to programatically upload a file to a bucket that's set to be a static website in the S3 Console.
The bucket is named foo.ourdomain.com and is hosted in eu-west. I'm using the following code to try and test if I can upload a file:
$client = \Aws\S3\S3Client::factory(array('key' => bla, 'secret' => bla));
$client->upload('foo.ourdomain.com', 'test.txt', 'hello world', 'public-read');
This is pretty much like it is in the examples, however, I received the following exception:
PHP Fatal error: Uncaught Aws\S3\Exception\PermanentRedirectException: AWS Error Code: PermanentRedirect, Status Code: 301, AWS Request ID: -, AWS Error Type: client, AWS Error Message: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint: "foo.ourdomain.com.s3.amazonaws.com"., User-Agent: aws-sdk-php2/2.4.8 Guzzle/3.7.4 curl/7.22.0 PHP/5.3.10-1ubuntu3.8
At this point I was surprised as there's no mention of this in the manual for the S3 SDK. But okay, I found a method setEndpoint and adjusted the code to:
$client = \Aws\S3\S3Client::factory(array('key' => bla, 'secret' => bla));
$client->setEndpoint('foo.ourdomain.com.s3.amazonaws.com');
$client->upload('foo.ourdomain.com', 'test.txt', 'hello world', 'public-read');
I assumed that'd work, but I'm getting the exact same error. I've doublechecked and the endpoint mentioned in the exception byte-for-byte matches the one I'm setting in the second line.
I've also tried using foo.ourdomain.com.s3-website-eu-west-1.amazonaws.com as the endpoint (this is the host our CNAME points to as per the S3 consoles instructions). Didn't work either.
I must be missing something, but I can't find it anywhere. Perhaps buckets set to 'static website' behave differently in a way which is not currently supported by the SDK? If so, I can't find mention of it in the docs nor in the management console.
Got it. The solution was to change the initialisation of the client to:
$client = \Aws\S3\S3Client::factory(array(
'key' => bla,
'secret' => bla,
'region' => 'eu-west-1'
));
I.e. rather than specify an endpoint I needed to explicitly set the region in the options array. I guess the example code happens to use whatever the default region is.
If you don't want to initialize the client to a specific region and/or you'll need to work with different regions, I have been successful in using the getBucketLocation/setRegion set of calls as follows:
// Bucket location is fetched
$m_bucketLocation = $I_s3->getBucketLocation(array(
'Bucket' => $s_backupBucket,
));
// Bucket location is specified before operation is made
$I_s3->setRegion($m_bucketLocation['Location']);
I have one extra call, but solved my issue without the need to intervene on the factory.
I'm trying to use the OAuthProvider library from PHP.net and of course, it's not documented. I've followed Rasmus' tutorial and I've followed djpate.com's tutorial and neither of them to work for me and Rasmus doesn't link to any source, the source he does link to for examples is confusing and of course, doesn't work when ran.
I seem to always get a "signatures do not match" error which I don't understand really, because I've followed the tutorials to a T.
What's the flow supposed to be anyway?
1. Create consumer key/secret. Check.
2. Get the access token? I get errors -- Where does the signature come from?
3. Get the request token? I get error
I'm trying to create an OAuthProvider so that I can create 1 consumer account that can call my API remotely and it seems like this is very poorly documented for a beginner... in PHP land anyway.
If anyone has any working OAuthProvider libraries or can explain this to me in more detail I would greatly appreciate it.
Thanks in advance.
http://oauth.net/core/1.0a/ tells you the basic flow.
A consumer gets a consumer key and secret.
The consumer gets a request token.
The consumer redirects the user to the provider's authentication endpoint.
The user signs the request token (or doesn't).
The consumer swaps a authentication token for their signed request token.
The consumer uses their authentication token to access protected information.
http://oauth.net/core/1.0a/#signing_process describes how a request is signed.
"The signature process encodes the Consumer Secret and Token Secret
into a verifiable value which is included with the request."
If you are using the pecl oauth/oauthprovider code, the signature is automatically generated on both sides for you (undocumented but true). You can check to see what the signature is by putting the following in the oauthexception catch section in the provider:
catch (OAuthException $E)
{
error_log(print_r($this->provider, true));
echo OAuthProvider::reportProblem($E);
$this->oauth_error = true;
}
and the following in your oauth consumer oauthexception catch section:
catch(OAuthException $E)
{
error_log(print_r($oauth, true));
echo $E->getMessage();
}
In this way you can check your error logs to find out what the signatures are and whether they do in fact not match.
I am having similar error. This seems to be caused, in my case, due to a signature mismatch in http vs https url.
I would check if you are getting re-directed between http and https.
I am trying to write a small webapp that pulls data from Yammer. I have to go through Yammer's OAuth bridge to access their data. I tried using the Oauth php library and do the 3 way handshake. But at the last step, I get an error stating I have an invalid OAuth Signature.
Here are the series of steps:
The first part involves getting the request Token URL and these are the query parameters that I pass.
[oauth_version] => 1.0
[oauth_nonce] => 4e495b6a5864f5a0a51fecbca9bf3c4b
[oauth_timestamp] => 1256105827
[oauth_consumer_key] => my_consumer_key
[oauth_signature_method] => HMAC-SHA1
[oauth_signature] => FML2eacPNH6HIGxJXnhwQUHPeOY=
Once this step is complete, I get the request Token as follows:
[oauth_token] => 6aMcbRK5wMqHgZQsdfsd
[oauth_token_secret] => ro8AJxZ67sUDoiOTk8sl4V3js0uyof1uPJVB14asdfs
[oauth_callback_confirmed] => true
I then try to authorize the given token and token secret by passing the parameters to the authorize url.It takes me to Yammer's authentication page where I have allow my app to talk to Yammer.
Yammer then gives me a 4 digit code that I have to put back into my application which then tries to acquire the permanent access token. I pass the following information to the access token URL:
[oauth_version] => 1.0
[oauth_nonce] => 52b22495ecd9eba277c1ce6b97b00fdc
[oauth_timestamp] => 1256106815
[oauth_consumer_key] => myconsumerkey
[callback_token] => 61A7
[oauth_token] => 6aMcbRK5wMqHgZQsdfsd
[oauth_token_secret] => ro8AJxZ67sUDoiOTk8sl4V3js0uyof1uPJVB14asdfs
[oauth_callback_confirmed] => true
[oauth_signature_method] => HMAC-SHA1
[oauth_signature] => V9YcMDq2rP7OiZTK1k5kb/otMzA=
Here I am supposed to receive the Oauth Permanent access token, but instead I get a Invalid Oauth signature. I dont know what I am doing wrong. I use the same signaures to sign the request. Should I sign the request using the new token and secret? I tried that as well but to no avail. I even tried implementing this in java using signpost library and got stuck at the exact same place. Help Help!!
The callback_token was something Yammer introduced in response to an OAuth security advisory earlier this year. When OAuth 1.0a was released, it was instead named oauth_verifier. However, it's not unlikely that Yammer still supports their workaround but rename it and try again to be sure.
Also, the below is information from the Yammer Development Network yesterday:
Tomorrow we will be releasing some
changes to the Yammer API to
facilitate user network switching on
API clients. Most of the change is in
the OAuth Access Tokens call which
allows you to generate pre-authorized
OAuth access tokens for a given user.
One token will be generated for each
network they are in and your clients
switch networks by sending an API
request signed with the appropriate
token for that network.
I'm assuming that Yammer OAuth libraries might need to be updated per this change. I haven't taken a look at it yet.
Edit: My python-yammer-oauth library still works despite Yammer having changed things on their side.
Edit2: Could you try using signature method PLAINTEXT instead of HMAC-SHA1? I've had problems with Yammer and HMAC-SHA1.
I tried by using PLAINTEXT.. but for this method its giving me the same "Invalid OAuth signature" error even for requesting the token.
So is it possible to generate the access token we use HMAC-SHA1 and for accessing the actual API method i.e. for posting the message.. we use PLAINTEXT?
just found the problem!
I had forgotten to add an ampersand ("&") at the end of CONSUMER_SECRET. Perhaps this is your issue as well?