I am a newbie to JWT Token System in laravel 5 and using tymon JWT Auth
I managed to create my custom JWT token and my code as follows
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWTManager as JWT;
use JWTAuth;
use JWTFactory;
use Tymon\JWTAuth\Exceptions\JWTException;
public function login(Request $request)
{
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
// return response()->json(compact('token')); // This didnt work?Why?
return response()->json(compact($token))->header('Authorization','Bearer '.$token);
}
public function getUser(){
$token = JWTAuth::parseToken();
echo $token;
}
Here are my following clarifications required
// return response()->json(compact('token'));
Why this gave me an empty json object as {"token":{}}
Is it the right way, i could send my custom data in token and get it back the foo and baz values from the same token?
The output of my code while testing with postman is an empty array. as []. But my headers are added with Authorization →Bearer eyJ0eXAiOiJKV1QiLCJhbG...
Is this correct?
3a. Instead of a simple blank array, i need a success message as 'authorized':true. How can i achieve it?
How should i pass this token back to test. Where should this token be passed using postman. I passed it through Headers as shown in the image
How could i parse this token using laravel and get the custom data i.e foo and baz sent as a token. The method i called is getUser here.
I dont think the token creation is being built properly. Below is working code for login token creation. For this, make sure that the 'user' model under your config/jwt.php is the correct eloquent user model for your application.
$user = array(
'user' => $request->input('email'),
'password' => $request->input('pass')
);
$customClaims= ['usr' => $user['user']];
if(!$token = JWTAuth::attempt($user, $customClaims)){
abort(401);
}
else{
return response()->json(compact('token'));
}
Also included in the above code with the custom claims variable, you were on the right track with that just needs to be passed as a second parameter in the attempt function.
Only the client needs to send the authorization: Bearertoken header to prove that they are who they say they are (I am coming from an android client/server jwt background. So sorry if this doesnt apply to your application).
3a. For any subsequent pages that the user browses to, you simply add an if statement like this
if(!$user = JWTAuth::parseToken()->authenticate()){
abort(401);
}
else{
// Code allowing the user to see protected content
}
See answer to question 3. include an http header with authorization BearerToken
To extract the data from the JWT Payload, you will need to decode the base64 encoded text from the text after the first period in the token and send that to a string. Then run that string through the base64_decode($string) function. That should start to give you some of the payload data.
Hope this helps.
I had the same problem here and i got the following solution:
public function whatEver()
{
$token = JWTAuth::parseToken();
$response = $token->getPayload()->get('foo');
return $response
}
this should return bar.
you can use this method in your user model :
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [
'perms' => '
'
];
}
Related
in one of my projects i want to add a google one tap login for that i followed instructions as mentioned.
The front end is working fine but there is issue with the backend.
Here is my code.
I have added this script to the header.
and this code after body open
<div id="g_id_onload"
data-client_id="#####################.googleusercontent.com"
data-login_uri="/login/google/oneTap"
data-_token="{{csrf_token()}}"
data-method="post"
data-ux_mode="redirect"
data-auto_prompt="true">
</div>
This is the route
Route::get('/login/google/oneTap', [App\Http\Controllers\SocialLoginController::class, 'oneTap']);
In an article regarding one, tap login author told that it requires a post method but there is clarification on how to add a post method.
This is the article.
https://www.teachnep.com/blog/how-to-add-one-tap-login-to-laravel-project#
My backend code.
public function oneTap(REQUEST $request)
{
$token = $request->credential;
$tokenParts = explode('.', $token);
$tokenHeader = base64_decode($tokenParts[0]);
$tokenPayload = base64_decode($tokenParts[1]);
$jwtHeader = json_decode($tokenHeader);
$jwtPayload = json_decode($tokenPayload);
$user = $jwtPayload;
return $user;
}
It returns null;
Any help would be appreciated.
You must define your route using the POST verb instead of GET.
For example:
Route::post('/login/google', [GoogleSignInController::class, 'login'])
->name('login.google');
Even though you can verify the "ID Token" (this is how Google refers to the credential param) by yourself, it is recommended to use the official Google API Client Library.
You can add it to your project using Composer:
composer require google/apiclient
In addition to validate the credential field, you should validate the CSRF token provided in the g_csrf_token field.
To summarize:
/**
* Validate the "ID Token" using the Google API Client Library.
* https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
*/
public function login(Request $request)
{
if ($_COOKIE['g_csrf_token'] !== $request->input('g_csrf_token')) {
// Invalid CSRF token
return back();
}
$idToken = $request->input('credential');
$client = new Google_Client([
'client_id' => env('GOOGLE_CLIENT_ID')
]);
$payload = $client->verifyIdToken($idToken);
if (!$payload) {
// Invalid ID token
return back();
}
dd($payload);
}
Note that I printed the $payload at the end:
You may want to use the $payload['sub'] that represents the user id to register / authenticate this user in your application, as well as its name and picture.
I am implementing facebook data deletion callback but I got really lost and i can't continue on the JSON response that facebook is expecting.
Return a JSON response that contains a URL where the user can check the status of their deletion request and an alphanumeric confirmation code. The JSON response has the following form:
{ url: '<url>', confirmation_code: '<code>' }
that is the part that I got lost and stuck. My question is
what is the URL should do or show.
what is the logic between the confirmation code
so far here is what I did on my controller.
<?php
namespace App\Http\Controllers\User\Auth\Socialite;
use App\Models\User;
use Illuminate\Http\Request;
class FacebookSocialLoginController extends SocialLoginFactory
{
public function provider(): string
{
return 'facebook';
}
public function dataDeletionCallback(Request $request)
{
$signed_request = $request->get('signed_request');
$data = $this->parse_signed_request($signed_request);
$user_id = $data['user_id'];
// here will delete the user base on the user_id from facebook
User::where([
['provider' => 'facebook'],
['provider_id' => $user_id]
])->forceDelete();
// here will check if the user is deleted
$isDeleted = User::withTrashed()->where([
['provider' => 'facebook'],
['provider_id' => $user_id]
])->find();
if ($isDeleted ===null) {
return response()->json([
'url' => '', // <------ i dont know what to put on this or what should it do
'code' => '', // <------ i dont know what is the logic of this code
]);
}
return response()->json([
'message' => 'operation not successful'
], 500);
}
private function parse_signed_request($signed_request) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$secret = config('service.facebook.client_secret'); // Use your app secret here
// decode the data
$sig = $this->base64_url_decode($encoded_sig);
$data = json_decode($this->base64_url_decode($payload), true);
// confirm the signature
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
private function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
}
what is the URL should do or show.
The purpose of this URL, is what the documentation said - to provide a way for the user, to check on the status of their deletion request.
Not all apps will be able to delete all personal user data immediately, the moment the user requests it.
Some might need to keep a subset of the data for legal reasons; others might simply need some extra processing time, because the process can not be handled in a totally automated matter, and a human needs to get involved.
So the user is given this status check URL in response to their request – so that they can go visit that URL tomorrow, or two weeks or six months from now, and check on the status of their deletion request - were you able to delete all data by now, will it still take some time, is there some data that won’t be deleted for legal reasons, etc.
what is the logic between the confirmation code
Just a different way to access the same information. Maybe checking the status via the URL you provided is not enough for the user, so they might want to call or send an email to your support staff, to inquire about the status of their deletion request. Then they can give your support people that code, and they can go look up the necessary information via that.
If you check the code examples in the documentation, they are using the same code value in the status check URL, and as the confirmation code. So you can use the same code for both.
Create it, store it in your database, and associate the status of a particular user’s deletion request with that code.
I am trying to find the logged in user in my application using Auth but i get trying to get property of non-object which i understand clearly that it is returning null.
In my code below, an event triggers my webhook and post is sent to the address below. The function orderCreateWebhook triggers but that is where the error comes from..
The line $get_template = Order::where('id', Auth::user()->id);. Why is Auth returning null please? I am logged as well because i use auth in this same controller for another function which works fine.
Is it because it a webhook ?
Controller
public function registerOrderCreateWebhook(Request $request)
{
$shop = "feas.myshopify.com";
$token = "8f43d89a64e922d7d343c1173f6d";
$shopify = Shopify::setShopUrl($shop)->setAccessToken($token);
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json", ['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook',
'format' => 'json'
]
]);
}
public function orderCreateWebhook(Request $request)
{
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
In your function registerOrderCreateWebhook you appear to be making a request to shopify api and providing your webhook as the address which shopify will redirect the user to upon success. If this is correct, that request does not know about the user who generated the original request that made the api request since the request is coming from a completely different origin.
You would need to pass some key along with the url and then obtain the user within orderCreateWebhook. Something like:
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json",
['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook/some-unique-key',
'format' => 'json'
]
]);
My suggestion would be to have a unique hash stored somewhere that relates back to the user in your system, perhaps a column in your users table. I wouldn't use the user_id for security reasons. So you would end up with something like:
//route
Route::get('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
//or
Route::post('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
// depending on the request type used by api which calls this endpoint
// controller function
public function orderCreateWebhook($uniqueKey, Request $request)
{
$user = User::where('unique_key', $uniqueKey)->first();
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
Is it because it a webhook ?
Yes, you can't use sessions in a webhook. It's the shopify server which is making the call. You should read the doc, it may exist a way to give an unique identifier in your call to shopify api and get it back in the webhook to find your user associated.
just use this to get authenticated user
use the facade in your class/Controller
use Illuminate\Support\Facades\Auth
public function getAuthUser(){
$user = Auth::user()
if(!is_null($user)
{
//user is authenticated
}
else
{
// no user
}
}
I'm trying to learn Laravel and my goal is to be able to build a RESTful API (no use of views or blade, only JSON results. Later, an AngularJS web app and a Cordova hybrid mobile app will consume this api.
After some research, I'm inclining to choose JWT-Auth library for completely stateless benefit. My problem is: I have 2 main types of users: customers and moderators. Customers are not required to have a password. I need to be able to generate a token for access with the provided email only. If that email exists in the database and it belongs to a customer, it will generate and return the token.
If it exists and belongs to a moderator, it will return false so the interface can request a password. If the email doesn't exist, it throws an invalid parameter error.
I read the docs here and it says it's possible to use Custom Claims. But the docs doesn't explain what are claims and what it means the array being passed as custom claims. I'd like some input on how to go about achieving what I explain above.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthenticateController extends Controller
{
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
// verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong
return response()->json(['error' => 'could_not_create_token'], 500);
}
// if no errors are encountered we can return a JWT
return response()->json(compact('token'));
}
}
Thanks you.
Update
Bounty's code
public function authenticate(Request $request) {
$email = $request->input('email');
$user = User::where('email', '=', $email)->first();
try {
// verify the credentials and create a token for the user
if (! $token = JWTAuth::fromUser($user)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong
return response()->json(['error' => 'could_not_create_token'], 500);
}
// if no errors are encountered we can return a JWT
return response()->json(compact('token'));
}
try with this:
$user=User::where('email','=','user2#gmail.com')->first();
if (!$userToken=JWTAuth::fromUser($user)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
return response()->json(compact('userToken'));
it works for me, hope can help
Generating token for the customers (without password) can be achieved through
$user = \App\Modules\User\Models\UserModel::whereEmail('xyz#gmail.com')->first();
$userToken=JWTAuth::fromUser($user);
Here $userToken
will stores the token after existence check of email in the table configured in UserModel file.
I have assumed that you stores both customer and moderators in the same table, there must be some flag to discriminate among them. Assume the flag is user_type
$token = null;
$user = \App\Modules\User\Models\UserModel::whereEmail('xyz#gmail.com')->first();
if($user['user_type'] == 'customer'){
$credentials = $request->only('email');
$token =JWTAuth::fromUser($user);
}else if($user['user_type'] == 'moderator'){
$credentials = $request->only('email','password');
$token = JWTAuth::attempt($credentials);
}else{
//No such user exists
}
return $token;
As far as custom claims are concerned these are custom defined payloads which can be attached to token string.
For example, JWTAuth::attempt($credentials,['role'=>1]); Will attempt to add role object to token payload.
Once you decode the token string through JWT Facade JWTAuth::parseToken()->getPayload(); you in turn get all payloads defined in required_claims under config/jwt.php with additional role payload.
Refer https://github.com/tymondesigns/jwt-auth/wiki/Creating-Tokens#creating-a-token-based-on-anything-you-like
Let me know in case you requires anything else.
Rather than making a different login strategy for customers and moderators, you can add token authentication to both user type. this will makes your life easier and prepare for scalability.
In your api, you can just restrict moderator users to not have access to the api by sending
<?php
Response::json('error'=>'method not allowed')
Apart from this suggestion, I believe #Alimnjan code should work.
If you don't already have an App\User object, get it with something like
$user = App\User::find(1);
Generate the token using the fromUser() method of JWTAuth
$token = \JWTAuth::fromUser($user)
The above doesn't authenticate the user, it only generates a JWT token. If you need to authenticate the user, then you have to add something like this
\JWTAuth::setToken($token)->toUser();
I have already written an application in a procedural way and am trying to move into into a Laravel framework. I'm having trouble with the SOAP exchange section as I am getting an ID value that authenticates the user but cannot access that value (as a cookie) later in the program to authenticate the search.
Here is my code so far:
<?php namespace App;
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
use Illuminate\Http\RedirectResponse;
class SoapController {
private $auth_response;
private $cookie;
private $search_client;
private $search_response;
public function soapExchange() {
// create SOAP client and add service details
SoapWrapper::add(function ($service) {
$service
->name('WoSAuthenticate')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
SoapWrapper::service('WoSAuthenticate', function($service) {
// call authenticate() method to get SID cookie
$auth_response = $service->call('authenticate', []);
$cookie = $auth_response->return;
// test for cookie return
// print($cookie);
});
// create SOAP client and add service details
$search_client = new SoapWrapper;
$search_client::add(function ($service) {
$service
->name('WoSSearch')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
if (isset($auth_response->return)) {
// if there is an SID returned then add it to the cookie attribute of the search client
$search_client->__setCookie('SID', $cookie);
} else {
// route to relevant view to display throttle error
return redirect('throttle');
}
}
}
I am successfully retrieving the response from the Web API call and getting a code to authenticate the user, saved as $cookie. However, I need then to create another SoapWrapper for performing the search and this needs the ID code attached by using the __setCookie method. If nothing is returned by the authenticate call then it redirects to an error message via throttle.blade.php elsewhere.
Surely there is a way to return a value created from a function so that it can be used elsewhere?
** EDIT **
Looked into employing SoapClient instead and including all operations within a single function. It all relates to a specific Web API anyway so I guess separation of concerns is not so much of an issue. FYI the new class I am trying is this:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
Maybe try $GLOBALS?
<?php
$GLOBALS[data] = "something";
function abc(){
echo $GLOBALS[data];
}
?>
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
class SoapController extends Controller {
public $resultSoapStatus;
public $resultSoapAuthority;
public function heySoap{
SoapWrapper::add(function ($service) ...
$data = [
'MerchantID' => $MerchantID,
'Amount' => $Amount,
'Description' => $Description,
'Email' => $Email,
'Mobile' => $Mobile,
'CallbackURL' => $CallbackURL
];
SoapWrapper::service('test', function ($service) use ($data) {
$resultSoap = $service->call('PaymentRequest', [$data]);
$this->resultSoapStatus = $resultSoap->Status;
$this->resultSoapAuthority = $resultSoap->Authority;
});
if($this->resultSoapStatus == 100 && strlen($this->resultSoapAuthority) == 36)
{
//Do Something
}
else
{
return Redirect::back();
}
}
}
Enjoy bro