Greetings and Regards
I am designing an api with Laravel and using a passport for an app.
I'm having trouble logging in using the login endpoint.
The problem I encountered is as follows:
I will send the necessary parameters to the server including
client_id - grant_type - username - password - client_secret - scope:
{{server_url}}/api / v1 / login
In the login method:
After evaluating entries I send a request to oauth/token to receive theta token.
I get the error below.
GuzzleHttp\Exception\ClientException: Client error: POST
http://divar.local/oauth/token resulted in a 400 Bad Request
response: {"error":"invalid_grant","error_description":"The provided
authorization grant (e.g., authorization code, resource owner
(truncated...)
The contents of the login method
public function login()
{
$validator = Validator::make(request()->all(), [
'mobile_number' => 'required',
'password' => 'required',
]);
if ($validator->fails()) {
$response['errors'] = $validator->errors();
return response()->json($response, 401);
}
$clientOauthData = Client::where('password_client', 1)->where('name', 'Laravel Password Grant Client')->first();
$http = new \GuzzleHttp\Client;
$response = $http->post(env('APP_URL') . '/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => $clientOauthData->id,
'client_secret' => $clientOauthData->secret,
'username' => request('mobile_number'),
'password' => request('password'),
'scope' => '',
],
'headers' => [
'Accept' => 'application/json'
]
]);
$response = json_decode((string)$response->getBody(), true);
return response()->json($response, 200);
}
Request headers :
Request body :
This is how I always proceed this request.
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|mobile_phone',
'password' => 'required|string',
]);
$http = new \GuzzleHttp\Client;
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'your client ID',
'client_secret' => 'secret ID,
'username' => $request->mobile_phone,
'password' => $request->password,
// 'scope' => '',
],
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if ($e->getCode() == 401) {
return response()->json(['message' => 'This action can\'t be perfomed at this time. Please try later.'], $e->getCode());
} else if ($e->getCode() == 400) {
return response()->json(['message' => 'These credentials do not match our records.'], $e->getCode());
}
return response()->json('Something went wrong on the server. Please try letar.', $e->getCode());
}
}
Related
I have code in Laravel that allows users to log in using their OTP, and it will authenticate the login by using the OTP. For it to work on mobile, I need an access_token to show it as a response.
this is my code
public function phone_login_checker(Request $request){
$validator = Validator::make($request->all(), [
'phone' => 'required',
'otp_no' => 'required',
]);
if ($validator->fails()) {
return $this->sendError('', ['errors' => $validator->errors()]);
}
if ($user = DB::table('users')
-> where('otp_no', '=' , $request->input('otp_no'))
-> where('phone', '=' , $request->input('phone'))
->exists())
{
$userdetail = DB::table('users')-> where('phone', '=' , $request->input('phone'))->first();
Auth::loginUsingId($userdetail ->id, $remember = true);
$token = $userdetail->remember_token;
return response()->json([
'status'=>1,
'access_token' => "", // Here, I have to add the access token
'user' =>Auth::user(),
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
else{
return response()->json([
'action' => 'showError',
'type' => 'error',
'message' => 'otp is incorrect'
]);
}
}
how can i have access_token?
I get the following error when logging in with my mobile phone:
Uncaught (in promise) Error: Request failed with status code 422
how can i fix this?
My Controller
public function login(Request $request)
{
$req = Request::create(route('passport.token'), 'POST', [
//'grant_type' => 'password',
'client_id' => 2,
'client_secret' => '326g3KM3giN4o3UHITByxPLHaZlWzqfZbWs0vWLd',
'phone_number' => $request->phone_number,
//'password' => $request->password,
]);
$response = app()->handle($req);
if ($response->status() == 400) {
return response()->json([
'message' => ''
]);
} else if ($response->status() == 401) {
return response()->json([
'message' => ''
]);
}
return $response;
I also redefined functions in the user model
public function findForPassport($identifier) {
return $this->where('phone_number', $identifier)->first();
}
public function validateForPassportPasswordGrant($password)
{
return true;
}
I've read your description. So, you have to store phone and code in the table users. To simplify it, you can store code in DB field password as an encrypted value.
$user->phone_number = $request->phone_number;
$user->password = bcrypt($code);
And then during login you can use your own code:
$req = Request::create(route('passport.token'), 'POST', [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => '326g3KM3giN4o3UHITByxPLHaZlWzqfZbWs0vWLd',
'phone_number' => $request->phone_number,
'password' => $request->code,
]);
$response = app()->handle($req);
When I execute login, access token is not created, in the log files I get the following errors:
local.ERROR: The resource owner or authorization server denied the request.
and
(InvalidArgumentException(code: 0): The JWT string must have two dots at.
I made a request using the insomnia program and everything works fine there, the access token appears in the database
My function login:
public function login(Request $request)
{
$req = Request::create(route('passport.token'), 'POST', [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'lvDjvx5UgzeYJxQMjjLMSFL3psqXj2iuw2xLL6Yh',
'phone_number' => $request->phone_number,
'password' => $request->password,
]);
$response = app()->handle($req);
if ($response->status() == 400) {
return response()->json([
'message' => 'Н',
]);
} else if ($response->status() == 401) {
return response()->json([
'message' => '',
]);
}
return $response;
}
try this code:
public function login(Request $request)
{
$req = Request::create(route('passport.token'), 'POST', [
'grant_type' => 'password',
'client_id' => Your client_id,
'client_secret' => 'Your client_secret ',
'username' => $request->username,
'password' => $request->password
]);
$response = app()->handle($req);
if ($response->status() === 400) {
return response()->json([
'message' => 'Н',
]);
}
if ($response->status() === 401) {
return response()->json([
'message' => '',
]);
}
return $response;
}
In model User
public function findForPassport($identifier) {
return $this->where('phone_number', $identifier)->first();
}
Make sure "password_client" column is true in database for the client you're using.
If it's a "personal_access_token" it cannot provide password grant_type response. They are used to get client_credentials grant_type response.
You can find that out in your oauth_clients table.
I hope the answer will help you
I'm trying to test my login endpoint where a successful response would return the access_token among other things.
I'm using RefreshDatabase, so I changed the login method on the controller to retrieve the client_secret via a DB call. I tested with a dd() and I can confirm that the client_secret changes on each phpunit run in the terminal. The credentials are correct and the API endpoint works - just not when it's run via a test. For example, I have the passport tables set up on my mysql server and I can login successfully when running Postman. It's only when trying to run a test do I get a 401 error.
Here is my AuthTest
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class AuthTest extends TestCase
{
use RefreshDatabase;
/**
* #test
*/
public function a_user_receives_an_access_token()
{
\Artisan::call('passport:install');
$user = factory('App\User')->create();
$response = $this->json('POST', '/api/login', [
'username' => $user->email,
'password' => 'password'
]);
$response
->assertJson([
'access_token' => true
]);
}
}
routes/api.php
Route::post('login', 'AuthController#login');
AuthController#login:
public function login(Request $request) {
$http = new \GuzzleHttp\Client;
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => '2', //config('services.passport.client_id')
'client_secret' => DB::table('oauth_clients')->where('id', 2)->pluck('secret')[0], //config('services.passport.client_secret'),
'username' => $request->username,
'password' => $request->password
]
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if ($e->getCode() == 400 || $e->getCode() == 401) {
return response()
->json([
'status' => $e->getCode(),
'message' => 'Your email and/or password are incorrect',
'expanded' => $e->getMessage()
]);
}
return response()
->json([
'status' => $e->getCode(),
'message' => $e->getMessage()
]);
}
}
I took a look at this question and the accepted answer: How to test authentication via API with Laravel Passport?
I am unable to use the following
public function setUp() {
parent::setUp();
\Artisan::call('migrate',['-vvv' => true]);
\Artisan::call('passport:install',['-vvv' => true]);
\Artisan::call('db:seed',['-vvv' => true]);
}
This results in an error:
PHP Fatal error: Declaration of Tests\Feature\AuthTest::setUp() must be compatible with Illuminate\Foundation\Testing\TestCase::setUp()
Edit: I just added
public function setUp() :void {
parent::setUp();
\Artisan::call('migrate',['-vvv' => true]);
\Artisan::call('passport:install',['-vvv' => true]);
\Artisan::call('db:seed',['-vvv' => true]);
}
but the problem still persists
Edit again:
If I test the oauth route directly, it passes.
public function testOauthLogin() {
$oauth_client_id = 2;
$oauth_client = OAuthClient::findOrFail($oauth_client_id);
$user = factory('App\User')->create();
$body = [
'username' => $user->email,
'password' => 'password',
'client_id' => $oauth_client_id,
'client_secret' => $oauth_client->secret,
'grant_type' => 'password',
'scope' => '*'
];
$this->json('POST','/oauth/token',$body,['Accept' => 'application/json'])
->assertStatus(200)
->assertJsonStructure(['token_type','expires_in','access_token','refresh_token']);
}
But my custom endpoint that uses guzzle fails. I do not know why
Edit again:
I think the issue is with Guzzle, but I'm not sure. I found another implementation of what I'm trying to do, which is the following:
public function login(Request $request) {
$request->request->add([
'username' => $request->username,
'password' => $request->password,
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'scope' => '*'
]);
$response = Route::dispatch(Request::create(
'oauth/token',
'POST'
));
}
The above works.
I'm using laravel with JWT Auth to connect my laravel project to mobile, this is my api controller at laravel
public function login(Request $request) {
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// all good so return the token
$user = Sentry::authenticate($credentials, false);
return response()->json([
'code' => '200',
'message' => 'success',
'last_updated' => $user->updated_at->format("Y-m-d\TH:i:s\Z"),
'data' => [
'id' => $user->id,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email,
'username' => $user->username,
'token' => $token
]
]);
}
but how to set the credentials using email or username with this JWT?
If you are using "tymon/jwt-auth" package, then you can just pass the user object to the JWTAuth class and bam!, you can get the JWT token in return which you can use to make user go through the app.
$user = User::where('email', $email)
->orWhere('username', $username)
->first();
$token = null;
if (!$token = JWTAuth::fromUser($get_info)) {
return $this->respondInternalError( 'Can\'t generate the JWT token right now, try again later!', 'object', 400);
}
return response()->json([
'code' => '200',
'message' => 'success',
'last_updated' => $user->updated_at->format("Y-m-d\TH:i:s\Z"),
'data' => [
'id' => $user->id,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email,
'username' => $user->username,
'token' => $token
]
]);
You can login using email or using the following format
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);
}
}