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
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;
}
}
I am creating an API using API-Platform and have set up my user entity etc using the standard symfony security bundle (https://symfony.com/doc/current/security.html#retrieving-the-user-object)
I have the login working with REST at {url}/api/login using JWT but I cannot see any way of sending my login details with GraphQL
The API-platform documentation shows how to set up security and how to setup GraphQL separately but doesn't really show how to combine them.
https://api-platform.com/docs/core/graphql
https://api-platform.com/docs/core/fosuser-bundle
How do I make the login accessible in GraphQL?
Currently, I only have the createUser updateUser and deleteUser mutations, I assume I would need an authenticateUser one?
Yes, you'll need a custom mutation for the login.
Assuming you are using the API Platform standard docs for the API, you are using JWT to authenticate your calls, you need a UserMutationResolver for auth:
<?php
namespace App\Resolver;
use ApiPlatform\Core\GraphQl\Resolver\MutationResolverInterface;
use App\Entity\User;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Doctrine\ORM\EntityManagerInterface;
final class UserMutationResolver implements MutationResolverInterface
{
public function __construct(
private UserPasswordEncoderInterface $userPasswordEncoder,
private JWTTokenManagerInterface $JWTManager,
private EntityManagerInterface $em,
)
{}
/**
* #param User|null $item
*
* #return User
*/
public function __invoke($item, array $context)
{
// Mutation input arguments are in $context['args']['input'].
if ($context["info"]->fieldName == 'loginUser') {
$userRepository = $this->em->getRepository("App:User");
$user = $userRepository->findOneBy(['email' => $item->getEmail()]);
if ($this->userPasswordEncoder->isPasswordValid($user, $item->getPlainPassword())) {
$token = $this->JWTManager->create($item);
$user->setToken($token);
}
return $user;
}
}
}
Then you add that custom mutation to the User entity. Be sure to add the names of the auto-generated mutations/queries or they will disappear (item_query, create, update, delete, collection_query). You'll also need to disable some of the stages, since this is a mutation Api Platform will try and save this as a new user, which we don't want, so as you see below, 'write' => false and 'validate' => false
// api/src/Entity/User.php
// imports etc .
// ...
#[ApiResource(
normalizationContext: ["groups" => "user:read"],
denormalizationContext: ["groups" => "user:write"],
attributes: [
'write' => true,
],
graphql: [
'item_query',
'create',
'update',
'delete',
'collection_query',
'login' => [
'mutation' => UserMutationResolver::class,
'write' => false,
'validate' => false,
'args' => [
'email' => ['type' => 'String!', 'description'=> 'Email of the user ' ],
'password' => ['type' => 'String!', 'description'=> 'Password of the user ' ]
]
],
],
iri:"http://schema.org/Person",
)]
#[UniqueEntity(fields: ["email"])]
class User implements UserInterface
{
// ...
This will create a mutation that you can access like this:
mutation {
loginUser(input: {email:"test1#test.com", password:"password"}) {
user {
token
}
}
}
and the result should look something like this:
{
"data": {
"loginUser": {
"user": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MTgzNjM1NDQsImV4cCI6MTYxODM2NzE0NCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdDFAdGVzdC5jb20ifQ.pSoAyNcaPa4MH2cxaAM4LJOGvirHfr94GMf_k20eXlF1LAaJyXRraKyC9hmBeKSUeAdIgowlfGFAHt96Z4EruBlkn2mbs3mj3uBWr2zqfNTVyQcicJDkJCO5EpbpexyLO5igD9qZU__4ctPvZcfWY-dJswSfiCTP1Uz0BiGFsGqb72chd8Rhn5Btls-D6b9Uuzl9ZZeLj2pIuBA-yi_CMm3CzopKIJ1NySMT8HyvafHcTdfpzFWFPoUqxkVAzt4U6tqBpEnTqmwRW_3kTisJhIY9xH2uXKghz2VWM6mvTL1PahZgbwLqsVb_sBOOEtiASpGf8WNc1uXtKNhBCb_YJw"
}
}
}
}
I cannot see any way of sending my login details with GraphQL
Auth protected queries should be sent with Authorization header. Exact method depends on client-side technology, f.e. Apollo client supports this by middleware.
You can use existing REST login endpoint (fetch/get token) or create login mutation - example.
Another inspiration can come from a more complex example apollo-universal-starter-kit
I am building an API and I am using Laravel Passport for authentication.
The API is being used for our mobile app so we're using the Password Grant Client.
Everything works great, and a user can login to get an access token. We have created a register endpoint which allows a user to sign up. We need the API to return an access token at this point too.
Looking through the docs there is no way to create an access token programmatically.
How can I create an access token for a Password Grant Client in my controller? I obviously don't want to do a HTTP request to my own API to get it.
I know I can use a Personal Access Grant Client and call createToken on the user model, but that means the access token is associated with a different Client. This doesn't seem right to me.
Try something like this
class UserController extends Controller
{
protected function login(Request $request)
{
$request->request->add([
'grant_type' => 'password',
'client_id' => '3',
'client_secret' => '6BHCRpB4tpXnQvC1DmpT7CXCSz7ukdw7IeZofiKn',
'scope' => '*'
]);
// forward the request to the oauth token request endpoint
$tokenRequest = Request::create('/oauth/token','post');
return Route::dispatch($tokenRequest);
}
}
I've been toying with Passport for a couple of weeks now and from what I've seen in the documentation it doesn't expose many of the methods it uses for creating tokens. While you may not easily be able to "create an access token for a Password Grant Client in my controller" - what you can do is use Route::dispatch to forward the request for a token to your Passport Password Grant route.
To do this in the controller you are using to issue tokens, use the AuthenticatesUsers trait so you have access to the Password Grant route, create a request, and dispatch that request to the Password Grant route:
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class IssueTokensController extends Controller
{
use AuthenticatesUsers;
protected function issueApiToken(Request $request)
{
// forward the request to the oauth token request endpoint
$tokenRequest = Request::create(
'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest);
}
}
This method of course requires you to have set up Passport and a Password Grant Client.
This answer is based off of another answer to a similar question by Raymond Lagonda - see https://stackoverflow.com/a/40433000/4991377
Patrick has got the right idea, and this is what I ended up doing:
(I don't think Sanju's answer is right because you need to make a http request)
<?php
namespace MyApp\Http\Controllers\API;
use Illuminate\Http\Request;
use Laravel\Passport\Http\Controllers\ConvertsPsrResponses;
use League\OAuth2\Server\AuthorizationServer;
use MyApp\Http\Controllers\APIController;
use Illuminate\Auth\AuthenticationException;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Response as Psr7Response;
class LoginController extends APIController
{
use ConvertsPsrResponses;
/**
*
* #param Request $request
* #param AuthorizationServer $authServer
* #return \Illuminate\Http\JsonResponse
* #throws AuthenticationException
* #throws \League\OAuth2\Server\Exception\OAuthServerException
*/
public function login(Request $request, AuthorizationServer $authServer)
{
$token = $this->getPasswordToken($request, $authServer);
$data = [
"token_details" => $token,
];
return $this->successResponse(
'Successful Login',
200,
$data
);
}
/**
* #param $request
* #param AuthorizationServer $authServer
* #return mixed
* #throws \League\OAuth2\Server\Exception\OAuthServerException
*/
private function getPasswordToken($request, AuthorizationServer $authServer)
{
$parsedBody = [
'grant_type' => 'password',
'client_id' => config('app.client_id'),
'client_secret' => config('app.client_secret'),
'username' => $request->username,
'password' => $request->password,
'scope' => '',
];
$serverRequest = new ServerRequest(
$request->server(),
[],
null,
$request->method(),
'php://input',
$request->header(),
[],
[],
$parsedBody
);
$response = $this->convertResponse(
$authServer->respondToAccessTokenRequest($serverRequest, new Psr7Response)
);
return json_decode($response->getContent());
}
}
I know I can use a Personal Access Grant Client and call createToken on the user model, but that means the access token is associated with a different Client
not sure what you mean by that, could you explain more?
Now this is not ideal but you might be able to inject \League\OAuth2\Server\Grant\PasswordGrant and use
respondToAccessTokenRequest(ServerRequestInterface $request
ResponseTypeInterface $responseType,
\DateInterval $accessTokenTTL)
you would have to build all those objects, but this is the only public method for password that returns any token information.
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.