I have installed CakePhp 3.8 and i need use JWT authentication.
I have tryed installing and configuring CakePHP/Authentication (https://book.cakephp.org/authentication/1.1/en/index.html) but can not configure this.
My configuration:
PHP 7.2.19
MySQL 5.7.27
Apache 2.4.29
CakePHP 3.8
Authentication Plugin 1.1
firebase/php-jwt
I followed the guide configurations, and have add at AppController.php
// /src/Controller/Appcontroller.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Authentication.Authentication', [
'logoutRedirect' => '/administrators/login' // Default is false
]);
....
In Application.php
// /src/application.php
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
....
public function getAuthenticationService(ServerRequestInterface $request, ResponseInterface $response)
{
$service = new AuthenticationService();
$service->loadIdentifier('Authentication.JwtSubject');
$service->loadAuthenticator('Authentication.Jwt', [
'returnPayload' => false
]);
return $service;
}
....
public function middleware($middlewareQueue)
{
....
// Add the authentication middleware
$authentication = new AuthenticationMiddleware($this);
// Add the middleware to the middleware queue
$middlewareQueue->add($authentication);
return $middlewareQueue;
}
How i can login for first time and retrive JWT token?
-------------------EDIT-------------------
Thankyou, your solutions workly perfectly.
But now i have CORS problem with Angular FE GET request, befor GET this try one OPTIONS request whit CORS error.
I have this CORS policy in my AppController
// Accepted all CORS
$this->response = $this->response->cors($this->request)
->allowOrigin(['*'])
->allowMethods(['GET','POST','PUT','DELETE','OPTIONS','PATCH']) // edit this with more method
->allowHeaders(['X-CSRF-Token']) //csrf protection for cors
->allowCredentials()
->exposeHeaders(['Link'])
->maxAge(60)
->build();
You'd have to handle that on your own, ie create an endpoint that handles login requests, and upon successful authentication creates a JWT token containing the required identifier.
For username/password authentication for example you can use the Form authenticator and the Password identifier:
$service->loadIdentifier('Authentication.Password');
$service->loadIdentifier('Authentication.JwtSubject');
$service->loadAuthenticator('Authentication.Form', [
'loginUrl' => '/users/login'
]);
$service->loadAuthenticator('Authentication.Jwt', [
'returnPayload' => false
]);
With that example in UsersController create a login() action like this (that's just a very basic, hopefully self-explanatory example), check the authentication status, if valid generate a token, if invalid generate an error:
public function login()
{
if ($this->Authentication->getResult()->isValid()) {
$userId = $this->Authentication->getIdentityData('id');
$token = \Firebase\JWT\JWT::encode(
['sub' => $userId],
\Cake\Utility\Security::getSalt()
);
$status = 200;
$response = [
'token' => $token,
];
} else {
$status = 403;
$response = [
'error' => 'Authentication required',
];
}
return $this
->getResponse()
->withStatus($status)
->withType('json')
->withStringBody(json_encode($response));
}
It probably wouldn't hurt if the cookbook would have a complete example for token authentication.
See also
Authentication Cookbook > Migration from the AuthComponent > Login action
Authentication Cookbook > Quick Start > Checking the login status
Authentication Cookbook > Quick Start > Using Stateless Authenticators with other Authenticators
Related
I'm using external identity provider to authenticate users, created a SPA client (got client_id & client_secret), configured API with audience & scope, so once users authenticated they will get access_token (will be authorized) to access multiple custom micro-services (APIs).
When my custom API receives a request with a bearer Access Token (JWT) the first thing to do is to validate the token. In order to validate JWT I need to follow these steps:
Check that the JWT is well formed (Parse the JWT)
Check the signature. My external identity provider only supports RS256 via the JWKS (JSON Web Key Set) URL (https://{domain}/.well-known/jwks.json), so I can get my public key following this URL.
Validate the standard claims
Check the Application permissions (scopes)
There are a lot of packages/libraries (i.e. https://github.com/tymondesigns/jwt-auth) to create JWT tokens but I can't find any to validate it using those steps above. Could anyone please help to find suitable Laravel/PHP package/library or move me to the right direction in order to achieve my goals (especially point #2).
I did something similar in the past, I don't know if this may help but I'll give it a try. To use a public key, you should download it, put it somewhere on the disk (storage/jwt/public.pem for example) and then link it in the jwt config config/jwt.php with the ALGO (you can see supported algorithms here
'keys' => [
// ...
'public' => 'file://'.storage_path('jwt/public.pem'),
// ...
],
'algo' => 'RS256',
Then, you should have a custom Guard, let's call it JWTGuard:
<?php
namespace App\Guard;use App\Models\User;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWT;class JWTGuard implements Guard
{
use GuardHelpers;
/**
* #var JWT $jwt
*/
protected JWT $jwt;
/**
* #var Request $request
*/
protected Request $request;
/**
* JWTGuard constructor.
* #param JWT $jwt
* #param Request $request
*/
public function __construct(JWT $jwt, Request $request) {
$this->jwt = $jwt;
$this->request = $request;
}
public function user() {
if (! is_null($this->user)) {
return $this->user;
}
if ($this->jwt->setRequest($this->request)->getToken() && $this->jwt->check()) {
$id = $this->jwt->payload()->get('sub');
$this->user = new User();
$this->user->id = $id;
// Set data from custom claims
return $this->user;
}
return null;
}
public function validate(array $credentials = []) { }
}
This should do all your logic of validation, I used a custom user implementation, the class signature was like:
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements AuthenticatableContract {
// custom implementation
}
Finally, you should register the guard in the AuthServiceProvider and in the auth config
public function boot()
{
$this->registerPolicies();
$this->app['auth']->extend(
'jwt-auth',
function ($app, $name, array $config) {
$guard = new JWTGuard(
$app['tymon.jwt'],
$app['request']
);
$app->refresh('request', $guard, 'setRequest');
return $guard;
}
);
}
then allow it in the config
<?php
return [
'defaults' => [
'guard' => 'jwt',
'passwords' => 'users',
],
'guards' => [
// ...
'jwt' => [
'driver' => 'jwt-auth',
'provider' => 'users'
],
],
// ...
];
You can then use it as a middleware like this:
Route::middleware('auth:jwt')->get('/user', function() {
return Auth::user();
}
Does this sound good to you?
In the end I've used the Auth0 SDK for Laravel - https://auth0.com/docs/quickstart/backend/laravel/01-authorization. Nice and clean solution.
Hello I try to setup cakephp for rest client (with login auth) for ionic (angular) app.
Ok, I configure CakePhp like this setup tutorial and for example I get data that:
public function projects()
{
$projects = $this->Projects->find('all');
$this->set([
'projects' => $projects,
'_serialize' => ['projects']
]);
}
and get data via $.http in Ionic
This work perfectly but I try to configure cake auth for mobile client.
I don't know how I do this. In my Resttest Controller i wrote code where set session Id for ionic app, but ionic not cache this session and I think is my cakePhp code is wrong.
CakePHP controller:
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Controller\Component\RequestHandlerComponent;
// use Cake\View\Helper\SessionHelper;
class ResttestController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadModel('Projects');
$this->loadModel('Task');
$this->loadModel('User');
$this->viewBuilder()->layout(false);
$this->response->header('Access-Control-Allow-Origin', '*');
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => $this->name,
'action' => 'login',
// '_ext'=>'json'
],
'authorize'=>['Controller'],
]);
// Basic setup
$this->Auth->config('authorize', ['Controller']);
}
public function login(){
header('Access-Control-Allow-Headers: Content-Type, x-xsrf-token');
$this->response->header('Access-Control-Allow-Methods', '*');
if($this->request->is('post')){
$postdata = file_get_contents("php://input");
$d = json_decode($postdata);
if($this->Auth->user()){
$response =array("success"=>2,'msg'=>'logged After');
}
// $d = $this->request->data;
if(!$d->password || !$d->login){
$response = array("success"=>0,'msg'=>'n');
}
$u = $this->User->find()
->where(['email'=>$d->login])
->first();
if($u){
$salt = $u->salt;
$input_password = crypt($d->password, '$2y$12$' . $salt);
$password = $u->password;
if($password == $input_password){
$tok = self::getToken();
$u->token = $tok;
$out = $this->Auth->setUser($u);
$response = array("success"=>1,'msg'=>'logged', 'token'=>$tok, 'out'=>$out,'sadga'=>$this->Auth->identify,'asf'=>$this->Auth,'adsafsfq'=>$d,'$this->request'=>$this->request,'$this->response'=>$this->response,'apache_request_headers '=>apache_request_headers());
}else{
$response = array("success"=>0,'msg'=>'n');
}
}else{
$response = array("success"=>0,'msg'=>'n');
}
}else{
$response =array("success"=>0,'msg'=>'n');
}
$this->set([
'response' => $response,
'_serialize' => ['response']
]);
}
private function getToken(){
return crypt(sha1(md5(uniqid(rand(), true))));
}
public function testAuth(){
}
}
This code return session and user data but not work and I think is not good method for mobile auth. Do you have any idea for auth for cakephp ?
How I make my code more security ?
When we split application to backend api and frontend, we should consider backend as stateless application. This mean you can't use session for auth.
Instead you should implements auth/login and auth/register rest endpoints that will return some token for example JWT.
For cakephp2 you can easely find such library: https://github.com/t73biz/cakephp2-jwt-auth
Use this authenticator instead of Form when you configure Auth component.
From front end side pass token like it is described in the plugin.
I have a question regarding Authentication in Laravel 5.x. I’ve been specifically looking at tymondesigns/jwt-auth and irazasyed/jwt-auth-guard packages to do the JSON web token authentication token handling in my Laravel application.
I am not using a local database whatsoever, nor do I want to. I have environment variables set up in .env for my API’s URL, USERNAME & PASSWORD. The Guzzle PHP HTTP client is doing the trick just fine, connecting and returning data between the API and my application as needed.
However, I need to set up Authentication within my Laravel instance. I run into problems here, and the auth is wanting a DB connection.
$token = JWTAuth::attempt($credentials)
Here's the exception:
PDOException in Connector.php line 55:
SQLSTATE[HY000] [14] unable to open database file
How can I make use of JWT without using a database?
How can I COMPLETELY shut-off database connections within Laravel?
Thanks.
UPDATE:
Using tymon/jwt-auth, I've set things up within the routes, Kernel, Middleware, etc.
I created a "claim" successfully, but I need to create the token by encoding the "payload."
$this->username = $request->username;
$sub = $this->username;
$iat = time();
$jti = md5($sub . $iat);
$aud = env('APP_URL');
$this->claims = [
'sub' => $sub,
'iat' => $iat,
'exp' => time() + (2 * 7 * 24 * 60 * 60),
'nbf' => $iat,
'iss' => 'khill',
'jti' => $jti,
'aud' => $aud,
];
$payload = JWTFactory::make($this->claims);
How do I get the custom token?
You should define a custom Authentication Provider and set it in config/jwt.php.
Example of provider
Put this class anywhere you like.
namespace MyNamespace;
use Tymon\JWTAuth\Providers\Auth\AuthInterface;
class MyCustomAuthenticationProvider implements AuthInterface
{
public function byCredentials(array $credentials = [])
{
return $credentials['username'] == env('USERNAME') && $credentials['password'] == env('PASSWORD');
}
public function byId($id)
{
// maybe throw an expection?
}
public function user()
{
// you will have to implement this maybe.
}
}
Example of configuration
In the providers array in config/jwt.php, change this:
'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter',
to this:
'auth' => 'MyNamespace\MyCustomAuthenticationProvider',
Other considerations
Using the env() function anywhere is not good practice. It's better to use it in your config files, and then use the config() function anywhere else.
You may need to reimplement also the User Provider.
JWTAuth::attempt() won't help you with this, because it hits the database for you behind the scenes. You need some other way to check the environment credentials.
Add a custom method to a class somewhere which will do that for you or pass the credentials against the API you are hitting with Guzzle.
Code example:
public function authenticate($username, $password)
{
if(!$username === env('USERNAME') or !$password === env('PASSWORD')) {
// return a message that the user could not be authenticated or false.
}
// Generate the JWT token here and store it somewhere.
}
As a quick fix I decided to implement the following custom code...
1) Created custom middleware to handle the logic.
class CustomMiddleware
{
protected $loginPath = 'login';
public function handle($request, Closure $next) {
$logged_in = $request->session()->get('logged_in');
if (!$logged_in) {
return redirect()->guest('login')->with('flag','1');
}
return $next($request);
}
}
2) Added a reference to the middleware class.
class Kernel extends HttpKernel
{
protected $routeMiddleware = [
'custom' => \App\Http\Middleware\CustomMiddleware::class,
];
}
3) Added it to routes.php.
Route::group(['middleware' => ['custom']], function () {
// Add routes here
}
yes.
you can create jwt token without database using tymondesigns/jwt-auth package...
for that you have to use jwt::encode method...
let me explain ...
first you have to put your credential in .env file...
then i am recomending you to use custom claims ...
after that you can create jwt token using below code ...
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$factory = JWTFactory::customClaims($customClaims);
$token = JWTAuth::encode($payload);
for further details you can refer below link
wiki
I have a problem with POST cURL request to my application.
Currently, I'm building RESTFUL registration function using laravel 5.
The routes for this is example is
localhost:8000/user/create
I pass value using cURL function on terminal
curl -d 'fname=randy&lname=tan&id_location=1&email=randy#randytan.me&password=randytan&remember_token=Y&created_at=2015-03-03' localhost:8000/auth/register/
And this is my routes.php
Route::post('user/create', 'UserController#create');
And this is my function to store the registration user
public function create()
{
//function to create user.
$userAccounts = new User;
$userAccounts->fname = Request::get('fname');
$userAccounts->lname = Request::get('lname');
$userAccounts->id_location = Request::get('id_location');
$userAccounts->email = Request::get('email');
$userAccounts->password = Hash::make(Request::get('password'));
$userAccounts->created_at = Request::get('created_at');
$userAccounts->save();
return Response::json(array(
'error' => false,
'user' => $userAccounts->fname . " " . $userAccounts->lname
), 200);
}
Executing the cURL syntax above, I'm getting this error TokenMismatchException
Do you have any ideas?
Because I'm implementing middleware only in my few urls, and this cURL registration url is not tight into any authentication mechanism.
Thanks before.
In Laravel 5 (latest version) you can specify routes you want to exclude in /app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'rest-api/*', # all routes to rest-api will be excluded.
];
}
Hope this helps.
Laravel 5 enforces CSFR token authentication in middleware by default.
you can disable CSFR on selected route Here is the link
or you can try some of these solutions. Hope so it will help.
changing your csfr token method /app/Http/Middleware/VerifyCsrfToken.php
public function handle ($request, Closure $next)
{
if ( !$request->is("api/*"))
{
return parent::handle($request, $next);
}
return $next($request);
}
In my case, i needed to add the route on api.php instead of web.php
I have been trying to implement the JWT-Aut in Laravel's Dingo API package, but I am stuck at users Login part, I have checked the official docs for auth but cant figure out, here is what I have done till now.
Protected the routes
Route::api(['version' => 'v1', 'protected' => true], function () {
Route::resource('users', 'UserController');
});
Added JWT auth provider in dingo/config
'jwt' => function ($app) {
return new Dingo\Api\Auth\JWTProvider($app['tymon.jwt.auth']);
}
Installed the JWT-Auth from Github docs
Tried Login using below sample code from JWT-Auth docs using Postman but getting {token : false}
Route::post('auth/login', function () {
$credentials = Input::only('email', 'password');
if ( ! $token = JWTAuth::attempt($credentials) )
{
// return 401 error response
}
return Response::json(compact('token'));
});
If someone can guide how I can login logout, & signup user and make a request with Authorization: Bearer <token> will be very helpful.
If someone could share your auth controller for same will be lifesaver :)
It looks like you are not returning a response when the credentials are incorrect - so the token will be equal to false in that case.
Here is an example:
Route::post('auth/login', function () {
$credentials = Input::only('email', 'password');
if ( ! $token = JWTAuth::attempt($credentials) )
{
// return the 401 response
return Response::json(['error' => 'invalid_credentials'], 401);
}
return Response::json(compact('token'));
});