I'm using https://github.com/tymondesigns/jwt-auth on my lumen application.
Here's my composer.json
"laravel/lumen-framework": "5.3.*",
"tymon/jwt-auth": "^1.0#dev",
I've read a of tutorials on how to install. Some of which are:
https://scotch.io/tutorials/role-based-authentication-in-laravel-with-jwt
https://laravelista.com/posts/json-web-token-authentication-for-lumen
I am able to make it work on my local and successfully return the token. But the problem is that instead of using eloquent on the provider that fetches data from database.sqlite, I want to use database as my driver.
With that, I have set
'providers' => [
'users' => [
'driver' => 'database',
'table' => 'user_table',
// 'driver' => 'eloquent',
// 'model' => App\User::class,
],
],
on my config/auth.php
Since it is now connection thru a database, it now uses the DatabaseUserProvider.php
I need to modify some codes though.
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, app('hash')->make($user->getAuthPassword()));
}
Notice that I added a app('hash')->make() when validating the password.
It then inject the retrieved user into the GenericUser object.
/**
* Get the generic user.
*
* #param mixed $user
* #return \Illuminate\Auth\GenericUser|null
*/
protected function getGenericUser($user)
{
if (! is_null($user)) {
return new GenericUser((array) $user);
}
}
Since it is on the GenericUser object, it gives an error of:
Argument 1 passed to Tymon\JWTAuth\JWT::fromUser() must be an instance of Tymon\JWTAuth\Contracts\JWTSubject, instance of Illuminate\Auth\GenericUser given
In order to fix this, I have to "hack" it by removing the JWTSubject injection on every method under the tymon\jwt-auth\src\JWT.php
Is there a better way to clean this up?
You can easily fix it by making your authenticating user implement JWTSubject, which is the most right thing to do.
class GenericUser implements JWTSubject {
[...]
}
But, since you're dealing with a Laravel native implementation, I'd suggest you to extend it and implement JWTSubject, instead of going everywhere in jwt-auth's code and remove the type-hints.
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.
So I use a Service Class (extends from TYPO3\CMS\Core\Authentication\AuthenticationService) to authenticate our Frontend Users using OAuth2. These Services are automatically instantiated and called via Typos own Middleware: FrontendUserAuthenticator.
In this class I used to save data from the authentication result to $GLOBALS['TSFE']->fe_user using setKey('ses', 'key', 'data'), which seems is not possible anymore since v10. How would I go about still doing this?
The documentation is sparse
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/9.4/Deprecation-85878-EidUtilityAndVariousTSFEMethods.html
https://docs.typo3.org/m/typo3/reference-coreapi/10.4/en-us/ApiOverview/Context/Index.html
I've tried the following:
constructor injecting the TSFE using DI
class FrontendOAuthService extends AuthenticationService
{
public function __construct(TypoScriptFrontendController $TSFE) {
=> LogicException: TypoScriptFrontendController was tried to be injected before initial creation
changing the Middlewares order to have it instantiate before the Auth Middleware
(packages/extension_name/Configuration/RequestMiddlewares.php)
return [
'frontend' => [
'typo3/cms-frontend/tsfe' => [
'disabled' => true,
],
'vendor/extension_name/frontend-oauth' => [
'target' => \TYPO3\CMS\Frontend\Middleware\TypoScriptFrontendInitialization::class,
'before' => [
'typo3/cms-frontend/authentication',
],
'after' => [
'typo3/cms-frontend/eid',
'typo3/cms-frontend/page-argument-validator',
],
],
],
];
=> UnexpectedValueException: Your dependencies have cycles. That will not work out.
instantiating the TSFE myself
/** #var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** #var DealerService $dealerService */
$lang = $site->getDefaultLanguage();
$siteLanguage = $objectManager->get(SiteLanguage::class, $lang->getLanguageId(), $lang->getLocale(), $lang->getBase(), []);
/** #var TypoScriptFrontendController $TSFE */
$TSFE = $objectManager->get(
TypoScriptFrontendController::class,
GeneralUtility::makeInstance(Context::class),
$site,
$siteLanguage,
GeneralUtility::_GP('no_cache'),
GeneralUtility::_GP('cHash')
);
=> the $TSFE->fe_user is an emptystring ("")
using the UserAspect
/** #var Context $context */
$context = GeneralUtility::makeInstance(Context::class);
$feUser = $context->getAspect('frontend.user');
$feUser->set...
=> Aspects are read-only
adding vars to the user data in the getUser method of the AuthenticationService
(packages/extension_name/Classes/Service/FrontendOAuthService.php)
public function getUser()
{
$user = allBusinessCodeHere();
$user['my_own_key'] = 'myData';
return $user;
=> is not propagated to the UserAspect(frontend.user) nor the $TSFE->fe_user
I'm out of ideas guys.
I had a similar problem when i wanted to use redirects with record links.
I ended up disabling the original redirect middleware and adding my own with a mocked version of tsfe.
The extension can be found here:
https://github.com/BenjaminBeck/bdm_middleware_redirect_with_tsfe
Late to the party, but I had the same issue and was able to solve it:
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/10.0/Breaking-88540-ChangedRequestWorkflowForFrontendRequests.html states:
Storing session data from a Frontend User Session / Anonymous session
is now triggered within the Frontend User
(frontend-user-authenticator) Middleware, at a later point - once the
page was generated. Up until TYPO3 v9, this was part of the
RequestHandler logic right after content was put together. This was
due to legacy reasons of the previous hook execution order. Migration
Consider using a PSR-15 middleware instead of using a hook, or
explicitly call storeSessionData() within the PHP hook if necessary.
In my MyAuthenticationService extends AbstractAuthenticationService in method getUser() I set $_SESSION['myvendor/myextension/accessToken'] to the token received by the external oauth service. In my SaveSessionMiddleware I save this token to the FrontendUserAuthentication object using setKey() which by then is available:
EXT:myextension/Configuration/RequestMiddlewares.php
return [
'frontend' => [
'myvendor/myextension/save-session-middleware' => [
'target' => \MyVendor\MyExtension\Middleware\SaveSessionMiddleware::class,
'after' => [
'typo3/cms-frontend/authentication',
],
]
]
];
EXT:myextension/Classes/Middleware/SaveSessionMiddleware.php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
class SaveSessionMiddleware implements MiddlewareInterface {
/**
* #param ServerRequestInterface $request
* #param RequestHandlerInterface $handler
* #return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
if (!empty($_SESSION['myvendor/myextension/accessToken'])) {
$this->getFrontendUserAuthentication()->setKey(
'ses',
'myvendor/myextension/accessToken',
$_SESSION['myvendor/myextension/accessToken']);
unset($_SESSION['myvendor/myextension/accessToken']);
}
return $handler->handle($request);
}
private function getFrontendUserAuthentication(): FrontendUserAuthentication {
return $GLOBALS['TSFE']->fe_user;
}
}
Laravel 5.5
I want to change direction of api token that used in TokenGaurd so,
i created a custom guard named CafeTokenGaurd extends TokenGuard, i define __construct function into it like what i want, something like this:
public function __construct(UserProvider $provider, Request $request) {
parent::__construct($provider, $request);
$this->inputKey = 'api_key'; // I want changing this part
$this->storageKey = 'api_key';
}
Now i want to define api_key from relation with users table like this:
device_user table -> token
i want to define specific tokens for each devices user have, and i want to set api key input and storage key to this column in pivot table between users and devices,
how i should this?!
Thanks
Since the version Laravel 5.7.28, You can simply set up in config/auth.php.
'guards' => [
'api' => [
'driver' => 'token',
'input_key' => 'token', // The input name to pass through
'storage_key' => 'token', // The column name to store in database
'provider' => 'users',
],
],
Because you need to change how the user is retrieved out of the database, you actually need to create and use a custom UserProvider, not a custom Guard. You'll only need the custom guard if you feel like renaming the input key or storage key from api_token.
So, you'll need a new custom UserProvider class that knows how to retrieve your user with the given credentials (token), and you'll need to tell Auth to use your new custom UserProvider class.
First, assuming you're still using Eloquent, start by creating a new UserProvider class that extends the base EloquentUserProvider class. In this example, it is created at app/Services/Auth/MyEloquentUserProvider.php. In this class, you will need to override the retrieveByCredentials function with the details on how to retrieve the user with the provided token.
namespace App\Services\Auth;
use Illuminate\Auth\EloquentUserProvider;
class MyEloquentUserProvider extends EloquentUserProvider
{
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {
return;
}
// $credentials will be an array that looks like:
// [
// 'api_token' => 'token-value',
// ]
// $this->createModel() will give you a new instance of the class
// defined as the model in the auth config for your application.
// Your logic to find the user with the given token goes here.
// Return found user or null if not found.
}
}
Once you've created your class, you need to let Auth know about it. You can do this in the boot() method on your AuthServiceProvider service provider. This example will use the name "myeloquent", but you can use whatever you want (except "eloquent" and "database").
public function boot()
{
$this->registerPolicies();
Auth::provider('myeloquent', function($app, array $config) {
return new \App\Services\Auth\MyEloquentUserProvider($app['hash'], $config['model']);
});
}
And finally, you need to tell Auth to use your new myeloquent user provider. This is done in the config/auth.php config file.
'providers' => [
'users' => [
'driver' => 'myeloquent', // this is the provider name defined above
'model' => App\User::class,
],
],
You can read more about adding custom user providers in the documentation here.
I'm using Tymon JWT to generate my token in Laravel. I have followed the guide in Tymon's github site carefully to add my custom claims like so:
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
JWTAuth::attempt($credentials, $customClaims);
I managed to generate a token after authenticating the user, but when I decode the token with JWT decoder, I only see the default claims, but not my custom claim.
You are using Tymon JWT version 1.0.0 maybe?
From Github Tymon JWT
For version 0.5.* See the WIKI for documentation.
Documentation for 1.0.0 is coming soon, but there is an unfinished guide here
Use
JWTAuth::customClaims(['foo' => 'bar'])->attempt(['login' => '', 'password' => '']);
You can using:
$store = [
'id' => 1,
'name' => 'Store1'
];
$token = JWTAuth::customClaims(['store' => $store])->fromUser($user);
And get info:
$storeData = JWTAuth::getPayload()->get('store');
I was able to add custom claims by putting them the getJWTCustomClaims method on my JWTSubject.
Example: if you want to specify a guard for your user class and put that information inside the token, you do the following :
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
/**
* #return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* #return array
*/
public function getJWTCustomClaims()
{
return [
'guard' => 'admins', // my custom claim, add as many as you like
];
}
}
Be aware not to add too many custom claims because they will increase the size of the token.
Here is the version of Tymon-jwt that i use : "tymon/jwt-auth": "dev-develop#f72b8eb as 1.0.0-rc.3.2"
Hope this helps
Heyo!
I know it's a common problem people having problems with custom providers and web service authentication. I'm spending hours trying to figure out how to do that but I'm almost freaking out.
So, the thing is: I'm using the Symfony Firewalls with a custom UserProvider and a AbstractGuardAuthenticator as well. The problem is in the loadUserByUsername($username) function inside the UserProvider implementation.
For security reasons I can't retrieve the user password from Parse (my web service), and the loadUserByUsername($username) function asks for that. Even in the documentation about how to create a custom user provider using web services they are retrieving the user password from the database.
So what's the solution in that case? What can I do when I don't have access to the user password?
My current code is something like that:
$app['app.authenticator'] = function () {
return new Authenticator($app);
};
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/login/$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
'guard' => array(
'authenticators' => array(
'app.authenticator'
),
),
'users' => function () use ($app) {
return new UserProvider($app);
},
)
);
The Authenticator.php is quite big code because extends the AbstractGuardAuthenticator class. But I'm basically using this one from Symfony docs. The only thing Is that I'm sending to the UserProvider class the username AND the password as well, because that way I can check if the user and password are right. Like this:
public function getUser($credentials, UserProviderInterface $userProvider) {
return $userProvider->loadUserByUsername($credentials);
}
And my UserProvider class is the default one, I'm just checking inside the loadUserByUsername function if the credentials comming from my Authenticator are right. Something like this:
public function loadUserByUsername($credentials) {
$encoder = new BCryptPasswordEncoder(13);
try {
$user = ParseUser::logIn($credentials['username'], $credentials['password']);
} catch (ParseException $error) {
throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
}
return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}
The problem is: after the login (everything with the login is working fine), Silex calls the loadUserByUsername function in every page which needs to be secured, but just sending the username parameter.
So basically, I don't know what to do guys. I'm really trying to figure out how to get this thing working.
Thanks for your help!
I have a similar implementation and this issue is well known. In my user provider I have the methods loadUserByUsername($username) and refreshUser(UserInterface $user). Since I have the same issue like you, I don't check the user in loadUserByUsername but simple return a new Object with only the username in it to not disturb the flow. loadUserByUsername doesn't make sense for external APIs, so I simply jump over it. The method refreshUser is either beeing called on every request, this is usefull.
In your AbstractGuardAuthenticator you have the method createAuthenticatedToken, which returns an token. There you should have the full authentificated user:
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using.
*
* #param UserInterface $user
* #param string $providerKey
*
* #return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
//do login stuff
//save password in user
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
);
}
}
Then, I would't use loadUserByUsername but refreshUser instead. Both are called on every request:
/**
* Don't use
* #codeCoverageIgnore
* #param string $username
* #return User
*/
public function loadUserByUsername($username)
{
return new User($username, null, '', ['ROLE_USER'], '');
}
/**
* Refresh user on every subrequest after login
* #param UserInterface $user
* #return User
*/
public function refreshUser(UserInterface $user)
{
$password = $user->getPassword();
$username = $user->getUsername();
//login check
}