Laravel/Lumen Auth JWT token not valid in subsequent requests, is it possibly expired? - php

I have an app that uses Laravel/Lumen and its Auth guard JWT tokens for login.
I send a request to
http://myserver.com/authenticate
and I get a token in response.
Then when I use this token in subsequent requests
http://myserver.com/users
with the token in the header
Authorization : Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJjbG96ZXJ0b29sc1YyIiwianRpIjoiNTBiMjE1MjllZGIxMmI4OGJlYTJmOTQxMTViNjc2NmYiLCJpYXQiOjE1MzY1NTU3NzEsIm5iZiI6MTUzNjU1NTc3NiwiZXhwIjoxNTM2NTcwOTc2LCJkYXRhIjp7ImVtYWlsIjoia3lsZWtvb3BtYW5AZ21haWwuY29tIiwiYXZhdGFyIjoiIiwiZmlyc3RfbmFtZSI6Ikt5bGUiLCJsYXN0X25hbWUiOiJLb29wbWFuIiwiaWQiOjMzNH0sInN1YiI6MzM0fQ.p20K56BW0c_J-xlk9gV6wDFafxgNuKUOmgk-4ExKhh9qPw79R0bpm-QbnVQFtYlatB_MjLYK1NdUt5GlGaOE9w
The request obviously usually comes back with a 200, (on my local server anyways)
However, on my production server all subsequent requests with the provided token come back with a 401 / Unauthorized
All the settings are the same on both servers.
I have this in my .env on both my production server, and local server.
JWT_KEY=yUyg2oo3M2N0Lf0CnsbG1ztsL1ovA70K
JWT_EXPIRE_AFTER=15200
JWT_ISSUER=mysite
JWT_ID_FIELD=id
JWT_NBF_DELAY=25
DB_TIMEZONE=+00:00
APP_TIMEZONE=UTC
My assumption is that it has something to do with expiry and/or server time.
Like I think that the token is coming back already expired, therefore on the subsequent request it is invalid.
Am I correct in thinking this is where the issue lies? And how do I go about fixing it/testing it?

You can visit https://jwt.io and paste your token into the encoded field to get back the expire date and check then in the developer console
const currentTime = Date.now() / 1000
if( exp < currentTime) {
// is expired
}
I have something like this in my code:
const checkExpiredJwtDate = token => {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jwtObject = JSON.parse(window.atob(base64))
const currentTime = Date.now() / 1000;
if (jwtObject.exp < currentTime) {
// is expired...
}
};
How to decode jwt token in javascript

Related

How to verify a Paypal webhook notification DIY style (without using Paypal SDK)

Upon integrating the smart button of Paypal I have issues to verify webhook notifications sent by Paypal. The examples I have found are either outdated or do not work.
Is there a way to verify the webhook notifications, ideally in a DIY way (ie. without having to use the bulky and complex Paypal API)?
To the best of my knowledge, this code is only one that actually works. All other examples I have found on stack overflow will not work because instead of passing the ID of the webhook itself when composing the signature string, they use the ID of the webhook event, thus the verify will fail.
The webhook ID will be generated once you add the webhook in the developer backend of Paypal. After creation of the webhook you will see its id in the list of installed webhooks.
The rest is pretty straight forward: We get the headers and the HTTP body and compose the signature using Paypal's recipe:
To generate the signature, PayPal concatenates and separates these
items with the pipe (|) character.
"These items" are: The transmission id, the transmission date, the webhook id and a CRC over the HTTP body. The first two can be found in the header of the request, the webhook id in the developer backend (of course, that id will never change), the CRC is calculated like shown below.
The certificate's location is in the header, too, so we load it and extract the private key.
Last thing to watch out for: The name of the algorithm provided by Paypal (again in a header field) is not exactly the same as understood by PHP. Paypal calls it "sha256WithRSA" but openssl_verify will expect "sha256WithRSAEncryption".
// get request headers
$headers=apache_request_headers();
// get http payload
$body=file_get_contents('php://input');
// compose signature string: The third part is the ID of the webhook ITSELF(!),
// NOT the ID of the webhook event sent. You find the ID of the webhook
// in Paypal's developer backend where you have created the webhook
$data=
$headers['Paypal-Transmission-Id'].'|'.
$headers['Paypal-Transmission-Time'].'|'.
'[THE_ID_OF_THE_WEBHOOK_ACCORDING_TO_DEVELOPER_BACKEND]'.'|'.
crc32($body);
// load certificate and extract public key
$pubKey=openssl_pkey_get_public(file_get_contents($headers['Paypal-Cert-Url']));
$key=openssl_pkey_get_details($pubKey)['key'];
// verify data against provided signature
$result=openssl_verify(
$data,
base64_decode($headers['Paypal-Transmission-Sig']),
$key,
'sha256WithRSAEncryption'
);
if ($result==1) {
// webhook notification is verified
...
}
elseif ($result==0) {
// webhook notification is NOT verified
...
}
else {
// there was an error verifying this
...
}
Answering this for nodejs, as there are subtle security issues and some missing logic in original (but very helpful) answer. This answer addresses the following issues:
Someone putting in their own URL and thereby getting authentication of their own requests
CRC needs to be an unsigned integer, not a signed integer.
NodeJs < 17.0 is missing some built in X509 functionality.
Ideally one should validate the signing cert with the built in cert chain
but NodeJS < 17.0 can't do this easily AFAICT. The trust model relies on TLS and the built in nodejs trust chain for the cert fetch URL and not the returned cert from cert URL , which is probably good enough.
const forge = require('node-forge');
const crypto = require('crypto')
const CRC32 = require('crc-32');
const axios = require('axios');
const transmissionId = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-ID'];
const transmissionTime = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-TIME'];
const signature = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-SIG'];
const webhookId = '<your webhook ID from your paypal dev. account>';
const url = paypalSubsEvent.headers['PAYPAL-CERT-URL'];
const bodyCrc32 = CRC32.str(paypalSubsEvent.body);
const unsigned_crc = bodyCrc32 >>> 0; // found by trial and error
// verify domain is actually paypal.com, or else someone
// could spoof in their own cert
const urlObj = new URL(url);
if (!urlObj.hostname.endsWith('.paypal.com')) {
throw new Error(
`URL ${certUrl} is not in the domain paypal.com, refusing to fetch cert for security reasons`);
}
const validationString =
transmissionId + '|'
+ transmissionTime + '|'
+ webhookId + '|'
+ unsigned_crc;
const certResult = await axios.get(url); // Trust TLS to check the URL is really from *.paypal.com
const cert = forge.pki.certificateFromPem(certResult.data);
const publicKey = forge.pki.publicKeyToPem(cert.publicKey)
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(validationString);
verifier.end();
const result = verifier.verify(publicKey, signature, 'base64');
console.log(result);
You can use the following steps with Paypal API's
Create App and get the Client ID and Secret from the Developer dashboard
Create Webhook inside App and get a webhook ID
Implementation PayPal API's
https://www.postman.com/paypal/workspace/paypal-public-api-workspace/collection/19024122-92a85d0e-51e7-47da-9f83-c45dcb1cdf24?action=share&creator=22959279
Get the new Access token with help of Client ID and Secret, every time connect with PayPal.
4.Use the webhook Id, Access Token, and request Headers to verify the Webhook
try{
$json = file_get_contents('php://input');
$data = json_decode($json);
$paypalmode = ($this->dev_mode == 0) ? '' : '.sandbox';
$API_Endpoint = 'https://api-m' . $paypalmode . '.paypal.com/v1/';
//step-01 get token
$res_token = getToken($API_Endpoint);//get Token mention in above postman link
//step-02 validate webhook
$webhook_id = 'XXXXXX';
$post_data = array(
"webhook_id" => $webhook_id ,
"transmission_id" => $_SERVER['HTTP_PAYPAL_TRANSMISSION_ID'],
"transmission_time" => $_SERVER['HTTP_PAYPAL_TRANSMISSION_TIME'],
"cert_url" => $_SERVER['HTTP_PAYPAL_CERT_URL'],
"auth_algo" => $_SERVER['HTTP_PAYPAL_AUTH_ALGO'],
"transmission_sig" => $_SERVER['HTTP_PAYPAL_TRANSMISSION_SIG'],
"webhook_event" => $data
);
$res = verifyWebhook($API_Endpoint . 'notifications/verify-webhook-signature',
$res_token['access_token'], $post_data);//use postman 'verify-webhook-signature' api mention in webhook section
if (isset($res->verification_status) && $res->verification_status == 'SUCCESS') {
//success
}else{
//failure
}
} catch (Exception $ex) {
//error
}
Responding to this to save potential headaches but the above example does not work because an authentication token is needed to be sent along with your get request for the cert file "file_get_contents($header['Paypal-Cert-Url'])" will not work on its own.
Simply include your authentication token in the header and it'll work.

"unauthorized_client" error when using PHP client to refreshToken with Google API client?

I am requesting access to the user's data from an IOS app using this code from the GoogleSignIn pod
[GIDSignIn sharedInstance].uiDelegate = self;
[[GIDSignIn sharedInstance] setScopes:#[ ... ];
[[GIDSignIn sharedInstance] signInSilently];
I then receive the accessToken and the refreshToken in the appDelegate and I send that to the server running PHP and using this google client library (https://github.com/googleapis/google-api-php-client) ... for the first hour everything is working fine and the access token is valid and allowing me access to all the scopes. Then after an hour the access token expires and I try to refresh it using this code
$client = new Google_Client();
$client->setApplicationName(<app name>);
$client->setDeveloperKey(<google_oauth_developer_key>);
$client->setClientSecret(<google_oauth_client_secret>);
$client->setClientId(<google_oauth_client_id>);
$client->refreshToken("1/12ju3ZZim-r6co195lIVNNvzBq9x5G64GJCWkYsOQ18");
and the "refreshToken" function above returns this error
=> [
"error" => "unauthorized_client",
"error_description" => "Unauthorized",
]
It's worth noting that the app name, google_oauth_developer_key, google_oauth_client_secret, and google_oauth_client_id have not changed. They are the same ones that were working earlier to retrieve the user's contacts, calendar events, etc when the access token was not yet expired.
What could be causing this error?
You need to actually use your refresh token at some point.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->getAccessToken();
Code ripped from my Oauth2Authentication.php sample
Note
Also remember that refresh tokens are user / client based. So if the token was generated by one client then a different client will probably not be able to use it. I will be very surprised if you can use a refresh token created in Ios code in a php application. The client types are different.
Ok so I figured out what I did wrong. I originally followed these instructions https://developers.google.com/identity/sign-in/ios/sign-in which assume you need online access only, but I actually need offline access to the scopes so I followed these instructions https://developers.google.com/identity/sign-in/ios/offline-access and it works now. So instead of having the IOS app send up the access token and the refresh token separately like this
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
if (!error) {
NSString *accessToken = user.authentication.accessToken;
NSString *refreshToken = user.authentication.refreshToken;
I simply have it send up the serverAuthCode like this
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
if (!error) {
NSString *serverAuthCode = user.serverAuthCode;
and then on the server I run this code to get the access token
$access_token = $client->authenticate($google_server_auth_code);
$client->setAccessToken($access_token);
and then when the access token expires I run this to refresh it
$access_token = $client->refreshToken($client->getRefreshToken());
$client->setAccessToken($access_token);

How can I verify firebase token server side?

I have a firebase account where I manually create the users who will be abled to use my site (sign up is not public, but it is not relevant to this inquiry)
In the login page, the authentication is through javascript.
Once the user enters their mail and password the corresponding function is executed, I get the token and I send it to my PHP server via url redirection. Something like this:
firebase.auth().signInWithEmailAndPassword(inputemail, inputpassw)
.then( function(user) {
myEmail = user.email;
myUid = user.uid;
user.getIdToken()
.then( function(token){
myToken = token;
location.href = "http://www.example.com/verify?email="+myEmail+"&token="+myToken+"&uid="+myUid;
});
}, function (error) {
...
});
Then, on my server I will have the mail, the uid, and the token.
So, my question is:
How do I verify that the token is valid? It is impossible?
I know the token is encrypted, but the key is public... so anyone could make a valid token!
I mean, for instance, I have an expired token, I can decode it, change the expiration time, encode it again and gain access to my server without knowing any password
Is there something I'm missing?
Apparently I can not verify the token via REST.
What alternative do I have?
From the Firebase Documentation:
The Firebase Admin SDK has a built-in method for verifying and decoding ID tokens. If the provided ID token has the correct format, is not expired, and is properly signed, the method returns the decoded ID token. You can grab the uid of the user or device from the decoded token.
So, you do not need to worry about someone trying to generate fake tokens.
To verify the token in PHP, as described in the docs Firebase Admin SDK for PHP
Minimal code for verifying the token:
use Kreait\Firebase;
use Firebase\Auth\Token\Exception\InvalidToken;
//create Firebase factory object
//$firebase = (new Firebase\Factory())->create();
//get a token from client
//$idTokenString = 'eyJhbGciOiJSUzI1...';
try {
$verifiedIdToken = $firebase->getAuth()->verifyIdToken($idTokenString);
} catch (InvalidToken $e) {
echo $e->getMessage();
}
$uid = $verifiedIdToken->getClaim('sub');
$user = $firebase->getAuth()->getUser($uid);
echo $user;

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.

How is basic authentication header validated/checked in a rest api

I'm building a very tiny api.
In the api im authenticating the request using basic authentication header coming from the request.
This is the code upto which I have done
$headers = apache_request_headers() ;
// print_r($headers);
if(isset($headers['Authorization'])){
//$credentials = base64_decode($headers);
print_r($headers['Authorization']);
}
I got the Authorization header as 'Basic YXBpa2V5OmFqZWVzaA==' Now how will I check if this basic authorization header is valid?
Should I decode the base64 string in the username:password format and check it with the DB or when the username and pass is generated,do I have to store it in a base64 format and compare the request base64 string with the one in DB??
I would like to know what is the standard practice of validating a basic authentication request?
Please suggest some ideas. I am just starting up, please excuse stupidity in questions.
i suggest you to use a api token rather than a user/password combination. with a simple auth token you get two benefits.
Usernames or Passwords may change with the time and if so the user is forced to change all usernames and passwords in his application to get there application back to work. a simple token are constant and don't require any change when the username and password of the user changed.
With basic auth the client need to send there username + password in a unencrypted format. this is not very secure and on the worst case, unauthorized can login into the service backend with username + password from the request. simple token are only valid for api calls.
Generate a simple random token for each API user like Eq57dwypZaFW4f2xxRzFaGjwCYinOn6l13Mvds00P2ZzgdMPTk and require to send this token on each api request with the request header like
X-API-TOKEN: Eq57dwypZaFW4f2xxRzFaGjwCYinOn6l13Mvds00P2ZzgdMPTk
X-API-CLIENT-ID: 123456
on the server side validate with
<?php
$token = $_SERVER['X-API-TOKEN'];
$userID = $_SERVER['X-API-CLIENT-ID'];
$isValid = $findInDatabase->findToken($userID, $token);
if( $isValid )
{
// process api request
} else {
// invalid token
}

Categories