Laravel - Correct way to catch cURL exceception - php

I am building a simple REST API package using cURL and would like to catch an error and then return a view. I am able to throw an error if I dd($e) but if I try and return a view it just continues with the code after the catch function. Shouldn't PHP kill the process and just go to the login view?
try{
$response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
'username' => $this->username,
'password' => $this->password
]);
} catch(\Illuminate\Http\Client\ConnectionException $e) {
return view('auth.login');
}
If I get a cURL timeout exception I just want to go back to the login page for now. If I put in a bogus IP address obviously it will timeout after 2 seconds, which is what I am testing.
Using Laravel Http client, how can I catch that error and display the auth login view?

Unlike Guzzle, Laravel's HttpClient does not throw errors if the response is > 400.
You should simply use an if statement to check the response status code. See: https://laravel.com/docs/8.x/http-client#error-handling
You can call use the following checks:
// Determine if the status code is >= 200 and < 300...
$response->successful();
// Determine if the status code is >= 400...
$response->failed();
// Determine if the response has a 400 level status code...
$response->clientError();
// Determine if the response has a 500 level status code...
$response->serverError();
So in your case you can simply do something like this:
$response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
'username' => $this->username,
'password' => $this->password
]);
if ($response->failed()) {
return view('your-view')->with([
'message' => 'Failed.',
]);
}

Could you try this please?
try {
$response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
'username' => $this->username,
'password' => $this->password
]);
} catch(\Illuminate\Http\Client\ConnectionException $e) {
return view('auth.login')->with('errorMessage', $e->getMessage());
}
And you can show the error on the frontend, like below;
#if(!empty($errorMessage))
<div class="alert alert-danger"> {{ $errorMessage }}</div>
#endif

It is better if you change your approach in using Laravel's HTTP client,
Move time-intensive tasks - like Http requests - to a job, and run that job in a background queue.
Then, for example, if you wanna use that Http request for authentication, handle your auth logic on that job, don't forget to log and handle exceptions.
afterward, protect your private routes using middlewares and make redirect unauthenticated users to your login page in that middleware.
I hope I could be clear

Related

Laravel Socialite: Laravel\Socialite\Two\InvalidStateException

I want to create social login using Google and Facebook. In the first step, I want to create Google Login. When I select the Google ID and Callback it shows me this error.
Laravel\Socialite\Two\InvalidStateException
http://localhost:8000/customer/login/google/callback?authuser=1&code=4%2F0AX4XfWgtwfNmX20WgsbTWXCO0joa1BrfjX0Iif1jqoxTqvT-AqwRVRgwyJwR95EuFlBx9Q&prompt=consent&scope=email%20profile%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=e7PCC5OnNvtkQ6e7GBquKsTtAjWYiIuMdFZ477Kt
URL :
Route::get('/customer/login/google/callback', 'handleGoogleCallback')->name('callback.to.google');
Controller :
public function handleGoogleCallback() {
try {
$user = Socialite::driver('google')->user();
dd($user);
} catch (\Throwable $th) {
throw $th;
}
}
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => 'http://localhost:8000/customer/login/google/callback',
],
GOOGLE_CLIENT_ID="xxxxxxxxxxxxx-xxxxxxxxxxxxxx.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="GOCSPX-qzx7jVkboSS_xxxxxxxxxxxxx"
How to solve this problem??
Your state is incorrect. state is a parameter that laravel-socialite added to auth request once you did /redirect. Seems like you trying complete auth operation two+ times from just only google.com request. Retry auth from /redirect request.
PS. sorry for my English

Keycloak password policy

So, I have integrated keycloak API endpoints in my Symfony project..
Regarding https://ultimatesecurity.pro/post/password-policy/ we have added 'not username' policy to test on creating new user within the app.
Idea is to delcare specific method with defined endpoint which will do this.
I was checking the documentation and could not find any endpoint that can check for password policy rules --> documentation
Idea for it:
$options = [
'headers' => $this->getAuthJsonHeaders()
];
try {
$endpoint = sprintf('auth/admin/realms/%s/', $this->realm);
$response = $this->request('GET', $endpoint, $options);
return $response;
} catch (\Exception $e) {
$this->exception('Can`t reset user password on Keycloak. ' . $e->getMessage());
}
This is what I get:
when dumping results
To get the list of the password policies being used by the Realm, you should call the following endpoint:
GET <KEYCLOAK_HOST>/auth/admin/realms/<YOUR_REALM>
from the JSON response extract the field:
passwordPolicy
which for instance if you have set Minimum length to 12 and Hashing Iterations to 27500 the passwordPolicy would be "length(12) and hashIterations(27500)"

Flashing appropriate 200 message upon logout, laravel

Using laravel 5.2, I'm hitting a logout listener at the time of logging out that logs the user out of their cognito instance.
This seems to be successful but I"m having a hard time getting a working message to flash on logout, specifically, I want the 200 message response from the auth call below
I have handled the JSON for errors at the endpoint call, but if the part of my try block is successful then I get a 200 response with message "User logged out successfully".
How should I properly flash the 200 message on the sign in screen/redirect after logging out?
try {
$authService = new AuthService();
$authLogout = $authService->logout($logoutToken);
}catch (\Exception $e) {
$response = json_decode($e->getResponse()->getBody()->getContents());
$message = $response->message;
return Redirect::back()->withErrors($message);
}
Add to a session
session(['key' => 'value']);
Retrieve from the session
$value = session('key');
if you use the rest api, you can wrtite this in your logout fonction
public function logout (Request $request)
{
$token = $request->user()->token();
$token->revoke();
$response = 'You have been succesfully logged out!';
return response(["message"=>$response], 200);
}

How stop CognitoIdentityProviderException from returning 500 status code in AWS PHP sdk

I am developing a Web application. In my app, I need to login user to the AWS cognito system. I can log in to the system successfully. But the only problem is when the username and password provided by the user are not valid, my Laravel framework kills the application returning 500 internal server status code. But I want to do something else when the username and password are not valid. I tried using try catch block, but it is not overriding the error. Please, see my code below.
try{
$client = new CognitoIdentityProviderClient([
'version' => 'latest',
'region' => env('AWS_REGION', '')
'credentials' => [
'key' => env('AWS_IAM_KEY', ''),
'secret' => env('AWS_IAM_SECRET', '')
]
]);
$result = $client->adminInitiateAuth([
'AuthFlow' => 'ADMIN_NO_SRP_AUTH',
'ClientId' => COGNITO_APP_CLIENT_ID,
'UserPoolId' => COGNITO_USER_POOL_ID,
'AuthParameters' => [
'USERNAME' => $request->email,
'PASSWORD' => $request->password,
],
]);
//Error thrown here if the username and password are not valid.
//continue
}
catch(Exception $e)
{
//I want to do something here if the error is thrown because of the invalid credentials without killing the app by throwing 500 status code.
}
As you can see in the above code if the user credentials are not valid, the SDK will throw the error. It will kill the app by returning 500 status code. I do not want to stop there. So, I used the try catch block to catch the error and continue in the code. But the try catch block is not catching the error as well.
This is the screenshot.
So, how can I stop the AWS sdk from stopping the application throwing 500 status code?
Finally, I found the solution. Laravel works with namespaces. So, instead of using just Exception in the try catch block, I needed to put "\" as a prefix. So the try catch becomes like this.
try{
//code
}
catch(\Exception $e) //pay attention to the "\"
{
}

Sending more data with Laravel passport oauth/token

So, I'm using Laravel+Passport and so far is working fine.
But, I would like to made a small change to the passport code(well, not in the vendor folder, I hope), once that I would request the User to change it's password in case that he is doing the first login.
So, what I would need is two things (I believe):
1 - How can I add one more info to the oauth/token response? Together with the access_token, I would like to add one column from the DB that is needsNewPassword=true/false.
2 - In case that needsNewPassword is true, then, the app will redirect to another screen, where the user will set a new password. I would set the new password, remove the flag for needsNewPassword and send back a new access_token to the user. The user then, would use only that access_token. How can I regenerate a new access_token?
Thanks for you help! João
Right,
I answering my own question, in case someone needs to do the same. Maybe is not the best way, but is working.What I did is:
Create a new route, like /api/login that points to a method (be sure that is Outside of your middleware "auth", once that it's not sending the token in thi call). E.g: Route::post('/login', 'Auth\LoginController#apiLogin');
in the method, you do a request to the oauth/token and, with the result, you add the fields that you want.
test
function apiLogin(Request $request) {
$tokenRequest = $request->create('/oauth/token', 'POST', $request->all());
$request->request->add([
"client_id" => 'your_client_id',
"client_secret" => 'your_client_secret',
"grant_type" => 'password',
"code" => '*',
]);
$response = Route::dispatch($tokenRequest);
$json = (array) json_decode($response->getContent());
$json['new_value'] = '123456';
$response->setContent(json_encode($json));
return $response
}
This is working for me. In my case, I also have just one app so, my client_id, client_secret, grant_type and code is added in the server side. The client only need to pass username(or email, depends of what you are using) and password and then it will get the access_token and the other info that I want to send as well.
Hope that this helps someone else too.
Cheers,
joao
#joao.sauer
Your own answer is working like a charm, but if you wan't a bit more freedom, you could extend Passport's own AccessTokenController.
A simple example:
use App\Models\User;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Response;
class AccessTokenController extends \Laravel\Passport\Http\Controllers\AccessTokenController
{
public function issueToken(ServerRequestInterface $request)
{
try {
//get username (default is :email)
$username = $request->getParsedBody()['username'];
//get user
$user = User::where('email', '=', $username)->firstOrFail();
//issuetoken
$tokenResponse = parent::issueToken($request);
//convert response to json string
$content = $tokenResponse->getBody()->__toString();
//convert json to array
$data = json_decode($content, true);
if(isset($data["error"]))
throw new OAuthServerException('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
//add access token to user
$user = collect($user);
$user->put('access_token', $data['access_token']);
return Response::json(array($user));
}
catch (ModelNotFoundException $e) { // email notfound
//return error message
}
catch (OAuthServerException $e) { //password not correct..token not granted
//return error message
}
catch (Exception $e) {
////return error message
}
}
}
credits to Messi89:
Laravel Passport - Customize The Token Response
I found a simple solution without need new request, controller or extends, just add parameters to request and call issueToken via app, it can useful for starter:
// in routes/api.php
Route::post('/token',function(Request $request){
$request->request->add([
'grant_type' => 'password',
'client_id' => '2',
'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
]);
return app()->call('\Laravel\Passport\Http\Controllers\AccessTokenController#issueToken');
});
Also can add try...catch block to handle exceptions or add parameters to response before send to client
Route::post('/token',function(Request $request){
$request->request->add([
'grant_type' => 'password',
'client_id' => '2',
'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
]);
try {
$response = app()->call('\Laravel\Passport\Http\Controllers\AccessTokenController#issueToken');
$newResponse = json_decode($response->content());
// Add parameters to response here
$newResponse->user = ['user'=>'user','pass'=>'pass'];
return Response()->json($newResponse);
}catch (Laravel\Passport\Exceptions\OAuthServerException $e) {
if ($e->statusCode() == 400) {
return response()->json(['message' => 'Invalid request. Please enter username and password.'], $e->statusCode());
} else if ($e->statusCode() == 401) {
return response()->json(['message' => 'Your credentials are incorrect. Please try again.'], $e->statusCode());
}
return response()->json('Something went wrong on the server. Please try later.', $e->statusCode());
}
});

Categories