is Jwt token was UNIQUE - php

I'm using JWT web token system. I was able to generate TOKENS successfully.
I'm creating JWT tokens in Laravel as follows
I'M USING FOLLOWING TECHNOLOGY STACK
LARAVEL 5.2 framework
JWT (package)
SAMPLE CODE
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthenticateController extends Controller
{
public function authenticate(Request $request)
{
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// all good so return the token
return response()->json(compact('token'));
}
}
SAMPLE OUTPUT
I'm GETTING
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
QUESTION 1
Is the generated token is UNIQUE?

JWT is unique in a way that no two same users can have the same token generated for them

In general, JWT is actually replacing the combination of username and password. What it means, instead of keep sending username and password for each request for a restricted resources, the server will return a unique token after verifying the the credentials is correct on the first time the user login. Afterwards, every request will includes the token which will be checked as valid or not before fulfilling the request.
So, if two user comes in and login with two valid credentials, it will receive two different token from the server.

Related

Keycloak- passed parameter to meet password policy requirements

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)

Laravel Oauth2 controller using League OAuth2 client

I'm trying to use the League OAuth2 Client to allow users to authenticate my Laravel web app to set appointments on their calendar. NOTE: I'm not trying to let users login to my site or authenticate into my site using OAuth! I just want to be able to let users add appointments to their own calendars.
I'm basically following the flow outlined here: https://github.com/thephpleague/oauth2-google and have created a single controller (called OauthController with a single method, redirectGoogle. My redirect route (which is registered with Google) is https://example.com/oauth2/google. When I hit this endpoint in my Laravel app, I get redirected to Google to approve my app to access my account data as expected, and then redirected back to the controller endpoint.
However it fails every time at the exit('Invalid state'); line.
Here's the controller method code:
public function redirectGoogle(Request $request)
{
$provider = new Google([
'clientId' => config('oauth.google_oauth_id'),
'clientSecret' => config('oauth.google_oauth_secret'),
'redirectUri' => 'https://example.com/oauth2/google',
]);
if (!empty($request->input('error'))) {
// Got an error, probably user denied access
dd($request->input('error'));
} elseif (empty($request->input('code'))) {
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
session(['oauth2state', $provider->getState()]);
Log::info('Storing provider state ' . session('oauth2state')); <-- Log entry exists so we know session value was written
header('Location: ' . $authUrl);
exit;
} elseif (empty($request->input('state')) || ($request->input('state') !== session('oauth2state', false))) {
Log::error($request->input('state') . ' did not equal stored value ' . session('oauth2state', false)); <-- Log entry exists
// State is invalid, possible CSRF attack in progress
exit('Invalid state'); <-- Breaks here
} else {
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $request->input('code')
]);
// Optional: Now you have a token you can look up a users profile data
try {
// We got an access token, let's now get the owner details
$ownerDetails = $provider->getResourceOwner($token);
// Use these details to create a new profile
dd('Hello %s!', $ownerDetails->getFirstName());
} catch (Exception $e) {
// Failed to get user details
dd('Something went wrong: ' . $e->getMessage());
}
// Use this to interact with an API on the users behalf
echo $token->getToken() . PHP_EOL;
// Use this to get a new access token if the old one expires
echo $token->getRefreshToken() . PHP_EOL;
// Unix timestamp at which the access token expires
echo $token->getExpires() . PHP_EOL;
dd();
}
}
The strange thing is that the log messages noted in the code above both exist, and the values match (at least, it is attempting to write the first session variable with a value that would match the second log file's value):
[2020-05-04 21:02:48] local.INFO: Storing provider state 4963a33bbd5bcf52d3e21c787f24bd7b
[2020-05-04 21:02:51] local.ERROR: 4963a33bbd5bcf52d3e21c787f24bd7b did not equal stored value <null>
Why is it that the second time through the code the oauth2state session value is null, when it was successfully written on the first loop?
NOTE: the problem appears to be that the sessions are different, which makes sense, but how can this session stay consistent, or otherwise keep the data straight?
[2020-05-05 15:25:06] local.INFO: Session id: bV7F5mNM69rJAVJNWK9ZD0rcoN284FxXvjNAmUiw
[2020-05-05 15:25:06] local.INFO: Storing provider state 7351b313b741df41a6be9a049f71db6b
[2020-05-05 15:25:10] local.INFO: Session id: VNiBxr1gYYIA9Nr11x9c4JJArHOiKQScEGh2jkuc
[2020-05-05 15:25:10] local.ERROR: 7351b313b741df41a6be9a049f71db6b did not equal stored value <null>
EDIT2: I've tried the tutorial here which uses a slightly different approach using Laravel and the League Oauth library-- it has the exact same problem, the session ID is different between the two requests, meaning there's no way you'll ever get a match between the state keys.
I believe the problem lies with how you redirect to google.
Problem:
Laravel needs to run trough the whole request in order to persist values into the session.
By using exit; you are interrupting the request and therefore Laravel will not get the chance to persist the values into the session.
Solution:
By using the redirect() helper as suggested in the docs, Laravel will be able to complete the request.
elseif(empty($request->input('code'))) {
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
session(['oauth2state', $provider->getState()]);
Log::info('Storing provider state ' . session('oauth2state'));
return redirect($authUrl);
}
Explanation:
In Laravel you can decide when a middleware is run, from the docs:
Before & After Middleware
Whether a middleware runs before or after a request depends on the
middleware itself. For example, the following middleware would perform
some task before the request is handled by the application:
public function handle($request, Closure $next)
{
// Perform action
return $next($request);
}
However, this middleware would perform its task after the request is
handled by the application:
public function handle($request, Closure $next)
{
$response = $next($request);
// Perform action
return $response;
}
Now if we take a look at how Laravel persists the session data in the StartSession middleware, you can see here that Laravel tries to persist the data into the session after the request has been handled by the application, so by using exit;, die(); or dd(); your are stopping the script and Laravel never gets the opportunity to persist the values in the session.
protected function handleStatefulRequest(Request $request, $session, Closure $next)
{
// Before middleware
$request->setLaravelSession(
$this->startSession($request, $session)
);
$this->collectGarbage($session);
$response = $next($request);
// After middleware
$this->storeCurrentUrl($request, $session);
$this->addCookieToResponse($response, $session);
$this->saveSession($request);
return $response;
}

Check if Token is Valid

I am using third party REST API in my SYMFONY 4.3 app. My app requires checking if token is valid before any request. When is the best place to check if the token is valid and if not try to refresh before request in symfony? Any before request filter in symfony exists? or is there global object when I can fetch all request and if header is 401 I can perform specific action
Now I have central point in my app and all requests are passed through this function. But in future when I will have other request not passed through this function I have to make next function etc... and I am searching place where put isTokenValid code, I am thining about place like " call this function before any request to API "
Should i Use it?
https://symfony.com/doc/current/event_dispatcher/before_after_filters.html#token-validation-example
public function prepareRequest($method, $endPoint) {
.........
// Users can have many tokens connected to different accounts on third party app
$apiTokens = $user->getApiTokens();
/** #var ApiToken $apiToken */
foreach ($apiTokens as $apiToken) {
if ($this->isTokenValid($apiToken)) {
............. make request with specifed apiToken
}
public function isTokenValid(ApiToken $token): bool
{
if token is not valid return false
if token date expired try to refresh token
if token is not valid or refreshing token fails return false else return true
}
The solution I'd like to suggest is to use lexik/jwt-bundle I use it in almost all of mine front-end authentication projects for example you can customize the default response (JWT token not found / not valid) to return the response you desire. You can create both anonymous true or false routes for your purpose I guess anonymous should be true even though your token expired you will extend its lifetime. In case you want some insights put a comment to this answer and I'll provide as best as I can

Twitter API responds with "Your credentials do not allow access to this resource" while calling statuses/update.json

I'm using Hybridauth 3 in my PHP app to make some periodical tweets on behalf of my account.
The app has all possible permissions. I'm giving it all permissions when it asks for them on the first auth step.
After that Twitter redirects me to the specified callback URL and there I'm getting a pair of access_token and access_token_secret.
But when I'm trying to make a tweet using these tokens - it gives me:
{"errors":[{"code":220,"message":"Your credentials do not allow access to this resource."}]}
Here's how I'm trying to make a tweet:
$config = [
'authentication_parameters' => [
//Location where to redirect users once they authenticate
'callback' => 'https://mysite/twittercallback/',
//Twitter application credentials
'keys' => [
'key' => 'xxx',
'secret' => 'yyy'
],
'authorize' => true
]
];
$adapter = new Hybridauth\Provider\Twitter($config['authentication_parameters']);
//Attempt to authenticate the user
$adapter->setAccessToken(/*tokens I've got from getAccessToken() on /twittercallback/*/);
if(! $adapter->isConnected()) {
// never goes here, so adapter is connected
return null;
}
try{
$response = $adapter->setUserStatus('Hello world!');
}
catch (\Exception $e) {
// here I've got the error
echo $e->getMessage();
return;
}
Tried to recreate tokens and key\secret pairs and passed auth process for the app many times, including entering password for my Twitter account (as suggested in some posts on stackoverflow) but still have this error.
P.S. According to this, Hybridauth has fixed the issue in the recent release.
It looks like you are using application authentication as opposed to user authentication. In order to post a tweet, you must authenticate as a user. Also, make sure your Twitter app has read/write privileges.
After comparing headers of outgoing requests from my server with the ones required by Twitter, I've noticed that Hybris doesn't add very important part of the header: oauth_token. At least it's not doing this in the code for Twitter adapter and for the scenario when you apply access token with setAccessToken(). It's just storing tokens in the inner storage but not initializing corresponding class member called consumerToken in OAuth1 class.
So to initialize the consumer token properly I've overridden the apiRequest method for Twitter class (before it used the defalut parent implementation) and added a small condition, so when consumer token is empty before the request - we need to try to init it.
public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [])
{
if(empty($this->consumerToken)) {
$this->initialize();
}
return parent::apiRequest($url, $method, $parameters, $headers);
}
I'm not sure that I've fixed it the best way, but as long as it's working - that's fine.
For your info setAccessToken was fixed in v3.0.0-beta.2 (see PR https://github.com/hybridauth/hybridauth/pull/880)
I faced the same error when implementing a sample app in clojure and the following resource was a huge help to sort out my confusion about application-only auth vs user authentication: https://developer.twitter.com/en/docs/basics/authentication/overview/oauth

laravel - Google Analytics API Authentication

I am trying to create a web-app with a simple dashboard with Analytics data for the accounts who logged in with Google. I am using Laravel with Socialite package, and I can log the users in with Google currently. I have my developer client-key and client-secret. I set scopes for Analytics read-only, and offline access, and I store customer name, email, Google ID, access token and refresh token in my database. I can log the user in without any problem.
What I want to do is for now, just access the profiles an Analytics account currently has. I followed Analytics API documentation examples but could not get it working. Since I am storing an access token and a refresh token, I think I should be able to authenticate the current user and get their Analytics data, but I could not find any simple methods from the Client and Analytics libraries. I will need to access their Analytics data offline, and this is why I think I should be able to authorize my requests with access token and refresh token, but I do not get any Analytics specific data from user login process. I am completely lost now, how do I authorize my requests to Anayltics API? I have been using AdWords API for more than 8 months, and everything is crystal clear in AdWords API documentation, but I could not get anything working with Analytics API.
These are my user login methods:
public function redirectToProvider()
{
$parameters = ['access_type' => 'offline'];
return Socialite::driver('google')
->scopes(['https://www.googleapis.com/auth/analytics.readonly'])
->with($parameters)
->redirect();
}
/**
* Obtain the user information from Google.
*
* #return Response
*/
public function handleProviderCallback()
{
$outsiderLogin = Socialite::driver('google')->stateless()->user();
$user = User::where('googleID', $outsiderLogin->id)->first();
// Register the user if there is no user with that id.
if (!$user) {
$user = new User;
$user->name = $outsiderLogin->name;
$user->googleID = $outsiderLogin->id;
$user->email = $outsiderLogin->email;
$user->token = $outsiderLogin->token;
$user->refreshToken = $outsiderLogin->refreshToken;
$user->save();
}
// Log the user in.
Auth::login($user);
return redirect('/home');
}
Thank you very much.
I have found the solution for now. At first, I figured that I needed the code that returns with authentication URL from Google, and when I inspect the Socialite package, I have found a protected method getCode() in \vendor\laravel\socialite\src\Two\AbstractProvider.php, which returns the code from the URL. I edited the source file of the package and changed the method type from protected to public, and that made it possible to use that method outside of the class, which allowed me to access the code from the URL, then store it in DB for further authentication requirements. But there were issues with this setup, first of all, I should find a way to keep that package without any update, since any update will rollback the changes I made to the source file. The second problem I faced was the way I store tokens. By default, Google Client API returns an array which contains the fields access_token, refresh_token, expires_in, id and created, and with these fields, it authenticates the requests to Analytics server. In my scenario, there were no standard array returning from the basic Socialite login process. There were access_token, refresh_token and expires variables and I stored them all in my database as well. This caused an issue with Google library, it asked for a structured array and I did not even have the variables expires_in and created, this is why I setup a fake array which tells Google to refresh token with every request, and this was not a good practice either.
At the end, I could not understand how to use any package online and I wrote my own simple authentication, and I do not know if it has any vulnerabilities, but it works for me, it may also work for those who needs it.
Here are my routes lines:
Route::get('auth/google', [
'as' => 'googleLogin',
'uses' => 'Auth\AuthController#redirectToProvider'
]);
Route::get('auth/google/callback', [
'as' => 'googleLoginCallback',
'uses' => 'Auth\AuthController#handleProviderCallback'
]);
And these are the AuthController methods:
/**
* Redirect the user to the Google authentication
*/
public function redirectToProvider()
{
// Create the client object and set the authorization configuration from JSON file.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/auth/google/callback');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType("offline");
$auth_url = $client->createAuthUrl();
return redirect($auth_url);
}
/**
* Obtain the user information from Google.
*
* #return redirect to the app.
*/
public function handleProviderCallback()
{
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
return redirect('auth/google');
} else {
// Authenticate the client, and get required informations.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->authenticate($_GET['code']);
// Store the tokens in the session.
Session::put('token', $client->getAccessToken());
$service = new Google_Service_Oauth2($client);
$userInfo = $service->userinfo->get();
$user = User::where('googleID', $userInfo->id)->first();
// If no match, register the user.
if(!$user) {
$user = new User;
$user->name = $userInfo->name;
$user->googleID = $userInfo->id;
$user->email = $userInfo->email;
$user->refreshToken = $client->getRefreshToken();
$user->code = $_GET['code'];
$user->save();
}
Auth::login($user);
return redirect('/home');
}
}
I have placed the client_secret.json file I have downloaded from Google API Console into the specified folder, this may be different for you. I have also modified the migration file in order to match the required segemnts. After these steps, I am able treat that user as it is a simple user that registered with the basic Laravel auth.
Now I can query, say, the accounts in the user's Google Analytics account like this:
/**
* #var $client to be authorized by Google.
*/
private $client;
/**
* #var $analytics Analytics object to be used.
*/
private $analytics;
public function __construct()
{
$this->client = $this->AuthenticateCurrentClient();
$this->analytics = new Google_Service_Analytics($this->client);
}
private function AuthenticateCurrentClient(){
$user = Auth::user();
$token = Session::get('token');
// Authenticate the client.
$client = new Google_Client();
$client->setAccessToken($token);
$client->authenticate($user->code);
return $client;
}
public function GetAccounts(){
try {
$accountsObject = $this->analytics->management_accounts->listManagementAccounts();
$accounts = $accountsObject->getItems();
return $accounts;
} catch (apiServiceException $e) {
print 'There was an Analytics API service error '
. $e->getCode() . ':' . $e->getMessage();
} catch (apiException $e) {
print 'There was a general API error '
. $e->getCode() . ':' . $e->getMessage();
}
}
There were thousands of times Stack Overflow has helped me, I hope this helps someone to get things working.
You're not really going to find what you're looking for with the Socialite package shipped with Laravel (which is more used for logins and that's about it).
You can however find many Google Analytic packages (along with many other Laravel bundles) here which should help you make API calls:
http://packalyst.com/s/google%20analytics
More specifically, this package: https://github.com/spatie/laravel-analytics
That, or run your own Guzzle and cURL scripts. I use Guzzle when I need something quick without building a full blown API.
However, there's an interesting post here about using Socialite to access GA data. But you're quite limited. If you're creating user driven dashboards, I'd opt for a separate package.
https://laracasts.com/discuss/channels/tips/how-i-made-google-analytics-work-with-socialite
I'am also trying to do the same thing. By far, I've user authentication at place with oAuth 2.0 and Socialite package. I need the list of sites to be fetched from GA. And am totally stuck there. It would really be great if you can guide me as on how should I move on further..

Categories