I'm working with Laravel Passport for the first time. I'm building an API that will be consumed by a mobile application.
For the authentication, this is what i'm doing:
public function login(Request $request)
{
$loginData = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if( $loginData->fails() )
{
return response()->json(['error' => $loginData->errors()], 401);
}
if( Auth::attempt(['email' => request('email'), 'password' => request('password')]) )
{
$data = [
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'username' => $request->email,
'password' => $request->password,
'scope' => '',
];
$request = Request::create('/oauth/token', 'POST', $data);
return app()->handle($request);
}
else
{
return response()->json(['error' => 'Invalid credentials'], 401);
}
}
Successful login returns the access token as such:
{
"token_type": "Bearer",
"expires_in": 31622400,
"access_token": "access_token",
"refresh_token": "refresh_token"
}
Now the aim is to use this access token by passing it to the header of all routes that need authentication.
For example in my routes/api.php file, i have this route:
Route::post('/login', 'API\AuthController#login');
Route::apiResource('/tasks', 'API\TaskController')->middleware('auth:api');
In my TaskController, all the methods in it need to be passed a header that contains the access token so as to authenticate the request. This is where my question is. I can't seem to find a resource so far that explains how to do this. So in every request i need to pass something like this to the header of the request:
Accept: application/json
Authorization: Bearer access_token
This is my TaskController index method. How do i pass the access token in here?
public function index()
{
//how do i capture the access token in here so that i can pass it to the request header?
return TaskResource::collection(auth()->user()->tasks()->latest()->paginate(4));
}
Is there a default Passport method that can be used to pass the access token to required requests or how can it be done?
use Illuminate\Http\Request;
public function index(Request $request)
{
$bearerToken = $request->header('Authorization'); // will return "Bearer access_token" string
}
Then you will need to get access_token from "Bearer access_token" string.
If you need access_token to retrieve user, you can use $user = $request->user() to get user.
using $request->bearerToken(); method you can get the request token.
use Illuminate\Http\Request;
public function index(Request $request)
{
echo $request->bearerToken();
}
Related
I have created a REST API with LARAVEL SANCTUM. I have tested api on postman and it works as expected but when mobile developer uses it on ionic app, it returns for login "TOKEN MISMATCH".
Here is my API route
Route::post('/register', [ApiController::class, 'register']);
Route::post('/login', [ApiController::class, 'login']);
Here is the ApiController for login
public function login(Request $request){
$fields = $request->validate([
'email' => 'required|string|email|max:255',
'password' => 'required|string|min:8'
]);
//validate login parameters
//check email
$user = User::where('email', $fields['email'])->first();
//check password
if(!$user || !Hash::check($fields['password'], $user->password)){
return response([
'message' => 'Invalid Credentials'
], 401);
}
$token = $user->createToken('myapptoken')->plainTextToken;
//return $user->createToken($request->device_name)->plainTextToken;
$response = [
'user' => $user,
'token' =>$token,
];
return response($response, 201);
}
EndPoint: https://findajob.ng/api/login
Email:johndeo1#gmail.com
Password: 12345678
This might not be a problem from backend, but in other-hand, if everything work in postman, you might try to:
Change support_credentials in config\cors to true.
Add withCredential header to front-end.
For my job, I have to make an app which never use the database directly. I have to only request an API even for the connection.
So... There is my problem. I try to make an auth with a user that i'm getting from API but i'm always redirect to the login page.
My API auth :
public function login(Request $request)
{
$credentials = request(['use_username', 'password']);
$this->guard()->factory()->setTTL(config('jwt.ttl') * 12);
if (!$token = $this->guard()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$user = auth()->user();
return $this->respondWithToken($user, $token);
}
protected function respondWithToken($user, $token)
{
$cookie = cookie('jwt', $token, 60 * 12); // 12h
return response()->json([
'user' => $user,
'token' => $token
])->withCookie($cookie);
}
My web app auth :
public function login(Request $request)
{
$response = Http::post(env('app_url').'/api/auth/login', $request->only('use_username', 'password'));
$cookie = cookie('jwt', $response['token'], 60 * 12); // 12h
$user = ( new User() )->forceFill( $response['user'] );
if (Auth::login($user)) {
$request->session()->regenerate();
return redirect('/');
}
flash('error')->error();
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
My API guard (default guard) :
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false
]
The goal is to authenticate the user in the API, get a JWT token and auth the user in the web app with the same user that got in the API. After, all my request to the API have to use the JWT token get during the login... Maybe with a HttpOnly cookie ?
Well, i can't connect my user to the web app, i'm always unauthenticate and redirect to th elogin form, can someone help me ?
I'm using tymon/jwt-auth library with PHP 8
I made a UserController which generats an accessToken when a user registered succesfully on a page.
class UserController extends Controller
{
/**
* Login Method: in here we call Auth::attempt with the credentials the user supplied.
* If authentication is successful, we create access tokens and return them to the user.
* This access token is what the user would always send along with all API calls to have access to the APIs.
* Register Method: like the login method, we validated the user information,
* created an account for the user and generated an access token for the user.
*/
public function login()
{
$credentials = [
'email' => request('email'),
'password' => request('password')
];
if (Auth::attempt($credentials)) {
$success['token'] = Auth::user()->createToken('MyApp')->accessToken;
return response()->json(['success' => $success]);
}
$status = 401;
$response = ['error' => 'Unauthorized'];
return response()->json($response, $status);
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
]);
if ($validator->fails()) {
return response()->json(['error' => $validator->errors()], 401);
}
$input = $request->all();
$input['password'] = bcrypt($input['password']);
$user = User::create($input);
$success['token'] = $user->createToken('MyApp')->accessToken;
$success['name'] = $user->name;
return response()->json(['success' => $success]);
}
public function getDetails()
{
return response()->json(['success' => Auth::user()]);
}
}
My problem is that I want to remove the token when the user logs out but I dont know how to remove the access token from the user.
logout function in my UserController
public function logout()
{
Auth::user()->tokens->each(function($token, $key) {
$token->delete();
});
return response()->json([
'message' => 'Logged out successfully!',
'status_code' => 200
], 200);
}
When I test it with postman with the GET route: http://127.0.0.1:8000/api/logout. Am I missing something?
UPDATE
Here s my api.php file:
Route::resource('categories', 'App\Http\Controllers\CategoryController');
Route::post('register', 'App\Http\Controllers\UserController#register');
Route::post('login', 'App\Http\Controllers\UserController#login');
/**
* We can group the routes we need auth for
* under common middleware. It secures our routes
*/
Route::group(['middleware' => 'auth:api'], function(){
Route::get('logout', 'App\Http\Controllers\UserController#logout');
});
I am testing it in postman using the route: http://127.0.0.1:8000/api/logout and passing the Bearer token, which I get from the login request, as a value.
It should be POST Request instead of GET request, because your deleting/making change to the database.
The route should look like this:
Route::POST('logout', 'App\Http\Controllers\UserController#logout')->middleware('auth:api');
And the logout method in in UserController should be.
public function logout()
{
auth()->user()->tokens->each(function ($token, $key) {
$token->delete();
});
return response()->json([
'message' => 'Logged out successfully!',
'status_code' => 200
], 200);
}
In your logout function, it should expire the token, not delete it
public function logout(Request $request)
{
$request->user()->token()->revoke();
return response()->json([], Response::HTTP_NO_CONTENT);
}
OR if you wanna expire all his tokens:
use Illuminate\Support\Facades\Auth;
public function logout(Request $request)
{
$userTokens = Auth::user()->tokens();
foreach($userTokens as $token)
{
$token->revoke();
}
}
I want to allow only authenticated users to access some API routes. I use the default Laravel authentication system. After the default login, I want to be able to access a route, but I get the "Unauthenticated" message.
So, after login, I am redirect to the home route which uses the HomeComponent file. Here, using axios, I am making a call to the step API route where I am trying to get the id of the authenticated user, but instead I receive an error message. What am I doing wrong?
api.php
Route::middleware('auth:api')->group(function () {
Route::get('application/step', ['as' => 'application.step', 'uses' => 'ApplicationController#step']);
});
ApplicationController.php
public function step() {
print_r(auth()->user());
die('---');
// code to get authenticated user step
return json_encode(array('step' => 7));
}
LoginController.php
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
$user = User::where('email', $request->email)->firstOrFail();
if ($user && !$user->isAdmin()) {
if (Auth::attempt(['email' => $request->email, 'password' => $request->password], true)) {
$token = $user->createToken('TokenName')->token;
$token->save();
return redirect()->route('home');
}
else {
return back()->withInput($request->only('email'));
}
}
return back()->withInput($request->only('email'))->withErrors(['denied' => 'You are not allowed to access this page.']);
}
HomeComponent.vue
...
getStep() {
axios.get("/api/application/step")
.then((response) => {
this.step = response.data.step;
})
.catch((err) => {
console.log('Cannot get step', err);
});
}
auth:api middleware only work with Passport. This auth:api middleware check valid access token.
And I think you are not using passport for login
composer require laravel/passport
In your case you can only use auth middleware instead auth:api
I'm trying to figure out how to test my Passport-driven logout function, which looks like this:
public function logout() {
$accessToken = auth()->user()->token();
$refreshToken = DB::table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true
]);
$accessToken->revoke();
return response()->json(['status' => 200]);
}
I am using the Passport actingAs helper in setting up the response.
Passport::actingAs(
$user,
['read write']
);
$response = $this->post('/logout')
->assertStatus(200);
The test fails, as the code gives a 500 error, as auth()->user()->token() is ... empty-ish? $accessToken->id is 0, for example, which it shouldn't be, which means the code fails.
What I'm not sure about is if this is expected behavior because of how Passport's actingAs helper works and I can't actually test the logout function, or if there's something wrong with my logout function. Halp!
My routes:
Route::post('login', 'Auth\LoginController#login');
Route::group(['middleware' => 'auth:api'], function() {
Route::post('logout', 'Auth\LoginController#logout');
});
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
JsonApi::register('v1', ['namespace' => 'Api'], function (Api $api, $router) {
$api->resource('training-locations');
$api->resource('courses');
});
ETA: My login function, if it's helpful:
public function login(Request $request, Client $client){
$this->validateLogin($request);
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$response = $client->post(config('app.url') . '/oauth/token', [
'form_params' => [
'client_id' => config('auth.proxy.client_id'),
'client_secret' => config('auth.proxy.client_secret'),
'grant_type' => config('auth.proxy.grant_type'),
'username' => $request->email,
'password' => $request->password,
'scopes' => '[read write]'
]
]);
if ($response->getStatusCode() === 200) {
$this->clearLoginAttempts($request);
return response($response->getBody()->getContents(), $response->getStatusCode());
}
$this->incrementLoginAttempts($request);
return response($response->getBody()->getContents(), $response->getStatusCode());
}
In order to investigate if this is actually an issue, I tried hitting the endpoint via Postman. It gets into the function, but it does not find the auth()->user(). I tried some other endpoints using the same route group, and it was able to find the auth()->user() with them. What might cause it to go missing like that?
It's a little late, but maybe someone will find it helpful. In order to test the logout function you have provided you'll need to pass the token as authorization header.
$response = $this->post('/logout', [], ['Authorization' => 'Bearer ' . $token])
->assertStatus(200);
I'm not sure why, but the access token can't be retrieved with $user->token() when using actingAs, but it can be retrieved with $user->tokens()->first()
.