NoActiveAccessToken even though Token Exists - OAuth2 + Lumen of Laravel + Swift - php

I accessed to the access_token successfully, however I'm having problem getting the user_id. I am getting error:
NoActiveAccessTokenException Authorizer.php line 104 Tried to access session data without an active access token
What am I doing wrong?
$app->get('/me', function () {
$id = Authorizer::getResourceOwnerId();
return response()->json(['id' => $id]);
});
On Swift (note that token is valid):
func me(handler: (data: NSDictionary?, error: String?) -> Void)
{
let url = NSURL(string: "/me", relativeToURL: self.baseUrl)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
if let t = self.getAccessToken()
{
request.setValue("Bearer \(t)", forHTTPHeaderField: "Authorization")
print(t) // Token is valid "y7bPb1NgUBxmHSIbwsgBgPxJcM0ywnithG5PvEEv"
let taskInstance = DataTaskHandler()
taskInstance.make(request, handler: { (result, error) -> Void in
if let res = result
{
if let jsonDictionary = JSONParser(data: res).dictionary()
{
print(jsonDictionary)
handler(data: jsonDictionary, error: nil)
}
}
})
}
else
{
handler(data:nil, error: "The access token is not available")
}
}

Take into account that you access_token is only valid for shor time. SO after that time, the access token must be refreshed, using the refresh_toke, grant_type. Check here the details: https://github.com/lucadegasperi/oauth2-server-laravel/wiki/Implementing-an-Authorization-Server-with-the-Refresh-Token-Grant
Alternatively you can increase the time for the access_token. Just go to config/oauth.php and change the ttl index (normally is 3600 -> 1 hour), please take into account that one access_token must have shor time for security reasons, so dot increase too much the ttl.
EDIT:
It is importan to check that you are using the oauth middleware in the controller method or during the request, because this middleware stablish the access_token value during the whole request.
Just add this in the constructor of your controller.
public function __construct()
{
$this->middleware('oauth');
}
Hope it helps.
Bets wishes.
PS (for other readers): This question is related with the Lumen course here

Related

How to Auth::check() user on Laravel with API calls

I'm using VueJS in my front-end, so I have this API call somewhere in the front-end code:
let products = axios.get('api/products');
And then in the routes/api.php:-
Routes::get('products', 'ProductsController#index');
in the ProductsController's index method:
public function index () {
if ( Auth::check() ) {
$user = Auth::user();
return $user->products;
}
// return 'You don't have any products right now!';
}
The index method will always return null even if the user is logged-in!
So how to authenticate the users in this case while using the API calls?
Are you sending auth token because when we write the API we need to deal with auth token and not with Auth::check().
You need to send auth token with user id at the time of api call and verify that details to proceed.
You can check user Auth of API calls by this:
At first add this use code in your Class:
use Auth;
Then add this code in your function:
if (Auth::guard('api')->check())
{
logger(Auth::guard('api')->user()); // to get user
}else{
logger("User not authorized");
}
Return the data via json and decode them in your frontend again. Maybe this helps?
Make sure you're sending the CSRF token, somewhere in your bootstrap.js file, you should have something like this. Also make sure you have the csrf token somewhere in the meta tag
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common = {
'X-CSRF-TOKEN': Laravel.csrfToken,
'X-Requested-With': 'XMLHttpRequest'
};
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

com.google.firebase.auth.FirebaseAuthInvalidCredentialsException The custom token format is incorrect. Please check the documentation PHP-Laravel

I was using "kreait/firebase-php" to generate Firebase Custom token in Laravel. My code to generate Custom Token is as follows:
public function getToken(){
$user = Auth::user();
$serviceAccount=ServiceAccount::fromJsonFile(__DIR__.'/service_account.json');
$firebase = (new Factory)
->withServiceAccount($serviceAccount)
->create();
$customToken=$firebase->getAuth()->createCustomToken("id");
return response()->json(["custom_token"=>$customToken]);
}
I am getting JWT token without any problem. But when I try to signIn using this token in android I get token format incorrect error. My Android Code is as Follows :
firebaseAuth.signInWithCustomToken(customToken)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d("SignIn", "signInWithCustomToken:success");
FirebaseUser user = firebaseAuth.getCurrentUser();
Log.e("current User is",user.getUid());
} else {
Log.w("Sign iN failed", "signInWithCustomToken:failure", task.getException());
Toast.makeText(MainActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
/// updateUI(null);
}
}
});
And Logcat output is :
com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The custom token format is incorrect. Please check the documentation.
at com.google.android.gms.internal.zzdxm.zzao(Unknown Source)
at com.google.android.gms.internal.zzdwn.zza(Unknown Source)
at com.google.android.gms.internal.zzdxx.zzap(Unknown Source)
at com.google.android.gms.internal.zzdya.onFailure(Unknown Source)
at com.google.android.gms.internal.zzdxo.onTransact(Unknown Source)
at android.os.Binder.execTransact(Binder.java:565)
Please help me to resolve this error. Thank you.
The generated custom token is an instance of Lcobucci\JWT\Token- when you pass it to response()->json(...), the json() method tries to json_encode() the object instead of using its string represenation, which will not work.
So, if you want to use the string representation of a JWT Token returned by one of the SDK's methods, you need to cast the object to a string:
return response()->json(["custom_token" => (string) $customToken]);

I don't understand JWT refresh token's behaviour (LARAVEL)

I have just tried JWT auth with LARAVEL and this https://github.com/tymondesigns/jwt-auth
But there's something i can't understand. In their config they put :
'ttl' => env('JWT_TTL', 60), // in munutes
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), // in minutes
What i understant : the token's live is 1hour and can be refreshed within 2 weeks
But after 3hours, if i try to query something, it says "token expired".
Does this system mean, a user must get his token updated / refreshed within every hour but with a limit of 2 weeks ? I don't get it.
How can a user persist login with this kind of system ? How is the refresh Token useful when after the first hour, though it hasn't been 2 weeks yet, i can't get a fresh token ?
thanks
UPDATE: CODE
config/jwt.php
'ttl' => 2, // 2 minutes
'refresh_ttl' => 5, // 5 minutes
routes/api.php
Route::post('/login', 'AuthController#login');
Route::get('/test', 'AuthController#test')->middleware('jwt.auth', 'jwt.refresh');
Http/Controllers/AuthController
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthController extends Controller
{
public function test()
{
return response()->json(['coucou' => 1]);
}
public function login(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'));
}
}
AND THIS IS THE FLOW :
request to /login with {username: xxx, password: xxx}
response of /login > {token: xxxxxxx}
request to /test straight after (10 secs) with Bearer xxxxxx
response of /test > the good json response with NEW TOKEN in HEADER
request to /test after 3 minutes (so 3mins 10 secs have past now, less than the 5min of refresh limit)
response of /test > token expired
I don't understand.
After the access token is expired you can use the refresh token to get a new access token without asking the user to input his username and password again.
Only after the refresh token is expired, the user needs to login again.
But after 3hours, if i try to query something, it says "token expired".
that's because the access token is expired.
Does this system mean, a user must get his token updated / refreshed within every hour but with a limit of 2 weeks ? I don't get it.
yes. You keep the refresh token in your client system and use it to request a new access token when the access token is expired.
Ok, finally I have something that works.
Remove "jwt.refresh" from your middleware. This is for one-pass-tokens as I´ve commented above.
I couldn´t get JWTAuth::refresh() to work with blacklists enabled. A "TokenBlacklistedException" is thrown when I call JWTAuth::refresh() even though I know its only expired since I do this in the catch block for "TokenExpiredException". Bug? Workaround:
JWT_BLACKLIST_ENABLED=false
You need to have a refresh endpoint you can call if /test returns 401. I use same as login, but it´s kind of custom in my case.
...
try
{
if($token = JWTAuth::getToken())
{
JWTAuth::checkOrFail();
}
$user = JWTAuth::authenticate();
}
catch(TokenExpiredException $e)
{
JWTAuth::setToken(JWTAuth::refresh());
$user = JWTAuth::authenticate();
}
if($user /*&& check $user against parameter or not*/)
{
return response()->json([
'user' => $user->profile(),
'accessToken'=> JWTAuth::getToken()->get(),
], 200);
}
else
{
return response()->json(false, 401); //show login form
} ...
This is what I did for mine, I have to set the token time to be valid to 24 hours by doing this
'ttl' => env('JWT_TTL', 1400)
I changed the 60 to 1440, and my token now last for a day.

Wrong number of segments in token (OAuth Google Api with php)

I have a php application that use OAuth2 to authenticate users to their account.
until yesterday, everything worked very well.
But today, and without changing my code, when I try to access to my account, and after I authenticates to my google account, I obtain a blank page.
I debug the code and I found that it crashed when the Google_Client try to verifyIdToken and more exactly in the function verifySignedJwtWithCerts because : $segments = explode(".", $jwt); find 4 segments and not 3.
here is my code :
...
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectUri);
$client->setScopes("email");
if(!isset($_GET['code']))
header("Location: ".$client->createAuthUrl());
else
{
$client->authenticate($_GET['code']);
$_SESSION["access_token"] = $client->getAccessToken();
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
} else {
$authUrl = $client->createAuthUrl();
}
$ticket = $client->verifyIdToken();
if ($ticket) {
$admin = $ticket->getAttributes();
}
$_SESSION["email"] = $admin["payload"]["email"];
...
Can anyone offer an explanation?
Thank's in advance
Some how I didn't find any answer in Google. So I going to share what I do to make it work.
The short answer: Because the token you use in verifyIdToken() is invalid.
The long answer as below:
I'm not sure if it is the correct steps:
After Google Login Authentication, we will get the "Authorization
Code" from oauthplayground (for testing). Please enter your oauth client_id and oauth client secret in the setting before usage.
I have use the "Authorization Code" to get the other tokens by writing
$token = $client->fetchAccessTokenWithAuthCode("THE_AUTHORIZATION_CODE");
Inside $token, I have receive the following: access_token, token_type, expires_in, refresh_token, id_token, created.
Now use the id_token in $payload = $client->verifyIdToken(id_token);
Then you will get the correct information you needed like names, aud, exp, iss etc in $payload.
Note: To avoid your code return the "Wrong number of segments in token" error again, you have to try and catch the verifyIdToken method
try {
$payload = $client->verifyIdToken(id_token);
}
catch (Exception $e) {
echo "Invalid id token";
}
On my side, I've been working on MERN Application and, the issue was on the way I send the tokenId on the backend.
Check if the idToken you are sending to the backend is the same as the one you are getting on googleSuccess.
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;//Wrong
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};
After:
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};

Performing requests to ETSY store allowing access automatically PHP OAUTH

I am using a library to connect to my ETSY store and pull data from receipts to bring them into my personal website (database).
After making the request using OAuth, I get to the ETSY site to "Allow Access"
https://www.etsy.com/images/apps/documentation/oauth_authorize.png
Then, I need to manually click on Allow Access and my request will be completed and will display the data requested.
I would like to avoid the process of manually clicking on "Allow Access", since I want my personal site to automatically display information pulled from ETSY orders.
Here is my current code for page etsyRequest.php:
$credentials = new Credentials(
$servicesCredentials['etsy']['key'],
$servicesCredentials['etsy']['secret'],
$currentUri->getAbsoluteUri()
);
// Instantiate the Etsy service using the credentials, http client and storage mechanism for the token
/** #var $etsyService Etsy */
$etsyService = $serviceFactory->createService('Etsy', $credentials, $storage);
if (!empty($_GET['oauth_token'])) {
$token = $storage->retrieveAccessToken('Etsy');
// This was a callback request from Etsy, get the token
$etsyService->requestAccessToken(
$_GET['oauth_token'],
$_GET['oauth_verifier'],
$token->getRequestTokenSecret()
);
// Send a request now that we have access token
$result2 = json_decode($etsyService->request('/receipts/111111'));
//echo 'result: <pre>' . print_r($result, true) . '</pre>';
echo $result2->results[0]->seller_user_id;
How could I automate the Allow Access part and get the returned value for my request by just running this page?
You can resolved this problem by simply save the returned "access token" and "token secret".
Steps to do it:
After making the request using OAuth, you get to the ETSY site to
"Allow Access". after allowing it will show a oauth_verifier pin.
After you enter this pin in your code it will set "access token" and
"token secret" to your request.you just need to save them in
variables or database.
next time when to create any request to etsy you just have to set
these access token" and "token secret" with your oauth_consumer_key
and oauth_consumer_secret. you don't need oauth_verifier pin at that time.
it will work util you revoke permission from your etsy account.
I did this in my java code because i mm facing same problem and its working.(sorry i m not good enough in php) here is my sample code may this helps-
public void accessEtsyAccount(String consumer_key, String consumer_secret, String requestToken, String tokenSecret, String shopName) throws Throwable{
OAuthConsumer consumer = new DefaultOAuthConsumer(
consumer_key, consumer_secret
);
if(StringUtils.isBlank(requestToken) || StringUtils.isBlank(tokenSecret) ){
OAuthProvider provider = new DefaultOAuthProvider(
"https://openapi.etsy.com/v2/oauth/request_token",
"https://openapi.etsy.com/v2/oauth/access_token",
"https://www.etsy.com/oauth/signin");
System.out.println("Fetching request token from Etsy...");
// we do not support callbacks, thus pass OOB
String authUrl = provider.retrieveRequestToken(consumer, OAuth.OUT_OF_BAND);
System.out.println("Request token: " + consumer.getToken());
System.out.println("Token secret: " + consumer.getTokenSecret());
System.out.println("Now visit:\n" + authUrl
+ "\n... and grant this app authorization");
if(Desktop.isDesktopSupported()){
Desktop desktop = Desktop.getDesktop();
try {
desktop.browse(new URI(authUrl));
} catch (IOException | URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec("xdg-open " + authUrl);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Enter the PIN code and hit ENTER when you're done:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String pin = br.readLine();
System.out.println("Fetching access token from Etsy...");
provider.retrieveAccessToken(consumer, pin);
} else {
consumer.setTokenWithSecret(requestToken, tokenSecret);
}
System.out.println("Access token: " + consumer.getToken());
System.out.println("Token secret: " + consumer.getTokenSecret());
URL url = new URL("https://openapi.etsy.com/v2/private/shops/"+shopName+"/transactions");
HttpURLConnection request = (HttpURLConnection) url.openConnection();
consumer.sign(request);
System.out.println("Sending request to Etsy...");
request.connect();
System.out.println("Response: " + request.getResponseCode() + " "
+ request.getResponseMessage());
System.out.println("Payload:");
InputStream stream = request.getInputStream();
String stringbuff = "";
byte[] buffer = new byte[4096];
while (stream.read(buffer) > 0) {
for (byte b: buffer) {
stringbuff += (char)b;
}
}
System.out.print(stringbuff);
You need to save the access token when you have requested the Etsy store for the first time and then the same access token can be used for later calls. This would prevent you from clicking ALLOW ACCESS again and again when requesting Etsy store through API.

Categories