Firebase JWT library can't verify Python JWT token - php

So I have a client in python and a backend in PHP. The python client is using pyjwt and the php server is using Firebase JWT.
When encoding and decoding tokens within php everything works fine. But when I encode tokens from python and decode them with php the Firebase library returns an error:
Firebase\JWT\SignatureInvalidException Object
(
[message:protected] => Signature verification failed
[string:Exception:private] =>
[code:protected] => 0
[file:protected] => /var/www/vendor/firebase/php-jwt/src/JWT.php
[line:protected] => 110
...
The encoding python code is the following
import jwt
...
payload = {'sub': self.client.id, 'exp': (datetime.now() + timedelta(days=1)).timestamp()}
context['token'] = jwt.encode(payload, os.environ['JWT_KEY'], algorithm='HS256')
and the PHP code is the following
$key = getenv("JWT_KEY");
return (array) \Firebase\JWT\JWT::decode($token, $key, array('HS256'));

OK, I found the problem. The issue was the secret KEY ussed by the different parties. The key had a problem when trying to be readed by PHP and thats why it was creating different tokens. The libraries are ok, they can communicate between each other without any problem.

Related

Flutter Firebase Auth - Token verification problem in PHP server

I am using Firebase authentication in my Flutter app to manage the users (Facebook, Google, Email...). When the user log in my app, I send the Firebase token to my PHP server, where that token is verified with JWT.
The problem is that, although the token generated by the email login is correctly verified, the token generated by the Facebook or Google login fails with "SignatureInvalidException: Signature verification failed".
Facebook login code in the Flutter app:
FirebaseAuth auth = FirebaseAuth.instance;
final LoginResult result = await FacebookAuth.instance.login();
final FacebookAuthCredential facebookAuthCredential = FacebookAuthProvider.credential(result.accessToken.token);
UserCredential user = await auth.signInWithCredential(facebookAuthCredential);
String token = await auth.currentUser.getIdToken();
print(token.toString());
The information of the token is correctly validated in the https://jwt.io/ debugger, so the header and the payload are fine.
In the PHP server (Same code for both cases):
Validating the email&password token (OK):
stdClass Object ( [iss] => https://securetoken.google.com/project-123546 [aud] => project-123546 [auth_time] => 1603424825 [user_id] => S2gpfdsa156dsacdsfQ2z1 [sub] => S2gpfdsa156dsacdsfQ2z1 [iat] => 1603424825 [exp] => 1603428425 [email] => user#example.com [email_verified] => [firebase] => stdClass Object ( [identities] => stdClass Object ( [email] => Array ( [0] => user#example.com ) ) [sign_in_provider] => password ) )
Validating the Facebook token (KO):
Fatal error: Uncaught Firebase\JWT\SignatureInvalidException: Signature verification failed in server\vendor\firebase\php-jwt\src\JWT.php:122 Stack trace: #0 server\token.php(18): Firebase\JWT\JWT::decode('eygswbdsasdJSUzI...', '-----BEGIN CERT...', Array) #1 {main} thrown in server\vendor\firebase\php-jwt\src\JWT.php on line 122
PHP code:
$token = "...TOKEN-FROM-APP...";
$pkeys_raw = file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com');
$pkeys = json_decode($pkeys_raw, true);
$decoded = JWT::decode($token, $pkeys, ["RS256"]);
print_r($decoded);
The KeyID match with the key in https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com, but I think that the app (or Firebase) is not signing correctly the token with the private key in the Facebook and Google login. However, I am using auth.currentUser.getIdToken() too in the email&password login, so there are not differences.
Any idea how to resolve this?
Investigating in the firebase library for Flutter I found the answer to this problem. I post it here just in case it was helpfull for someone.
The library is fine and the signature is correctly generated.
The reason is totally unrelated to the library - debugPrint() in Flutter/Dart on the Android platform does not have enough buffer to print out the entire token string.
So the problem is the print(token.toString());, if you send the token to the server it will be correctly decoded.
All the info about this issue:
https://github.com/FirebaseExtended/flutterfire/issues/2728
I think you can use log to get the whole token:
log(token);
Even though jwt token is invalid somehow from https://jwt.io, but I am still able to verify it from my backend which is Java using Firebase Admin SDK.
FirebaseAuth.getInstance(firebaseApp).verifyIdToken(token);
Also, you can print like this:
final idToken = await firebaseCredential.user!.getIdToken(true);
print(idToken.substring(0, 1000));
print(idToken.substring(1000));

AWS php-sdk Lambda Error

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.

CreateSignedURL failure AWS SSE-C with PHP

I have been using AWS SDK V3 for PHP to put object to S3 with Server side encryption using customer provided key. The documentation is quite sketchy (or at least I havent found it).
For uploading the object using the S3client, I use putobject with
$params['SSECustomerAlgorithm'] = 'AES256';
$params['SSECustomerKey'] = $this->encryptioncustkey;
$params['SSECustomerKeyMD5'] = $this->encryptioncustkeymd5;
The $this->encryptioncustkey is a plain customer key (not base64_encoded because the SDK seems to be doing that) and this->encryptioncustkeymd5 = md5($this->encryptioncustkey,true);
The put object works fine. However, the problem is in generating a createSignedURL.
$cmd = $client->getCommand('GetObject', array(
'Bucket' => $bucket,
'Key' => $storedPath,
'ResponseContentDisposition' => 'attachment;charset=utf-8;filename="'.utf8_encode($fileName).'"',
'ResponseContentType' => $ctype,
'SSECustomerAlgorithm' => 'AES256',
'SSECustomerKey' => $this->encryptioncustkey,
'SSECustomerKeyMD5' => $this->encryptioncustkey64md5
));
but I get a weird response indicating that it is missing "x-amz-server-side-encryption" (ServerSideEncryption) which according to documentation is not required for SSE-C. Even if I set it to ServerSideEncryption='AES256' it has no effect.
<Error>
<Code>InvalidArgument</Code>
<Message>
Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.
</Message>
<ArgumentName>x-amz-server-side-encryption</ArgumentName>
<ArgumentValue>null</ArgumentValue>
<RequestId>A3368F6CE5DD310D</RequestId>
<HostId>
nHavXXz/gFOoJT0tnh+wgFTbTgGdpggRkyb0sDh07H7SomcX7HrcKU1dDzgZimrQwyaVQEqAjdk=
</HostId>
</Error>
I was running into the same issue and tried every possible permutation to try to get it to work. I finally concluded that this use case is not supported. Reading through the scattered and arcane documentation on the subject, it seems the only way to access SSE-C content on S3 is by specifying the x-amz-server-side-encryption-customer-algorithm/x-amz-server-side-encryption-customer-key/x-amz-server-side-encryption-customer-key-MD5 fields in the HTTP request header, not in the URL.
What I ended up doing is to store the content in question in S3 unencrypted and with the ACL set to private (you could upload it as such from the get go, or use copyObject() to make a copy with those settings). Then, when I wanted to get the ["time-bombed"] pre-signed URL for the GET request, I just used the command similar to the one in your question, but omitting the SSE parameters. That worked for me.

PHP Library: app_key error

I get this error when I try to login using the oAuth PHP example script (oauth2-login-example.php)
Please provide your Application Key in the URL as "?app_key=".
I have replaced the default app_key and client_secret with my codes.
You can test it here: http://www.daysofthedead.net/hub/examples/oauth2-login-example.php
You need to apply for an API key from Eventbrite for your account.
Just head to https://www.eventbrite.com/api/key and create one. Then replace the "YOUR_APP_KEY" and "YOUR_CLIENT_SECRET" with the values that Eventbrite provide.
$login_widget_html = Eventbrite::loginWidget(array( 'app_key' => 'YOUR_APP_KEY',
'client_secret' => 'YOUR_CLIENT_SECRET'));

Invalid OAuth Signature returned while using Yammer Api

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?

Categories