so, I have JWT authentication API using CI4, and one of the method is filtering which is telling if you has the token for JWT or not, if the user don't have the token it will return to an exception telling that JWT authentication is failed like this :
Postman :
Web :
is it possible to make it more simpler like this ?, just a response and without a trace exception like in the first image
Here is the
jwt_helper.php :
<?php
use App\Models\AuthModel;
use Firebase\JWT\JWT;
function getJWT($headerAuth)
{
if (is_null($headerAuth)) {
throw new Exception("JWT Authentication failed");
}
return explode(" ", $headerAuth)[1];
}
function validateJWT($encodedToken)
{
$key = getenv('JWT_SECRET_KEY');
$decodedToken = JWT::decode($encodedToken, $key, ['HS256']);
$authModel = new AuthModel();
// * xxxx.xxxx.xxxx
$authModel->getEmail($decodedToken->email);
}
function createJWT($email)
{
$requestTime = time();
$tokenTime = getenv('JWT_TOKEN_TIME');
$tokenExpireTime = $requestTime + $tokenTime;
$payload = [
'email' => $email,
'iat' => $requestTime,
'exp' => $tokenExpireTime
];
$jwt = JWT::encode($payload, getenv('JWT_SECRET_KEY'), 'HS256');
return $jwt;
}
This full stack is displayed only in DEVELOPMENT.
Edit your .ENV file accordingly.
CI_ENVIRONMENT = production
https://codeigniter.com/user_guide/general/environments.html#error-reporting
Related
I'm having some issues with using Shopify's GraphQL API. I've already made a bunch of REST calls, but for this one I would need GraphQL.
I'm trying to add videos to certain products and this is what I have so far:
mutation productCreateMedia($productId: ID!, $media: [CreateMediaInput!]!) {
productCreateMedia(productId: $productId, media: $media) {
media {
alt
}
mediaUserErrors {
code
field
message
}
product {
id
}
}
}
and for variables, I have an array of:
$gid = "gid://shopify/Product/".row('shopifyID');
$videoLink = "https://www.youtube.com/watch?v=".row('youtubeID');
$media = array('originalSource'=>$videoLink,'mediaContentType'=>'EXTERNAL_VIDEO');
$variables = array ('productId'=>$gid,'media'=>$media);
I use the next function for the call:
function graph($query , $variables = []){
$domain = 'domain.myshopify.com';
$url = 'https://'.$domain.'/admin/api/2020-01/graphql.json';
$request = ['query' => $query];
if(count($variables) > 0) { $request['variables'] = $variables; }
$req = json_encode($request);
$parameters['body'] = $req;
$stack = HandlerStack::create();
$client = new \GuzzleHttp\Client([
'handler' => $stack,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Shopify-Access-Token'=>'myAPIpass' // shopify app accessToken
],
]);
$response = $client->request('post',$url,$parameters);
return $body = json_decode($response->getBody(),true);
}
But what I'm getting back is:
Variable productId of type ID! was provided invalid value
I used php-shopify SDK for REST API, but couldn't figure out how it works for GraphQL, so went with the usual way of just calling the JSON endpoint.
Any help in what I'm doing wrong here?
So...to answer my own question...the shopify ID string has to be base 64 encoded.
I added just this line and it works now:
$gid = base64_encode($gid);
I've been trying to return user data using access token but keep getting error:
Invalid payload
My method was to get the token then find the user id from oauth_access_tokens table. My code is as follows:
public function authenticateUser($token){
$user_id = DB::table('oauth_access_tokens')->where('id', trim($token))->value('user_id');
$user = \App\User::find($user_id);
Auth::login($user, true);
}
The token is something like this:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjkyZGU3ZGYyMDcxZjgzMzU5YWUxMmRlYzM4ZGJiM2EyMTk0NzEyYTQ5NmRiNzgwZWJkMDg2Yjc0NThkZjU0NmFlZmU2Yzg0N2Q0Mjc5MDAxIn0.eyJhdWQiOiIxIiwianRpIjoiOTJkZTdkZjIwNzFmODMzNTlhZTEyZGVjMzhkYmIzYTIxOTQ3MTJhNDk2ZGI3ODBlYmQwODZiNzQ1OGRmNTQ2YWVmZTZjODQ3ZDQyNzkwMDEiLCJpYXQiOjE1NzczNzE4MDYsIm5iZiI6MTU3NzM3MTgwNiwiZXhwIjoxNjA4OTk0MjA1LCJzdWIiOiIzMCIsInNjb3BlcyI6W119.Io4xkJYEczbI7rhFD_UKAoe7v_1-RLJXjA6XqGIe2nRAWEgMkg-mokQUiGz41xYVazmDmACDwwYSRr-iTTzwc591NABfxsmMk7OdYkUKb93UTA3JhKClEGSP82y1QrIfm9XTZ0KKDaCKlfKqye1Aobj9zFthQdApegTaK61ReLQa7MzO6EM5fcZ3udsLL3QpKXFuyO6JcPKRauKIbA8oNIKEdadprLWJSeQieIyA8lpYOr453QzgZGgzCwPY1U2RmIbCzqyNQD_L5264-ix1503KxgPt4F_Cl82WXm7tNsZKNwE-vGKhCc2CcgAgTV1lIj7ItDf2KpDh_Jt96Uiv2eJ3OtXYvuOTErz9mNnQ1T38hxQmKDh8XlG3f7JgIWWzN6m8ItBV1KyGZi0-vn2HXetkZTNIyfJV8E5-RaGUzIKX7RejWd5BVaqFw0OjDYPeliVOaZzfcZCRnPDSJBGwf7YqJrRXP61LMasn_ZJ-i8G5JIaQx2vdmfYgE41O5F9fE5uEF5-mIV979RbnswL6CJsSGmmUMzC7mPhqL6HtPu2hMTnfHbKY0-efqtzZ5I2TBQU6ODM37RFN5TEljoEgBFG6kAImkGDy4QFH5uqt6V7-ZFxvrKQzQozgezSgA6ITF1sRb7yWfI-9rF7sYE_aKu3r1_KRr4UJLoZqFyvGPP0
Isn't it the token that I should pass to the function above. When I pass it to base64_decode, I see the JSON object along with other gibberish. What am I doing wrong here?
I have never used Laravel Passport before, but I would imagine that the user is already authenticated when using the token. So maybe a route like:
Route::get('/user', function(Request $request) {
return Auth::user();
})->middleware('auth:api');
I've reached to way to do so eventually by sending a request to the api in the other machine while adding the token in the header:
public function authenticateUser($token) {
$client = new \GuzzleHttp\Client();
try {
$response = $client->request('GET', env('APP_API_URL') . '/api/v2/user_data', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $token,
],
]);
$request = (string) $response->getBody();
$request = json_decode($request);
$user = User::where('email', $request->data->user->email)->first();
Auth::login($user, true);
} catch (RequestException $e) {
dd('Something went wrong while connection to the api');
}
}
I've successfully managed to connect to the AdSense API and run a report. However, it requires a log in each time I run it, so it won't run as a cron job.
I've found a few other questions related to this. Some advise a service account, while others point out that a service account does not work with AdSense. The proposed solution is to store a token on my server, but I've been struggling to get that to work. Here is my code so far (which works, but requires manual log in):
$scriptUri = "http://".$_SERVER["HTTP_HOST"].$_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
$client->setAccessType('offline');
$client->setApplicationName('My Application name');
$client->setClientId(' MY ID ');
$client->setClientSecret(' MY SECRET ');
$client->setRedirectUri($scriptUri);
$client->setDeveloperKey(' MY KEY '); // API key
$accountId = " MY ACCOUNT " ;
$adClientId = " MY CLIENT " ;
// $service implements the client interface, has to be set before auth call
$service = new Google_Service_AdSense($client);
if (isset($_GET['logout'])) { // logout: destroy token
unset($_SESSION['token']);
die('Logged out.');
}
if (isset($_GET['code'])) { // we received the positive auth callback, get the token and store it in session
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
}
if (isset($_SESSION['token'])) { // extract token from session and configure client
$token = $_SESSION['token'];
$client->setAccessToken($token);
}
if (!$client->getAccessToken()) { // auth call to google
$authUrl = $client->createAuthUrl();
header("Location: ".$authUrl);
die;
}
$startDate = '2015-11-01';
$endDate = 'today';
$optParams = array(
'metric' => array(
'EARNINGS'),
'dimension' => array('DATE'),
'sort' => '+DATE',
'filter' => array(
'CUSTOM_CHANNEL_NAME==Mega Seating Plan'
)
);
// Run report.
$report = $service->accounts_reports->generate($accountId, $startDate,
$endDate, $optParams);
if (isset($report) && isset($report['rows'])) {
// Get results.
foreach($report['rows'] as $row) {
$date = $row[0] ;
$earnings[$date] = $row[1] ;
}
} else {
print "No rows returned.\n";
}
Can anybody give me any pointers about how I can incorporate token storage into the above code, please?
Thank you to #jkns.co for the previous answer here which helped me to get it working.
Here's my final code:
$scriptUri = "I HAD TO PUT MY ABSOLUTE URL HERE, OTHERWISE THE CRON JOB WOULD LOOK IN THE WRONG PLACE" ;
$client = new Google_Client();
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
$client->setAccessType('offline');
$client->setApprovalPrompt ("force"); // This line had to be added to force the approval prompt and request a new token
$client->setApplicationName('My Application name');
$client->setClientId('BLAH');
$client->setClientSecret('BLAH');
$client->setRedirectUri($scriptUri);
$client->setDeveloperKey('BLAH'); // API key
$accountId = "BLAH" ;
$adClientId = "BLAH" ;
// $service implements the client interface, has to be set before auth call
$service = new Google_Service_AdSense($client);
if (isset($_GET['logout'])) { // logout: destroy token
unset($_SESSION['token']);
die('Logged out.');
}
if (isset($_GET['code'])) { // we received the positive auth callback, get the token and store it in session
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
// If it successfully authenticates, I request the refresh token
$refreshToken = $client->getRefreshToken();
storeRefreshToken($refreshToken) ; // This function stores the token in MySQL
}
else { // Otherwise it loads the refresh token from MySQL
$refreshToken = getRefreshToken() ;
$client->refreshToken($refreshToken);
$_SESSION['token'] = $client->getAccessToken();
}
if (isset($_SESSION['token'])) { // extract token from session and configure client
$token = $_SESSION['token'];
$client->setAccessToken($token);
}
if (!$client->getAccessToken()) { // auth call to google
$authUrl = $client->createAuthUrl();
header("Location: ".$authUrl);
die;
}
How to insert the records to couchDB in laravel. i have done the retrieval part but now I want to do insert, update and delete .
My retrieval code is below.
class couchdbcontroller extends Controller
{
public function getdata()
{
$content =null;
try {
$client = new Client();
$apiRequest = $client->request('GET','http://localhost:5984/user/_design/userdesign/_view/user-view?limit=20&reduce=false');
$code = $apiRequest->getBody()->getContents();
} catch (RequestException $re) {
//For handling exception
return $re->error;
}
return $code;
//return response()->json($code);
}
}
Inserting code below:
public function guzzle_insert_doc()
{
$client = new Client();
$res = $client->request('PUT', 'http://localhost:5984/login/new_doc',[
'uname' => 'admin',
'password' => 'admin123',
]);
//return $res;
}
Error: Client error: PUT http://localhost:5984/login/new_doc resulted in a 400 Bad Request response:
{"error":"bad_request","reason":"invalid UTF-8 JSON"}
From my google search, you could do something like this :
<?php
$client = new Client();
$doc = ['title'=>'This is a new doc'];
$res = $client->request('PUT', 'http://localhost:5984/db/new_doc',['json' => $doc]);
I assume you're using Guzzle (If I am wrong, tell us what your are using)
I didn't test my code since I don't have time to setup a laravel project with Guzzle. See documentation for further help.
I'm using the 'HTTP Basic Authentication' feature of laravel. I want to customize the error message which is generated from laravel if the entered credentials are wrong.
Is it possible to catch the 401 Error which is generated when HTTP Auth fails?
Hope you can help me.
Regards
Basic Auth
Try to capture 401 error and return cusom view?!
App::error(function($exception, $code)
{
switch ($code)
{
case 401:
return Response::view('errors.403', array(), 401);
case 403:
return Response::view('errors.403', array(), 403);
case 404:
return Response::view('errors.404', array(), 404);
case 500:
return Response::view('errors.500', array(), 500);
default:
return Response::view('errors.default', array(), $code);
}
});
Using Auth library
I think, code is pretty straightforward and self explaining.
Just to note, $errors variable is of type MessageBag and is available in views even if you don't set it explicitly! Which is great! :)
I used simple routing, place it into your controllers
app/routes.php
Route::get('auth', function()
{
$creds = array(
'email' => Input::get('email'),
'password' => Input::get('password'),
);
if ( ! Auth::attempt($creds))
{
$errors = new MessageBag;
$errors->add('login', trans("Username and/or password invalid."));
return Redirect::to('/')->withErrors($errors);
}
return Redirect::to('/protected/area');
});
Route::get('/', function(){
return View::make('hello');
});
// app/views/hello.php
#if($errors->has('login'))
{{ $errors->first('login') }}
#endif
Here's how I did it:
Route::filter('auth.basic', function()
{
$message = [
"error" => [
"code" => 401,
"message" => "Invalid Credentials"
]
];
$headers = ['WWW-Authenticate' => 'Basic'];
$response = Auth::basic();
if (!is_null($response)) {
return Response::json($message, 401, $headers);
}
});
If you look in Illuminate\Auth\Guard you'll find the basic method that's called by Auth::basic(). It either returns null or a Response object via the getBasicResponse method.
/**
* Attempt to authenticate using HTTP Basic Auth.
*
* #param string $field
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response|null
*/
public function basic($field = 'email', Request $request = null)
{
if ($this->check()) return;
$request = $request ?: $this->getRequest();
// If a username is set on the HTTP basic request, we will return out without
// interrupting the request lifecycle. Otherwise, we'll need to generate a
// request indicating that the given credentials were invalid for login.
if ($this->attemptBasic($request, $field)) return;
return $this->getBasicResponse();
}
Here's getBasicResponse:
/**
* Get the response for basic authentication.
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function getBasicResponse()
{
$headers = array('WWW-Authenticate' => 'Basic');
return new Response('Invalid credentials.', 401, $headers);
}
Here we finally have our 'Invalid credentials.' text that we're looking to change. We see it's just returning an instance of a Symphony response with a 401 status code and the Basic Auth header and null in all other occasions. So, we'll just check for a non-null result and if we get one, return our new response as shown above.
Also, if you want it to actually be stateless you should use:
Auth::onceBasic()
I don't know how future proof this method is, but it works as of Laravel 4.1.
Final results once again:
Route::filter('auth.basic', function()
{
$message = [
"error" => [
"code" => 401,
"message" => "Invalid Credentials"
]
];
$headers = ['WWW-Authenticate' => 'Basic'];
$response = Auth::onceBasic();
if (!is_null($response)) {
return Response::json($message, 401, $headers);
}
});