How to show password reset link in response - php

I have the code below which sends the password reset link and it works fine. What I want to do is show password reset link or token in response. (Yes, I know it's dangerous.)
$status = Password::sendResetLink(
$request->only('email'),
function ($user, $token) {
(\DB::table('password_resets')
->updateOrInsert(
['email' => $user->email],
[
'token' => md5($token)
]
))
? $user->sendPasswordResetNotification(md5($token))
: null;
}
);
if ($request->expectsJson()) {
if ($status === Password::RESET_LINK_SENT) {
return response()->json(['message' => __($status)], 200);
} else {
return response()->json(['message' => __($status)], 422);
}
}
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
I changed the code as below but I am getting 500 Internal Server Error. What's wrong?
$status = Password::sendResetLink(
$request->only('email'),
function ($user, $token) use (&$token) {
(\DB::table('password_resets')
->updateOrInsert(
['email' => $user->email],
[
'token' => md5($token)
]
))
? $user->sendPasswordResetNotification(md5($token))
: null;
}
);
if ($request->expectsJson()) {
if ($status === Password::RESET_LINK_SENT) {
return response()->json(['message' => __($status), 'link' => md5($token)], 200);
} else {
return response()->json(['message' => __($status)], 422);
}
}
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}

One way would be like this:
$token = DB::table('password_resets')->latest('created_at')->first()->token;
You get the last record inserted in the table (by sorting created_at column) which is the latest password reset and you can get the token from that.
return response()->json(['message' => __($status), 'link' => $token], 200);
And you getting the 500 Server Error could be from the addition of use (&$token) in the second code. use is the way to use the variables out of the function scope in the function and you don't have any variable named $token (Don't confuse with the $token in the function). And you're using the use to use a function variable outside of its scope which won't work.
Also you could set APP_DEBUG to true (NOT IN PRODUCTION MODE) to see the reason for 500 error.

Related

I can't change error message in my project

I have a Laravel 8 project. I want to change magic auth error message. And I did updated my code like this.
'This code has already been used' I replaced this message with this in the context of the code 'You will get a link in your email inbox every time you need to log in or register. Keep in mind that each link can only be used once.'
OLD AuthController.php
public function magicauth(Request $request)
{
$auth = app('firebase.auth');
$email = $request->email;
$oobCode = $request->oobCode;
$exits = User::where('email', $email)->first();
if(!is_null($exits))
{
if(is_null($exits->firebaseUserId))
{
$fUser = $auth->createUser([
'email' => $exits->email,
'emailVerified' => true,
'displayName' => $exits->name,
'password' => ($exits->email . $exits->id),
]);
$firebaseID = $fUser->uid;
$exits->update([
'firebaseUserId' => $firebaseID
]);
}
}
try
{
$result = $auth->signInWithEmailAndOobCode($email, $oobCode);
$firebaseID = $result->firebaseUserId();
$user = User::where('firebaseUserId', $firebaseID)->first();
if(is_null($user))
{
return view('auth.messages', ['message' => 'User not found']);
}
if($user->role_id != 3)
{
return view('auth.messages', ['message' => 'User is not creator']);
}
Auth::login($user);
return redirect()->route('home');
} catch (\Exception $e) {
return view('auth.messages', ['message' => 'This code has already been used.']);
}
return redirect()->route('login');
}
NEW AuthController.php
public function magicauth(Request $request)
{
$auth = app('firebase.auth');
$email = $request->email;
$oobCode = $request->oobCode;
$exits = User::where('email', $email)->first();
if(!is_null($exits))
{
if(is_null($exits->firebaseUserId))
{
$fUser = $auth->createUser([
'email' => $exits->email,
'emailVerified' => true,
'displayName' => $exits->name,
'password' => ($exits->email . $exits->id),
]);
$firebaseID = $fUser->uid;
$exits->update([
'firebaseUserId' => $firebaseID
]);
}
}
try
{
$result = $auth->signInWithEmailAndOobCode($email, $oobCode);
$firebaseID = $result->firebaseUserId();
$user = User::where('firebaseUserId', $firebaseID)->first();
if(is_null($user))
{
return view('auth.messages', ['message' => 'User not found']);
}
if($user->role_id != 3)
{
return view('auth.messages', ['message' => 'User is not creator']);
}
Auth::login($user);
return redirect()->route('home');
} catch (\Exception $e) {
return view('auth.messages', ['message' => 'You will get a link in your email inbox every time you need to log in or register. Keep in mind that each link can only be used once.']);
}
return redirect()->route('login');
}
But when I try now, I see that the message has not changed. How can I fix this?
Please follow below steps:
If you haven't done it yet, delete or rename the old AuthController class, use only new one, with new message.
Make sure routes going to the methods in the new controller
Run composer dump-autoload.
If the problem still persist I'd check whether some kind of cache mechanism is enabled in php, like opcache.

Is there any way to change response of auth:api , A laravel api_token

I am creating API with Default api-authentication
I am using laravel 6.x
Its run when i generate on user register and pass generated token with request.
But
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
My api route file as below
Route::middleware('auth:api')->post('/listUser', 'ApiController#listUser');
I have manage my points as below
For Point 1
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
I made change in App/Exceptions/handler.php
Modify render function as below
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->is('api/*')) {
return response()->json(['error' => 'Not Found'], 404);
}
//return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
It workrs well because i have an api based routes
My api route look likes
// Request with Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => ['api','auth:api'] ], function () {
Route::post('/myProfile', 'ApiController#myProfile');
});
// Request without Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => 'api'], function () {
Route::post('/register', 'ApiController#register');
});
For Point 2
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
For that i have created a function checkValidations in ApiController and check user id is associated with particular token or not as below:
In that function i check in way that
Check for all validation passed from called method
Match token associated with user id then return success
else return invalid token response
Function Code
public function checkValidations($required = [], $request = [])
{
$validator = Validator::make($request->all(), $required);
if ($validator->fails()) {
$this->response[] = array(
'status' => 'false',
'response_msg' => implode(",",$validator->messages()->all()),
);
return array('response' => $this->response);
} else if(isset($request['api_token']) && auth('api')->user()->id ==
$request['id']) {
return 'success';
} else {
$this->response[] = array(
'status' => 'false',
'response_msg' => 'Invalid token',
);
return array('response' => $this->response);
}
}
And call that checkValidations from any function and can reuse it as
public function myProfile(Request $request)
{
$validation = [
'id' => 'bail|required|exists:users',
'api_token' => 'bail|required|min:60|max:60'
];
if( $this->checkValidations($validation, $request) == 'success'){
$this->response[] = array(
'status' => 'true',
'response_msg' => 'Success',
'data' => auth('api')->user()
);
}
return array('response' => $this->response);
}
May be there is many other best way to manage that points, but i didn't found, so i manage in above ways.
You can configure a custom response in the Authenticate middleware. e.g.
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($guard === 'api') {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
You can do this by extending the TokenGuard, with your custom logic. Or you can create a new Middleware, which asserts that user authenticated by API matches the passed user ID.
I just verified the kind of exception if is related with authentication and then the URL( as API guard use '/api' just verify it) and fire the response.
if($exception instanceof \Illuminate\Auth\AuthenticationException){
if($request->is('api/*')){
return response()->json([
'success' => false,
'message' => 'User not logged'
]);
}
}
I made the below change in app/Exceptions/Handler.php.
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Not Authorized'], 404);
}
return redirect()->guest(route('login'));
}
Add use Illuminate\Auth\AuthenticationException in the document. Also, do not forget to add X-Requested-With:XMLHttpRequest to your request header. (Or Headers in postman)
return redirect()->guest(route('login')); is to redirect you to login page when you are not using the APIs.

How to check user input in laravel

I have a column input named Address, and how to check, if user input like null, 'null' and '' , the return response will be error. I have make it, but it not working.
This is my code:
$address = $request->input('address');
if ($address == null)
{
return response()->json(['message'=>'no data','success'=>0]);
}
elseif($address == '')
{
return response()->json(['message'=>'no data','success'=>0]);
}
elseif($address == 'null')
{
return response()->json(['message'=>'no data','success'=>0]);
}
else
//process
}
Check out laravel validation: here.
For example:
$request->validate([
'address' => 'required'
])
If you want to check if input is filled you can do this:
$request->filled('address')
Check docs for Retriving inputs.
in your case:
if(!$request->filled('address')){
return response()->json(['message'=>'no data','success'=>0]);
}
You may use the empty function, which would return FALSE if var exists and has a non-empty, non-zero value. Otherwise returns TRUE. This implies the following conditions considered as empty:
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
Snippet for your reference:
$address = $request->input('address');
if(empty($address)){
return response()->json(['message' => 'no data','success' => 0]);
}
Use laravel Validation, for more information checkout laravel doc link
$validator = Validator::make($request->all(), [
'address' => 'required'
]);
if ($validator->fails()) {
return response()->json(['error' => $validator->errors()->first()], 422);
}
use validation facades : https://laravel.com/docs/5.7/validation
use Illuminate\Support\Facades\Validator;
public function MyFunction(Request $request){
try {
$validator = Validator::make($request->all(), [
'address' => 'required',
]
);
if ($validator->fails()) {
$response=array('status'=>'error','errors'=>implode(',', $validator->errors()->all()));
return response()->json($response, 400);
}else{
// validation success
}
} catch (\Exception $e) {
$response=array('status'=>'error','result'=>0,'errors'=>'Internal Server Error');
return response()->json($response, 500);
}
}
}

Laravel 5.7 Auth with email/username and password using tymon/jwt

I am using jwt-auth for my Laravel 5.7 app. Currently, I'm allowing the client to enter email and password as user credentials.
However, I also want to let the client to enter their username in place of their email. So they have 2 choices: email or username.
How can I do that in my code?
My UserController#authenticate
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json([
'status' => 401,
'message' => 'invalid_credentials',
], 401);
}
} catch(JWTException $e) {
return response()->json([
'status' => 500,
'message' => 'token_creation_failed',
], 500);
}
return response()->json(compact('token'));
}
Thanks in advance
In your AuthController, add this to the login method;
public function login()
{
$loginField = request()->input('login');
$credentials = null;
if ($loginField !== null) {
$loginType = filter_var($loginField, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
request()->merge([ $loginType => $loginField ]);
$credentials = request([ $loginType, 'password' ]);
} else {
return $this->response->errorBadRequest('What do you think you\'re doing?');
}
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
This is how I handled mine where user can choose either email or phone to login.
public function login(Request $request)
{
//validate incoming request
$this->validate($request, [
'email_phone' => 'required|string',
'password' => 'required|string',
]);
try {
$login_type = filter_var( $request->email_phone, FILTER_VALIDATE_EMAIL ) ? 'email' : 'phone';
// return $login_type;
$credentials = [$login_type => $request->email_phone, 'password'=>$request->password];
if (! $token = Auth::attempt($credentials)) {
return response()->json($this->customResponse("failed", "Unauthorized"), 401);
}
return $this->respondWithToken($token);
} catch(JWTException $e) {
return response()->json($this->customResponse("failed", "An error occured, please contact support.", $user), 500);
}
}

Check for active user state with laravel

This is pretty standard login function and validation that works nicely. But I also want to check that the user is active. I have set up a column in my users table with 'active' set to either 0 or 1.
public function post_login()
{
$input = Input::all();
$rules = array(
'email' => 'required|email',
'password' => 'required',
);
$validation = Validator::make($input, $rules);
if ($validation->fails())
{
return Redirect::to_route('login_user')
->with_errors($validation->errors)->with_input();
}
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
);
if (Auth::attempt($credentials))
{
// Set remember me cookie if the user checks the box
$remember = Input::get('remember');
if ( !empty($remember) )
{
Auth::login(Auth::user()->id, true);
}
return Redirect::home();
} else {
return Redirect::to_route('login_user')
->with('login_errors', true);
}
}
I've tried something like this already:
$is_active = Auth::user()->active;
if (!$is_active == 1)
{
echo "Account not activated";
}
But this can only be used within the 'auth attempt' if statement and at that point the users credentials(email and pass) are already validated. So even if the users account if not active at this point they are already logged in.
I need a way to return validation to let them know they still need to activate their account and check if their account is set at the same time their email and pass are being checked.
Filters are the way to go. It's easy and clean to solve this problem, see my example below.
Route::filter('auth', function()
{
if (Auth::guest())
{
if (Request::ajax())
{
return Response::make('Unauthorized', 401);
}
else
{
return Redirect::guest('login');
}
}
else
{
// If the user is not active any more, immidiately log out.
if(Auth::check() && !Auth::user()->active)
{
Auth::logout();
return Redirect::to('/');
}
}
});
Can't you use something like this:
if (Auth::once($credentials))
{
if(!Auth::user()->active) {
Auth::logout();
echo "Account not activated";
}
}
Just make the active field one of the confirmations. You can do this:
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
'active' => 1
);
if (Auth::attempt($credentials))
{
// User is active and password was correct
}
If you want to specifically tell the user they are not active - you can follow it up with this:
if (Auth::validate(['username' => $input['email'], 'password' => $input['password'], 'active' => 0]))
{
return echo ('you are not active');
}
A better solution might be to create an Auth driver that extends the Eloquent Auth driver already in use and then override the attempt method.
Then change your auth config to use your driver.
Something like:
<?php
class Myauth extends Laravel\Auth\Drivers\Eloquent {
/**
* Attempt to log a user into the application.
*
* #param array $arguments
* #return void
*/
public function attempt($arguments = array())
{
$user = $this->model()->where(function($query) use($arguments)
{
$username = Config::get('auth.username');
$query->where($username, '=', $arguments['username']);
foreach(array_except($arguments, array('username', 'password', 'remember')) as $column => $val)
{
$query->where($column, '=', $val);
}
})->first();
// If the credentials match what is in the database we will just
// log the user into the application and remember them if asked.
$password = $arguments['password'];
$password_field = Config::get('auth.password', 'password');
if ( ! is_null($user) and Hash::check($password, $user->{$password_field}))
{
if ($user->active){
return $this->login($user->get_key(), array_get($arguments, 'remember'));
} else {
Session::flash('authentication', array('message' => 'You must activate your account before you can log in'));
}
}
return false;
}
}
?>
In your login screen, check for Session::get('authentication') and handle accordingly.
Alternatively, allow them to log in but don't let them access any pages other than one that offers a link to resend the activation email.
This is what I do:
if (\Auth::attempt(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']], $request->has('remember'))) {
if (\Auth::once(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']])) {
if (!\Auth::user()->FlagActive == 'Active') {
\Auth::logout();
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'Active' => 'You are not activated!',
]);
}
}
return redirect('/');
}
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'EmailWork' => $this->getFailedLoginMessage(),
]);

Categories