Firebase JWT-php 'Signature verification failed' on JWT::decode - php

Here's my code generating the token:
public static function GenerateNewAuthTokens(User $user): string {
$issuedAt = new DateTimeImmutable();
$expire = $issuedAt->modify('+' . AuthenticationHelper::AUTH_EXPIRE_MINUTES . ' minutes');
$username = $user->Username;
$issuedAtTimestamp = $issuedAt->getTimestamp();
$auth_data = [
'iat' => $issuedAtTimestamp, // Issued at: time when the token was generated
'iss' => AuthenticationHelper::SERVER_NAME, // Issuer
'nbf' => $issuedAtTimestamp, // Not before
'exp' => $expire->getTimestamp(), // Expire
'userName' => $username, // User name
];
return JWT::encode(
$auth_data,
AuthenticationHelper::SECRET_KEY,
AuthenticationHelper::ALGORITHM
);
}
Here is my code attempting to decode the token:
public static function GetAuthData(): ?object {
$headers = getallheaders();
if (isset($headers) && count($headers) && isset($headers['Authorization']) && strlen($headers['Authorization']) > 7) {
try {
$token = explode(" ", $headers['Authorization'])[1];
$decodedToken = JWT::decode($token, new Key(AuthenticationHelper::SECRET_KEY, AuthenticationHelper::ALGORITHM));
return $decodedToken;
} catch (\Throwable $th) {
//TODO
$err = $th;
}
}
return null;
}
It throws the "Signature verification failed" error in the JWT code here.
So far as I can tell - I'm following the example given on the repo home screen to a reasonable approximation.
I am using HS512 but have tried HS256 as well with no difference.
I have confirmed that the token I'm attempting to decode is exactly what was generated in the first method.
It's failing the compare check here, due to $hash and $signature not matching.

So turns out I wasn't sending the exact same token back that I was receiving. When JWT encodes the token data, it trims off the = at the end of any of the base64 encoded strings.
What I had stored contains those = at the end (usually). Because of this, when it ran its compare - it failed.
In summary - dur - check the values better.

Related

Ebay: Auth token is invalid. Validation of the authentication token in API request failed

I'm trying to use Ebay PHP SDK to connect to Ebay and fetch sellers selling item. For this I used following steps:
Step 1: Get authorize token and code for logged-in user. I used following code to implement.
use \DTS\eBaySDK\OAuth\Services as OauthService;
use \DTS\eBaySDK\OAuth\Types as OauthType;
use \DTS\eBaySDK\Constants;
use \DTS\eBaySDK\Trading\Services;
use \DTS\eBaySDK\Trading\Types;
use \DTS\eBaySDK\Trading\Enums;
$service = new OauthService\OAuthService([
'credentials' => $config['sandbox']['credentials'],
'ruName' => $config['sandbox']['ruName'],
'sandbox' => true
]);
$oauthParam = [
'client_id' => $config['sandbox']['credentials']['appId'],
'redirect_uri' => $config['sandbox']['redirect_uri'],
'response_type' => 'code',
'scope' => 'https://api.ebay.com/oauth/api_scope'
];
$urlParam = '';
$query = [];
foreach($oauthParam as $key => $param) {
$query[] = "$key=$param";
}
$urlParam = '?' . implode('&', $query);
$url = 'https://signin.sandbox.ebay.com/authorize' . $urlParam;
#session_start();
if(isset($_SESSION['ebay_oauth_token'])) {
$token = $_SESSION['ebay_oauth_token']['code'];
}
else {
if(isset($_GET['code'])) {
$token = $_GET['code'];
$_SESSION['ebay_oauth_token']['code'] = $token;
$request = new OauthType\GetUserTokenRestRequest();
$request->code = $token;
$response = $service->getUserToken($request);
if ($response->getStatusCode() !== 200) {
//Error
} else {
$_SESSION['ebay_oauth_token']['access_token'] = $response->access_token;
}
} else {
#header('location: ' . $url);
}
}
$userOauthToken = $_SESSION['ebay_oauth_token']['access_token'];
The above code is working as expected. That is the user is redirected to Sign In Page to authorize himself and get the set of Code and Access Token.
Step 2: Fetch Selling Items using code obtained from Step #1. I've used following code to implement the functionality.
$request->RequesterCredentials = new Types\CustomSecurityHeaderType();
$request->RequesterCredentials->eBayAuthToken = $token; //Obtained from Step 1
$request->ActiveList = new Types\ItemListCustomizationType();
$request->ActiveList->Include = true;
$request->ActiveList->Pagination = new Types\PaginationType();
$request->ActiveList->Pagination->EntriesPerPage = 10;
$request->ActiveList->Sort = Enums\ItemSortTypeCodeType::C_CURRENT_PRICE_DESCENDING;
$pageNum = 1;
do {
$request->ActiveList->Pagination->PageNumber = $pageNum;
$response = $service->getMyeBaySelling($request);
if (isset($response->Errors)) {
//Error Output
}
if ($response->Ack !== 'Failure' && isset($response->ActiveList)) {
foreach ($response->ActiveList->ItemArray->Item as $item) {
//Output response
}
}
$pageNum += 1;
} while ({condition});
I'm having problem in Step #2. It is generating Invalid Token while running the code.
I would highly appreciate if anyone help me.
You are mixing the requests. In the second part of your code, the request belongs to the Trading API that uses the Auth'n'Auth token, and you are trying to make the call using the OAuth token. These 2 tokens are different and work for different APIs.
You have 2 options.
Either keep the second part of your code, which appears to be correct, actually, but use the Auth'n'Auth token (that you can generate from the developer account). In this case, the first part is useless.
Keep the first part of your code, and delete the second part. In this case, you need to rewrite your second part of code using the OAuth API instead of the Trading API.

Getting the acess token from Guzzle Oauth authentication

I'm currently trying to develop a connection to the Reddit api through Oauth using Guzzle. I get to the point where I authenticate in Reddit, then I get to the authorization token, but I can't take the access token from the Guzzle response so I can set it as a cookie and using on subsequent requests. My current code looks like this:
public function __construct(){
if(isset($_COOKIE['reddit_token'])){
$token_info = explode(":", $_COOKIE['reddit_token']);
$this->token_type = $token_info[0];
$this->access_token = $token_info[1];
} else {
if (isset($_GET['code'])){
//capture code from auth
$code = $_GET["code"];
//construct POST object for access token fetch request
$postvals = sprintf("code=%s&redirect_uri=%s&grant_type=authorization_code",
$code,
redditConfig::$ENDPOINT_OAUTH_REDIRECT);
//get JSON access token object (with refresh_token parameter)
$token = self::runCurl(redditConfig::$ENDPOINT_OAUTH_TOKEN, $postvals, null, true);
//store token and type
if (isset($token->access_token)){
$this->access_token = $token->access_token;
$this->token_type = $token->token_type;
//set token cookie for later use
$cookie_time = 60 * 59 + time(); //seconds * minutes = 59 minutes (token expires in 1hr)
setcookie('reddit_token', "{$this->token_type}:{$this->access_token}", $cookie_time);
}
} else {
$state = rand();
$urlAuth = sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&state=%s",
redditConfig::$ENDPOINT_OAUTH_AUTHORIZE,
redditConfig::$CLIENT_ID,
redditConfig::$ENDPOINT_OAUTH_REDIRECT,
redditConfig::$SCOPES,
$state);
//forward user to PayPal auth page
header("Location: $urlAuth");
}
}
This is my authentication flow. The I have the runCurl method that is going to make the guzzle requests:
private function runCurl($url, $postVals = null, $headers = null, $auth = false){
$options = array(
'timeout' => 10,
'verify' => false,
'headers' => ['User-Agent' => 'testing/1.0']
);
$requestType = 'GET';
if ($postVals != null){
$options['body'] = $postVals;
$requestType = "POST";
}
if ($this->auth_mode == 'oauth'){
$options['headers'] = [
'User-Agent' => 'testing/1.0',
'Authorization' => "{$this->token_type} {$this->access_token}"];
}
if ($auth){
$options['auth'] = [redditConfig::$CLIENT_ID, redditConfig::$CLIENT_SECRET];
}
$client = new \GuzzleHttp\Client();
$response = $client->request($requestType, $url, $options);
$body = $response->getBody();
return $body;
}
The problem resides here, the getBody() method returns a stream, and if I use getBody()->getContents() I get a string, none of which can help me.
Any idea on how can I get the access token so I can finish the authentication process?
To answer the question itself - you just need to cast $body to string. It should be a json, so you will also need to json_decode it to use it as an object in the code above. So instead of return $body; in your runCurl, you need to do:
return json_decode((string)$body);
I would recommend to use the official client tho. The code in the question has some unrelated issues, which will make it costy to maintain.

How to handle Slim API JWT Authentication

I already generated a token from my api login using this code:
if ($isCorrect == 1) {
$key = "example_key";
$token = array(
"iss" => "http://mywebsite.com",
"iat" => 1356999524,
"nbf" => 1357000000,
'data' => [
'userName' => $UserName,
]
);
$jwt = JWT::encode($token, $key);
$decoded = JWT::decode($jwt, $key, array('HS256'));
$unencodedArray = ['jwt' => $jwt];
echo json_encode($unencodedArray);
}
So I have a token now, how can I use the token to other api? What I mean is, i dont want anybody to perform this api without logging in.
This is my sample API method:
$app->get('/api/user/{UserId}', function($request){
//Select query here
});
This is the library i used: https://github.com/firebase/php-jwt
Thank you very much for your help.
You Just need to add a middleware method for your API
that will check the validation of the JWT token with that user name
Then pass the request to the API
`
$app->add( function ( $Req ,$Res ,$next ){
//get token,username from the user
$token= $Req->getParsedBodyParam('token');
$user_name=$Req->getParsedBodyParam('username');
//check for empty of any of them
if(empty ($token)|| empty($user_name) ){
$message=array("success"=>false,'message'=>'Some data is empty');
return $Res->withStatus(401)
-> withJson($message);
}
else{
//Validation test for the taken for this user name
$decoded_token = $this->JWT::decode($token, 'YourSecret key', array('HS256'));
if( isset($decoded_token->data->userName) && $decoded_token->data->userName == $user_Name ){
$message=array('message'=>'the token is valid');
//pass through the next API
$Res=$next($Req,$Res->withJson($message));
return $Res;
}
else{
$message=array("sccess"=>false,"message"=>"Token Validation Error",'code'=>201);
return $Res->withStatus(401)
->withJson($message);
}
}
});
`

PHP MusicBrainz get the first release date

i am trying to get the first release date of a song using Musicbrainz. To get this i am using the mikealmond musicBrainz library.
The problem i have is that when i try to execute exactly the same code as in this example (https://github.com/mikealmond/MusicBrainz/blob/master/examples/first-recording-search.php) i always get an authentication error.
Client error response [status code] 401 [reason phrase] Unauthorized [url] http://musicbrainz.org/ws/2/artist/0383dadf-2a4e-4d10-a46a-e9e041da8eb3?inc=releases+recordings+release-groups+user-ratings&fmt=json
Therefore i tried to add my username and password to the request:
$brainz = new MusicBrainz(new GuzzleHttpAdapter(new Client()),'myusername','mypassword');
$brainz->setUserAgent('myapplicationname', '0.2', 'http://localhost:443/');
If i call the url in the error message manually and enter my username and password i get the array i expect.
I just had a discovery: If I removed -"+ user - ratings"- it does not require authentication.
Therefore i commented the lines with "user - ratings" in my project
Now i think it works, but the performance of the query is very bad and often i get Error 503 // The MusicBrainz web server is currently busy. Please try again later. //
It takes a few seconds just for one song. Does someone know if this is normal or if i still have some kind of mistake?
My code....
//Create new MusicBrainz object
$brainz = new MusicBrainz(new GuzzleHttpAdapter(new Client()), 'username', 'password');
$brainz->setUserAgent('applicationname', '0.2', 'http://localhost:443/');
// set defaults
$artistId = null;
$songId = null;
$lastScore = null;
$firstRecording = array(
'releaseDate' => new DateTime()
);
// Set the search arguments to pass into the RecordingFilter
$args = array(
"recording" => 'we will rock you',
"artist" => 'Queen',
);
try {
// Find all the recordings that match the search and loop through them
$recordings = $brainz->search(new RecordingFilter($args));
$recorings i can print and in the loop i can print each $recording, but the error comes when i extract the informations
/** #var $recording \MusicBrainz\Recording */
foreach ($recordings as $recording) {
// if the recording has a lower score than the previous recording, stop the loop.
// This is because scores less than 100 usually don't match the search well
if (null != $lastScore && $recording->getScore() < $lastScore) {
break;
}
$lastScore = $recording->getScore();
$releaseDates = $recording->getReleaseDates();
$oldestReleaseKey = key($releaseDates);
if (strtoupper($recording->getArtist()->getName()) == strtoupper($args['artist'])
&& $releaseDates[$oldestReleaseKey] < $firstRecording['releaseDate']
) {
$firstRecording = array(
'releaseDate' => $recording->releases[$oldestReleaseKey]->getReleaseDate()
);
}
}
pr($firstRecording);
} catch (Exception $e) {
pr($e->getMessage());
}
$brainz = new MusicBrainz(new GuzzleHttpAdapter(new Client()), 'username', 'password');
You must set your MusicBrainz account credentials. Replace 'username' with your account username, and 'password' with the password used to login to MusicBrainz.org

JWT (JSON Web Token) in PHP without using 3rd-party library. How to sign?

There are a few libraries for implementing JSON Web Tokens (JWT) in PHP, such as php-jwt. I am writing my own, very small and simple class but cannot figure out why my signature fails validation here even though I've tried to stick to the standard. I've been trying for hours and I'm stuck. Please help!
My code is simple
//build the headers
$headers = ['alg'=>'HS256','typ'=>'JWT'];
$headers_encoded = base64url_encode(json_encode($headers));
//build the payload
$payload = ['sub'=>'1234567890','name'=>'John Doe', 'admin'=>true];
$payload_encoded = base64url_encode(json_encode($payload));
//build the signature
$key = 'secret';
$signature = hash_hmac('SHA256',"$headers_encoded.$payload_encoded",$key);
//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature";
echo $token;
The base64url_encode function:
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
My headers and payload perfectly match the validation site's default JWT, but my signature doesn't match so my token is flagged as invalid. This standard seems really straightforward so what's wrong with my signature?
I solved it! I did not realize that the signature itself needs to be base64 encoded. In addition, I needed to set the last optional parameter of the hash_hmac function to $raw_output=true (see the docs. In short I needed to change my code from the original:
//build the signature
$key = 'secret';
$signature = hash_hmac('sha256',"$headers_encoded.$payload_encoded",$key);
//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature";
To the corrected:
//build the signature
$key = 'secret';
$signature = hash_hmac('sha256',"$headers_encoded.$payload_encoded",$key,true);
$signature_encoded = base64url_encode($signature);
//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature_encoded";
echo $token;
If you want to solve it using RS256 (instead of HS256 like OP) you can use it like this:
//build the headers
$headers = ['alg'=>'RS256','typ'=>'JWT'];
$headers_encoded = base64url_encode(json_encode($headers));
//build the payload
$payload = ['sub'=>'1234567890','name'=>'John Doe', 'admin'=>true];
$payload_encoded = base64url_encode(json_encode($payload));
//build the signature
$key = "-----BEGIN PRIVATE KEY----- ....";
openssl_sign("$headers_encoded.$payload_encoded", $signature, $key, 'sha256WithRSAEncryption');
$signature_encoded = base64url_encode($signature);
//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature_encoded";
echo $token;
Took me way longer than I'd like to admit
https://github.com/gradus0/appleAuth
look method $appleAuthObj->get_jwt_token()
<?php
include_once "appleAuth.class.php";
// https://developer.apple.com/account/resources/identifiers/list/serviceId -- indificator value
$clientId = ""; // com.youdomen
// your developer account id -> https://developer.apple.com/account/#/membership/
$teamId = "";
// key value show in -> https://developer.apple.com/account/resources/authkeys/list
$key = "";
// your page url where this script
$redirect_uri = ""; // example: youdomen.com/appleAuth.class.php
// path your key file, download file this -> https://developer.apple.com/account/resources/authkeys/list
$keyPath =''; // example: ./AuthKey_key.p8
try{
$appleAuthObj = new \appleAuth\sign($clientId,$teamId,$key,$redirect_uri,$keyPath);
if(isset($_REQUEST['code'])){
$jwt_token = $appleAuthObj->get_jwt_token($_REQUEST['code']);
$response = $appleAuthObj->get_response($_REQUEST['code'],$jwt_token);
$result_token = $this->read_id_token($response['read_id_token']);
var_dump($response);
var_dump($result_token);
}else{
$state = bin2hex(random_bytes(5));
echo "<a href='".$appleAuthObj->get_url($state)."'>sign</a>";
}
} catch (\Exception $e) {
echo "error: ".$e->getMessage();
}

Categories