I'm trying to make a scopes configuration. The scopes are build in the follow schema:
module:submodule:action
Also, I defined on boot section
Passport::tokensCan($scopes);
However, I got the following error when I'm trying to generate a token using oauth2 with Passport:
The requested scope is invalid, unknown, or malformed
The problem was method tokensCan receive an array, the keys must be the scopes for evaluate later with middleware scope or scopes.
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
A good tip is go to app\Providers\AuthServiceProvider
In the method boot add the follows code:
public function boot()
{
// ...
Passport::routes();
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}
It's work for me. (:
Related
I'm trying to implement the **Authorization Code Grant with PKCE ** flow in Laravel Passport. Its documentation says that I have to use this command:
php artisan passport:client --public
That's fine, but I have to do it manually, I want to create those clients automatically, I mean, bring to my users a dashboard where they can create their clients. For that, I need to call through Ajax (or fetch) from my frontend some endpoint of Laravel Passport who allows me to create PKCE clients, but I don't know which endpoint call.
Laravel Passport provides the POST /oauth/clients endpoint to create clients, but this only create Authorization Code Grant Clients (without PKCE) and I don't know how to specify to create a PKCE client (same problem for Password Grant Tokens clients).
I was searching in the Laravel Passport's Client Controller and I realized that I can't specify what kind of client I want, this is the store method of Laravel Passport's Client Controller:
public function store(Request $request) {
$this->validation->make($request->all(), [
'name' => 'required|max:191',
'redirect' => ['required', $this->redirectRule],
'confidential' => 'boolean',
])->validate();
$client = $this->clients->create(
$request->user()->getAuthIdentifier(), $request->name, $request->redirect,
null, false, false, (bool) $request->input('confidential', true)
);
if (Passport::$hashesClientSecrets) {
return ['plainSecret' => $client->plainSecret] + $client->toArray();
}
return $client->makeVisible('secret');
}
As you can see, in the create() method of the client, values are "hard-coded" preventing me to create another kind of client through the JSON API.
Also, I was searching in the Laravel Passport's Client Model and I realized that, with the model I can specify what kind of grants I want for that client:
protected $casts = [
'grant_types' => 'array',
'personal_access_client' => 'bool',
'password_client' => 'bool',
'revoked' => 'bool',
];
So, there have to be a way to create clients for any type of grants (like PKCE), but I don't know how.
Should I override the Laravel Passport's Client Controller? How can I do it? I know that I can use the model to create my own clients in my own endpoint, but I want to keep the POST /oauth/clients route, so I would need to override the Laravel Passport's Client Controller, but I don't know how, can you help me, please?
I need to validate extra fields in my users table before i create the requested tokens, but i can't find a simple way to do it with Passport.
I find similar workarunds which returns a token using $user->createToken() but i can't find someone covering all default /oauth/token options like the refresh_token
Also i see Passport have some simple ways to customize the username column and the password validation but i think this not cover my neededs.
Update
Im not sure if this is the best solution but in Laravel 5.8 the passport package have this validateForPassportPasswordGrant() function which allow you to add some extra conditionals before allowing the authentication process to get completed.
class User extends Authenticatable
{
public function validateForPassportPasswordGrant($password)
{
if ($this->active != true) {
throw OAuthServerException::accessDenied('The account is not active');
}
return Hash::check($password, $this->password);
}
}
In your login method
if (Auth::attempt(['email' => $request->email, 'password' => $request->password, 'is_active' => 1, 'username' => $request->username])) {
// create a token here
}
I have setup sub-domain routing on my app (using Laravel 5.4) with the following web.php route:
Route::domain('{company}.myapp.localhost')->group(function () {
// Locations
Route::resource('/locations' , 'LocationController');
// Services
Route::resource('/services' , 'ServiceController');
});
However as my show and edit endpoints require an ID to be passed, using a normal route('services.show') helper results in an ErrorException stating Missing required parameters for [Route: services.create] [URI: services/create].
I appreciate this is necessary, but as the company is associated to the user on login (and is in the sub-domain) I don't want to be passing this for every view. I want to set this at a global level.
To avoid repeated queries, I thought about storing this in the session as so (in the :
protected function authenticated(Request $request, $user)
{
$current_company = $user->companies->first();
$company = [
'id' => $current_company->id,
'name' => $current_company->name,
'display_name' => $current_company->display_name
];
$request->session()->put('company', $company);
}
Which is fine, but I wonder if I can pass this to the route as a middleware or something. What's be best solution here?
Recommendation: remove the slash before the resource name.
The resource method will produce the following URIs:
/services/{service}
So, you should define your routes like this:
Route::domain('{company}.myapp.localhost')->group(function () {
// Locations
Route::resource('locations' , 'LocationController');
// Services
Route::resource('services' , 'ServiceController', ['only' => ['index', 'store']]);
Route::get('services');
});
I ran into this exact issue today, I poked around in the source and found a defaults method on the url generator that allows you to set global default route parameters like so:
app('url')->defaults(['yourGlobalRouteParameter' => $value]);
This will merge in whatever value(s) you specify into the global default parameters for the route url generator to use.
I'm currently working on a web application which requires users to verify before they are able to use their account.
I'm using Cartalyst's Sentry to register the users, and sending the email using the built in Mail function, but whenever I register I get the following error:
Argument 1 passed to Illuminate\Mail\Mailer::__construct() must be an instance of
Illuminate\View\Environment, instance of Illuminate\View\Factory given,
called in
/var/www/vendor/laravel/framework/src/Illuminate/Mail/MailServiceProvider.php
on line 34 and defined
I can't figure out what causes this.
At the top of my code I included "use Mail" otherwise I would get another error:
Class '\Services\Account\Mail' not found
Code
// Create the user
$user = $this->sentry->register(array(
'email' => e($input['email']),
'password' => e($input['password'])
));
$activationCode = $user->getActivationCode();
$data = array(
'activation_code' => $activationCode,
'email' => e($input['email']),
'company_name' => e($input['partnerable_name'])
);
// Email the activation code to the user
Mail::send('emails.auth.activate', $data, function($message) use ($input)
{
$message->to(e($input['email']), e($input['partnerable_name']))
->subject('Activate your account');
});
Anybody got an idea what the solution for this error is?
Thanks in advance,
Kibo
Remove /bootstrap/compiled.php I think it will work for you.
You need to remove this from your Mail::send call. The function should be the third parameter so I'm not sure what you're trying to do here -- the $input['email'] field will already be available within the function due to your "use ($input)"
$email = e($input['email']
I'm building a multi-tenant app, using the subdomain to separate the users.
e.g. .myapp.com
I want to give each tenant their own database too.
How can I detect the subdomain and set the database dynamically?
Also, the code below is from the official documentation and shows us how we can get the subdomain when setting up a route. But how do we pass the subdomain value to a controller function?
Route::group(array('domain' => '{account}.myapp.com'), function()
{
Route::get('user/{id}', function($account, $id)
{
//
});
});
The best way to achieve this would be in a before filter that you apply to the route group.
Route::group(['domain' => '{account}.myapp.com', 'before' => 'database.setup'], function()
{
// Your routes...
}
This before filters gets a $route parameter and a $request parameter given to it, so we can use $request to get the host.
Route::filter('database.setup', function($route, $request)
{
$account = $request->getHost();
}
You could then use the account to adjust the default database connection using Config::set in the filter. Perhaps you need to use the default connection first up to fetch the users database details.
$details = DB::details()->where('account', '=', $account)->first();
// Make sure you got some database details.
Config::set('database.connections.account', ['driver' => 'mysql', 'host' => $details->host, 'database' => $details->database, 'username' => $details->username, 'password' => $details->password]);
Config::set('database.connections.default', 'account');
During runtime you create a new database connection and then set the default connection to that newly created connection. Of course, you could leave the default as is and simply set the connection on all your models to account.
This should give you some ideas. Please note that none of this code was tested.
Also, each method on your controllers will receive the domain as the first parameter. So be sure to adjust for that if you're expecting other parameters.