-
Notifications
You must be signed in to change notification settings - Fork 780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to change the ResponseType #94
Comments
Passport can't really set additional (private) claims the way it is now. In fact, Passport makes use of the php league's /**
* Generate a JWT from the access token
*
* @param CryptKey $privateKey
*
* @return string
*/
public function convertToJWT(CryptKey $privateKey)
{
return (new Builder())
->setAudience($this->getClient()->getIdentifier())
->setId($this->getIdentifier(), true)
->setIssuedAt(time())
->setNotBefore(time())
->setExpiration($this->getExpiryDateTime()->getTimestamp())
->setSubject($this->getUserIdentifier())
->set('scopes', $this->getScopes())
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
->getToken();
} You'll notice the |
Thank you for the reply @craigpaul. Is there any plans to do this implementation any soon? |
Wouldn't know, you'd have to ask one of the contributors I suppose? @taylorotwell ? |
Have the same need. League\OAuth2 provides for extra params (see below) but I don't see how I can override AuthorizationServer::getResponseType since that is in the PassportServiceProvider. /** |
Hello everyone, looking into this issue but I'm curious to know what type of custom claims can be attached to a token also how do you normally attach those claims while creating the token? Thank you |
@themsaid Private claims can be anything that two parties agree on using, they are not limited to a predefined set of keys. Read more about the payload here.
As for how they are normally attached, I'm not sure what you mean, if you're talking about how they are represented in the payload, then here is an example.
As for how to attach them to the token, we can either attach parameters to the response or attach custom claims to the JWT as they are two different things. To attach parameters to the response we can override the Hope that answers your question, if not let me know and I'll help with whatever I can. |
@craigpaul thank you for the detailed explanation, that answers my questions :) I believe the desired behaviour here is to attach custom claims to the JWT, I just need some real world example so that I can work on a proposal with a valid use case, do you have any in mind? |
In my use case, my app user has resources that can be accessed through devices or APIs. Let say the user has 3 resources: A, B, C and 2 devices: 1, 2. I was hoping a token can be created like: $scopes = [
'get-resources',
];
$claims = [
'resources' => ['A', 'B'],
];
// Pass token to device
$token = $user->createToken('Device 1', $scopes, $claims)->accessToken; Then I would get the claims from the token and validate them in policies, requests or controllers. $claims = $user->token()->claims; |
I think this feature is really interesting if the token is consumed by a distant resource service, which relies on valid tokens from the authorization service. |
I would love to use this feature as well when consuming my own API. |
I've managed to duplicate this code and test that I can see 'dsl' = 'rules' appears in the resulting JWT. I'm thinking if we add a: public function __construct($userIdentifier, array $scopes = [], other_claims = []) ...that simply iterated through those claims we're somewhat there. IF someone thinks this is a good idea, then I'll fork this and fiddle about. IF someone would be willing to help me figure out how to get a test rig setup and to make the test to test this (there are tests I'm sure and I wouldn't want to introduce a change without them), I'd be really appreciative. |
I think allowing custom claims would really make the access token much more useful. Rather than only being able to authenticate requests to the api it would allow us to display additional user information without pounding the api for the same simple information over and over. Static website use caseMy user interface is a single page application which is build and served from a cdn. All authentication is handled by my auth service which is running on passport. Each time the page loads I want to show the current users name and avatar in the header, as well as limit some ui elements based on the roles of the user. Currently I make an ajax call with the token to fetch the user data. With custom claims that ajax call would go away and I could just use something like jwt-decode to get the information I need from the access token itself. Resource server use caseIn addition to the auth service and the front end I have multiple resource servers that must all authorize requests using the auth service as the source of truth. When a user wants to update a restricted resource I must verify the user has the correct permission to do so. This means I need to make a request to the auth service to retrieve the users permissions before I can proceed. If the permissions were available on the access token then I could use the public key from my auth service to verify and decode the access token then I would have all the information I needed to continue. WebsocketsWhen users connect to my site I want to connect them to a web socket once they are authenticated, I also want to add their name to a list of online users. Given only the access token I need to again requests the users basic info (name, avatar) from my auth service. If this information were on the access token that would not be necessary. Just like the resource server my socket server could use my auth services public key to validate and decode the token giving me all the information necessary. @themsaid I hope these use cases are helpful. I'm currently accomplishing this by registering a custom AccessTokenRepository but it would be nice If someone could find a clean way to implement this as a core feature. Here is a gist of my current setup in case it helps https://gist.github.com/RDelorier/9ec45bbb595b7e21c30df80c34b03cac |
@themsaid It's been 8 months since this was raised. Are there any plans on supporting this? I would prefer a native passport jwt solution versus going with another jwt package. |
If you are not too concerned with stuffing the extra parameters inside the token, we have this brittle workaround:
Then this:
|
Jumping on the bandwagon because I also have a need for custom claims. I'm working on a multi-tenant SaaS application and it would be great if I could store the user's group ID in the token. Especially because my users can belong to multiple groups, so checking which group they currently have access to has been a major challenge so far. |
Custom claims still haven't been added. At this moment, I have to make a request for a token and then make a request to get my users information - this hurts my feelings. |
looking for the same feature here... :-( |
Need this too. |
I needed this too. I don't know whether my way is recommended but I did the following.
The resulting JWT created should be something like below:
|
Claims support is a fairly essential part of any stateless identity framework. Would love to know when this is going to be implemented. Basic gist here would be that some time after successful authentication, we need a place to hook code that returns a dictionary of claims. As I mention in the issue I link below, my use case is including more than the user's ID in the token. Specifically the tenant the token applies to as well as the current organization. cc. #676 |
In 3rd step, there is |
@yavuzkoca you can remove that line. |
@JannieT Your answer worked for me but i suggest a change; pass only 'private' keyword rather than 'oauth-private.key' to 'makeCryptKey' function. |
@onamfc |
if this ever does get implemented, it would be outstanding to have a way to check claims as easily as you can check scopes via middleware, helper methods etc. I got really far with @onamfc's gist (thank you so much) but needing to check the claim values in middleware and still wanting to use Might be for the best? But I would love to have the option of doing one or both.... my experience with past oauth/jwt implementations have not made this difficult at all, though obviously the advantages of laravel as a whole outweigh this particular concern. |
in the future i may abandon scopes for this and add in the custom claim stuff above and supply my own token model to include those claims in the DB somewhere so I can build a middleware around it that checks for a combination of feature set the user has access to and scope granted to a token? I hate that I can't just get at the claims on the token (unless i can and i don't know how). as of now the scopes drive the whole thing in what feels like a very unnatural way |
We've been extending Passport to add custom claims for a while now. We currently use a method similar to the gist a few posts up. Today I had a look if it would be possible to extract claims support into a package. I made a quick proof-of-concept. It seems to work in our Passport setup, but feel free to play with it and maybe report back on the package issue tracker. I'd love to hear people's ideas about it. This is for the current version of Passport, it likely won't work for previous versions yet. |
We are currently running the above package in production, and I know of 2 others that do as well, so I guess it's all working fine. I'll keep it updated as newer versions of Passport appear and this issue hasn't been resolved in Laravel Passport itself in some way. |
I did an article for who that want to do this and don't know how to. Overloading some classes (less as I can). I hope this help others in the future I have done and tested it with Laravel 6 y Passport 7.5 |
@onamfc It worked in Laravel 5.8. Thanks! <3 |
Tested it with laravel 7.10.x and laravel passport 8.5.0 and it is working. Great job. Thanks a lot :) |
I don't expect laravel to support this anytime soon, mostly because the league-server package does not really allow for an easy way to do this. Laravel probably does not want to overwrite league trait methods like we're forced to do. There's also plenty of issues about this on the league repo. For now my package gives people a really easy way to add claims to passport JWTs. |
For Laravel 8 This worked perfectly well for me |
@ifeanyi2667 you can just use my package, see link a few posts up |
Thank you |
For Laravel 8 and Passport 10. <?php
declare(strict_types=1);
namespace App\Auth;
use DateTimeImmutable;
use App\Models\User;
use Laravel\Passport\Bridge\AccessToken as PassportAccessToken;
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
class AccessToken extends PassportAccessToken
{
use AccessTokenTrait;
/**
* Generate a JWT from the access token
*
* @return Token
*/
private function convertToJWT()
{
$this->initJwtConfiguration();
return $this->jwtConfiguration->builder()
->permittedFor($this->getClient()->getIdentifier())
->identifiedBy($this->getIdentifier())
->issuedAt(new DateTimeImmutable())
->canOnlyBeUsedAfter(new DateTimeImmutable())
->expiresAt($this->getExpiryDateTime())
->relatedTo((string) $this->getUserIdentifier())
->withClaim('scopes', $this->getScopes())
->withClaim('user', $this->getUserClaim())
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
}
/**
* Get the user claim for the JWT.
*
* @return array
*/
private function getUserClaim()
{
$user = User::find($this->getUserIdentifier());
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'roles' => $user->roles->pluck('name'),
];
}
} |
Hey all. I've taken some time today to look into this. I agree with @corbosman that atm it's unfeasible for us to overwrite the functionality from league/oauth2-server. If they make changes which we miss we can end up with broken functionality. Therefor we want to wait until they offer us an API for us to easily add custom claims to JWT tokens. However, @corbosman was so nice to provide a package for this: https://github.com/corbosman/laravel-passport-claims I've sent in a PR to the docs to include his package so people can find their way to it. Please just remember that you're overriding league/oauth2-server behaviour and can potentially miss any bug fixes or such that they could provide in future releases. I also recommend to advocate and request this feature in league/oauth2-server. After it's been added, we can provide this in Passport natively. Thanks! |
The sent in PR to the docs was unfortunately rejected. This means that we're going to hold off until league/oauth2-server offers an API for us to make this possible. |
Thanks for the code samples, it really worked, but in my case I encountered some problems . I have 2 resource servers, say I am making a getToken request to Authorization server from server A and want to include custom claims to the token payload ( organizations: [] ), at the same time I need to get that info from server B , but ,say, if I create Personal Access Token for current user (after request want to invalidate/delete, actually other solutions didn't come to my mind for now) to make request to server B (which verifies with public key taken from Authorization server) and server B finds organization info related to current user . But that way it goes into loop . If passport provided a way to include claims to token payload, this way may anyway go into loop. Do you guys have any idea to solve this problem or ever had a similar case in the past? |
hey guys, any official solution yet ? |
У меня не сработало. Вот декодированный токен. { |
For people tring to do this in 2024 +, Laravel 10.48. I found an easier way to do this; you still need to create a class that extends from Laravel\Passport\Bridge\AccessToken, like this one : <?php
namespace App\Passport;
use DateTimeImmutable;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Laravel\Passport\Bridge\AccessToken as BaseToken;
use League\OAuth2\Server\CryptKey;
class AccessToken extends BaseToken {
private $privateKey;
private $jwtConfiguration;
public function setPrivateKey(CryptKey $privateKey)
{
$this->privateKey = $privateKey;
}
public function initJwtConfiguration()
{
$this->jwtConfiguration = Configuration::forAsymmetricSigner(
new Sha256(),
InMemory::plainText($this->privateKey->getKeyContents(), $this->privateKey->getPassPhrase() ?? ''),
InMemory::plainText('empty', 'empty')
);
}
public function __toString()
{
return $this->convertToJWT()->toString();
}
public function convertToJWT() {
$this->initJwtConfiguration();
// ID of the user : $this->getUserIdentifier()
// You can get your user like this : $user = User::find($this->getUserIdentifier());
return $this->jwtConfiguration->builder()
->permittedFor($this->getClient()->getIdentifier())
->identifiedBy($this->getIdentifier())
// Removing milliseconds
->issuedAt(DateTimeImmutable::createFromFormat('Y-m-d H:i:s', (new DateTimeImmutable())->format('Y-m-d H:i:s')))
->canOnlyBeUsedAfter(DateTimeImmutable::createFromFormat('Y-m-d H:i:s', (new DateTimeImmutable())->format('Y-m-d H:i:s')))
->expiresAt(DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $this->getExpiryDateTime()->format('Y-m-d H:i:s')))
->relatedTo((string) $this->getUserIdentifier())
->withClaim('scopes', $this->getScopes())
// ->withClaim('custom-claims', $this->getUserIdentifier())
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
}
} Then, the only thing you need to do is modify the app\Providers\AuthServiceProvider.php and set the new AccessTokenEntity with the one you just created : <?php
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Passport\AccessToken;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::useAccessTokenEntity(AccessToken::class);
}
} Now Passport will generate tokens using the class you created. |
@AnissBoua how do we update the token with new claims? |
@amsanket22 In the |
Hi,
I want to add some user info into my JWT. It can be done by changing the response type of the authorization server with bearer token supporting extra params.
Ref: https://github.com/thephpleague/oauth2-server/blob/master/tests/ResponseTypes/BearerTokenResponseWithParams.php
But I am not sure how can I change the response type inside the passport.
The text was updated successfully, but these errors were encountered: