I am using Firebase/JWT with php. I am trying to read the token in "decoded" php file but it shows be Signature verification failed not sure why that is happening. This is how I am encoding the token
<?php
use \Firebase\JWT\JWT;
require 'vendor/autoload.php';
require('config/Database.php');
$db = new Database;
$key = "helloworld";
//$jwt = JWT::encode($token, $key, 'HS512');
$post = file_get_contents("php://input");
$postdata = json_decode($post);
if($postdata){
$email = $postdata->email;
$password = $postdata->password;
$query = "SELECT * FROM users WHERE email = :email";
$db->query($query);
$db->bind(":email", $email);
$rows = $db->resultset();
if(password_verify($password, $rows[0]["hash"])){
$rows[0]["Success"] = "Success";
$token = array(
"rows" => $rows
);
$jwt = JWT::encode($token, $key, 'HS256');
header("auth: " . $jwt);
echo json_encode($jwt, 128);
}else{
echo "Failed";
}
}
?>
Then I am decoding the token in this file
<?php
use \Firebase\JWT\JWT;
require 'vendor/autoload.php';
require('config/Database.php');
$db = new Database;
$key = "helloworld";
//$jwt = JWT::encode($token, $key, 'HS512');
$post = file_get_contents("php://input");
$postdata = json_decode($post);
if($postdata){
$userData = $postdata->userdata;
// check if token is same stored in the database then decode
$jwt = JWT::decode($userData, $key, array('HS256'));
echo $jwt;
}
?>
It fails, returning a "Signature verification failed" error.
Any help is appreciated. Thank you.
The problem why it wasn't verifying was because I was using json_encode on the jwt and that caused it to be wrapped around quotes again so the token looked something like this ""eY0lkajflajk...."" and that caused the verifying exception. Thank you #zerkms for bring that up.
Related
I have some some problem...
I create an Microsoft Team WEBHOOK:
WEBHOOK Creation
WEBHOOK Secret
But teams dont send hmac hash authorization to my PHP Script header...
<?php
//The secret share with Microsoft Teams
$secret = "{MY TEAMS WEBHOOK SECRET}";
//get headers
$a = getallheaders();
$provided_hmac = substr($a['Authorization'],5);
//Get data from request
$data = file_get_contents('php://input');
//json decode into array
$json = json_decode($data, true);
//hashing
$hash = hash_hmac("sha256",$data,base64_decode($secret), true);
$calculated_hmac = base64_encode($hash);
//start log var
$log = "\n========".date("Y-m-d H:i:s")."========\n".$provided_hmac."\n".$calculated_hmac."\n";
try{
//compare hashs
if(hash_equals($provided_hmac,$calculated_hmac))
throw new Exception("No hash matching");
//response text
$txt="Hi {$json["from"]["name"]} welcome to your custom bot";
echo '{
"type": "message",
"text": "'.$txt.'"
}';
$log .= "Sended: {$txt}";
}catch (Exception $e){
$log .= $e->getMessage();
}
//write log
$fp = fopen("log.txt","a");
fwrite($fp, $log . PHP_EOL);
fclose($fp);
?>
Any one can help me with this ?
I try some methods but dont work.
Header dont have any part/string with authorization hash...
Thanks for help.
I have built a log in system and I am adding google recaptcha for security. I am getting an error on this line: $result = json_decode($url, TRUE);
The error says;
failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request.
This is my first time using recaptcha and I am not sure if this is a common mistake.
<?php
$secret = '*****';
$response = $_POST['g-recaptcha-response'];
$remoteip = $_SERVER['REMOTE_ADDR'];
$captcha = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=$secret&response=$response&remoteip=$remoteip");
$result = json_decode($url, TRUE);
$username;
$password;
$captcha;
if (isset($_POST['username']))
$username = $_POST['username'];
if (isset($_POST['password']))
$password = $_POST['password'];
if (isset($_POST['g-recaptcha-response']))
$captcha = $_POST['g-recaptcha-response'];
if (!$captcha) {
echo '<p class="error-message">Please Complete The Captcha!</p>';
header("location: login.php");
exit;
}
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=6LfG-S8UAAAAAIqW1sBE31yMPyO4zeqOCgDzL1mA&response=" . $captcha . "&remote=" . $_SERVER['REMOTE_ADDR']), true);
if ($response['success'] == false) {
echo '<p class="error-message">Please Fill Captcha!</p>';
} else {
echo '<p class="error-message2">Welcome</p>';
}
if (isset($_POST['submit'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$q = $handler->prepare('SELECT * FROM users WHERE username = ?');
$q->execute(array($username));
$result = $q->fetch(PDO::FETCH_ASSOC);
if ($result !== false) {
$hash_pwd = $result['password'];
$hash = password_verify($password, $hash_pwd);
if ($hash) {
$_SESSION['username'] = $username;
header("location:index.php");
return;
} else {
echo '<p class="error-message3"><br><br>You have ented an incorrect login!<br>Please try again</p>';
}
}
}
?>
If this really is your complete code:
It seems you are using $url (in the line $result = ...) without having initialized it before.
Additionally, I would expect that a variable with name $url contains an URL, and URLs are not in JSON format, so this raises some alarm signs. You eventually do not want to JSON-parse an URL, but instead parse what this URL returns when calling it.
Secondly, sometimes the line numbers within error messages or warnings are misleading. I highly doubt that the error you have mentioned (HTTP request failed) is related to json_decode(). json_decode(), as the name implies, just parses a string in JSON format, but does not load anything via HTTP.
So the error message probably comes from the line above ($captcha = file_get_contents(...);). I suppose that the URL you give there is wrong, or that Google refuses the request for another reason.
The first thing I would do is putting that URL into a variable and print it out (e.g. by using error_log()).
If that does not lead to the source of the problem, I would copy that URL (not from the code, but from the output produced by error_log()) and paste it directly into the address bar of a new browser window. If this yields the expected result (you should see Google's answer to the request in the browser window), the error is in your code. Otherwise, the error is in the URL.
<?php
session_start();
error_reporting(E_ALL);
try {
$ini = parse_ini_file("/var/www/admin.ini");
$user = $ini['user'];
$pass = $ini['pass'];
$name = $ini['name'];
$host = $ini['host'];
$handler = new PDO('mysql:host='.$host.'; dbname='.$name, $user, $pass);
$handler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e){
error_log($e);
echo $e->getMessage();
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => [
'secret' => '********',
'response' => $_POST['g-recaptcha-response'],
],
]);
$response = json_decode(curl_exec($curl));
if (!$response->success) {
if (isset($_POST['submit'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$q = $handler->prepare('SELECT * FROM users WHERE username = ?');
$q->execute(array($username));
$result = $q -> fetch(PDO::FETCH_ASSOC);
if ($result !== false) {
$hash_pwd = $result['password'];
$hash = password_verify($password, $hash_pwd);
if ($hash) {
$_SESSION['username'] = $username;
header("location:index.php");return;
}
else {echo '<p class="error-message3"><br><br>You have ented an incorrect login!<br>Please try again</p>';
}
}
}
}
?>
I am working in building a restful api using php & slim for my app backend and also we have a web version but my team use pure php with the web suit , we work separately . it is just the first time that i have the backend responsibility I have a miss understanding about how i can handle the authentication in a secure and professional way i have read this article but i need a detailed way how to implement it in php & slim and extend it to the web team so to use the same authentication technic .
this is how the log in / sign up code that i have used :
help me to improve it :
$app->post('/api/create_user', function( $request , $response , $args ){
require('../config.php');
$email = $_POST['email'];
$qry= "select * from user where email ='". $email."'";
$result=$mysqli->query($qry);
if(mysqli_num_rows($result)>0){
$user = new stdClass();
$user->status=0;
$user->error=" the email is registered ";
$result = new stdClass();
$result->result=$user;
}
else {
$password = md5($_POST['password']);
$image=$_FILES['image']['name'];
$email=$_POST['email'] ;
$nickname =$_POST['nickname'];
$birthDay=$_POST['birthdate'];
$insert_req="INSERT INTO user VALUES ('', '$email', '$password','$nickname')";
$insert_user_result=$mysqli->query($insert_req);
if ($insert_user_result) {
$user = new stdClass();
$user->status=1;
$result = new stdClass();
$result->result=$user;}
else {$user = new stdClass();
$user->status=2;
$user->error=mysql_error();
$result = new stdClass();
$result->result=$user;}
}
if (isset($result)){
header('Content-type: application/json');
echo json_encode($result);}
});
?>
<?php
$app->post('/api/login', function( $request , $response , $args ){
require('../config.php');
$email =$_POST['email'];
$password = md5($_POST['password']);
$findemail_qry= "select user_id from user where email ='". $email."'";
$findemail_result =$mysqli->query($findemail_qry);
if(mysqli_num_rows($findemail_result)>0)
{
$login_qry="select user_id from user where email ='". $email."'AND password ='".$password."'";
$login_result =$mysqli->query($login_qry);
if(mysqli_num_rows($login_result)>0)
{
$data =mysqli_fetch_assoc ($login_result);
$user_id=$data['user_id'];
$user = new stdClass();
$user->status=1;
$user->user_id=$user_id;
$result = new stdClass();
$result->result=$user;}
else
{$user = new stdClass();
$user->status=2;
$user->error="wrong password";
$result = new stdClass();
$result->result=$user; }
}
else
{$user = new stdClass();
$user->status=0;
$user->error=" this email not registered ";
$result = new stdClass();
$result->result=$user;}
if (isset($result)){
header('Content-type: application/json');
echo json_encode($result);
}
});
?>
You can use JWT in your slim application as your authntication layer for Your API
You can use this library to implement it
Slim JWT
In a simple way you can use something like this
Try to use Basic authorization to send user name and password
Then You will generate a new JWT Token ,That will used in all others API
<?php
use \Firebase\JWT\JWT;
use \Slim\Middleware\HttpBasicAuthentication\AuthenticatorInterface;
require '../vendor/autoload.php';
$app = new \Slim\App;
class RandomAuthenticator implements AuthenticatorInterface {
public function __invoke(array $arguments) {
//validation for user and password
$Password=$arguments['password'];
$user=$arguments['user'];
if(($Password=="admin") &&($user=="admin") ){
return true;
}
else{
return false ;
}
}}
//add http basic middleware for login route
$app->add(new \Slim\Middleware\HttpBasicAuthentication([
"path" => "/login",
"realm" => "Protected",
"authenticator" => new RandomAuthenticator()
]));
$app->post("/login", function ($request, $response, $arguments) {
//generate JWT token
$now = new DateTime();
$future = new DateTime("now +20 minutes");
$server = $request->getServerParams();
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp(),
"sub" => $server["PHP_AUTH_USER"],
];
$secret = "TOURSECERTKEY";
$token = JWT::encode($payload, $secret, "HS512");
$data["status"] = "ok";
$data["token"] = $token;
return $response->withStatus(201)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
});
//Add jWT token Authorization middleware for all API
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => ["/"],
"passthrough" => ["/login"],
"secret" => "TOURSECERTKEY",
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]));
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();
}
Using the default Tumblr v2 API, I'm able to connect to my application, and retrieve posts from my own account. However, I'd like users to be able to connect their own accounts. I've tried to figure this out but I'm not entirely sure how to use OAuth (im using this class). How is this done?
The code I'm using to retrieve dashboard posts is:
$consumerKey = 'xxx';
$consumerSecret = 'xxx';
$tumblr = new Tumblr\API\Client(
$consumerKey,
$consumerSecret
);
var_dump( $tumblr->getDashboardPosts() ); // using var_dump for testing purposes only
This code works, but it's only returning the code for my PERSONAL account.
I figured it out, thanks to Github user seejohnrun.
require_once 'include/util.php';
$consumerKey = 'XXX';
$consumerSecret = 'XXX';
$client = new Tumblr\API\Client($consumerKey, $consumerSecret);
$requestHandler = $client->getRequestHandler();
$requestHandler->setBaseUrl('https://www.tumblr.com/');
// If we are visiting the first time
if (!$_GET['oauth_verifier']) {
// grab the oauth token
$resp = $requestHandler->request('POST', 'oauth/request_token', array());
$out = $result = $resp->body;
$data = array();
parse_str($out, $data);
// tell the user where to go
echo ' GO ';
$_SESSION['t']=$data['oauth_token'];
$_SESSION['s']=$data['oauth_token_secret'];
} else {
$verifier = $_GET['oauth_verifier'];
// use the stored tokens
$client->setToken($_SESSION['t'], $_SESSION['s']);
// to grab the access tokens
$resp = $requestHandler->request('POST', 'oauth/access_token', array('oauth_verifier' => $verifier));
$out = $result = $resp->body;
$data = array();
parse_str($out, $data);
// and print out our new keys we got back
$token = $data['oauth_token'];
$secret = $data['oauth_token_secret'];
echo "token: " . $token . "<br/>secret: " . $secret;
// and prove we're in the money
$client = new Tumblr\API\Client($consumerKey, $consumerSecret, $token, $secret);
$info = $client->getUserInfo();
echo "<br/><br/>congrats " . $info->user->name . "!";
}