I'm working on google api client integrated with Laravel5.2 and I'm facing problem just to list different files content into my drive account.
The client seems to be ok with credientials and accessing the service as google is showing the request ok on the usage monitoring but I'm still getting nothing here is the code I use:
Middleware:
public function handle($request, Closure $next)
{
$client_email = 'id#kribbean-wave.iam.gserviceaccount.com';
$private_key = file_get_contents(base_path().'/resources/assets/kribbean-wave.p12');
$scopes = array('https://www.googleapis.com/auth/drive');
$credentials = new \Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key
);
$client = new \Google_Client();
$client->setAssertionCredentials($credentials);
$client->setAccessType('offline');
if ($client->getAuth()->isAccessTokenExpired()){
$client->getAuth()->refreshTokenWithAssertion();
$request->request->set('client',$client);
}
return $next($request);
}
and the Controller:
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('Google');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$client = $request->request->get('client');
$service = new \Google_Service_Drive($client);
$result = $service->files->listFiles();
$list = $result->getItems();
dd(json_encode($list));
return view('index')->with('result',$list);
}
}
The middleware is working as expected but dd(json_encode($list)); is empty.
Can you help please?
To allow service account to access the files in my own Google Drive, I have to give permission to the email address of the service account. Once the permissions are setup correctly, the list gets populated with the files that the service account is allowed to access.
Related
I am using passport personal access token to secure my API. Here are some of the code snippets.
// api.php
Route::apiResource('categories', 'CategoryController');
AuthServiceProvider.php
public function boot()
{
$this->registerPolicies();
//scopes
Passport::tokensCan([
'admin' => 'Perform every action',
'user' => 'Perform only normal user actions',
]);
// passport routes
Passport::routes();
//
}
CategoryController.php
class CategoryController extends Controller
{
function __construct()
{
$this->middleware('api:auth', ['scopes: admin']);
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
return CategoryResource::collection(Category::all());
}
...
As you can see I have used the admin scope which is only accessible to admin users. But the problem is category model can only be edited or updated by the admin scope and can be accessed by both admin and user scopes. What could be the best approach to address this issue?
One solution that worked for me is I have used middleware two times with different scopes.
class CategoryController extends Controller
{
function __construct()
{
$this->middleware('api:auth', ['scopes: admin'])->except(['index']);
$this->middleware('api:auth', ['scopes: user'])->only(['index']);
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
return CategoryResource::collection(Category::all());
}
...
I am working in Laravel 7 and have a middleware that checks if the user has a current user agreement, if not it redirects to a form that offers the current agreement. When the offer is accepted I need to redirect back to where they were originally going. I think I need to put something in the session so that when my controller stores their acceptance it can redirect back to the original route.
class VerifyAgreement
{
public function handle($request, Closure $next, $agreement)
{
if(UserAgreement::outOfDate($agreement)){
return redirect()->route('agreement.offer', $agreement);
}
return $next($request);
}
}
I think I need to get the current request and pass it to the redirect so the User Agreement controller can capture it somehow and then redirect once the agreement is stored... I am not sure.
class AgreementController extends Controller
{
public function offer(Agreement $agreement)
{
return view('agreement.offer',['agreement' => $agreement]);
}
public function agree(Request $request)
{
$agreement_uuid = App\Agreement::findOrFail($request->agreement)->uuid;
UserAgreement::create(['user_uuid'=>auth()->user()->uuid, 'agreement_uuid'=>$agreement_uuid]);
//redirect something something something
}
}
As mentioned in the comments by #Ruben Danielyan, the Illuminate\Routing\Redirector has some methods that you may find useful
Redirector.php
/**
* Create a new redirect response to the previously intended location.
*
* #param string $default
* #param int $status
* #param array $headers
* #param bool|null $secure
* #return \Illuminate\Http\RedirectResponse
*/
public function intended($default = '/', $status = 302, $headers = [], $secure = null)
{
$path = $this->session->pull('url.intended', $default);
return $this->to($path, $status, $headers, $secure);
}
/**
* Set the intended url.
*
* #param string $url
* #return void
*/
public function setIntendedUrl($url)
{
$this->session->put('url.intended', $url);
}
Whenever I try to add a product to a wishlist I am redirected to a login page where I enter my credentials and after that it keeps reloading and this error appears.
Thereafter, when I return to home page and refresh I am logged in. But when I try to access a page directly which requires login, it works perfectly fine. This error has been appearing for sometime now, it was previously working fine.
routes:
Auth::routes();
Route::group(['middleware'=>'auth'],function (){
Route::get('/checkout','PageController#checkout')->name('checkout');
Route::post('/coupon','PageController#coupon')->name('coupon.check');
Route::post('/order', 'OrderController#store')->name('order.store');
Route::post('/orderinfo', 'OrderInfoController#store')->name('orderinfo.store');
Route::get('/invoice/{order}','PageController#invoice')->name('invoice');
Route::resource('/profile', 'ProfileController');
Route::get('/wishlist', 'WishlistController#index')->name('wishlist.index');
Route::get('/wishlist/{product_id}/remove', 'WishlistController#remove')->name('wishlist.remove');
Route::get('/wishlist/{product_id}', 'WishlistController#quick')->name('wishlist.quick');
Route::resource('/review', 'ReviewController');
Route::get('/orders', 'PageController#order')->name('orders');
Route::group(['middleware'=>'admin'],function () {
Route::resource('/admin/products', 'ProductController');
Route::resource('/admin/categories', 'CategoryController');
Route::resource('/admin/subcategories', 'SubcategoryController');
Route::resource('/admin/coupons', 'CouponController');
Route::resource('/admin/taxes', 'TaxController');
Route::resource('/admin/discounts', 'DiscountController');
Route::get('/admin/index', 'PageController#admin')->name('admin.index');
Route::post('/admin/ajax/category', 'PageController#ajax')->name('ajax.category');
Route::resource('/admin/users', 'UserController');
Route::resource('/admin/tracks', 'TrackController');
Route::get('/order', 'OrderController#index')->name('order.index');
Route::get('/order/{order}', 'OrderController#show')->name('order.show');
});
});
Route::get('/product/{product}','PageController#product')->name('product.view');
Route::get('/','PageController#index')->name('index');
Route::get('/about-us','PageController#about_us')->name('about_us');
Route::resource('/contact-us','ContactController');
Route::get('/shop','PageController#shop')->name('shop');
Route::get('/home', 'HomeController#index')->name('home');
Route::post('/cart', 'CartController#add')->name('cart.add');
Route::get('/cart{product}', 'CartController#quick')->name('cart.quick');
Route::get('/cart/show', 'CartController#show')->name('cart.show');
Route::patch('/cart/{product_id}', 'CartController#update')->name('cart.update');
Route::get('/cart/{product}/remove', 'CartController#remove')->name('cart.remove');
Route::get('/shop/filter/{subcategory_id}','PageController#filter')->name('filter.product');
Route::get('/shop/category/{category}','PageController#shop_2')->name('filter.categories');
Login Controller:
<?php
namespace App\Http\Controllers\Auth;
use App\Category;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function redirectTo()
{
}
public function showLoginForm()
{
$categories = Category::all();
$cart_items = session()->get('cart');
$sub_total = 0;
if (!empty($cart_items)) {
foreach ($cart_items as $item) {
$sub_total = ($item['price'] * $item['quantity']) + $sub_total;
}
}
return view('login', ['cart_items' => $cart_items, 'sub_total' => $sub_total,'categories'=>$categories]);
}
}
This is how I am sending get request and which gives error after login:
<a class="add-wishlist" title="wishlist" href="{{route('wishlist.quick',$product->id)}}"><i class="fa fa-heart"></i></a>
Wishlist Controller:
<?php
namespace App\Http\Controllers;
use App\Category;
use App\Helpers\helper;
use App\Product;
use App\Wishlist;
use Illuminate\Http\Request;
class WishlistController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
$categories= Category::all();
$cart_items = helper::cart_data();
$sub_total = helper::sub_total($cart_items);
$user_id = auth()->user()->id;
$wishlist = Wishlist::all()->where('user_id', '=', $user_id);
$products = [];
foreach ($wishlist as $list) {
$products[] = Product::find($list->product_id);
}
return view('wishlist', ['wishlist' => $wishlist, 'products' => $products,'sub_total'=>$sub_total,'categories'=>$categories,'cart_items'=>$cart_items]);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
$user_id = auth()->user()->id;
$check = Wishlist::all()->where('user_id', $user_id)->where('product_id', $request['product_id']);
if ($check->isEmpty()) {
Wishlist::create([
'user_id' => $user_id,
'product_id' => $request['product_id']
]);
}
return redirect()->back();
}
/**
* Display the specified resource.
*
* #param \App\Wishlist $wishlist
* #return \Illuminate\Http\Response
*/
public function show(Wishlist $wishlist)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param \App\Wishlist $wishlist
* #return \Illuminate\Http\Response
*/
public function edit(Wishlist $wishlist)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Wishlist $wishlist
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Wishlist $wishlist)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param \App\Wishlist $wishlist
* #return \Illuminate\Http\Response
*/
public function remove(Request $request)
{
//
$user_id = auth()->user()->id;
Wishlist::where('user_id', $user_id)->where('product_id', $request['product_id'])->delete();
return redirect()->back();
}
public function quick($product_id)
{
//
$user_id = auth()->user()->id;
$check = Wishlist::all()->where('user_id', $user_id)->where('product_id', $product_id);
if ($check->isEmpty()) {
Wishlist::create([
'user_id' => $user_id,
'product_id' => $product_id
]);
}
return redirect()->back();
}
}
Firstly, 419 error indicate an expired session. I notice you are using the session helper method session() inside showLoginForm(). You should be aware that if a user is logged out or his/her session expires then that user cannot access the data stored in that session as it will be wiped clean. Trying to access session data this way through showLoginForm is counter-intuitive as the user will most likely have been logged out or had an expired session before accessing the login form - except for the case where the user is accessing the login form for the first time. This could be a possible cause of the 419 errors.
You can remove the piece of code where you are trying to access the session data to any of your several controllers that require authentication. Then, you are sure that the user has a valid session before accessing session data.
However, to redirect users after a successful login Laravel uses either the $redirectTo variable or redirectTo() method of the LoginController. If the method is defined, it overrides the variable and if not, the variable is used.
From your LoginController, none of them is defined. Usually, the variable is set to redirect to the homepage - $redirectTo = '/home'. However, to meet your requirement of redirecting to the page that required the login, you must use the redirectTo() method.
You can achieve this by using the helper method url()->previous() within LoginController.php like this:
public static $previous;
public function showLoginForm() {
self::$previous = url()->previous();
// continue with your code.
}
public function redirectTo()
{
return self::$previous;
}
notice that I store the previous url when i first show the login form. after a successful login, this url should be available for me to redirect to.
UPDATE 1:
The problem route
Route::get('/cart{product}', 'CartController#quick')->name('cart.quick');
has a problem. You are missing a forward slash after /cart. You should notice this issue when you look at the generated url in the link. The correct form should be
Route::get('/cart/{product}', 'CartController#quick')->name('cart.quick');
UPDATE 2:
Since the route wishlist.quick is going through the auth middleware, do not use redirect()->back() for going back to the same page after user action with that route.
This is because, with the auth middleware in place, redirect()->back() is not always pointing to same location.
For instance, an unauthenticated user accessing the wishlist.quick route will be redirected to the login page. If login is successful the request continues to wishlist.quick route. Now, try to guess where the redirect()->back() inside WishlistController#quick is pointing to. Right! Surprisingly, it is pointing to the login page. So now the authenticated user completes his/her request with WishlistController#quick and is directed to the login page again. The login controller detects the user is authenticated and redirects the user to wherever he/she is coming from - WishlistController#quick. Again, there is redirect()->back() sending the user back again to the login page. You see the infinite redirect loop clearly in this funny scenario.
SOLUTION:
Change the line
return redirect()->back();
to
return $this->index();
Since WishlistController#quick doesn't return a view of its own, WishlistController#index is the best place to return to. Infact, you have to make this change for all routes that pass through a middleware and redirects the user back.
In other words, do not use redirect()->back() in a route that goes through middleware, if you really mean to go back to the same page.
A common issue with Laravel throwing a 419 error is because of a missing #csrf inside the form.
<form method="post" action="<some route>" >
#csrf
<input ...... />
</form>
If you are sending any data in a form, please ensure you have the above CSRF token.
If you do have this token, can you add the form in the main question?
I'm really new building laravel apps, I have a restful laravel API and a web app, I want the client web app to authenticate against the API and store the user in the session, I've registered a new UserProvider and set it on the config`s auth like bellow
ServiceProvider
public function boot()
{
$this->registerPolicies();
Auth::provider('apiAuthServiceProvider', function ($app, $config) {
return new UserProvider(new ApiUserService());
});
}
Config/Auth
'providers' => [
'users' => [
'driver' => 'apiAuthServiceProvider',
],
],
UserProvider Class
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Auth\UserProvider as IlluminateUserProvider;
class UserProvider implements IlluminateUserProvider
{
private $userService;
public function __construct($userService)
{
$this->userService = $userService;
}
/**
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
// Get and return a user by their unique identifier
}
/**
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
// Get and return a user by their unique identifier and "remember me" token
}
/**
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
// Save the given "remember me" token for the given user
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// Get and return a user by looking up the given credentials
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Check that given credentials belong to the given user
}
}
The Custom UserProvider injects a UserService class, with is responsible for making requests to the API and return the user...
I`m so lost, what UserProvider methods should i override from "UserProvider" Interface? "retrieveById", "retrieveByToken", "updateRememberToken", "retrieveByCredentials" and "validateCredentials" ? Or should I override all of them? Considering the the client web app will have a login form, and the user will authenticate sending the email and password (grant_type = password), I'm also confusing about the token, how should I store the token and refresh token in the session? Is that possible to set session timeout as the same as the token expiration time? Where would I call the retrieveByCredentials's UserProvider to pass the authentication params? Thanks in advance....
You should override only the functions you need. Most of the standard functionality should be already defined in the User Provider you are inheriting from. I've only inherited my custom user providers from Illuminate\Auth\EloquentUserProvider (Laravel 5.4 here), so please double check how the class you are inheriting from works. If, for example, you need to retrieve your user by an ID different than your default id field, you should override retrieveById.
I know I am not the only person who has come up to this point. Does anyone know how to properly implement a custom grant in Laravel(5.3) Passport?
Or
Have a good link/tutorial to reference how to properly do it?
I know there's this package:
https://github.com/mikemclin/passport-custom-request-grant
But I'm asking for a more "Do it yourself" approach.
Thank you in advance.
namespace App\Providers;
use App\Auth\Grants\FacebookGrant;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Bridge\RefreshTokenRepository;
use Laravel\Passport\Passport;
use League\OAuth2\Server\AuthorizationServer;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
app(AuthorizationServer::class)->enableGrantType(
$this->makeFacebookGrant(), Passport::tokensExpireIn()
);
Passport::routes();
//
}
/**
* Create and configure a Facebook grant instance.
*
* #return FacebookGrant
*/
protected function makeFacebookGrant()
{
$grant = new FacebookGrant(
$this->app->make(RefreshTokenRepository::class)
);
$grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn());
return $grant;
}
}
EDIT:
sorry for only posting this code, i do not know how much this code is going to be useful to you.
Well, here i'll leave my implementation of FacebookGrant, hope this helps someone.
<?php
namespace App\Auth\Grants;
use Illuminate\Http\Request;
use Laravel\Passport\Bridge\User;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AbstractGrant;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
class FacebookGrant extends AbstractGrant
{
/**
* #param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(
RefreshTokenRepositoryInterface $refreshTokenRepository
) {
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new \DateInterval('P1M');
}
/**
* {#inheritdoc}
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
\DateInterval $accessTokenTTL
) {
// Validate request
$client = $this->validateClient($request);
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
$user = $this->validateUser($request, $client);
// Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
// Issue and persist new tokens
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
// Inject tokens into response
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);
return $responseType;
}
/**
* #param ServerRequestInterface $request
*
* #return UserEntityInterface
* #throws OAuthServerException
*/
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
$facebookId = $this->getRequestParameter('facebook_id', $request);
if (is_null($facebookId)) {
throw OAuthServerException::invalidRequest('facebook_id');
}
$email = $this->getRequestParameter('email', $request);
if (is_null($email)) {
throw OAuthServerException::invalidRequest('email');
}
$user = $this->getUserEntityByUserFacebookId(
$facebookId,
$email,
$this->getIdentifier(),
$client
);
if ($user instanceof UserEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}
return $user;
}
/**
* Retrieve a user by the given Facebook Id.
*
* #param string $facebookId
* #param string $email
* #param string $grantType
* #param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
*
* #return \Laravel\Passport\Bridge\User|null
* #throws \League\OAuth2\Server\Exception\OAuthServerException
*/
private function getUserEntityByUserFacebookId($facebookId, $email, $grantType, ClientEntityInterface $clientEntity)
{
$provider = config('auth.guards.api.provider');
if (is_null($model = config('auth.providers.'.$provider.'.model'))) {
throw new RuntimeException('Unable to determine authentication model from configuration.');
}
$user = (new $model)->where('facebook_id', $facebookId)->first();
if (is_null($user)) {
$user = (new $model)->where('email', $email)->first();
if (is_null($user)) {
return;
}
// Now that we retrieved the user with the email, we need to update it with
// the given facebook id. So the user account will be linked correctly.
$user->facebook_id = $facebookId;
$user->save();
}
return new User($user->getAuthIdentifier());
}
/**
* {#inheritdoc}
*/
public function getIdentifier()
{
return 'facebook';
}
}
I am not sure what do you mean by custom grant but you can use passport service for password grant which can be customized however you like.
Reference: https://laravel.com/docs/5.3/passport#password-grant-tokens
To give more insight, you get a client_id and client_secret which all users of your api will use to get access_token and refresh_token using their password and email as well and then you can customize the process by adding features via middleware.
For example you can check some customized header if present or change database based on request to perform further query and so on.
Maybe I got whole wrong idea about your ques, if that's the case then please specify more about your needs.