I've created code and authenticated user using guard but it doesn't provides me a api_token
/**
* This'll provide login authentication to the user
* #param Request $request
* #return json
*/
public function authenticate(Request $request)
{
//getting and setting locale for the request
$locale = ($request->header('lang-code') == 'KO') ? "ko" : "en";
app()->setLocale($locale);
$credentials = $request->only('email','password');
try {
// verify the credentials and create a token for the user
if (!$token = auth()->attempt($credentials)) {
return response()->json(['success' => parent::FAILURE, 'message' => trans('messages.api.login.failure')],401);
}
} catch (GeneralException $e) {
// something went wrong
return response()->json(['success' => parent::FAILURE, 'message' => trans('messages.api.login.tokenNC')],500);
}
return response()->json(['success' => parent::SUCCESS, 'message' => trans('messages.api.login.success'), 'data' => auth()->user()],200);
}
Above function is working fine but I'm not getting token when I use auth()->guard('api')->user()->api_token. This column is already within my DB even though I'm not able to generate api_token what can be the issue over here.
EDITED
routes/api.php:
Route::group(['namespace' => "Api\\v1", 'as' => 'api.v1.', 'prefix' => 'v1'], function () {
Route::any('/login', 'AccessController#authenticate');
});
config/auth.php:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
You may need to make sure that any routes that will be using Token Authentication are being protected by the auth:api middleware.
Like this example :
Route::group(['prefix' => 'api/v1', 'middleware' => 'auth:api'], function () {
Route::post('/authentificate', 'AuthController#authentificate');
});
You can use model mutators to do that automatically or override the boot method of your Model Class which is in your case User Model
protected static function boot()
{
parent::boot();
static::creating(function($model)
{
$model->api_token = $model->generateCode();
});
}
protected function generateCode()
{
return bin2hex(openssl_random_pseudo_bytes(16));
//you can use your own random fucntion here or you can use inbuilt Crypt funciton
}
Related
I used jwt-auth.
It works well in login, but when I try to register it and go through the login request the error below occurred.
"message": "Unauthenticated."
Here is the source code:
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => True,
],
routes/api.php:
Route::group([
'middleware' => 'api',
'namespace' => 'App\Http\Controllers',//must include the path
'prefix' => 'auth'
], function ($router) {
Route::post('login', 'AuthController#login');
Route::post('signup', 'AuthController#signup');
Route::post('logout', 'AuthController#logout');
Route::post('refresh', 'AuthController#refresh');
Route::post('me', 'AuthController#me');
});
AuthController.php
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login,signup']]);
}
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function signup(Request $request){
$validation = $request->validate([
'email' =>'required',
'name' =>'required',
'password'=>'required|min:6|confirmed',
]);
$data = array();
$data['name'] = $request->name;
$data['email'] = $request->email;
$data['password'] = Hash::make($request->password);
DB::table('users')->insert($data);
return $this->login($request);
}
I provided DB and Hash classes. In postman, I put the route in the POST method and set headers Accept and content-type as application/json formate and in the body part, I set x-www-form and input all the keys and values with confirmation_password key. It not inserting into the database and showing the error message. I tried it by clearing the config cache and route cache. I also tried it with raw and json format.
i think you problem with ur constractor middleware u didn't make the except value right u have to make to different values for except like that
$this->middleware('auth:api', ['except' => ['login','signup']]);
Better use create or save Method to store the new user. After you store user in your database you can generate a usertoken and send him back. And the user is logged in. Every further request is then regulated via the token.
$token = // generate an new token with the JWT toker Helper method ;
return response()->json([
'status' => 'ok',
'token' => $token,
'user' => $user
], 201);
First things first, I am using Hyn-Multi Tenant in my laravel 6 application Where there is a central database [connection = system] handles multiple tenant database. So far this package has helped me a lot but my application needs passport implementation for apis which is not documented in the package.
However there are other tutorials which claim passport implementation on Hyn package. I followed them and able to create access token per tenant user.
This is my config/auth.php:
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'system-users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'system',
],
'staff' => [
'driver' => 'session',
'provider' => 'staff',
],
'api' => [
'driver' => 'passport',
'provider' => 'staff',
'hash' => false,
],
'student' => [
'driver' => 'passport',
'provider' => 'student',
'hash' => false,
],
],
'providers' => [
'system' => [
'driver' => 'eloquent',
'model' => App\Models\System\User::class,
],
'staff' => [
'driver' => 'eloquent',
'model' => App\Models\Tenant\Staff::class,
],
'student' => [
'driver' => 'eloquent',
'model' => App\Models\Tenant\Student::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
My each tenant models uses UsesTenantConnection trait
This is my EnforceTenancy middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Config;
class EnforceTenancy
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
Config::set('database.default', 'tenant');
return $next($request);
}
}
This is my AuthServiceProvider.php
public function boot()
{
$this->registerPolicies();
Passport::routes(null, ['middleware' => 'tenancy.enforce']);
// FOLLOWING CODE IS HAVING PROBLEM
//Passport::useTokenModel(OAuthAccessToken::class);
//Passport::useClientModel(OAuthClient::class);
//Passport::useAuthCodeModel(OAuthCode::class);
//Passport::usePersonalAccessClientModel(OAuthPersonalAccessClient::class);
$this->commands([
\Laravel\Passport\Console\InstallCommand::class,
\Laravel\Passport\Console\ClientCommand::class,
\Laravel\Passport\Console\KeysCommand::class,
]);
\Laravel\Passport\Passport::tokensExpireIn(\Carbon\Carbon::now()->addMinutes(10));
\Laravel\Passport\Passport::refreshTokensExpireIn(\Carbon\Carbon::now()->addDays(1));
}
So far all good, now I am going to explain in points,
When I call createToken('MyApp') I am able to generate token on tenant db, for example:
if (Auth::guard('staff')->attempt(['email' => $request->email, 'password' => $request->password])) {
$user = Auth::guard('staff')->user();
$auth_tokens = $user->createToken('MyApp');
$access_token = $auth_tokens->accessToken;
...
}
but to access login protected apis, I am sending bearer access token in header
window.axios
.get("/api/meta",{
headers: fetchAuthHeaders()
})
.then(response => {
if(true == response.data.status) {
var data = response.data.data;
this.school.name = data.school_meta.name;
this.school.logo = data.school_meta.logo;
} else{
alert(response.data.message);
}
})
api.php
Route::domain('{hostname}.lvh.me')->group(function () {
Route::middleware('tenant.exists')->group(function () {
Route::get('/get-oauth-secret', 'Tenant\MetaController#getOAuthData');
Route::post('validate-login','Tenant\AuthController#validateLogin');
Route::middleware(['auth:api'])->group(function (){
Route::get('meta','Tenant\AuthController#getMetaData'); //this api
});
});
});
I am getting response as {"message":"Unauthenticated."}
Once the token is generated in step 1, I copy this token and paste into postman's header section and uncomment the custom passport models in AuthServiceProvider.php as shown below
AuthServiceProvider.php
public function boot()
{
...
// UNCOMMENTED FOLLOWING CUSTOM PASSPORT MODELS
Passport::useTokenModel(OAuthAccessToken::class);
Passport::useClientModel(OAuthClient::class);
Passport::useAuthCodeModel(OAuthCode::class);
Passport::usePersonalAccessClientModel(OAuthPersonalAccessClient::class);
...
}
Now I can access api/meta route but while login and creating token I am getting error:
ErrorException: Trying to get property 'id' of non-object in file /home/winlappy1/Desktop/multi_tenancy/vendor/laravel/passport/src/PersonalAccessTokenFactory.php on line 98
I just want to know where I am going wrong, I know my explanation is quite ambiguous and confusing but thats all how I can explain my issue. I am ready to provide more clarification but I need to resolve this issue.
Try to add
\App\Http\Middleware\EnforceTenancy::class
into the beginning of $middlewarePriority array in Kernel.php
Also use Laravel Passport 9.1.0 which support multi Auth
Try to do this
#AuthServiceProvider
Add this
public function boot()
{
$this->registerPolicies();
This one is to check if the database is Tenant or not
$website = \Hyn\Tenancy\Facades\TenancyFacade::website();
if ($website != null) {
Passport::useClientModel(PassportClient::class);
Passport::useTokenModel(PassportToken::class);
Passport::useAuthCodeModel(PassportAuthCode::class);
Passport::usePersonalAccessClientModel(PassportPersonalAccessClient::class);
}
$this->commands([
\Laravel\Passport\Console\InstallCommand::class,
\Laravel\Passport\Console\ClientCommand::class,
\Laravel\Passport\Console\KeysCommand::class,
]);
\Laravel\Passport\Passport::tokensExpireIn(\Carbon\Carbon::now()->addMinutes(10));
\Laravel\Passport\Passport::refreshTokensExpireIn(\Carbon\Carbon::now()->addDays(1));
}
Along with these add The four models
Like this
Create four Models Which enforce the Tenants
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\AuthCode;
class PassportAuthCode extends AuthCode
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\Client;
class PassportClient extends Client
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\PersonalAccessClient;
class PassportPersonalAccessClient extends PersonalAccessClient
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\Token;
class PassportToken extends Token
{use UsesTenantConnection;}
Also use (tenancy.enforce) middleware Enforcetenancy
'tenancy.enforce' => \App\Http\Middleware\EnforceTenancy::class, $Routemiddleware kernel.php
EnforceTenancy.php middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
class EnforceTenancy
{
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
Config::set('database.default', 'tenant');
return $next($request);
}
}
Force the tenant routes through tenancy.enforce middleware
Also publish the new migrations and migrate:fresh as new fields are added to the new passport
I want to use JWT to login in my API but it always give me
error: "Unauthorized".
Before this, i already register the email and password of the user in my database before trying to login
here's my code :
class UserController extends Controller
{
public function __construct()
{
$this->middleware('auth:api', ['except' => 'login']);
}
public function login(Request $request) {
$validator = Validator::make($request->all(), [
'email' => 'required|email|max:255',
'password' => 'required|string|min:6|max:255',
]);
if($validator->fails()) {
return response()->json([
'status' => 'error',
'messages' => $validator->messages()
], 200);
}
if (! $token = auth()->attempt(['email' => $request->email, 'password' => $request->password])) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
protected function respondWithToken($token) {
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => Auth::guard()->factory()->getTTL() * 60
]);
}
/**
* Get the guard to be used during authentication.
*
* #return \Illuminate\Contracts\Auth\Guard
*/
public function guard()
{
return Auth::guard('api');
}
here's my guard and default in config/auth.php :
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
here's my frontend using VueJs that does the login :
axios.post('http://localhost:8000/api/login', loginData)
.then(response => {
console.log(response)
})
.catch(error => {
commit('loginStop', error.response.data.error);
commit('updateAccessToken', null);
console.log(error.response);
})
here's my web.php :
Route::group(['prefix' => 'api'], function() {
Route::post('/register', 'UserController#register');
Route::post('/login', 'UserController#login');
});
Here's my output of php artisan route:list :
Based on the comments, you aren't hashing the password of the user that you inserted in the database.
But you have to do it as Auth::attempt checks if the provided password would match the hash stored in the database.
If you still want to register the user manually, you can hook up a tinker interactive shell with php artisan tinker command and then user Hash::make('yourpassword') to generate an hashed password using the setted Laravel's password hashing system (defaults to bcrypt).
Then you just have to copy the output string into your database. The login should finally work as the Auth guard now can check the user input agains a correct database user with a proper hashed password.
I had a totally different issue.
The auth()->attempt method was not comparing the password to my User model password for some reason.
After adding the following to my User model:
public function getAuthPassword() {
return $this->password;
}
Everything worked like a charm
The same problem to me, I solved by:
Use tinker to generate user credentials, email and password
content type in Headers: application/x-www-form-urlencoded
Auth middleware in laravel to authenticate users using my custom guard but everytime i call a route with that middleware I get the error that :
Route [login] not defined.
Right now, i am just trying to make sure that the middleware is being called.This is what i have done so far:
public function handle($request, Closure $next, $guard = null)
{
return "hi";
if (Auth::guard('api')->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('hi/login');
}
return $next($request);
}
the above is the handle method for authenticate.php. this is my code for guard
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'access_token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
The Routes:
This is the route group in which i have a sub-group that implements the auth
middleware
Route::group(['prefix' => 'app'], function() use ($router) { }
This is the route i am testing the middleware on Route::get('/subscribedcompanies','PromotionController#getFavoriteCompanies');
One more Thing i'd like to add is i'm trying to authenticate based on access_token from database. i have changed the authenticate and credentials function in login controller as follows:
protected function credentials(Request $request)
{
return array_merge($request->header('authorization'));
}
public function authenticate(Request $request)
{
$credentials = $request->header('authorization');
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('/');
}
}
just give name to your login route as login
Route::post('/login', 'LoginController#index')->name('login');
I would like to have some routes which only be available for auth:user OR auth:admin middlewares.
I tried following code :
Route::group(['middleware' => ['auth:user', 'auth:admin']], function () {
//many routes here
});
But seems like these routes are available for auth:user AND auth:admin at the same time!!!
I don't want AND. I need OR.
Any helps would be appreciated
Update 1
I decided to create new guard userOradmin in /config/auth.php file.
As you can see I have created new guard called userOradmin which points to provider usersOrAdmins (plural names) :
'guards' => [
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins'
],
'userOradmin' => [
'driver' => 'session',
'provider' => 'usersOradmins'
]
]
And the provider is :
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class
],
'usersOradmins' => [
'driver' => 'eloquent',
'model' => [App\Admin::class, App\User::class] // <-- Is that right?
]
The problem is here. Should I assign that two classes to model like that?!
You need to make a new middleware for this, auth:userOrAdmin. Middlewares do not interact with each other, so neither of those middlewares know that the other exists. They just get a request, check it, and send it down the line, so every middleware is inherently AND.
Swap out the Authenticate middleware with this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Authenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string ...$guards
* #return mixed
*/
public function handle($request, Closure $next, ...$guards)
{
if ($this->check($guards)) {
return $next($request);
}
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
/**
* Determine if the user is logged in to any of the given guards.
*
* #param array $guards
* #return bool
*/
protected function check(array $guards)
{
if (empty($guards)) {
return Auth::check();
}
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
Auth::shouldUse($guard);
return true;
}
}
return false;
}
}
Then you can use it in your routes:
Route::group(['middleware' => ['auth:user,admin']], function () {
//many routes here
});