My application is a single page app using Angular 1.x on the client side and Laravel 5.3 for my server/api. I easily managed to make the Auth0 authentication working on my client side (angular) and it successfully generates a token. Moving to my api (laravel), unfortunately I can't access any routes that is protected by the auth0.jwt middleware even though the Authorization header is present. Its response is a simple text that says Unauthorized user.
I'm using Chrome postman to test out the routes on my api.
I tried to trace down the function that is responsible for the response by checking the vendor\auth0\login\src\Auth0\Login\Middleware\Auth0JWTMiddleware.php and found out that the CoreException is being thrown.
Here's the handle method of the Auth0JWTMIddleware:
/**
* #param $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, \Closure $next)
{
$auth0 = \App::make('auth0');
$token = $this->getToken($request);
if (!$this->validateToken($token)) {
return \Response::make('Unauthorized user', 401);
}
if ($token) {
try {
$jwtUser = $auth0->decodeJWT($token);
} catch (CoreException $e) {
return \Response::make('Unauthorized user', 401);
} catch (InvalidTokenException $e) {
return \Response::make('Unauthorized user', 401);
}
// if it does not represent a valid user, return a HTTP 401
$user = $this->userRepository->getUserByDecodedJWT($jwtUser);
if (!$user) {
return \Response::make('Unauthorized user', 401);
}
// lets log the user in so it is accessible
\Auth::login($user);
}
// continue the execution
return $next($request);
}
My suspect is that the token that generates from Auth0 has newer algorithm or something and the Laravel Auth0 package doesn't already supports it.
I followed exactly the documentation provided by Auth0 and I also cloned their sample projects just to make sure the configurations are correct but unfortunately it doesn't work also. Any thoughts or ideas on how can I solve my issue? Thanks in advance!
I had the same problem. There were 2 things I had to change in order to resolve. Credit to #Kangoo13 for the first suggestion:
1; Check that in your config/laravel-auth0.php file that secret_base64_encoded = false. You will notice in your Auth0 dashboard next to your key it states "The client secret is not base64 encoded"
'secret_base64_encoded' => false,
2; in the same config file, Check that 'supported' has the correct spelling. It would appear someone has incorrectly typed "suported", if you've just applied the default config file after running composer then chances are this is wrong!
Looking in JWTVerifier.php it does appear to cater for the misspelled key but it will default to 'HS256', Auth0 guide explicitly states you should be using 'RS256:
'supported_algs' => ['RS256'],
Hope that helps!
I read the suported_algs issue above but skim read it and missed the fact you have to correct the spelling for it to work, spent an extra day trying to figure it out before re-reading. Hopefully next person to read it sees this and doesn't miss the spelling issue! Thanks #user1286856
Related
I have defined get Keycloak method on my Symfony project.
I am getting excepted results in the matter of array where I am extracting the list of password policies. Currently it's set just 'not username' rule:
I could not find any other endpoint within keycloak in a documentation where I can pass my password string as parameter and see if it's meeting the requirements defined in password policies.
I will provide GET function which is returning the thing I just described. I think it will do the work if it could be modified to provide password string.
public function validateKeycloakPassword()
{
$options = [
'headers' => $this->getAuthJsonHeaders()
];
try {
$endpoint = sprintf('auth/admin/realms/%s/', $this->realm);
return $this->request('GET', $endpoint, $options);
} catch (\Exception $e) {
$this->exception('Can`t get password policy information on Keycloak. ' . $e->getMessage());
}
}
and in my controller, endpoint:
/**
* #Route("/check", name="check")
*/
public function validatePassword()
{
$violations = $this->service->validateKeycloakPassword();
return $violations['passwordPolicy'];
}
To summerize:
Is there any endpoint in keycloak where I can pass my password variable and check if it meets requirements defined in password policies Probably with PUT method.
As far as I know there is no such endpoint. Nevertheless, there is a workaround that you can do, create a dummy_user that will be used to test the passwords. Get the ID from that user, which you can get from the Keycloak Admin console or by using the endpoint:
curl -X GET <KEYCLOAK_HOST>/auth/admin/realms/<REALM_NAME>/users/?username=dummy_user
From the JSON response, extract the user ID. Then you call the following endpoint:
PUT <KEYCLOAK_HOST>/auth/admin/realms/<REALM_NAME>/users/<USER_ID>/reset-password
with the request payload:
{"type":"password","value":"<THE_PASSWORD_THAT_YOU_WANT_TO_TEST>","temporary":false}
You will get a 400 if the password is not valid, and with the response you can look at the error. An example of such response:
error:"invalidPasswordMinLengthMessage"
error_description:"Invalid password minimum length 8"
Obviously, this will not work for the username policy, but that one can be easily check in your app (e.g., password != username)
I am using Laravel Spark for billing my customers. In case they will pay with 3D Secure cards (I test it with card no. from stripe doc's https://stripe.com/docs/testing#regulatory-cards: 4000 0027 6000 3184) I have the following problem:
The stripe pop-up/modal opens and I click 'Complete Authentication'.
After that the authentication process starts and the user gets directed to http://127.0.0.1:8000/stripe/payment/pi_xxx?redirect=/home
Here I get the following Laravel error (I do not find other error causes in console or somewhere else):
I've added stripe/* in VerifyCsrfToken Class already...
Maybe do I need to test this case on my server?
Very weird and I guess thats a sign that I do not have any issues with stripe, with laravel instead. When I remove the query parameter ?redirect=home I get this screen:
When I proceed I do not get redirected.. Of course because there is no redirect uri...
Does any one had this issue before?
For me this is a bug in Spark. I've searched for all occurences where Stripe used a redirect. One indication for me that this could really be a bug is:
In subscription-notice.blade.php file there ist the link build as follows:
{!! __('Please :linkOpen confirm your payment :linkClose to activate your subscription!', ['linkOpen' => '', 'linkClose' => '']) !!}
The part '?redirect='.url('/home').' creates a full valid URL with host address.
Not only a relative path! These relative paths runs into the 403 Error in my case.
Like in the RegisterController:
/**
* Handle a registration request for the application.
*
* #param \Laravel\Spark\Contracts\Http\Requests\Auth\RegisterRequest $request
* #return \Illuminate\Http\Response
*/
public function register(RegisterRequest $request)
{
list($user, $paymentId) = Spark::interact(
Register::class, [$request]
);
Auth::login($user);
event(new UserRegistered($user));
if ($user instanceof MustVerifyEmail && ! $user->hasVerifiedEmail()) {
$user->sendEmailVerificationNotification();
}
return response()->json([
'redirect' => $paymentId ?
'/'.config('cashier.path').'/payment/'.$paymentId.'?redirect='.$this->redirectPath()
: $this->redirectPath(),
]);
}
$this->redirectPath() returns a relative path. I've changed this part into:
return response()->json([
'redirect' => $paymentId ?
'/'.config('cashier.path').'/payment/'.$paymentId.'?redirect='.config('app.url').$this->redirectPath()
: $this->redirectPath(),
]);
In this case I took the host address from my config and put it in front of the relative path.
Just for better understanding, the returned URL above is used here (register-stripe.js):
/*
* After obtaining the Stripe token, send the registration to Spark.
*/
sendRegistration(paymentMethod) {
this.registerForm.stripe_payment_method = paymentMethod;
Spark.post('/register', this.registerForm)
.then(response => {
window.location = response.redirect;
});
}
There are some more cases where I needed to override some JavaScript or PHP sources...
register process (showed here)
update process of payment information
create subscription with existing account
I hope I could help others with that! If necessary I could also post the exact places where I've changed the redirect URL in the comments.
I came up with a (dirty?) workaround:
In my StripWebHookController there is some code that makes a notification for the user:
if ($billable) {
$model = config('cashier.model');
$notifiable = $billable instanceof $model ? $billable : $billable->owner;
if (in_array(Notifiable::class, class_uses_recursive($notifiable))) {
$payment = new Payment(StripePaymentIntent::retrieve(
$payload['data']['object']['payment_intent'],
$billable->stripeOptions()
));
$notifiable->notify(new $notification($payment));
}
}
Now, this is a notification that apparently makes the StripePaymentIntent notification that is created in the PaymentController of Cashier (located /var/www/html/site/vendor/laravel/cashier/src/Http/Controllers)
There is a VerifyRedirectUrl middleware that is causing the problem right. So when you comment it out, the 403 disappears:
public function __construct()
{
//$this->middleware(VerifyRedirectUrl::class);
}
However, the "go back" button after accepting the payment is not working so I'll check that out. But for now, this 403 is at least gone. If I can't another solution, I'll go with this.
My project is built on Yii2 and uses google/apiclient for the purposes of login to the web interface. There's also an android app which connects to the API and uses bearer authentication against Google tokens (which I believe pulled in firebase/jwt). This has worked fine since early 2018 until the week commencing 10th September 2018. No code was changed in my system.
Since then, attempting to login to the web interface (oAuth) gives
yii\authclient\InvalidResponseException: Request failed with code: 400, message: {
"error" : "redirect_uri_mismatch",
"error_description" : "Bad Request"
The site is correctly listed in the Google developer console (where I've also not changed anything) so the redirect_uri_mismatch is not expected.
Upgrading google/apiclient allows me to login to the web interface but breaks the app's token auth, giving:
Your request was made with invalid credentials
I can provide the full stack trace if required, however, I'm hoping someone else has encountered the same and can point me in the right direction. Using firebase/jwt v4 in the live system allows token auth to function but using v4 in test with the upgraded google/apiclient fails the auth with the same credentials error as above.
Can anyone provide any guidance please?
To get refresh token on first login you need to set "?access_type=offline" in "authUrl". And then save it somewhere, in database, files etc.
If you have no database or if you ok with prompting access rights every time on login in you can add "&approval_prompt=force" to same URL. Thats will enforce Google send you refresh token every time you are logging in, but also enforces showing access rights prompt every time.
class MyGoogleClient extends yii\authclient\clients\Google
{
/**
* Set to true if you want to get refresh token always on login
* #var type
*/
public $enforceRefreshToken = false;
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
if (is_array($this->scope)) {
$this->scope = implode(' ', $this->scope);
}
$additionalParams = [];
if ($this->autoRefreshAccessToken) {
$additionalParams['access_type'] = 'offline';
}
if ($this->enforceRefreshToken) {
$additionalParams['approval_prompt'] = 'force';
}
if (!empty($additionalParams)) {
$this->authUrl = $this->composeUrl($this->authUrl, $additionalParams);
}
}
/**
* {#inheritdoc}
* #return string return URL.
*/
protected function defaultReturnUrl()
{
return Yii::$app->getUrlManager()->createAbsoluteUrl([Yii::$app->controller->getRoute(), 'authclient' => 'google']);
}
}
I have set-up Laravel using passport as per the documentation here: https://laravel.com/docs/5.3/passport
A few people have asked about this using the oAuth implementation but I'm trying to use the personal access tokens not oAuth. One suggestion was to remove the auth middleware but obviously this leaves the application wide open so anyone can make requests.
I have the following route (in routes/api.php):
Route::get('/test', function(){
return 'returned string from test route';
})->middleware('auth:api');
This works if I remove the auth middleware so the route is working correctly but when enabling the auth middleware again I get the following error in postman:
{"error":"Unauthenticated."}
These are the headers being sent via postman:
GET /api/test HTTP/1.1
Host: localhost:8000
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImU4ZmY5MDMwY2EyM2E2MDYwODViN2Y3ZWNiMzcxNDY1MzQxNDViNTk4ODU4NmZhNDljYzU2YjMzYWZiNzhkYTk5OTIwZWMzYzEwNTBkNjZjIn0.eyJhdWQiOiIyIiwianRpIjoiZThmZjkwMzBjYTIzYTYwNjA4NWI3ZjdlY2IzNzE0NjUzNDE0NWI1OTg4NTg2ZmE0OWNjNTZiMzNhZmI3OGRhOTk5MjBlYzNjMTA1MGQ2NmMiLCJpYXQiOjE0NzU1MDMxNjUsIm5iZiI6MTQ3NTUwMzE2NSwiZXhwIjowLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.IpzKK29dJCpliUXQvPRss87kGFngFcXXwV3jRwhbZOZLxl-4UV70cBsSigmqUuBsHQ4onVl_Cjcq6cEmMFvTZZr7D9AtY3EmScvMPjoFh4KQ3wgd5CoyWfcLQgoBxbElNxL0xW2fIQhpeQd_8Yz_Pr5BByGVTpxfg4JJZ4PzovvZsa2R3izYtqw6-qeurQOtsfOnot5uoLDeDDc76klifnfHfOcNZSoIFGNP3gIGKYBe6lfFuDViR_mQkwQS5_UmERt3GSkEvJjGMtwcRjWY7VPAJ4tvWLnyLw0roGU2e37L0wsqfJ8OrG0Cipv-anXAW_utSo-fiVMr8ZeAWIPguq73Zd44x95YY3nNPOKD5dVIRZM7rQgdhjIwTEz1ggtSXLp-Fu3QOtXaHUahCHvjOTdiTYEa-GR4TZ5wGzt-aRhjdBB7WTe0C6T9ZWVwQr0kJk8AxW6ne87wwJYp_shGunTclZ3SCq5VYg2K_MclbJl65-dT8x-nwqg0lqfNx9s1wmtryrMFIPoBEyaGNEK1aWGHKq418-BIQ1_UAhcHHtEXclWvsGWwhyo3aso-E-sCN2o_IkYvSboIsdFAIXvDvQmoAwis6f1J57zWH8AW1ynCFcBgzBDjIyiaCE5nqtb_4zbEXr8L1EbcllbtZkq3vd9w996kO7xlpBEWwPY8IWg
Accept: application/json
Cache-Control: no-cache
Postman-Token: 6bc483b2-23df-acce-7eef-5a443f8f5d45
Firstly, NEVER modify the vendor files unless you have a fully legitimate reason for doing so and there's a feature you wish to see implemented or a bug you've discovered and fixed in the package you're using.
Expiration time on the JWT might be already set to expire as soon as it's made. Here's a link you can use to check the "ttl" (Time To Live) field of your access tokens:
https://jwt.io/
If you find that your tokens are expiring on creation, you can go to your app\providers\AuthServiceProvider.php class and add in these methods on use of Passport Class:
use Carbon\Carbon;
use Laravel\Passport\Passport;
...
Class AuthServiceProvider extends ServiceProvider {
...
...
public function boot() {
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(Carbon::now()->addYears(20));//You can also use addDays(10)
Passport::refreshTokensExpireIn(Carbon::now()->addYears(20));//You can also use addDays(10)
Passport::pruneRevokedTokens(); //basic garbage collector
}
}
Make sure you're using the most recent version of Passport
Currently I'm on version 1.0.8 but I might already be out of date as they and the community are constantly pushing new revisions every few weeks.
Here are links to some related issues regarding this problem. You might be able to locate your answer within one of the below links. If what's mentioned above isn't what you're looking for.
Passport - "Unauthenticated." - Laravel 5.3
Very Detailed
https://github.com/laravel/passport/issues/151
in passport.php there are two functions
public static function tokensExpireIn(DateTimeInterface $date = null)
{
if (is_null($date)) {
return static::$tokensExpireAt
? Carbon::now()->diff(static::$tokensExpireAt)
: new DateInterval('P100Y');
} else {
static::$tokensExpireAt = $date;
}
return new static;
}
/**
* Get or set when refresh tokens expire.
*
* #param \DateTimeInterface|null $date
* #return \DateInterval|static
*/
public static function refreshTokensExpireIn(DateTimeInterface $date = null)
{
if (is_null($date)) {
return static::$refreshTokensExpireAt
? Carbon::now()->diff(static::$refreshTokensExpireAt)
: new DateInterval('P100Y');
} else {
static::$refreshTokensExpireAt = $date;
}
return new static;
}
you must change P100Y to P1Y. and also in PassportserviceProvider.php at line 101 there is code
$server->enableGrantType(
new PersonalAccessGrant, new DateInterval('P100Y')
);
change P100Y to P1Y. hope it helps you :)
Please check if the token was copied properly, i always observed when i copy the personal tokens, in the last there is a word 'Close' copied also.
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImU4ZmY5MDMwY2EyM2E2MDYwODViN2Y3ZWNiMzcxNDY1MzQxNDViNTk4ODU4NmZhNDljYzU2YjMzYWZiNzhkYTk5OTIwZWMzYzEwNTBkNjZjIn0.eyJhdWQiOiIyIiwianRpIjoiZThmZjkwMzBjYTIzYTYwNjA4NWI3ZjdlY2IzNzE0NjUzNDE0NWI1OTg4NTg2ZmE0OWNjNTZiMzNhZmI3OGRhOTk5MjBlYzNjMTA1MGQ2NmMiLCJpYXQiOjE0NzU1MDMxNjUsIm5iZiI6MTQ3NTUwMzE2NSwiZXhwIjowLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.IpzKK29dJCpliUXQvPRss87kGFngFcXXwV3jRwhbZOZLxl-4UV70cBsSigmqUuBsHQ4onVl_Cjcq6cEmMFvTZZr7D9AtY3EmScvMPjoFh4KQ3wgd5CoyWfcLQgoBxbElNxL0xW2fIQhpeQd_8Yz_Pr5BByGVTpxfg4JJZ4PzovvZsa2R3izYtqw6-qeurQOtsfOnot5uoLDeDDc76klifnfHfOcNZSoIFGNP3gIGKYBe6lfFuDViR_mQkwQS5_UmERt3GSkEvJjGMtwcRjWY7VPAJ4tvWLnyLw0roGU2e37L0wsqfJ8OrG0Cipv-anXAW_utSo-fiVMr8ZeAWIPguq73Zd44x95YY3nNPOKD5dVIRZM7rQgdhjIwTEz1ggtSXLp-Fu3QOtXaHUahCHvjOTdiTYEa-GR4TZ5wGzt-aRhjdBB7WTe0C6T9ZWVwQr0kJk8AxW6ne87wwJYp_shGunTclZ3SCq5VYg2K_MclbJl65-dT8x-nwqg0lqfNx9s1wmtryrMFIPoBEyaGNEK1aWGHKq418-BIQ1_UAhcHHtEXclWvsGWwhyo3aso-E-sCN2o_IkYvSboIsdFAIXvDvQmoAwis6f1J57zWH8AW1ynCFcBgzBDjIyiaCE5nqtb_4zbEXr8L1EbcllbtZkq3vd9w996kO7xlpBEWwPY8IWg Copy
If this is not your case check if that token exist, or generate a new one. you can use this format to protect route like this
Route::middleware('auth:api')->get('/home', function(){
return 'test';
});
I Had this problem ... two hours down the pan. Something very strange was happening and in my case I think Postman was the culprit.
Inspect the received header to rule it out:
Route::get('/test', function(){
dd( Request::header());
})/*->middleware('auth:api')*/; //disable auth middleware to inspect header
this is the strange thing I found:
.........
"authorization" => array:1 [
0 => b"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ ................
what the hell was the 'b' before Bearer ACCESS_TOKEN ???
I typed it out again and it dissapeared and auth middleware started working.
Inspect the header exactly and then add the middleware back. This might just be the cause!
In my Symfony 3.0.6 application, certain common routes are not available until a user's registration has been finalised. Until this has been done, I use an AccessDeniedHandlerInterface to redirect them to a route that prompts them to finalise it. I also use a flash message to explain what has happened:
/**
* Handles an access denied failure.
*
* #param Request $request
* #param AccessDeniedException $accessDeniedException
*
* #return Response may return null
*/
public function handle(Request $request, AccessDeniedException $accessDeniedException)
{
if (null === $token = $this->security->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
return;
}
/** #var User $user */
if (!$user->isFinalised()) {
$request->getSession()->getFlashBag()->add('info', 'You need to complete your registration before you can do this!');
return new RedirectResponse($this->router->generate('app_registration_complete'));
}
return;
}
However, a problem arises with Chrome's pre-fetch/pre-rendering service - if Chrome decides a URL is likely to be visited, it will pre-fetch it. A pre-fetch results in the flash message above being added to the user's session, so any subsequent page load means there are shown the message. If they do in fact go to a route they are not yet allowed to access, they see the message twice - one generated during the pre-fetch, and one generated when they actually navigated.
A similar issue is described in this question, but the accepted answer (use a POST for logout requests) doesn't really seem like the right solution in this context. The flash message is more of an explanatory addition to a GET redirection, as opposed to something that changes the state of the application. Also, there are many ways a user might reach one of these disallowed routes, and it doesn't make sense to me to convert all of these to trigger a POST, just to get around a pre-fetch issue in certain situations.
So... how should I deal with this?
I see one problem with your code. You should change to this:
if (null === $this->security->getToken()) {
return;
}
I don't think this will resolve the problem though.