How to handle Slim API JWT Authentication - php

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);
}
}
});
`

Related

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

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.

How to verify JWT signature in PHP?

I have a function that generates a JWT :
function getToken($user, $expTime){
$jwt = \Firebase\JWT\JWT::encode([
'iss' => request()->getBaseUrl(),
'sub' => "{$user['id']}",
'exp' => $expTime,
'iat' => time(),
'nbf' => time(),
'is_admin' => $user['role_id'] == 1
], getenv("SECRET_KEY"), 'HS256');
return $jwt;
}
This function returns the below token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJcL2FwaSIsInN1YiI6InVzNWIzY2M4YmRlMDc4MSIsImV4cCI6NTUxMDY1ODkyNDAwMCwiaWF0IjoxNTMwNzM4NTkwLCJuYmYiOjE1MzA3Mzg1OTAsImlzX2FkbWluIjpmYWxzZX0.3bMaxCaMprURZEDurnckZWSoDRp7ePMxZXDW0B6q6fk
When I use this token to make a request I get that:
{
"status": "error",
"message": "Signature verification failed"
}
To make it work I go to https://jwt.io/, add the key and verify it by passing the secret.
Then I get this token :
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIvYXBpIiwic3ViIjoidXM1YjNjYzhiZGUwNzgxIiwiZXhwIjo1NTEwNjU4OTI0MDAwLCJpYXQiOjE1MzA3Mzg1OTAsIm5iZiI6MTUzMDczODU5MCwiaXNfYWRtaW4iOmZhbHNlfQ.heF_L9LrFp7Hht2dbVtOMx_gdUtmPKzrMgxW1_jdWLo
And this works fine. But how to verify it with php code so I can send it to the user?
Code for response:
function loginUser($email, $password) {
try {
// Connecting to databas
$db = new db();
$db = $db->connect();
$user = findUserByEmail($email, $db);
if(empty($user)){
echo 'User not found';
exit;
}
if(!password_verify($password, $user['password'])) {
echo 'Password does not match';
exit;
}
$expTime = time() * 3600;
$jwt = getToken($user, $expTime);
// Close databse
$db = null;
} catch(PDOException $e){
echo $e->getMessage();
}
return $jwt;
}
If you're landing on this page because of a "Signature verification failed" Google search, here is one thing to consider. I was getting this error because there were two spaces between "Bearer" and my token in the Authorization header.
Wrong:
Authorization:Bearer eyJraWQiOiJDT2N...
Correct:
Authorization:Bearer eyJraWQiOiJDT2N...
Ok finally I made it work by changing a little the function that generates the token:
function getToken($user, $expTime){
$key = "secretkey";
$token = array(
'iss' => request()->getBaseUrl(),
'sub' => "{$user['id']}",
'exp' => $expTime,
'iat' => time(),
'nbf' => time(),
'is_admin' => $user['role_id'] == 1
);
return JWT::encode($token, $key);
}

Sending a hashed token as GET parameter

I have tried creating a RESTful API service. I have generated a token by hashing a string (using a randomly generated secret key that is stored in the database) that is returned by the login script on successful login, to the client end as a part of a JSON object. The client passes the token (along with some other fields as a JSON object) as a GET/POST parameter to get access to the other API services. However, it seems that when the token string is passed around as a JSON object, the string gets altered somewhere in the middle, and dehashing it with the secret key at the verification endpoint does not yield the same string as the string that was hashed. Result is an unsuccessful attempt at getting the data secured by the token.
I am adding parts of the code that are relevant:
Login Script
$secret = newsecret($rand);
$token = newtoken($secret, $str);
$qry1 = "UPDATE user_master set user_secret='".$secret."' where user_code='".$uid."'";
$res1 = mysqli_query($conn, $qry1);
$outdata = array("status" => "success", "username" => $un, "uid" => $uid, "token" => $token);
header('Content-type: application/json');
echo json_encode($outdata);
Client JS
$.post("http://www.ckoysolutions.com/apis/login.php", inputs).done(function(data){
if(data.status=="success") {
var inputs = '{ '
+'"uid" : "'+data.uid+'" , '
+'"token" : "'+data.token+'"'
+' }';
window.location='http://hasconpanel.ckoysolutions.com/hasconpanel.php?inputs='+inputs;
}
else {
alert(data.message);
}
});
Redirected page (http://hasconpanel.ckoysolutions.com/hasconpanel.php) sending token as json as a curl postfield for verification
if(isset($inputs->uid) && isset($inputs->token)) {
$token = $inputs->token;
$uid = $inputs->uid;
$auth_data = array("uid" => $uid, "token" => $token);
$auth_json = json_encode($auth_data);
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $auth_json,
CURLOPT_URL => "http://www.ckoysolutions.com/apis/authuser.php",
CURLOPT_HTTPHEADER => [
'Content-Type: application/json'
]
]);
$result = curl_exec($curl);
curl_close($curl);
echo $result;
}
Function used in http://www.ckoysolutions.com/apis/authuser.php to authenticate
$row = mysqli_fetch_array($res);
$secret = $row['user_secret'];
$token = $token;
$un = $row['user_name'];
$words = explode(" ",$un);
$fn = $words[0];
$udetails = $row['user_log'];
$udetails = json_decode($udetails);
$uip = $udetails->ip;
$date_time = $udetails->time;
$str = $date_time.$fn.$uip;
$chkstr = decrypt($secret, $token);
if($str == $chkstr) {
$outdata = array("status" => "success");
mysqli_close($conn);
}
else {
$outdata = array("status" => "failure");
mysqli_close($conn);
}
header('Content-type: application/json');
echo json_encode($outdata);
Please do suggest what might be going wrong here.
I had a similar issue to this and found that if the token is passed as a query string parameter and contains the + character it will get removed. I discovered the issue because the call wasn't always breaking. The easiest solution for me was to replace "+" with "P".
AJAX POST and Plus Sign ( + ) — How to Encode?

Yahoo App Authentication Laravel

i am using this library
https://github.com/artdarek/oauth-4-laravel
here is my code
public function loginWithYahoo() {
// get data from input
$token = Input::get( 'oauth_token' );
$verify = Input::get( 'oauth_verifier' );
// get yahoo service
$yh = OAuth::consumer( 'Yahoo' );
// if code is provided get user data and sign in
if ( !empty( $token ) && !empty( $verify ) ) {
// This was a callback request from yahoo, get the token
$token = $yh->requestAccessToken( $token, $verify );
$xid = array($token->getExtraParams());
$result = json_decode( $yh->request( 'https://social.yahooapis.com/v1/user/'.$xid[0]['xoauth_yahoo_guid'].'/profile?format=json' ), true );
dd($result);
}
// if not ask for permission first
else {
// get request token
$reqToken = $yh->requestRequestToken();
// get Authorization Uri sending the request token
$url = $yh->getAuthorizationUri(array('oauth_token' => $reqToken->getRequestToken()));
// return to yahoo login url
return Redirect::to( (string)$url );
}
}.
I am getting following error.. can any one please give some hint???Thanks in advance
Call to undefined method OAuth\OAuth2\Service\Yahoo::requestRequestToken()
Use this code:
$url = $yh->getAuthorizationUri();
return redirect((string)$url);
instead of this code:
// get request token
$reqToken = $yh->requestRequestToken();
// get Authorization Uri sending the request token
$url = $yh->getAuthorizationUri(array('oauth_token' => $reqToken->getRequestToken()));
// return to yahoo login url
return Redirect::to( (string)$url );

Laravel Facebook-Login / missing email

I am trying to create a Facebook-Login in my Laravel application. However, the array I get with all the user information does not contain the users email even though I already ask for permission to receive the email.
This is my code:
Route::get('login/fb', function() {
$facebook = new Facebook(Config::get('facebook'));
$params = array(
'redirect_uri' => url('/login/fb/callback'),
'scope' => 'email',
);
return Redirect::to($facebook->getLoginUrl($params));
});
Route::get('login/fb/callback', function() {
$code = Input::get('code');
if (strlen($code) == 0) return Redirect::to('/')->with('message', 'There was an error communicating with Facebook');
$facebook = new Facebook(Config::get('facebook'));
$me = $facebook->api('/me');
return $me;
Returning $me gives me all the important user information besides the email address.
Is there any way to fix this?
Any help would be much appreciated.
Thanks.
There are instances that facebook will not return an email. This can be because the user has not set a primary email, or their email has not been validated. In this case, your logic should check to see if an email was returned, if not, use their facebook email. FacebookUsername#facebook.com
//I used with sentry
// get data from input
$code = Input::get( 'code' );
// get fb service
$fb = OAuth::consumer( 'Facebook' );
// check if code is valid
// if code is provided get user data and sign in
if ( !empty( $code ) ) {
// This was a callback request from facebook, get the token
$token = $fb->requestAccessToken( $code );
// Send a request with it
$result = json_decode($fb->request( '/me?fields=id,name,first_name,last_name,email,photos' ), true);
$message = 'Your unique facebook user id is: ' . $result['id'] . ' and your name is ' . $result['name']. $result['email'];
//echo $message. "<br/>";
//Var_dump
//display whole array().
//echo('http://graph.facebook.com/'.$result['id'].'/picture?type=large<br>');
//dd($result);
$user = \User::where("email",$result['email'])->first();
if($user!=NULL){
$userxx = Sentry::findUserByLogin($result['email']);
Sentry::login($userxx, false);
return Redirect::to('Beşiktaş');
}
else
{
$k=str_random(8);
$user = Sentry::register(array(
'activated' => 1,
'facebook' => 1,
'password' => $k,
'email' => $result['email'],
'first_name' =>$result['first_name'],
'last_name' => $result['last_name'] ,
'avatar' => 'http://graph.facebook.com/'.$result['id'].'/picture?type=large',
));
Sentry::login($user, false);
return Redirect::to('Beşiktaş');
}
}
// if not ask for permission first
else {
// get fb authorization
$url = $fb->getAuthorizationUri();
// return to facebook login url
return Redirect::to( (string)$url );
}

Categories