I have managed to create jwtauth to connect my mobile app to octobercms backend
from this reference
but the last_login field is always empty, I believe this is not set by default.
this is authenticated function that I have
use Tymon\JWTAuth\JWTAuth;
public function __construct(JWTAuth $auth)
{
$this->auth = $auth;
}
public function authenticate(Request $request)
{
try {
if (! $token = $this->auth->attempt($request->only('email', 'password'))) {
return response()->json([
'errors' => [
'root' => 'Could not sign you in with those details.',
],
], 401);
}
} catch (JWTException $e) {
return response()->json([
'errors' => [
'root' => 'Failed.',
],
], $e->getStatusCode());
}
return response()->json([
'data' => $request->user(),
'meta' => [
'token' => $token,
],
]);
}
it's called by this route.php from jwtauth folder
Route::group(['prefix' => 'api'], function () {
Route::post('auth/login','Autumn\JWTAuth\Http\Controllers\AuthController#authenticate');
Route::post('auth/register', 'Autumn\JWTAuth\Http\Controllers\AuthController#register');
Route::post('auth/logout', 'Autumn\JWTAuth\Http\Controllers\AuthController#logout');
Route::group(['middleware' => 'jwt.auth'], function () {
Route::get('auth/me', 'Autumn\JWTAuth\Http\Controllers\AuthController#user');
});
how do we set user last_login timestamp?
I hope my question is clear to understand.
added plugin.php where i extended user plugin as requested by #HardikSatasiya since i got exception implementing his suggestion
use System\Classes\PluginBase;
use Rainlab\User\Controllers\Users as UsersController;
use Rainlab\User\Models\User as UserModels;
use Event;
class Plugin extends PluginBase
{
public function registerComponents()
{
}
public function registerSettings()
{
}
public function boot()
{
UserModels::extend(function($model){
$model->bindEvent('model.beforeSave',function() use ($model) {
$users = \BackendAuth::getUser();
$model->backend_users_id = $users->id;
//above line result exception when calling method as #HardikSatasiya suggested
if(!empty($model->avatar)){
$model->image_path = $model->avatar->getPath();
}
if(!empty($model->groups)){
$model->membership = $model->groups[0]['name'];
}
});
$model->addJsonable('users_detail','membership');
});
UsersController::extendFormFields(function($form,$model,$context){
$form->addTabFields([
'users_detail[0][gender]' => [
'label' => 'Jenis Kelamin',
'span' => 'left',
'tab' => 'User Profile',
'type' => 'radio',
'options' => [
'Pria' => 'Pria',
'Wanita' => 'Wanita'
]
],
'users_detail[0][ttl]' => [
'label' => 'Tempat/Tanggal Lahir',
'type' => 'text',
'span' => 'left',
'tab' => 'User Profile'
],
]);
});
}
i add additional fields to user table by this separate plugin..
Ok, may be because internal hooks are not called when this plugin externally logsin user.
May be we need to call it manually, this code snippet can do it, just put given code after successful login.
// this will fire hooks and update `last_login`
// get authenticated user from the jwt authmanager
$user = $this->auth->authenticate($token);
$user->afterLogin();
Added in your code below.
public function authenticate(Request $request)
{
try {
if (! $token = $this->auth->attempt($request->only('email', 'password'))) {
return response()->json([
'errors' => [
'root' => 'Could not sign you in with those details.',
],
], 401);
}
} catch (JWTException $e) {
return response()->json([
'errors' => [
'root' => 'Failed.',
],
], $e->getStatusCode());
}
// this will fire hooks and update `last_login`
// get authenticated user from the jwt authmanager
$user = $this->auth->authenticate($token);
$user->afterLogin();
// ^ this code
return response()->json([
'data' => $request->user(),
'meta' => [
'token' => $token,
],
]);
}
this snippet will update last_login as expected. i did not test it but it will work as it should.
if any doubt or problem please comment.
Related
I created a custom guard with a validate method
public function validate(array $credentials = [])
{
if (!isset($credentials['login']) || empty($credentials['login']) || !isset($credentials['password']) || empty($credentials['password'])) {
return false;
}
$user = $this->provider->retrieveById($credentials['login']);
if (!isset($user)) {
return false;
}
if ($this->provider->validateCredentials($user, $credentials)) {
$this->setUser($user);
return true;
} else {
return false;
}
}
. I also created a custom authorization provider with a validateCredentials method
public function validateCredentials(Authenticatable $user, array $credentials)
{
if ((strcmp($credentials['login'], $this->retrieveByCredentials($credentials)->login) === 0)) {
if (Hash::check($credentials['password'], $user->getAuthPassword())) {
return true;
}
}
return false;
}
. added them to (providers file, code) and to the config/auth
'defaults' => [
'guard' => 'custom',
'passwords' => 'users',
],
'guards' => [
'custom' => [
'driver' => 'custom',
'provider' => 'custom',
]
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'custom' => [
'driver' => 'custom'
],
],
And in AuthServiceProvider:
public function boot()
{
$this->registerPolicies();
Auth::provider('custom', function ($app, array $config) {
return new CustomerAuthProvider($app->make('App\Models\User'));
});
Auth::extend('custom', function ($app, $name, array $config) {
return new CustomAuthGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
});
}
.
In the login controller I check the entered data
if (Auth::validate($attemptData)) {
return response()->redirectToIntended('cabinet');
}
, but for some reason no redirect or authorization occurs.
But if I do this
if (Auth::validate($attemptData)) {
$user = Auth::user();
$id = Auth::id();
dd($user);
return response()->redirectToIntended('cabinet');
}
, then the correct user will be inside it
I'm currently developing a mobile apps for my web based system. I'm using laravel as my API .
Currently, I want to do where when User A login, it will only shown User A info.
How can I achieve that ?
This is my AuthController where it generate my tokens.
public function login(Request $request)
{
try {
$validateUser = Validator::make($request->all(),
[
'username' => 'required',
'password' => 'required'
]);
if($validateUser->fails()){
return response()->json([
'status' => false,
'message' => 'validation error',
'errors' => $validateUser->errors()
], 401);
}
$user = UserRegister::where('username', $request->username)->first();
if(!$user || !Hash::check($request['password'], $user->password)) {
return response([
'message' => 'Username & Password does not match'
], 401);
}
return response()->json([
'status' => true,
'message' => 'User Logged In Successfully',
'user' => $user,
'token' => $user->createToken("remember_token")->plainTextToken
], 200);
} catch (\Throwable $th) {
return response()->json([
'status' => false,
'message' => $th->getMessage()
], 500);
}
}
This is my api routes
Route::post('/login', [AuthController::class, 'login']);
// Homepage routes
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/technician', [HomepageController::class, 'technician']);
});
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
and this is my HomepageController where I tried to retrieve user A data only but it does not work.
public function technician()
{
$technician=JobRegister::where('job_assign', auth()->user()->name);
return response()->json($technician);
}
This is my query for web based system.
$query = "SELECT * FROM job_register WHERE job_assign ='{$_SESSION['username']}'"
i want to make user A can only look at their job details that been assign.
do you have any idea how to do it ? your help is much appreciated. thank you.
try this
$username=Auth()->user()->userName //this will return you username of logged in user
$query = "SELECT * FROM job_register WHERE job_assign ='$username'"
don't forget to use auth namespace top of in your controller like this
use Auth;
When the user created a new account I added it's API token and returned it to the user. But I'm having trouble wanting to return the API token to the user when they view their account information.
GET /account: Returns API Token in response.
This is my code file User.php:
public function index()
{
$users = User::where('id', auth()->user()->id)->get();
return response([
'data' => UserResource::collection($users),
'message' => 'Retrieve successfully'
], 200);
}
// POST
public function store(Request $request)
{
$data = $request->all();
$validator = Validator::make($data, [
'name' => 'required|max:255|string',
'email' => 'required|email|unique:users,email',
'password' => 'required|string',
]);
if ($validator->fails()) {
return response(['error' => $validator->errors(), 'Validation Error'], 400);
}
$users = User::create($data);
$token = $users->createToken('accessToken')->plainTextToken;
return response([
'data' => new UserResource($users),
'api_token' => $token,
'message' => 'Created successfully'
], 201);
}
This is my code file api.php (route):
Route::group(['prefix' => 'v1' ], function () {
// Account
Route::post('/account', [UserController::class, 'store']);
// Protected route
Route::group(['middleware' => ['auth:sanctum']], function () {
// Account
Route::get('/account', [UserController::class, 'index']);
});
});
Use $request->bearerToken() to get bearer token.
public function index()
{
$users = User::where('id', auth()->user()->id)->get();
return response([
'data' => UserResource::collection($users),
'api_token' => $request->bearerToken(),
'message' => 'Retrieve successfully'
], 200);
}
I want to get the errors from the server in the client, show them on the page when there's some troubles during login or registration, I think it's ok in the backend, but I don't know why they're not returning.
How to get that error messages from the validator in Vue?
I'm using: vuex, vue-router
My vuex file for login and register
actions: {
async login({ dispatch }, credentials) {
await axios.post('http://127.0.0.1:8000/api/login', credentials)
.then( res => {
if (res.data.success) {
// controllo se il token è buono
return dispatch('attempt', res.data.token)
}
})
.catch( err => {
console.log( err )
})
},
async attempt({ commit, state }, token) {
// blocco in caso ci fosse o meno il token
// se c'è il token
if (token) {
commit('SET_TOKEN', token)
}
// se non c'è
if(!state.token) {
return
}
// /blocco in caso ci fosse o meno il token
// provo a gettare l'user
try {
await axios.get('http://127.0.0.1:8000/api/user')
.then(res => {
commit('SET_USER', res.data)
})
} catch (e) {
commit('SET_TOKEN', null)
commit('SET_USER', null)
}
},
async register({ }, credentials) {
await axios.post('http://127.0.0.1:8000/api/register', credentials)
.then( () => {
})
.catch(err => {
console.log(err)
})
},
logoutAction({ commit }) {
return axios.post('http://127.0.0.1:8000/api/logout')
.then( () => {
commit('SET_TOKEN', null)
commit('SET_USER', null)
})
},
}
My controller
public function register(Request $request) {
$fields = $request->validate(
[
'name' => 'required|string',
'email' => 'required|string|unique:users,email',
'password' => 'required|string|confirmed'
]
);
$user = User::create([
'name' => ucwords($fields['name']),
'email' => $fields['email'],
'password' => bcrypt($fields['password']),
]);
$token = $user->createToken('token')->plainTextToken;
return response()->json(
[
'success' => true,
'user' => $user,
'token' => $token,
'message' => 'Registered successfully'
], 201);
}
public function login(Request $request) {
$fields = $request->all();
$validator = Validator::make($fields, [
'email' => 'required',
'password' => 'required'
]);
$user = User::where('email', $fields['email'])->first();
if($validator->fails()) {
return response()->json([
'message' => 'You must fill in all the fields!',
'errors' => $validator->errors()
], 401);
}
if(!$user || !Hash::check($fields['password'], $user->password)) {
return response()->json([
'message' => 'Invalid credentials.',
], 401);
}
$token = $user->createToken('token')->plainTextToken;
return response()->json(
[
'success' => true,
'user' => $user,
'token' => $token,
'message' => 'Logged in'
], 201);
}
public function logout(Request $request) {
auth()->user()->tokens()->delete();
return response()->json(
[
'message' => 'Logged out.'
]
);
}
Also I want to stop the user if the registration has empty fields, forcing him to stay in the register route, but with these settings down here the user will be redirected to the login page even if no registration fields are been typed in, as soon as I press enter or click 'register'.
p.s.: the 'home' route in which I'm pushing in the user is the page with the login form. So I want that the user will be redirect there only if the register form has been fulfilled.
submitRegistration() {
this.register(this.form)
.then(() => {
this.$router.push({name:'home'})
})
.catch((err) => {
// Ignore the vuex err regarding navigating to the page they are already on.
if (
err.name !== "NavigationDuplicated" &&
!err.message.includes(
"Avoided redundant navigation to current location"
)
) {
// But print any other errors to the console
console.log(err);
}
});
},
As the title says CreateFreshApiToken doesnt create any cookies. So I cant use it to auth a logged in user for other requests related to the user.
I tried to set a cookie on the response and it works perfectly fine. So this has to do something with CreateFreshApiToken not working.
AuthController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\User;
class AuthController extends Controller
{
public function signup(Request $request)
{
$request->validate([
'name' => 'required|string',
'email' => 'required|string|email|unique:users',
'password' => 'required|string'
]);
$user = new User([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password)
]);
$user->save();
return response()->json([
'message' => 'Successfully created user!'
], 201);
}
public function signin(Request $request)
{
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string'
]);
$credentials = request(['email', 'password']);
if(!Auth::attempt($credentials))
return response()->json([
'message' => 'Unauthorized'
], 401);
$user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
$token->save();
return response()->json([
'message' => 'Successfully signed in!'
]);
}
public function signout(Request $request)
{
$request->user()->token()->revoke();
return response()->json([
'message' => 'Successfully signed out!'
]);
}
public function user(Request $request)
{
return response()->json($request->user());
}
public function test()
{
return response()->json([
'message' => 'test'
]);
}
public function test2(Request $request)
{
return response()->json([
'laravel_token' => $request->cookie('laravel_token')
]);
}
}
Kernel.php
protected $middlewareGroups = [
'web' => [
//...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
'api' => [
'throttle:60,1',
'bindings',
\Barryvdh\Cors\HandleCors::class,
],
];
api.php
Route::group([
'prefix' => 'auth'
], function () {
Route::post('signin', 'AuthController#signin');
Route::post('signup', 'AuthController#signup');
Route::get('test', 'AuthController#test');
Route::get('test2', 'AuthController#test2');
Route::group([
'middleware' => ['auth:api']
], function() {
Route::get('signout', 'AuthController#signout');
Route::get('user', 'AuthController#user');
});
});
And this is my angular code:
test() {
return this.http.get('http://homestead.test/api/auth/test', {withCredentials: true})
.subscribe(response => {
console.log(response);
});
}
test2() {
return this.http.get('http://homestead.test/api/auth/test2', {withCredentials: true})
.subscribe(response => {
console.log(response);
});
}
I've also setup cors with https://github.com/barryvdh/laravel-cors successfully with 'supportsCredentials' enabled. I am also sending a useless GET request to see if any laravel_token is set in the cookie but no success.
CreateFreshApiToken is part of the web middleware group, so in order for it to set cookies you need your login page to be a web route (instead of an api route).
I resolved this part of the problem by replicating CreateFreshApiToken::handler in my login controller:
$response = response()->json([], 200);
// ADD THIS LINE:
$response->cookie(\Laravel\Passport\Passport::cookie(),$request->session()->token();
return $response;