ServiceProvider registered but not work in controller - php

I registered a ServiceProvider with easysms
app\Providers\EasySmsServiceProvider.php
<?php
namespace App\Providers;
use Overtrue\EasySms\EasySms;
use Illuminate\Support\ServiceProvider;
class EasySmsServiceProvider extends ServiceProvider{
public function register(){
$this->app->singleton(EasySms::class, function($app){
return new EasySms(config('easysms'));
});
$this->app->alias(EasySms::class, 'easysms');
}
}
then, I add it to config/app.php in providers
config/app.php
'providers' => [
...
// easysms
App\Providers\EasySmsServiceProvider::class,
],
I already checked bootstrap/cache/services.php and I am sure I registered it successful.
But when I try to use it in Controller, errors occurred, it seems like something wrong with EasySmsServiceProvider
here is my controller
app\Http\Controllers\V1\Auth\VerifySmsesController.php
<?php
namespace App\Http\Controllers\V1\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\V1\Auth\VerifySmsRequest;
use Overtrue\EasySms\EasySms;
use Illuminate\Support\Str;
use Cache;
class VerifySmsesController extends Controller{
public function store(VerifySmsRequest $request, EasySms $easySms){
$phone = $request->phone;
$code = str_pad(random_int(1, 999999), 6, 0, STR_PAD_LEFT);
try {
$result = $easySms->send($phone, [
'template' => config('easysms.gateways.aliyun.templates.register'),
'data' => [
'code' => $code
],
]);
} catch (\Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception) {
$message = $exception->getException('aliyun')->getMessage();
abort(500, $message ?: 'Errors In Sending SMS');
}
$key = 'verify_code_' . Str::random(15);
$expiredAt = now()->addMinutes(5);
Cache::put($key, ['phone' => $phone, 'code' => $code], $expiredAt);
return response()->json([
'key' => $key,
'expired_at' => strtotime($expiredAt->toDateTimeString()),
],201);
}
}
here is my routes/api.php
<?php
Route::group([
'prefix' => 'v1',
'name' => 'v1.',
'namespace' => 'V1'
], function(){
Route::group([
'prefix' => 'auth',
'name' => 'auth.',
'namespace' => 'Auth',
'middleware' => ['throttle:'.config('api.rate_limits.sign')]
], function(){
Route::resource('verify-smses', 'VerifySmsesController', ['only' => ['store']]);
});
});
I had try commands below to clear/reset my config, but nothing helps
php artisan config:clear
php artisan clear-compiled
composer dump-autoload
php artisan optimize
also when I try to use $result = app('easysms')->send(...) instead of $result = $easySms->send(...) above
still errors
I am using Laravel 7.8.1.
How can I fix these errors? Thanks a lot~
---update---
config/easysms.php
<?php
use \Overtrue\EasySms\Strategies\OrderStrategy;
return [
'timeout' => 10.0,
'default' => [
'strategy' => OrderStrategy::class,
'gateways' => [
'aliyun',
],
],
'gateways' => [
'errorlog' => [
'file' => '/tmp/easy-sms.log',
],
'aliyun' => [
'access_key_id' => env('SMS_ALIYUN_SMS_ACCESS_KEY_ID'),
'access_key_secret' => env('SMS_ALIYUN_SMS_ACCESS_KEY_SECRET'),
'sign_name' => env('SMS_ALIYUN_SMS_SIGN_NAME'),
'templates' => [
'register' => env('SMS_ALIYUN_SMS_TEMPLATE_REGISTER')
]
],
],
];
---update---
it works when I run code below in php artisan tinker directly
$sms = app('easysms');
try {
$sms->send($phone, [
'template' => config('easysms.gateways.aliyun.templates.register'),
'data' => [
'code' => 1234
],
]);
} catch (\Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception) {
$message = $exception->getException('aliyun')->getMessage();
dd($message);
}

Related

Api/v1 does not exist in config when running php artisan serve

I would like to build an API that does a GET request to a third party API. I created a controller that houses this logic and registered this as an HTTP end point using the JsonApiRoute::server() facade. I am absolutely new to Laravel and PHP.
However when I run php artisan serve I get the error:
Server /api/v1 does not exist in config or is not a valid class.
This is my code in routes:
JsonApiRoute::server('/api/v1')
->prefix('/api/v1')
->resources(function ($server) {
$server->resource('retrieve');
});
This is the code I have in config-> json-api-default:
return [
'resolver' => \CloudCreativity\LaravelJsonApi\Resolver\ResolverFactory::class,
'namespace' => null,
'by-resource' => true,
'model-namespace' => null,
'resources' => [
'posts' => \App\Post::class,
],
'use-eloquent' => true,
'url' => [
'host' => null,
'namespace' => '/api/v1',
'name' => 'api:v1:',
],
'controllers' => [
'transactions' => true,
'connection' => null,
],
'jobs' => [
'resource' => 'queue-jobs',
'model' => \CloudCreativity\LaravelJsonApi\Queue\ClientJob::class,
],
'encoding' => [
'application/vnd.api+json',
],
'decoding' => [
'application/vnd.api+json',
],
'providers' => [],
];
and in my controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class retrieve extends Controller
{
//
public function retrieveData(Request $request){
$response = Http::withHeaders([
'api-key' => '***',
])->get('https://data.mongodb-api.com/app/data-pkrpq/endpoint/getRandom');
return response()->json(['status'=> true,'data'=> json_decode($response->body()), 'Message'=>'Successfully retrieved'], 200);
}
}

Method Laravel\Passport\Guards\TokenGuard::attempt does not exist

In Laravel 9, I am trying to hit the login API with the custom guard client, I am getting the following error. Please help.
BadMethodCallException: Method Laravel\Passport\Guards\TokenGuard::attempt does not exist.
config/Auth.php
'guards' => [
...
'client' => [
'driver' => 'passport',
'provider' => 'client',
],
],
'providers' => [
...
'client' => [
'driver' => 'eloquent',
'model' => App\Models\Client::class,
],
],
Error line: if(!$authGuard->attempt($login)){
api/AuthController.php
public function login(Request $request){
$login = $request->validate([
'email' => 'required|string',
'password' => 'required|string',
]);
try {
$authGuard = Auth::guard('client');
if(!$authGuard->attempt($login)){
$data = 'Invalid Login Credentials';
$code = 401;
} else {
$user = $authGuard->user();
$token = $user->createToken('user')->accessToken;
$code = 200;
$data = [
'user' => $user,
'token' => $token,
];
}
} catch (Exception $e) {
$data = ['error' => $e->getMessage()];
}
return response()->json($data, $code);
}
Models/Client.php
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class Client extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
Screenshot:
I think Auth::attempt() is not compatible with passport.
So, you can use Auth::check() method instead.
attempt() is only available to guards implementing the StatefulGuard interface.
So i agree with John that attempt is not compatible with Passport.
You can try this it should work :
auth()->guard('client')->setUser($login); or Auth::guard('client')->setUser($login);
I solved it by changing the driver from passport to session in config/auth.php
'clients' => [
'driver' => 'session',
'provider' => 'clients',
],
I am not sure this is the correct solution, but it works.
Please feel free to post the answer if there is any better solution
Thanks

Laravel 8 Passport - Multi Auth setup

I am trying to setup Passport in Laravel 8 with two guards, but keep running into issues. I am using Postman to test.
I have two tables setup:
Users
Contacts
I can successfully register a user in both tables. However I can only authenticate and retrieve a token in the login method for the users table. I keep getting "Invalid Credentials" on contacts table. I am pretty sure the reason for this is because it's looking at the users table and not the contacts table when trying to authenticate the user. I think I am missing something in the setup process to allow the use of different tables when authenticating.
My codes is as follows:
auth.php
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
'api-crm' => [
'driver' => 'passport',
'provider' => 'contacts',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'contacts' => [
'driver' => 'eloquent',
'model' => App\Models\Contact::class,
],
],
api.php
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::post("/register", [ApiAuthController::class, 'register']);
Route::post("/login", [ApiAuthController::class, 'login']);
Route::post("/crm/register", [CrmAuthController::class, 'register']);
Route::post("/crm/login", [CrmAuthController::class, 'login']);
ApiAuthController.php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
class ApiAuthController extends Controller
{
public function register(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|max:55',
'email' => 'email|required|unique:users',
'password' => 'required|confirmed'
]);
$validatedData['password'] = bcrypt($request->password);
$user = User::create($validatedData);
$accessToken = $user->createToken('authToken')->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken]);
}
public function login(Request $request)
{
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response(['message' => 'Invalid Credentials']);
}
$accessToken = auth()->user()->createToken('authToken')->accessToken;
return response(['user' => auth()->user(), 'access_token' => $accessToken]);
}
}
CrmAuthController.php
namespace App\Http\Controllers\CRM;
use App\Http\Controllers\Controller;
use App\Models\Contact;
use Illuminate\Http\Request;
class CrmAuthController extends Controller
{
public function register(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|max:55',
'email' => 'email|required|unique:users',
'password' => 'required|confirmed'
]);
$validatedData['password'] = bcrypt($request->password);
$user = Contact::create($validatedData);
$accessToken = $user->createToken('authToken')->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken]);
}
public function login(Request $request)
{
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response(['message' => 'Invalid Credentials']);
}
$accessToken = auth()->user()->createToken('authToken')->accessToken;
return response(['user' => auth()->user(), 'access_token' => $accessToken]);
}
}
In crmAuthController.php login method, when you use auth()->attempt($loginData) it looks to validate login data on default users table.
so instead of using the attempt($loginData) you have to get crm user by email using "Contact" Model in your case.
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
$user = new \App\Models\Contact();
$check = $user->where('email',$loginData['email'])->exists();
if($check){
$users = $user->where('email',$loginData['email'])->first();
// verify the password
if (password_verify($loginData['password'],$users->password)) {
// Authentication passed...
$token = $users->createToken('YOUR TOKEN NAME');
return response($token);
}
else return response(['message' => 'Invalid Credentials']);
}
else return response(['message' => 'user doesnt exist with this email']);
Also once you logged in, to get the current user for CRM, use
Auth::guard('api-crm')->user();

How to use validationDefault method in cakephp 3

I am trying to use validationDefault method in my model UsersTable :
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->setTable('users');
$this->setPrimaryKey('id');
}
public function validationDefault(Validator $validator)
{
$validator->add('login', [
'length' => [
'rule' => ['minLength',3],
'message' => __('Login need to be at least 3 characters long')
]
]);
return $validator;
}
}
In my Users controller i have the following code :
<?php
namespace App\Controller;
use Cake\Validation\Validator;
class UsersController extends AppController {
public function initialize() {
parent::initialize();
}
public function add() {
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('Your user has been created.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to create your user.'));
}
My form looks like :
<?= $this->Form->create('Users', ['url' => ['controller' => 'Users', 'action' => 'add']]); ?>
<fieldset>
<legend><?= __('Ajouter un utilisateur') ?></legend>
<?= $this->Form->control('login') ?>
<?= $this->Form->control('password') ?>
<?= $this->Form->control('role', [
'options' => ['admin' => 'Admin', 'author' => 'Author']
]) ?>
</fieldset>
<?= $this->Form->button(__('Ajouter')); ?>
<?= $this->Form->end() ?>
If I use form with "aa" in login form, data are insert in database. But they should not be insert because validator define minLength to 3.
It's seem like validationDefault is not call when I use save method.
Here a debug of var $user :
object(Cake\ORM\Entity) {
'login' => 'g',
'password' => '',
'role' => 'admin',
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'login' => true,
'password' => true,
'role' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Users'
}
Note: if I use validator directly in controller, an error is print : Login need to be at least 3 characters long.But i don't want use validator directly in controller...
$validator = new Validator();
$validator->add('login', [
'length' => ['rule' => ['minLength', 3],
'message' => __('Login need to be at least 3 characters long')
]
]);
if($validator->errors($this->request->getData())) {
debug($validator->errors($this->request->getData()));
}
I ask this problem to channel #cakephp on IRC and they say to me to reinstall cakephp but using bake to generate model classes.
After that, all is working fine.

Lravel 5.4: JWT API with multi-auth on two tables one works the other not

I am using...
Laravel 5.4
tymon/jwt-auth : 1.0.0-rc.2
I have application with two authentications API one is customers and the other is drivers each one has it's own table.
now let me describe shortly JWT package installation and the updates I did on it.
I installed the package as described in the JWT documents exactly.
Now comes to the quick start here I updated two Models one is the User and the second Driver.
Comes here to the Configure Auth guard again I used the configuration for the two guards let me show a snapshot of my auth.php.
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'driver' => [
'driver' => 'session',
'provider' => 'drivers',
],
'driver-api' => [
'driver' => 'jwt',
'provider' => 'drivers',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'drivers' => [
'driver' => 'eloquent',
'model' => App\Models\Driver::class,
],
],
Now continue the application with authentication routes here is my Routes for the two Models
Here is the User and Driver Routes
Route::group( [
'prefix' => 'auth',
'middleware' => 'api'
], function () {
.......
});
Route::group( [
'prefix' => 'driver',
'middleware' => 'api'
], function () {
.......
});
Now comes the AuthController
in the JWT documentation the construct is writing like that.
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
I found some article that suggest to make it something like this to switch between the two models we have.
so here with my controller looks like now.
public function __construct() {
$this->user = new User;
$this->driver = new Driver;
}
public function userLogin( Request $request ) {
Config::set( 'jwt.user', 'App\Models\User' );
Config::set( 'auth.providers.users.model', User::class );
$credentials = $request->only( 'email', 'password' );
$token = null;
try {
if ( $token = $this->guard()->attempt( $credentials ) ) {
return response()->json( [
'response' => 'error',
'message' => 'invalid_email_or_password',
] );
}
} catch ( JWTAuthException $e ) {
return response()->json( [
'response' => 'error',
'message' => 'failed_to_create_token',
] );
}
return response()->json( [
'response' => 'success',
'result' => [
'token' => $token,
'message' => 'I am front user',
],
] );
}
public function driverLogin( Request $request ) {
Config::set( 'jwt.user', 'App\Models\Driver' );
Config::set( 'auth.providers.users.model', Driver::class );
$credentials = $request->only( 'email', 'password' );
$token = null;
try {
if ( ! $token = $this->guard()->attempt( $credentials ) ) {
return response()->json( [
'response' => 'error',
'message' => 'invalid_email_or_password',
] );
}
} catch ( JWTAuthException $e ) {
return response()->json( [
'response' => 'error',
'message' => 'failed_to_create_token',
] );
}
return response()->json( [
'response' => 'success',
'result' => [
'token' => $token,
'message' => 'I am driver user',
],
] );
}
public function me() {
return response()->json( $this->guard()->user() );
}
public function logout() {
$this->guard()->logout();
return response()->json( [ 'message' => 'Successfully logged out' ] );
}
public function refresh() {
return $this->respondWithToken( $this->guard()->refresh() );
}
protected function respondWithToken( $token ) {
return response()->json( [
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => $this->guard()->factory()->getTTL() * 60
] );
}
public function guard() {
return Auth::guard();
}
Now with is happening and the problems I faced
Now the driver api is working as login only Ex.
localhost:8000/api/driver/login Working fine
but when try to get the driver user id like this
localhost:8000/api/driver/me it return empty array
Second Issue comes.
the use login from the interface for Ex. http://localhost:8000/login it returns back to the login screen without any errors becouse the login information is right but the defaults in the auth.php is 'guard'=>'api' if I change it to 'guard'=>'web' it do the login correctly.
even the User API for Ex. localhost:8000/api/auth/login always return
{
"response": "error",
"message": "invalid_email_or_password"
}
Update
I solved half the way
I updated the AuthController to be something like this.
public function __construct() {
if ( Request()->url() == '/api/driver/me' ) {
$this->middleware( 'auth:driver-api', [ 'except' => [ 'login' ] ] );
} elseif ( Request()->url() == '/api/customer/me' ) {
$this->middleware( 'auth:api', [ 'except' => [ 'login' ] ] );
}
}
and the login function to be something like this.
public function login() {
if ( Request()->url() == '/api/driver' ) {
Config::set( 'auth.providers.users.model', Driver::class );
$credentials = request( [ 'email', 'password' ] );
if ( ! $token = auth()->attempt( $credentials ) ) {
return response()->json( [ 'error' => 'Unauthorized' ], 401 );
}
return $this->respondWithToken( $token );
}
Config::set( 'auth.providers.users.model', User::class );
$credentials = request( [ 'email', 'password' ] );
if ( ! $token = auth()->attempt( $credentials ) ) {
return response()->json( [ 'error' => 'Unauthorized' ], 401 );
}
return $this->respondWithToken( $token );
}
but still have problem in auth.php here it is
'defaults' => [
'guard' => 'driver-api',
'passwords' => 'users',
],
here I need to switch the 'guard'=>'api' to be 'guard'=>'driver-api' in case if URL request is localhost:8000/api/driver/login and 'guard'=>'api' in case if URL request is localhost:8000/api/customer/login any way to do this.
Update 2
Here is the driver Model
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Driver extends Authenticatable implements JWTSubject {
protected $guard = 'driver';
protected $fillable = [
...
'email',
'password',
...
];
protected $hidden = [
'password',
'remember_token',
];
public function getJWTIdentifier() {
return $this->getKey();
}
public function getJWTCustomClaims() {
return [];
}
}
and the User Model
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject {
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
public function getJWTIdentifier() {
return $this->getKey();
}
public function getJWTCustomClaims() {
return [];
}
I need some help, Ideas please.
There is no need to change the providers in config/auth.php.
You can change the __construct function in each of your controllers as follows. So that jwt know which model to authenticate.
DriverController
function __construct()
{
Config::set('jwt.user', Driver::class);
Config::set('auth.providers', ['users' => [
'driver' => 'eloquent',
'model' => Driver::class,
]]);
}
My example when i used multi auth with jwt
I have 2 models :
1. users
2. admins
the routes :
Route::post('auth/userlogin', 'ApiController#userLogin');
Route::post('auth/adminlogin', 'ApiController#adminLogin');
the controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use Config;
use JWTAuth;
use JWTAuthException;
use App\User;
use App\Admin;
class ApiController extends Controller
{
public function __construct()
{
$this->user = new User;
$this->admin = new Admin;
}
public function userLogin(Request $request){
Config::set('jwt.user', 'App\User');
Config::set('auth.providers.users.model', \App\User::class);
$credentials = $request->only('email', 'password');
$token = null;
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json([
'response' => 'error',
'message' => 'invalid_email_or_password',
]);
}
} catch (JWTAuthException $e) {
return response()->json([
'response' => 'error',
'message' => 'failed_to_create_token',
]);
}
return response()->json([
'response' => 'success',
'result' => [
'token' => $token,
'message' => 'I am front user',
],
]);
}
public function adminLogin(Request $request){
Config::set('jwt.user', 'App\Admin');
Config::set('auth.providers.users.model', \App\Admin::class);
$credentials = $request->only('email', 'password');
$token = null;
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json([
'response' => 'error',
'message' => 'invalid_email_or_password',
]);
}
} catch (JWTAuthException $e) {
return response()->json([
'response' => 'error',
'message' => 'failed_to_create_token',
]);
}
return response()->json([
'response' => 'success',
'result' => [
'token' => $token,
'message' => 'I am Admin user',
],
]);
}
}
I hope that's help you .
First let me thank you #AmrAbdelRahman for you efforts and your time.
My problem was the application always using my default authentication "guard" as my default looks like that
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
so every time I try to authenticate the other user which was the driver it fails during the default authenticate is api and the it should using driver in this case.
what I did in my case was making a switcher in my App\Providers\AppServiceProvider under the boot here is how it looks like
$this->app['router']->matched(function (\Illuminate\Routing\Events\RouteMatched $e) {
$route = $e->route;
if (!array_has($route->getAction(), 'guard')) {
return;
}
$routeGuard = array_get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
Now in my case if the $guard =api it will read that guard and act correctly and if it's driver it will use the new guard and act as expected. Hope this will help some one in future.

Categories