I have two laravel instances connected to an API. Default "password reset" functionality was created for the #1 website but now, I want to add new email templates for the users which are trying to reset their password from the second website.
public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
{
$view = $this->emailView;
return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
$m->to($user->getEmailForPasswordReset());
if (! is_null($callback)) {
call_user_func($callback, $m, $user, $token);
}
});
}
$view logs auth.emails.password from auth.php (email template for #1 website)
'passwords' => [
'users' => [
'provider' => 'users',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
],
I've added a hidden input to differentiate the users but I don't know how to use that in order to send another email template.. Any idea would be much appreciated!
Related
I am using Laravel + Breeze. My App will have two access sites:
Admins can login related to the users table (working)
Clients can login related to the clients table (to be discussed)
I don't know, if this is a good idea, but I want to share the already implemented Auth Service with another Model: clients.
What I already implemented is:
Logged in user can create a client
Client receives a email verification email from an event
Client can confirm email address and will be redirect to the login view
Now it becomes tricky for me. The AuthenticatedSessionController::store() method is checking for the $request->authenticate() and this runs agains the users table and fails for the clients table.
I slightly modified the method to redirect based on the type of login candidate:
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
$user = User::where('id', Auth::id())->first();
if ( $user ) {
return redirect()->intended(RouteServiceProvider::HOME);
}
$client = Client::where('id', Auth::id())->findOrFail();
if ( $client ) {
return redirect()->to('portal.show', $client->id);
}
}
but this works for users only as $request->authenticate() will not let clients in.
Now I need support and from here on I'm just guessing that the next step is the update I did in config/auth.php:
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'clients' => [
'driver' => 'eloquent',
'model' => App\Models\Client::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
'clients' => [
'provider' => 'clients',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
You will need to look into & modify authenticate method of App\Http\Requests\Auth\LoginRequest class.
One way of doing it could be to check for presence of email received via request against users and clients tables.
Then if the email belongs to a client - implement custom logic to validate credentials against clients table records and login the client
And if the email belongs to user - then let the request pass through as normal
Eg:
public function authenticate()
{
$type = DB::table('clients')->where('email', $this->email)->exists() ? 'client' : 'user';
if($type === 'user') {
return $this->authenticateUser();
}
//Validate credentials against `clients` table
// If valid retrieve the client and login the client
//Else throw ValidationException
}
//Copy the content of original authenticate to this method
protected function authenticateUser()
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
NOTE With this approach you are not doing any rate-limiting/throttling nor firing attempt events
For complete robust authentication system for clients, I guess it will be required to define a separate guard, user provider and then re-implement SessionGuard functionality all over again.
Another wild thought which comes to mind is, why not have Client be represented as a record on users table just for authentication purpose and may be have a one-to-one relation between them.
Working on a laravel 8 SPA with Vue and Sanctum. As a newbie with laravel I run into a issue.
I would guess that the issue is with the laravel session cookies as sanctum seems the keep on working but I could be wrong.
How I reproduce the problem:
When I login it works, sanctum token is generated, CXRF works, laravel auth works and when I
refresh the page it still keeps working. Still on the admin page.
When I hit the logout button it will delete all auth cookies and vuex values and I get redirected to the login page. This looks good
I want to login back but at this point it will throw an POST http://localhost:3000/auth/login 419 (unknown status)
3a. To prevent this I refresh the page first, I hit the login button and I am back on the admin page.
When I do the tirst 3 steps there's a chance that the authencation stops working and I will be redirected to the login page when I refresh the page.
What I have seen is that I have 4 cookies present when all is working propper.
appname_session (this the session?)
CXRF_token
io (not sure what this does)
random value
But I have seen is that it will add a 5th(or sometimes even more) cookie with a session value and this will lead to a logout and a invalid session(laravel side)
I have 4 routes, (web & api not in use for now)
If I need to post any files here let me know as I dont know what I need to post here
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
// 'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'admin' => [
// 'driver' => 'token',
'provider' => 'users',
],
'auth' => [
'driver' => 'session',
'provider' => 'users',
'hash' => false,
],
],
routes/auth.php
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::post('logout', [LoginController::class, 'logout']);
});
loginController.php
class LoginController extends Controller
{
use AuthenticatesUsers;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
protected function validator(array $data)
{
//validation rules
}
public function login(Request $request)
{
if ($this->validator($request->all())->fails()) {
$errors = $this->validator($request->all())->errors()->getMessages();
$clientErrors = [];
foreach ($errors as $key => $value) {
$clientErrors[$key] = $value[0];
}
return $this->sendError( 'Validation error', $clientErrors, 201);
} else {
$credentials = [
'email' => $request->email,
'password' => $request->password,
];
if (auth()->attempt($credentials)) {
$user = User::where('email', $request->email)->first();
$token = $user->createToken('auth_token')->plainTextToken;
$response = [
// data
];
return $this->sendResponse( 'Login successfully', $response, 200);
} else {
$msg = 'Invalid login';
$clientErrors = [
// errors
];
}
return $this->sendError( $msg, $clientErrors, 201);
}
}
public function logout(Request $request)
{
if (!empty($request->user())) {
AuthController::removeToken($request);
$request->session()->invalidate();
$request->session()->regenerateToken();
// Session::flush();
}
}
}
So I have login page that the admin or users can use to login. It went well until such time we have some changes and added the admin side. I only uses 1 table for all user types and I have role_id column that defines the users role. So if the role_id is 0, I have to redirect them to the dashboard page whereas if it's a user, will redirect to user page. I have tried as what is suggested in the internet but can't make it work. Here's what I have:
class UserLoginController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:user')->except('logout');
}
public function showLoginForm()
{
return view('auth.user-login');
}
public function login(UserLoginRequest $request)
{
// Attempt to log the user in
if (Auth::guard('user')->attempt(['email' => $request->email, 'password' => $request->password])) {
// if successful, then redirect to their intended location
return redirect()->intended(route('user.dashboard'));
}
// if unsuccessful, then redirect back to the login with the form data
if (! User::where('email', $request->email)->where('password', bcrypt($request->password))->first() ) {
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['status' => 'Incorrect username or password.']);
}
}
public function logout()
{
Auth::guard('user')->logout();
return redirect()->route('user.login');
}
}
config/auth
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'guest' => [
'driver' => 'session',
'provider' => 'guests',
],
'user' => [
'driver' => 'session',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'guests' => [
'driver' => 'eloquent',
'model' => App\Guest::class,
],
],
MODEL
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
protected $guard = 'user';
protected $fillable = [
//fillable cols
];
//change role id data type to integer
protected $casts = [
'role_id' => 'integer',
];
}
Now, where should I insert the condition to check if it's an admin or a user?
You can try it
public function login(UserLoginRequest $request)
{
// Attempt to log the user in
if (!Auth::guard('user')->attempt(['email' => $request->email, 'password' => $request->password])) {
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['status' => 'Incorrect username or password.']);
}
$user = Auth::guard('user')->user();
if ($user->role_id === 0) {
return redirect()->route('user.dashboard');
}
return redirect()->route('user.page');
}
Check below i have edited the method of login:
public function login(UserLoginRequest $request)
{
// Attempt to log the user in
if (Auth::guard('user')->attempt(['email' => $request->email, 'password' => $request->password])) {
// Over here this condition will be true when user is successfully login
// Below is the user data i have printed in that you can check the role of user which is login.
$user = Auth::user();
print_r($user);
}
// if unsuccessful, then redirect back to the login with the form data
if (! User::where('email', $request->email)->where('password', bcrypt($request->password))->first() ) {
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['status' => 'Incorrect username or password.']);
}
}
this is my config/auth.php
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'customers' => [
'provider' => 'customers',
'table' => 'customer_password_resets',
'expire' => 120,
],
'admin' => [
'provider' => 'admins',
'table' => 'admin_password_resets',
'expire' => 60,
],
],
when trying to reset customer password , I get the error 'This token is invalid".
This is my ResetsPassword.php -> reset()
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($response)
: $this->sendResetFailedResponse($request, $response);
}
Here is the password reset request:
A password reset token must be generated prior to posting to the password.update route. Typically, this occurs when the user enters their email address into a form before being sent a password reset link.
For a custom implementation, you may need to generate the token manually.
use Illuminate\Auth\Passwords\PasswordBroker;
// insert a token record into the password reset table
$token = app(PasswordBroker::class)->createToken($customer);
EDIT: The token is returned from the broker as an unhashed value, while at the same time, it is stored in the database as a hashed value. Make sure the unhashed token value is being submitted to the reset() method as the parameter token with no underscore, unlike the CSRF _token.
Also, your Customer model must extend Authenticatable.
class Customer extends Authenticatable
{
// ...
}
I want to create JWT using client model. All the login credentials are saved in the clients table. Here in my Laravel 5.4 application I dont want to have users model. My piece of code is being showing. Now when I am trying to login laravel querying from users table which I don't. I want it from clients table. All the required namespaces I have added top in my controller file. Need help to get a solution.
\Config::set('jwt.user', 'App\Client');
\Config::set('auth.providers.users.model', \App\Client::class);
$credentials = ["username"=>$user_name,"password"=>$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',
],
]);
I think you need to change the providers => users => model => to your custom namespace in config/auth.php
Example
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class, <= change this to your custom namespace
],
],