FirebaseAuthInvalidCredentialsException: The custom token format is incorrect - php

Hi I have an app using it's own server for authentications of users and other stuff except user chatting part which depends on Firebase Realtime Database, To secure my data on Firebase and App Server i am generating the JWT using https://github.com/firebase/php-jwt library. After generating JWT i am passing it to
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
// Here i get the exeception
//com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The custom token format is incorrect. Please check the documentation.
}
});
On app side this functions calls only when user login successfully and session never expire like all apps until user logout itself, that JWT token will stay on app for months and firebase allows only 1 hour expiry
So this is the same token which i am using to validate the app request to API and i saw somewhere that i need to set expiry to maximum one hour and if i gonna set it 1 hour then it will not work on app server side API validation. I am not sure how can i generate JWT for both sides because my existing way is showing the custom token format is incorrect.
The function which generate the token in PHP
function generateJWT($con,$userID,$cretedTime) {
$secret_Key = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXZYPWn0x0zltm\nk1uZiT/GwsB8izRu2uYpCQFrSPsqGZ/TaHHB1/SpMhEG2VOPBrcxOweqM0tyIx49\nt9lljZuuUiI3Cmeji2epWkPQJvBJIjKwl/6hcXAoVI0nPc82oUewSQFwbPOVCKq7\nM/JLKLSt9wpctxoutXUMD6idyHIiKQZdtKawSTRnMuylNCb5fUtKU7UuQbCnEOdF\nGPSgOdvxEi4lwQ8k0HSnj0ZQzJ7hkqS9da1DKYi1IMQn7IsL5RzOgY28Gb0l2gn\nK9rBoQtnxQKBgQCHUze4MRW7ExP0rkG93gRNXjopg/akjq0oG/VDO7rv85Pjw4Yx\nitVchWwu9lEtj5HlVRq/oti9b0aohZexNxrKJAE+68h5b8MRhoKkDXmQkWzYR0+j\nnlLgcj2qaEr/y+Utq8jj9F8I6uS3pF/zvcih0aS7j7p/VG3fAjBUy/uaXQKBgGDk\nS9ARY7wE0vfZqEO7FbUAsVktik3g9bwkwTivp1G7Zoq+hlfi0gPcZ7RxcBFsH7Ht\nrEBkUPOB+DYNnqoakH8+8lB9TboZEYepJB39qkuK/RweKoQdwVsBUpQGVrzPoemt\n0wRERBCMrqkfyY1fqDKFUkCigERBdL6fKTr+Gze9AoGAMcMsIfjxf8eZiSev7T4s\nvS1t404foWpWqcjoJpse/UWJnyKSmMKzJ/ulNOclq76MJN4YO5k8AdysXK8+azZG\n6i8qxXf9T/jnGoJSsxJ9KMolMMW+EF9B5mn61vQJyW72tCFoXFcOIBrGQS+THCPW\nAtY9K1/19yScEhdmhw8Ozek=\n-----END PRIVATE KEY-----\n";
$date = time();
//$expire_at = $date->modify('+3 minutes')->getTimestamp(); // Add 60 seconds
$domainName = "firebase-adminsdk-xxxx#xxxx-xxxx.iam.gserviceaccount.com";
$request_data = [
'iss' => $domainName,
'sub' => $domainName,
'aud' => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
'iat' => $date, // Issued at: time when the token was generated
// Issuer
//'exp' => $date+(60*60), // Maximum expiration time six month in seconds //15778476
'uid' => $userID, // User name
'created' => $cretedTime, // User name
];
$newToken = JWT::encode($request_data,$secret_Key,'RS256');
return $newToken;
}
The token is not valid with the current setup, it may be because of i am not using the expiry for firebaseAuth.signInWithCustomToken(Session.getJWT()) but setting expiry might break the server API validations too. Anyone have idea how to resolve the issue i have.

Related

Unable to send chatMessage with Teams Graph API php

With help from #CarlZhao I am finally getting a good understanding of the difference between OAuth and Graph. I am building the capability in my app for users to post messages to a team channel. So far I can list teams, channels, and delete channels. I am having a hard time trying to send a chatMessage. I understand that because sending a chatMessage is a delegated permission and not an application permission so from my understanding I have to use the accessToken created from OAuth when the user authenticated with my app.
What I am doing is saving that token in my database so I can call it when I am trying to send a chatMessage. Not sure if that is correct. So in my code, I am creating a new Graph instance, but I am using the access token of the user and not the token of the graph.
$useraccesstoken = "************************************";
// create a new OAuth graph from useraccesstoken
$graph_message = new Graph();
$graph_message->setAccessToken($useraccesstoken);
// post message
$data = [
'body' => [
'content' => 'This is a message from the API I made it works'
],
];
$message = $graph_message->createRequest("POST", "/teams/$group_id/channels/$channel_id/messages")
->addHeaders(array("Content-Type" => "application/json"))
->attachBody($data)
->setReturnType(Model\User::class)
->execute();
This is producing no errors, but nothing happens and the chatMessage is not posted. I have double-checked and my $group_id and $channel_id are correct.
Am I using the $useraccesstoken correctly? can I start a new Graph() instance with the $useraccesstoken?
Yes, you could start a new Graph() instance with the $useraccesstoken.
The graph API of sending messages doesn't return User::class. Try your code with
->setReturnType(Model\ChatMessage::class)
The access token is invalid for one hour by default, see here. You could not use it all the time, so it seems you don't need to store in the database. It's better to refresh token before the access token expires, and this step shows you how to refresh token.
The default is 1 hour - after 1 hour, the client must use the refresh
token to (usually silently) acquire a new refresh token and access
token.

How to validate a token without a database?

I have hosted two domains on the same server, domain A and domain B.
Domain A will generate the unique access token to the content of domain B.
Domain A
<?php
//http://php.net/manual/en/function.phpversion.php
//echo 'Version of PHP: ' . phpversion();
session_start();
//$expiry_timestamp = time() + $expiry;
//https://davidwalsh.name/random_bytes //https://secure.php.net/random_bytes
//$token = bin2hex(random_bytes(64));
$token = bin2hex(openssl_random_pseudo_bytes(64));
//$time_token = 12000;
//$time_token = srand(floor(time() / $time_token));
//echo $token;
$_SESSION['token']=$token;
?>
<html>
<head>
</head>
<body>
Content 1
</body>
</html>
The process of generating a token seems to be the right one, it has been easy to generate it.
Now comes my problem, how can I validate the generated token from domain A to domain B ?. The generated token must only be valid for the content that generated the token, the token must not be valid for other content, the token must be unique so that user can not share access to another user if it is not from his or her computer, the token must be valid only for 4 hrs of access after 4 hrs the token will no longer be valid to display the content must generate a new token to access again.
Can this process be done using a cookie without using a database?
Maybe identifying both domains A and B using a key, something like that
$APP_SECRET_KEY = "key code secret";
Using a shared secret key is a good approach here.
I tend to use HMAC when I need to generate and validate a token (e.g.: E-Mail verification) and don't want to store it in a DB. Plus, HMAC is built in to PHP, so no library is needed here.
The idea is, on top of your data, you add a signature to verify that this token was created by your application on Domain A. You generate the token the same way again on Domain B to verify it.
Example:
Shared function to generate the token:
function buildVerificationToken($expires, $content)
{
// Same function on both domains
$APP_SECRET_KEY = 'key code secret'; // Maybe move that out of source code
$tokenData = [
'expires' => $expires, // Include it in signatur generation to prevent user from changing it in URL
'content' => $content, // Create different token for different content
'ip' => $_SERVER['REMOTE_ADDR'], // Identify the browser to make it not shareable. Best approach I could think of for this part.
];
$serialized = json_encode($tokenData);
return hash_hmac('sha256', $serialized, $APP_SECRET_KEY);
}
Generate the token on Domain A:
<?php
$expires = time() + (4 * 3600); // +4h
?>
Content 1
Verify it on domain B:
$providedExpires = (int) $_GET['expires'];
$providedToken = $_GET['token'];
$verificationToken = buildVerificationToken($providedExpires, 'content1'); // Build token the same way
if (!hash_equals($verificationToken, $providedToken)) { // hash_equals instead of string comparison to prevent timing attacks
// User provided forged token, token for another content, or another IP
die('Bad token'); // However you want to handle this
}
if (time() > $providedExpires) { // Check expiry time. We can trust the user did not modify it as we checked the HMAC hash
die('Token expired'); // However you want to handle this
}
// User is allowed to see content1
Json Web Token (JWT) seems to fit your requirements. Both applications use one secret key to exchange tokens with encrypted data one another.
Example use-case:
Let the secret key $secret="secret"
The raw data tells us the Unix timestamp when the token is generated (the iat field), the user id (the sub field) and the content id (the content field)
$data = [
"sub" => "1234567890",
"iat" => 1516239022,
"content" => 1
];
Application A encodes the raw data with the secret key using HS256 algorithm ($token = jwt_encode($raw, 'HS256', $secret)). The output $token will be:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJjb250ZW50IjoxfQ.idM7d2fgmJVk3WjANwG-Gt6sY0lyE3eTvpKRpwITHRs
You can parse the token to see its content in the JWT home page.
The token is send to the application B. This application decodes the token with the same algorithm and shared secret key ($raw = jwt_decode($token, 'HS256', $secret)). The raw data will be available in the application B. This data can be used to validate the token:
Read the user id from sub field and check if it is correct
Read the content id from content field and check if it is correct
Read the timestamp when the token is generated from the iat field and check if it is in the last 4 hours.
There are several PHP libraries implement JWT for you.

Bigcommerce customer login api (single sign-on) invalid login issue

I'm new to bigcommerce and jwt tokens. I'm trying to get the customer login api to work on a trail store. But have not been able to successfully login a customer automatically.
I got it to work once or twice, but now it doesn't work anymore, and an unable to figure out the odd behavior since nothing changed with the code. I've tried googling if anyone else has had issues with the customer login api but have found nothing.
I've gone thru the tutorial on https://developer.bigcommerce.com/api/v2/#customer-login-api and copied the example provided.
Is there something I'm missing with the code below?
I've gone to developer.bigcommerce.com and created a draft app.
I got the Client ID and Client Secret from the draft app.
I've gone into my trial store and successfully installed the draft app.
I've tested this page on SSL as well.
Could it be because it's a trail store and it will only work if it's a real store?
Thanks.
Here is the php code below:
include "vendor/autoload.php";
use Bigcommerce\Api\Client as Bigcommerce;
use Firebase\JWT\JWT;
function getCustomerLoginToken($id, $redirectUrl = '', $requestIp = '') {
/*
if (empty(self::$client_secret)) {
throw new Exception('Cannot sign customer login tokens without a client secret');
}
*/
$payload = array(
'iss' => '#MyApp1's Client ID#',
'iat' => time(),
'jti' => bin2hex(random_bytes(32)),
'operation' => 'customer_login',
'store_hash' => '#Store Hash#',
'customer_id' => $id
);
if (!empty($redirectUrl)) {
$payload['redirect_to'] = $redirectUrl;
}
if (!empty($requestIp)) {
$payload['request_ip'] = $requestIp;
}
return JWT::encode($payload, "#MyApp1's Client Secret#", "HS256");
}
$jwt = getCustomerLoginToken(1);
header("Location: " . 'https://store-#Store Hash#.mybigcommerce.com/login/token/' . $jwt);
exit();
There are a couple of constraints that can cause errors:
The app must be installed on the store (seems like you're all good here - you can also test with an API token created locally in the store - https://support.bigcommerce.com/articles/Public/Store-API-Accounts/)
The app must have the Login OAuth scope
The JWT URL must be visited within about 30 seconds, or it won't work.
The computer/server that's generating the JWT needs to have a clock that's synchronized, otherwise your IAT value will appear to have been created more than 30 seconds ago or even in the future which will fail.
The URL can only be visited exactly once - if anything runs a GET request against it other than the intended end user's browser, it won't work. For example, if you send the URL in a Slack message, slack will try to preview the link by visiting it and therefore invalidate it.
It's good to double-check that your JWT is valid at https://jwt.io/
BigCommerce support has access to logs which can shed more light on the situation if you've ruled out the above.
Hope this helps!
do not need app client_id and Secret code , you need to api's client_id and secret code

How to implement 'Token Based Authentication' securely for accessing the website's resources(i.e. functions and data) that is developed in PHPFox?

I want to use methods and resources from the code of a website which is developed in PHPFox.
Basically, I'll receive request from iPhone/Android, I'll get the request and pass to the respective function from the PHPFox code, take the response from that function and return it back to the device.
For this purpose I've developed REST APIs using Slim framework.
But the major blocker I'm facing currently is in accessing the resources(i.e. functions and data) of PHPFox website.
I'm not understanding how should I authenticate the user using 'Token Based Authentication' in order to access the website's resources.
If someone could guide me in proper direction with some useful working example it would be really helpful for me.
N.B. : The proposed implementation of 'Token Based Authentication' should be very secure and fast in speed. The security should not be compromised in any way.
Following is the code I tried on my own but I don't know whether it's right or wrong. Is my approach correct or wrong. Please someone analyse it and let me know your feedback on it.
To create a token i use this function which takes as parameters, the user's data
define('SECRET_KEY', "fakesecretkey");
function createToken($data)
{
/* Create a part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"]; // It can be 'stronger' of course
/* Encoding token */
$token = hash('sha256', $tokenGeneric.$data);
return array('token' => $token, 'userData' => $data);
}
So a user can authentified himself and receive an array which contains a token (genericPart + his data, encoded), and hisData not encoded :
function auth($login, $password)
{
// we check user. For instance, it's ok, and we get his ID and his role.
$userID = 1;
$userRole = "admin";
// Concatenating data with TIME
$data = time()."_".$userID."-".$userRole;
$token = createToken($data);
echo json_encode($token);
}
Then the user can send me his token + his un-encoded data in order to check :
define('VALIDITY_TIME', 3600);
function checkToken($receivedToken, $receivedData)
{
/* Recreate the generic part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"];
// We create a token which should match
$token = hash('sha256', $tokenGeneric.$receivedData);
// We check if token is ok !
if ($receivedToken != $token)
{
echo 'wrong Token !';
return false;
}
list($tokenDate, $userData) = explode("_", $receivedData);
// here we compare tokenDate with current time using VALIDITY_TIME to check if the token is expired
// if token expired we return false
// otherwise it's ok and we return a new token
return createToken(time()."#".$userData);
}
$check = checkToken($_GET['token'], $_GET['data']);
if ($check !== false)
echo json_encode(array("secureData" => "Oo")); // And we add the new token for the next request
Am I right?
Thanks.
1st you should understand what's token based authentication. It could be explained as below.
The general concept behind a token-based authentication system is
simple. Allow users to enter their username and password in order to
obtain a token which allows them to fetch a specific resource -
without using their username and password. Once their token has been
obtained, the user can offer the token - which offers access to a
specific resource for a time period - to the remote site.
Read more
Now let's see what are the steps of implementing it in your REST web service.
It will use the following flow of control:
The user provides a username and password in the login form and clicks Log In.
After a request is made, validate the user on the backend by querying in the database. If the request is valid, create a token by
using the user information fetched from the database, and then return
that information in the response header so that we can store the token
browser in local storage.
Provide token information in every request header for accessing restricted endpoints in the application.
If the token fetched from the request header information is valid, let the user access the specified end point, and respond with JSON or
XML.
See the image below for the flow of control
You might be wondering what's a JWT
JWT stands for JSON Web Token and is a token format used in
authorization headers. This token helps you to design communication
between two systems in a secure way. Let's rephrase JWT as the "bearer
token" for the purposes of this tutorial. A bearer token consists of
three parts: header, payload, and signature.
The header is the part of the token that keeps the token type and encryption method, encoded in base64.
The payload includes the information. You can put any kind of data like user info, product info and so on, all of which is also stored in
base64 encoding.
The signature consists of combinations of the header, payload, and secret key. The secret key must be kept securely on the server-side.
You can see the JWT schema and an example token below;
You do not need to implement the bearer token generator as you can use php-jwt.
Hope the above explains your confusion. if you come across any issues implementing token based authentication let me know. I can help you.

Magento oAuth authorisation failed

I got a problem with oAuth authentification in magento.
I used following guide to create connection:
http://www.magentocommerce.com/api/rest/authentication/oauth_authentication.html
First of all I granted all privileges for all accounts in magento / System / WebServices / REST ... Also I created oAuth Consumer. I got with it two variables (key and secret).
According the guide (Getting an Unauthorized Request Token) I configured RESTClient for Firefox. Selected oAuth 1.0 option, inserted data from magento and added them to headers.
And now I have something like that:
http://www.mg19.local/oauth/initiate
OAuth oauth_version="1.0",
oauth_signature_method="PLAINTEXT",
oauth_nonce="pzmp8IZuroEP6gf",
oauth_timestamp="1410271763",
oauth_consumer_key="9ad2067e70a4c3b799ab2799203b3e3b",
oauth_signature="a37633084e79432568181ef00410140e%26"
Then if I submit this, I will get following error:
Status Code: 400 Bad Request
oauth_problem=parameter_absent&oauth_parameters_absent=oauth_callback
I don't know the main purpose of the callback link, therefore I used random link. For example: http://www.mg19.local
When i submit
http://www.mg19.local/oauth/initiate/?oauth_callback=http://www.mg19.local
I got following result:
oauth_token=e00fc8386ba523bdd1d79a2fe61d59cb&oauth_token_secret=ca0d999010b2b149e2d51feefc328722&oauth_callback_confirmed=true
According the guide I moved to the 2nd step (User Authorization):
I copied data from the response to request. And forward the link:
http://www.mg19.local/oauth/authorize
I redirected to the following page:
Authorize application
Postman requests access to your account
After authorization application will have access to you account.
Authorize | Reject
And when I select Authorize I'm getting the following error:
An error occurred. Your authorization request is invalid.
Using xDebug I have found that the problem is near:
/**
* Load token object, validate it depending on request type, set access data and save
*
* #return Mage_Oauth_Model_Server
* #throws Mage_Oauth_Exception
*/
protected function _initToken()
{
....
} elseif (self::REQUEST_AUTHORIZE == $this->_requestType) {
if ($this->_token->getAuthorized()) {
$this->_throwException('', self::ERR_TOKEN_USED);
...
I'm not sure, but I think, once autorization finished successfully, then I moved from index to account area page and when authorization start again - it fail and I move on index again.
Please give any advice.
For what I see, the callback URL is the one that is messing up the whole thing. Callback is the most important link in OAuth. The callback should be a valid URL pointing to you site.
Once the user logs in auth server (Magneto in your case) Magneto will do a callback to the Callback URI you provided with the oauth_verifier. Like below:
/callback?oauth_token=tz2kmxyf3lagl3o95xnox9ia15k6mpt3&oauth_verifier=cbwwh03alr5huiz5c76wi4l21zf05eb0
Then your server should all the token API /oauth/token with the all the required Authorization headers below. Pasted from Magneto document link you provided
oauth_consumer_key - the Consumer Key value provided after the registration of the application.
oauth_nonce - a random value, uniquely generated by the application.
oauth_signature_method - name of the signature method used to sign the request. Can have one of the following values: HMAC-SHA1, RSA-SHA1, and PLAINTEXT.
oauth_signature - a generated value (signature).
oauth_timestamp - a positive integer, expressed in the number of seconds since January 1, 1970 00:00:00 GMT.
oauth_token - the oauth_token value (Request Token) received from the previous steps.
oauth_verifier - the verification code that is tied to the Request Token.
oauth_version - OAuth version.
Hope this makes it clear. Please read the sections User Authorization and Getting Access Token sections of the link you pasted.
I'm using Guzzle and had a real hard time with it. In my case it was failing because I was using oauth_callback instead of callback, it worked when I changed it to:
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => $key,
'consumer_secret' => $secret,
'token' => null,
'token_secret' => null,
'callback' => 'https://callback.co.uk'
]);
$stack->push($middleware);
$client = new Client([
'base_uri' => $magentoCredentials->shopUrl,
'handler' => $stack
]);
$res = $client->post('/oauth/initiate?oauth_callback', ['auth' => 'oauth']);

Categories