I have created a custom request form for login validation, It works fine if I put email and password field that's what I set for rules, email and password is required, but the problem is, I can't use the errors data,
/**
* Signin Controller
*
* #param \App\Http\Requests\Auth\Signin $request
*/
public function signin(Signin $request)
{
if ($request->validated()) {
return response()->json(['hello' => 'hi']);
} else {
return response()->json(['error' => $request->errors()]);
}
}
This is the Request form
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
class Signin extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function messages()
{
return [
'email.required' => 'Email field is required.'
];
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function rules()
{
return [
'email' => 'required',
'password' => 'required'
];
}
}
Now when I try to make a post request to the signin controller, if email and password included then its return a json response, but if I only use email or password then it response a 405 method not allowed I'm expecting a json response with error data like errors()
As I understood, failedValidation would help you
use Illuminate\Http\JsonResponse;
class Signin extends FormRequest
{
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json($validator->errors(), JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
}
}
This return the JSON object with 422 error(default error code for validation)
I'm using Laravel api with a resource controller.
My api route
Route::apiResource('todos', TodoController::class)->middleware('auth:api');
Controller
public function index(TodoRequest $request): JsonResponse
{
$response = new Response();
// $withDeleted = $request->has('withDeleted') ? true : false;
$todos = $this->todoService->getAllTodosWithLists($request->query('withDeleted'));
$response->setMessage(ServerMessages::TODOS_RETRIEVE_SUCCESS);
return response()->json(
$response->build($todos),
$response->getCode($todos)
);
}
class TodoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
* Must match with FrontEnd fields
*
* #return array
*/
public function rules(): array
{
return [
'content' => 'required'
];
}
/**
* Get custom messages for validator errors.
*
* #return array
*/
public function messages(): array
{
return ['required' => 'The :attribute must not be left blanked.'];
}
}
My problem is everytime I add the TodoRequest in my parameter's it always return a response redirecting back to the default Laravel homepage.
Note: I am passing a query parameter inside my $request i.e withDeleted and probably will add more filters. This app was originally made in Lumen which perfectly well, and I did some migration to the latest Laravel 9 framework.
Here's a code snippet from laravel in-built RegisterController.
public function __construct(){
$this->middleware('guest'); //What does it actually do?
}
I know that it's a good practice to define middleware in the
controller constructor but I need to know what
$this->middleware('guest') actually does and what parameter (where
?) it sets.
Second question: within the same controller (RegisterController), we use RegisterUser (defined in namespace Illuminate\Foundation\Auth) but it seems we never use it throughout the controller (overriding methods or attributes). I'm a little bit confused. Thnaks in advance!
class RegisterController extends Controller
{
use RegistersUsers; //?????
/**
* Where to redirect users after registration.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
1) $this->middleware(...) called in a constructor of a controller is only adding what ever value is in ... to an array named middleware on the controller. That is it at that point. Nothing is "ran" at that point. You can see this functionality in the Controller your Controllers extend from, Illuminate\Routing\Controller.
Later when the Router needs to dispatch the Request it will build a middleware stack to pass the request through by gathering the middleware which includes asking the instance of the controller for its defined middleware via getMiddleware.
public function getMiddleware()
{
return $this->middleware;
}
2) The trait is including functionality and variables into your controller. There are routes pointing to some of these methods on the controller.
You could take all that code and paste it into your controller and it would be the same thing basically.
If you need to change any functionality of that controller or customize it you will end up redefining some of those methods or adjusting the variables.
I'm setting up an authentication route with my API.
I am using laravel 5.5 with tymondesigns/jwt-auth 1.0.0-rc.1 and Postman to interact with the API.
The authentication route/method seems to work:
/**
* Authenticates a json request, generating a token.
*
* #param Request $request
* #return JsonResponse
*/
public function authenticate(Request $request)
{
// grab credentials from the 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.',
'detail' => 'Please use your email and password to generate a token.'
],
401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(
[
'error' => 'Could not create token',
'detail' => 'There was an internal problem and your token could not be created.'
], 500
);
}
// all good so return the token
return response()->json(compact('token'));
}
A Postman API post request returns (what seems to be) a valid response, For example:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4"
}
To begin with, for basic testing purposes, I am feeding this to an action using GET, with the following suffix:
?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4
In order to test this, if I do the following:
public function globalObjects(Request $request): JsonResponse {
var_dump(JWTAuth::parseToken()->authenticate(), JWTAuth::getToken()); exit;
// ... later code that never gets reached
}
I get the following:
bool(false) object(Tymon\JWTAuth\Token)#809 (1) { ["value":"Tymon\JWTAuth\Token":private]=> string(384) "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4" }
.. as in:
I receive the token
it does not find the user
Items of note:
my primary key is id, but it is a UUID, so a binary(16)... and thus:
I set the identifier to be:'identifier' => 'email'
Following request: here's \config\jwt.php
return [
'secret' => env('JWT_SECRET', 'AqAWUTYISA56lrl2vcRtZQn4M4zk9onl'),
'ttl' => 60,
'refresh_ttl' => 20160,
'algo' => 'HS256',
'user' => 'App\User',
'identifier' => 'email',
'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
'providers' => [
'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter',
'jwt' => 'Tymon\JWTAuth\Providers\JWT\Namshi',
'auth' => 'Tymon\JWTAuth\Providers\Auth\Illuminate',
'storage' => 'Tymon\JWTAuth\Providers\Storage\Illuminate',
],
];
Thanks
Here is the solution I use in my API works perfectly well.
First rewrite the class Tymon\JWTAuth\Providers\Auth\Illuminate::class
<?php
namespace Scryba\Code\Laravel\Providers\Auth\Jwt;
use Tymon\JWTAuth\Contracts\Providers\Auth;
use Illuminate\Contracts\Auth\Guard as GuardContract;
class Illuminate implements Auth
{
/**
* The authentication guard.
*
* #var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Constructor.
*
* #param \Illuminate\Contracts\Auth\Guard $auth
*
* #return void
*/
public function __construct(GuardContract $auth)
{
$this->auth = $auth;
}
/**
* Check a user's credentials.
*
* #param array $credentials
*
* #return bool
*/
public function byCredentials(array $credentials)
{
return $this->auth->once($credentials);
}
/**
* Authenticate a user via the id.
*
* #param mixed $id
*
* #return bool
*/
public function byId($id)
{
//you can see i added hex2bin($id)because i save my UUID primary key as
//binary(16)
return $this->auth->onceUsingId(hex2bin($id));
}
/**
* Get the currently authenticated user.
*
* #return mixed
*/
public function user()
{
return $this->auth->user();
}
}
Then update \config\jwt.php file to
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
//'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
'auth' => Scryba\Code\Laravel\Providers\Auth\Jwt\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
in my routes file
////// Protected methods (requires Authenticatication)
$api->group(
[
'middleware' => ['api.auth'],
'providers' => ['jwt'],
],function($api){
$api->resource('cars', 'CarController', ['only' => [
'index' ,'show'
]]);
});
So in your case leave the source file \vendor\tymon\jwt-auth\src\JWTAuth.php as is and write your code as below in your custom class if applicable.
/**
* Authenticate a user via the id.
*
* #param mixed $id
*
* #return bool
*/
public function byId($id)
{
$id_text = $this->getPayload()->get('sub');
$uuid_helper = new UuidHelper();
$id = $uuid_helper->textIdToId($id_text);
return $this->auth->onceUsingId($id);
}
You token is received but not parsed correctly you need to check for token parsing success.
if (! $auth = JWTAuth::parseToken();) {
throw Exception('JWTAuth unable to parse token from request');
}
dd(\Auth::id());
i suggest you go to your kernal.php and add following lines in protected $routeMiddleware array
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
Then go to your routes and use it like middleware to authenticate like this
Route::group(['middleware' => 'jwt.auth'], function () {
// Your routes here that you want to protect
Route::get('foo', function () {
return 'Hello World';
});
]):
The answer is : "this is not compatible with this feature in it's current state without extending it yourself".
I hope this helps anyone else using UUIDs as a primary key. You can't do it without editing the vendor items or extending... But, this is an easy fix.
I will be raising this with the package author and hopefully finding a more permanent solution. However, here is a workaround:
Background:
UUIDs are stored as binary(16) id columns in the database.
This is not human readable/text friendly.
To get around this, id_text fields exist as auto generated mysql fields
note - you should never search using these (it stresses the db badly)
These are text readable and thus.. can be easily used in forms etc
I created a UuidHelper to allow for easy translation
Problem in JWT:
The authentication function tries to decode an id_text as an id
obviously, this always fails
In file \vendor\tymon\jwt-auth\src\JWTAuth.php
/**
* Authenticate a user via a token.
*
* #return \Tymon\JWTAuth\Contracts\JWTSubject|false
*/
public function authenticate()
{
$id_text = $this->getPayload()->get('sub');
$uuid_helper = new UuidHelper();
$id = $uuid_helper->textIdToId($id_text);
if (! $this->auth->byId($id)) {
return false;
}
return $this->user();
}
The documentation isn't the clearest, so I assumed 'identifier' => 'email' would sidestep this problem... turns out it doesn't. I will feed this back to the authors
Modifying the core was just an exercise... I am pretty sure this class can be extended, and I will be attempting this shortly. Editing sources is of course sub-optimal most cases.
However - I hope this digging will help people understand the problem.
I'm using custom authentication. Added user status(enable/disable) check in retrieveByCredentials function of my custom provider. Now how can I differentiate the error, whether its coming because user enter wrong credentials or because user is disabled?
So far I looked at following function sendFailedLoginResponse, but there is no way to differentiate.
Any suggestions how can I achieve this?
I've approached this in the following way:
/*
LoginController.php
*/
/**
* Override default login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'username';
}
/**
* Override default validation of the user login request.
*
* #param \Illuminate\Http\Request $request
*
* #return void
*/
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => [
'required',
'min:4',
'max:30',
'regex:/^[\S]*$/',
Rule::exists('users')->where(function ($query)
{
$query->where('active', 1);
})
],
'password' => 'required|min:6|max:100'
]);
}
Substituting out whatever the name and expected value of your field is for "active" will enable you to validate depending on whether or not users are active/enabled. You mentioned you've already done this but in this case the validation error will also be the validation message for the "exists" rule. In my application I don't actually need to care why a user failed login but checking the validation message may, I suppose, be enough in your case?