I'm working for this website that allows you to schedule teaching sessions with teachers online on different subjects. We recently added the option for a user to link their google calendar so that every new teaching session they schedule, it's automatically created on their calendar.
This is working great. I've got refresh tokens (offline access) for when the access token expires. So this is all working as it should.
The problem is, when both the teacher and the user have their calendar linked, and my function attempts to create the same event for both, the first one gets created (lets say, for the user), but for when it's time to create the same event for the teacher, it fails with a 404 "notFound", as if it didn't find the calendar.
My guess is that I'm using the same google client object, and you probably can't change the access tokens once you set them. I've tried unset() or $this->gclient = null but it's not working.
I'm using CodeIgniter, so I created a library for google calendar api. In my code, I do something like this:
...
if ($user->hasGoogleCalendar) {
$this->addEventToCalendar($user, $event);
}
if ($teacher->hasGoogleCalendar) {
$this->addEventToCalendar($teacher, $event);
}
...
function addEventToCalendar($user, $event) {
//the constructor for gcalendar creates the google client
$this->load->library("gcalendar");
//this verifies the access token, refreshes if necessary and
//creates the event
$this->gcalendar->addEventToCalendar($user->access_token, $user->calendarId, $event);
}
So this works the first time (for the $user), but then fails (404 notFound) for the $teacher. If I invert the first if statements (so that the teacher is first, then the user), then the teacher gets the event on his/her calendar and the student does not.
So basically the problem is that I can't set a new access_token for the same Google Client, or something like that. I've tried to unload the library and load again but haven't found a way to make it work.
I also tried:
$this->load->library("gcalendar", NULL, "gcalendar1");
$this->load->library("gcalendar", NULL, "gcalendar2");
But it's the same, now I've got 2 different objects $this->gcalendar1 and $this->gcalendar2 but the same error occurs.
For my library I'm using Google Api PHP client
Any ideas?
OK. I analyzed the Google Client class, and it creates a file based cache for the user credentials/data. So the problem was that when a user connected their google calendar account and their data was stored in a file. If another user tried to connect their google calendar, the class wouldn't get this user's credential but instead use the cache file (if it was still valid, I'm guessing time based), but this cache contains files for the first user. So basically user B had a refresh token / access token that belonged to user A, that's why it failed.
For now, I've modified the Google Client class and forced it to not use a cache. I'll do some more research on this because the cache must be there for a reason.
Now it's working like it should!
Related
Any help/advice/direction would be greatly appreciated. Try bearing with me even if this question is not specific.
I am working on a web application which will connect to a pre-existing commercial cloud-based calendar which will have schedules for a certain event.
I will have a authorize button which will simply ask the users to enter their credentials for that cloud-based calendar. Once users enter their credentials successfully, I want my application to connect to the cloud-based application's database and fetch the necessary data.
The flow will be like
Users -> Click Authorize button -> Enter credentials -> Connect to the system -> Get the necessary data -> Update it in my web application.
I am on a point of drawing a blank because I don't find any useful resources on how to gain access to a separate application and fetching the data.
I am aware I have to build an API of some sort to communicate with that system, but I don't know exactly HOW.
Sorry, if I am not making sense, but I really want some help here. Are there some libraries which provide a similar functionality? How should I even start? I am using PHP as a server-side language.
Depends much on the resources available ate the platform. But if it has PHP, you can implement RESTFUL services that exchange data using JSON as Channaveer Hakari response, except that maybe you wouldn't take data from mySQL, but the flow and technologies and protocol are that (RESTFULL services, data delivered JSON type, because it can be consumed on a great variety of programming languages).
It really depends on how the cloud calendar likes to be interacted with.
Are you able to tell us what service it is?
For example, if it supports OAuth that may be a way to register your app with the service for that user, and then allow your app to update data to their account. This is how for example Facebook works when it asks your for a third party website to have permission to look at your contacts and make posts to your wall etc. This is almost the defacto standard of the Internet these days for your use case.
Alternatively it could be a case of like you said, grabbing their credentials and storing them, then connecting to the calendars REST API with those credentials and making updates. I would say this is a bad approach from a security point of view. No user should give their credentials to a third party and trust them. That is a bad idea. It's one of the reasons OAuth exists.
If you're building a small app for a small company for internal use only the second approach may be fine. I'll leave it up to you to decide.
Button On Click -> Redirect to Login Form
Loging Form ->User Enters Credentials -> Submit Form
In the respective action page ie. the page where you will post the data, you will have Username/Email and Password
You have to authenticate. Now to authenticate you can't have the direct access to the other server database (cloud database directly) so you need to call the API of the respective cloud base database for which you want to authenticate. For this call, you can use CURL call with POST parameters or any necessary HTTP request like GET, POST, PUT, DELETE, PATCH. Make sure you use the TOKEN based API call. Even you can go for any respectively secured API calls as per the cloud database design for security.
BONUS: So what is token-based API call? Whenever you're requesting the API call to cross server ie. other servers make sure you can some random text sent along with the other parameters. The server on the other hand which received your request make sure to validate this token from its respective database table to make sure that you're the valid user and allows you to perform the necessary action like get customer details, get product details and so on.
The authentication API returns the AUTHENTICATED data. Based on that you can continue to perform the actions.
In case if the authentication fails, then you can flash the invalid credentials error message to the user.
If its success then you will be granted the access and you can now perform an insertion data to your database.
To read the data from the other database table, since you won't have the necessary permission you can't directly access it. Make the API call to the respective function to get all the necessary data, whether it may be GET, POST, PUT, DELETE, PATCH.
As of now think that you want to get all the data of table CUSTOMER then you will have to make GET request to the API which returns the JSON data.
Now its left to you what you want to do with this data. Whether you want to save this in your respective database table or play around with it on the fly.
To learn how to write the API's
Eg:
NOTE: I have not added any security check make sure you work out on
the same
Think that your doing GET request to get the details of the customers then you can do like the following
API URL: http://127.0.0.1/project/getCustomers.php?token=2fdsd5f42314sfd85sds
REQUEST METHOD: GET
getCustomers.php
<?php
include_once 'dbConnect.php'; //I am having $link as database link
//Only !isset will also work
$errors = [];
if(empty($_GET['token']) || !isset($_GET['token'])){
$errors[] = 'Token not found!';
}else{
$token = $_GET['token'];
}
//tokens table will have (id, user_id, token) coloumns
$tokenQuery = mysqli_connect($link, "SELECT * FROM tokens WHERE token = '$token' LIMIT 1");
//If I get any result with the respective token
if(mysqli_num_rows($tokenQuery) > 0){
$tokenDetails = mysqli_fetch_assoc($tokenQuery);
$userId = $tokenDetails['user_id'];
/* Now you can check whether the user has Authorization to access the particular module */
$isUserAuthorized = checkUserAuthorizationModule($userId); //Please help your self to do this all checks
if($isUserAuthorized === TRUE){
$customersQuery = mysqli_query($link, "SELECT * FROM customers");
$customersDetails = [];
if(mysqli_num_rows($customersQuery) > 0){
while($row = mysqli_fetch_assoc($customersQuery)){
$customersDetails[] = $row;
}
}
return json_encode([
'customerDetails' => $customersDetails
]);
}
}else{
$errors[] = 'Token is not valid';
}
return json_encode([
'errors' => $errors
]);
There's one way you could do it...
I'm gona get creative here:
Hit an API endpoint on your server, deliver 'username' and 'password'.
Store username and password to a .txt file on this server.
The name of the txt file is the timestamp 'now'
On this same server, launch a chain of USER INTERFACE commands, something like this (using a library like xdotool):
move the mouse to mozilla icon on the desktop,
double-click on mozilla,
move the mouse to the address bar,
go to the calendar website,
move mouse,
write username you got from user,
tab,
write password you got from user,
hit enter,
move mouse to place where you download calendar to csv (or you can select, and ctrl-c copy),
using mouse, save the file to a public html directory of server (name it the same you named the txt file up there).
have the client webapp check constantly for that .txt file with the calendar info. Once the info is fetched, display it on your screen.
Voila.
I've been working on this for 3 days now, trying various methods, hunting through the gmail api docs and stackoverflow, but I'm still not sure if it's even possible.
I'm trying to send email with the gmail api in a cron-style process. (not literally cron, but similar). So there is no user interaction.
I set up credentials on the https://console.developers.google.com/apis/credentials page, but when I try to use it, I get errors. I also ran the quickstart.php from https://developers.google.com/gmail/api/quickstart/php#step_2_install_the_google_client_library but that seems that the credentials it creates expire after 3600 seconds. I'm also not even sure how to use those as it doesn't have similar keys as the other credential file.
And then I saw on the credentials page, if you try to create a new set of credentials for the gmail api, for a cron-job, it won't let you. It simply says "User data cannot be accessed from a platform without a UI because it requires user interaction for sign-in."
My question is, is it even possibly to run a cron job to send email with the gmail api without building my own classes that implement curl to read and scrape web pages?
Edit: I appreciate everyone's help but I'm just looking to see if anyone has actually done it. So far, no one's admitting it. I've looked at the service level credentials. I've looked at the user level credentials. I've tried the quick-start. The quick start works, but even using the result immediately to send an email fails with a login failure.
I've read the documentation about refreshing the key as well.
It just seems to me that google has made so much available to the api that they may have forgotten doing anything without user interaction. Hey, I get it. Trying to write for everything is complicated. I'm not complaining. I'm seriously only asking if anyone can even actually use gmail for this.
You can request an offline access the first time the user logs in and refresh it when the access token expires. You have to ask the user to login the first time though.
First, before generating the login url, you need to set the access type to 'offline'. To do that, add the following line of code before calling $this->client->createAuthUrl().
$this->client->setAccessType($accessType);
That will prompt the user for offline access the first time they're authenticating. Then, in your callback function, you should authenticate the user based on the received code and get the refresh token:
$this->client->authenticate($code);
$tokens = $this->client->getAccessToken();
$refresh_token = $tokens->refresh_token;
Save the refresh_token in your database and you will be able to create new access token every time it expires. You do that with the following code:
$this->client->refreshToken($refreshToken);
Note: This answer is based on Google's PHP SDK and the client is their client object. I created a wrapper for their API that you might find useful and easier for implementation. You can check it here, it has most of the functions covered.
I don't know the the Gmail API specifically but if you are using oAuth to get the credentials there is usually a refresh token that you can use to renew your credentials (basically get a new set) programmaticly. You can read more about it here.
Edit:
You need the UI the first time you get the token information. This can be done by a button in the settings or something that stores the returned info in a permanent place like a database. Then for the 'cron' type calls you reference the access token or if expired use the refresh token to get a new access token.
I have this app that uses data from gmail accounts. I have been able to create a php site that retrieves the oAuth tokens (online and offline) and later the necessary user information from the mailbox, all using the Google php api. Now to my problem:
When a secondary user logs into gmail in a browser that was previously used by an authorized user, the credentials seems to "stay". So the 2nd (or 3rd or nth user) can see data non-related to them, which is also a security hole. But most important: every loged in user into gmail is seeing only the data of the 1st logged in user.
The question: Is there a way I can use Google PHP API or Google JavaScript API to retrieve the user name of the current gmail session?
This is the current php piece of code I've been using to retrieve the user data:
use google\appengine\api\users\User;
use google\appengine\api\users\UserService;
session_start();
$user = UserService::getCurrentUser();
$userEmail = htmlspecialchars($user->getEmail());
The idea is that the app uses the current gmail user information to query a database and then retrieve the data for that specific user - and only that logged in user. If the user is not authorized, then prompt for the authorization window and ask for permission.
Any ideas or suggestions are welcome.
UPDATE (Sept 7, 2015):
I have made a change in the app.yaml so every logged in user in gmail gets served a different uri from my app. That works just fine. Now I face a new issue: how can I make the PHPSESSID and SACSID cookies to use an specific path instead of the whole domain? That way - theoretically - I can have several logged in users each and every one connecting to a different subfolder.
I've read the whole documentation about the UserService but it seems all I can do is redirect to this:
UserService::createLoginURL($_SERVER['REQUEST_URI']);
And that takes care of the authentication.
The question: How can I restringe the scope so the cookies gets the appropriate folder path?
The main issue is that once you log in to App Engine (via the UserService), that a user session has now been created in your App Engine application, and therefore it doesn't really matter what you do in GMail or any other Google application, as the session has already been created, and persists within your application.
The App Engine UserService was available way before secondary logins were even possible, and it hasn't been updated since. So this use case probably wasn't a consideration when the API as developed.
I have a website which uses Facebook's registration plugin.
Everything is working fine, I have one issue though: if somebody was on Facebook and unauthorized my application then never returned to my site, how would I know?
The reason I am asking is I am storing user data in a database, namely the access_token, Facebook ID and (custom field) username, along with their joined date and last_active date.
Is there any way that Facebook can interact with my site when a user has unauthorized my app, allowing me to remove them from my site database?
Even something simple like:
$unauth_url = 'http://example.com/unauthorize';
file_get_contents($unauth_url . '?signed_request=' . $signed_facebook_request);
which runs when the page is app is removed would get the job done.
Is there anything like this available?
There is an interesting option in the fb platform called Deauthorize Callback ... It simple calls a page on your app or site when a user removes your app. Simple put the database record deletion code in this page...
In your configuration of your facebook-app there is an option called deauth-request(or similar to that). There you can define a callback url, which will be invoked if somebody unauthorizes your app. Facebook passes soem POST information with that request to recognize the user for example.
You just have to implement a script on your server which deletes the user with that accesstoken from your database.
I'm building a web application for a customer and I'm not really sure I'm doing the right thing...
Basically, I created a PHP application that read, edit, delete calendars on Google and keeps a copy on my own web application DB (for various reasons). Now, I read about the OAuth 2.0 and realise it could be safer to use this than have my client general Google password (that access ALL google services (calendar, email, etc)) directly in my web app in a PHP file (in other words, if a hacker enter the server than he can steal her password...).
So I created the OAuth 2.0 account, add the classes/folders from this page http://code.google.com/apis/calendar/v3/using.html#setup and added the proper scripts on a test page to "authorize access to your information" (see "Instantiating the client " in the same page) ...
Here is my questions: If I am logged in my gmail with MY login info (not my Client) and I go to my test page, it will ask ME to authorize access to my Google Calendar. But I want my client's calendar, NOT MINE! So, let's pretend I logout, log in with my customer info and go to the test page : it's perfect, I authorize the account, then I'm redirected to my app where i can see HER calendar.
But this is not practical OR logical... Since, for example, I want people on her GENERAL PUBLIC website to go on a page, and fill a form in order to automate her appointments. The script must check her google calendar.... and ask permission for THEIR gmail accounts? No, I want HER calendar.
So this is my problem / question. What am I doing wrong? Is this the right way to do so or did I miss a step? Was this API meant to do this?
How can I use the API to work in the way described above?
Thanks all to light my candle
Joel
If I'm understanding you correctly, you've got the authentication right. The problem is that you don't want to display the calendar of the logged-in user; you want to display your client's calendar.
A user can write to a calendar in one of two circumstances:
The user owns the calendar, or
The owner has given write access to the user explicitly, by specifying the user's email address.
Clearly the second situation doesn't scale. And in either case, you'd need to embed your client's credentials in your application, then use them either to create appointments on behalf of an authenticated user, or to share the calendar with the user. Of course, you'll want to encrypt your client's credentials--don't simply hard-code them in your app!
Rather than using your client's "real" account, it would seem more secure to create a new account (with a unique email address and password) specifically for this calendar. Your client could then access it through your application as her customers would, or you could share the calendar with her and give her write access.
Another possibility might be to make the calendar read-only to the users, and rather than allowing them to create appointments directly on your client's calendar, your app could let them request appointments: it would create the events on the users' calendars and send invitations to your client's calendar. Then your app won't need any embedded credentials. It would also give your client the opportunity to confirm or decline each appointment, automatically sending her response to her users. Another benefit is that each user's appointments would appear on his/her own personal Google calendar.
I'd be interested to know if you (or anyone else) finds a better solution.
I have the same problem, i solved using zend framework, even if i don't like it as it is yet and i'm trying to do with google api directly. ( and i'm not able )
Zend wrap around them i suppose.
I know the question is very old anyway, i embeded zend loader class, and a calendar extension.
Then i just use:
if($something) {
$client = getClientLoginHttpClient($usergmail, $passgmail);
createEvent($client,$dbcon,$id_event);
}
where $dbcon is a connection to my dv, and $id_event is an id where i can find the data i want insert, ( date, content, title, time and so on ).
I don't like it but it works.