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);
}
Related
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.
I've implemented system wide with JWT. Everything is working great except a key point, the expiration method.
Map<String, Object> header = new HashMap<>();
header.put("typ", Header.JWT_TYPE);
String compactJws = Jwts.builder()
.setHeader(header)
.claim("email", email)
.claim("password", password)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS256, settings.getString("keychain", "password"))
.compact();
Request request = new Request.Builder()
.url(SITE_URL + "secure.php")
.post(new FormBody.Builder().add("data", compactJws).build())
.tag("login")
.build();
okClient.newCall(request).enqueue(this);
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6Imd1eSIsInBhc3N3b3JkIjoiZ3V5IiwiaWF0IjoxNDk3NTU0MzAzLCJleHAiOjE0OTc1NTc5MDN9.4GFSC_OCYzkCxGetTFKKxoUkbpxWi51ccoKtIpPjz6g
<?php
$jwt = $_POST['data'];
require_once 'jwt/src/BeforeValidException.php';
require_once 'jwt/src/ExpiredException.php';
require_once 'jwt/src/SignatureInvalidException.php';
require_once 'jwt/src/JWT.php';
use \Firebase\JWT\JWT;
try {
$decoded_array = (array) JWT::decode($jwt, base64_decode("password"), array('HS256'));
}
catch (SignatureInvalidException $e) {
echo json_encode(array('user_id' => - 3, 'title' => 'Contact 3gcb19#gmail.com', 'msg' => 'SignatureInvalidException'));
exit(0);
}
catch (ExpiredException $e) {
echo json_encode(array('user_id' => - 3, 'title' => 'Contact 3gcb19#gmail.com', 'msg' => 'ExpiredException'));
exit(0);
}
catch (UnexpectedValueException $e) {
echo json_encode(array('user_id' => - 3, 'title' => 'Contact 3gcb19#gmail.com', 'msg' => 'UnexpectedValueException'));
exit(0);
}
$email = $decoded_array['email'];
$password = $decoded_array['password'];
Always throws an UnexpectedValueException
Remove the timestamps and it works fine on the php end
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
What am I missing here??
I have a login processing file in which I am attempting to set a cookie:
$expTime = time() + 3600;
$key = getenv("SECRET_KEY");
$token = array(
"iss" => request()->getBaseUrl(),
"sub" => [$user['id']],
"exp" => $expTime,
"iat" => time(),
"nbf" => time(),
"is_admin" => $user['role_id'] == 1
);
$jwt = JWT::encode($token, $key);
$accessToken = new Cookie('access_token', $jwt, $expTime, '/', getenv("COOKIE_DOMAIN"));
redirect('/', ['cookies' => [$accessToken]]);
I'm using Firebase/JWT to include a JWT as the cookie value. The SECRET_KEY and COOKIE_DOMAIN are pulled in from my .ENV file.
I then call my redirect()
function redirect($path, $extra = []) {
$response = new Response(
null,
Response::HTTP_FOUND,
array('location' => $path)
);
if (key_exists('cookies', $extra)) {
foreach ($extra['cookies'] as $cookie) {
$response->headers->setCookie($cookie);
}
}
$response->send();
}
I then test whether or not the cookie has been set in my index file:
if (request()->cookies->has('access_token')) {
echo "Logged in";
} else echo "No cookie :(";
My problem is that my test is returning "No cookie :(".
Any help would be greatly appreciated.
If you prefer you can fork it on GutHub:
jpradcliffe/user-authentication
I finally resolved the issue with some help (see comments below). The code as it stands above is correct. The issue was in my .env file.
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);
}
}
});
`
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 );
}