How to avoid 'Choose Account' screen with Google Calendar API? - google-api

Our app is importing the next 1000 events from a user's Google calendar API. We ran into the problem where nginx would timeout. To get around this I'm putting the pagination data into a session variable and making separate HTTP requests to the API. This works except for one problem: every time we make a new HTTP request the API asks the user to choose which account they want to use (one user with multiple gmail accounts). I would have thought that the pagination data would include account selection but this is apparently not the case. How can I programmatically select the email account within the HTTP request?

You can store it once
public static void setmCredential(GoogleAccountCredential _mCredential) {
mCredential = _mCredential;
mService = new com.google.api.services.calendar.Calendar.Builder(
transport, jsonFactory, mCredential)
.setApplicationName("YourApplicationName")
.build();
}
And then when caliing pass it like this
new MakeRequestTask(AccountCredential.mService).execute();

Related

How to access private files in s3 storage by laravel api for android application

Each user has a number of private files (photos, videos, etc.) stored on the s3 disk.
From the mobile application side, I send a request to Laravel web service to get the list of files and show it to the user on the client side.
I use the resource collection in Laravel to send responses and send my list to the mobile application.
My question is, how can I access the file itself using the file path on the client side?
Do I need to send a request to Laravel for each file to request a download type response for each file?
Considering that the number of files is more than one file and I want to show a list view inside the mobile application and I don't want to send a request to the server for each photo and download the photo.
I want the accessible links to be returned as soon as I get the list from the laravel app so that I can display them on the app side.
Laravel Side:
Route::get('api/user/{user}/files', function (User $user){
$files = $user->files();
return new FileCollection($files);
});
Route::get('api/download/{path}', function (string $path){
return Storage::disk('s3')->download($path);
});
Client Side:
What do I do here?
You can call Storage::disk('s3')->temporaryUrl($path, now()->addMinute()) to generate publicly accessible links for private files (links are going to expire in 1 minute in this example).

Create new Parse.Session programmatically for a user, without their password

I'm working on a existing Parse server, and I am currently adding an OAuth system, so that external apps can connect in the name of Parse.User.
I created different classes for codes and tokens, and now my external apps can send requests with an accessToken, corresponding to their application and user (who granted access).
I'm looking for a way to inform the Parse server that the "logged in user" in requests is the end user that authorized the OAuth application. For this, I have created an express middleware handling request before the Parse server middleware, extracting the access token from the request, getting the correct User and Application, and then I wanted to create a Parse.Session programmatically, get the token, and set it in the request as x-parse-session-token. This way, the next handler, Parse, would treat the request as authenticated and performed by the end user.
My problem here is that I cannot find a way to create a session programmatically, I'm aware of the Parse.User.logIn, but that works only with a password.
I've tried the following:
const oAuthSession = await new Parse.Session().save({
user: user // user got from Parse.Query(Parse.User) with masterKey
}, { useMasterKey: true })
But get a Cannot modify readonly attribute user error.
Any hidden method to programmatically create a Parse.Session without a password ?
As pointed out by #DaviMacêdo in the community forum: https://community.parseplatform.org/t/create-new-parse-session-programmatically-for-a-user-without-their-password/1751
We can inject the user directly in the request field, and it will be picked up by Parse: https://github.com/parse-community/parse-server/blob/f6a41729a7a3adc6bd5310cefb3458835b4abb58/src/middlewares.js#L199
const user = await new Parse.Query(Parse.User).get(‘idOfUser’);
req.userFromJWT = user;

How to handle social login? - example flow

I have more conteptual question, how exactly should I handle social login in my project.
The use case is that I would like to allow user to login with Facebook, and keep on my backend information about this user (email, firstname, lastname)
I have some proposal Flow, but I'm not sure if it's a proper approach.
Let's say that I have application architecture as above. Now I would like to explain step-by-step full success flow.
Client (Vue application) make a call to AuthProvider (Facebook)
AuthProvider returns access_token
Client after reciving access_token make a call to backend endpoint like /fb_profile with access_token and userID (?)
Backend make a call to AuthProvider to check if given by client access_token is valid or not.
AuthProvider returns information about user. Backend after getting information about user, save it to database and generate new JWT token
Backend returns generated token to user
Now my question is - Is this good approach? Or should i handle it in other way? Like keep more logic to backend part? Instead of make a call to Facebook from Client, maybe should I make a call to backend, and backend make a call to Facebook?
You seem to be on right track. There could me many ways to do the same thing, here is the way which is working for me using vue/laravel/passport/socialite/github.
Trigger redirect in controller from frontend,
Provider(here github app) is triggered in browser with its url using client id/app name saved in back end config/env. Fill out your login details
It will redirect as created in provider and configured in backend-> show it on frontend, in my case its
http://localhost:8080/authorize/github/callback
From frontend now trigger callback in controller, it will check if user details already exist and will insert if its first time user as per logic. Then it will send back access_token to frontend which can be used in frontend for all the operations
DB
The above will be the sequence of the request flow ( same as yours ).
This would be the standard practice we used to integrate with Facebook. In this case, I strictly advise you to use the JavaScript SDK for Facebook.
Refer below link in case if you run into the following issue:
Vuejs component wait for facebook sdk to load

Running Google Picker with offline access oAuth token

What I am doing:
I am integrating Google Picker on my page. This will allow users to select files from their Google Drive to be used in the web app. In the app, people in a group share a common google drive (i.e. they all can select files from account example#email.com) which was created by group admin by his email address. When the admin signs-up for the account we do OAuth and get access_token with refresh_token against our app on google (with offline access enabled). I plan to use the access_token and refresh-token of the admin, on other group user's account when they try to use picker to select files.
What I have done:
I have integrated the Google Picker successfully in my app using the basic code provided in docs. Then to achieve what I wanted, I removed following code from the example code:
gapi.load('auth', {'callback': onAuthApiLoad});
and
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
and
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
createPicker();
}
}
and instead of .setOAuthToken(oauthToken) I pass refreshed access_token directly as string (I get that from my server with an ajax call).
.setOAuthToken("<access_token>")
But every time I call picker.setVisible(true); I get a screen in an iframe saying In order to select an item from your online storage, please sign in.
Problem:
Try to add sign in listener. Listeners provide a way to automatically respond to changes in the current user's Sign-In session. For example, after your startup method initializes the Google Sign-In auth2 object, you can set up listeners to respond to events like auth2.isSignedIn state changes, or changes in auth2.currentUser.
Validating the token might be a possibility before using the token each time but that might add a lot of extra overhead for a rare use-case each time we load the picker and when calling the API endpoints with a token after the re-authentication issue, there was no key about the token being invalid. You can validate a token by making a web service request to an endpoint on the Google Authorization Server and performing a string match on the results of that web service request.

Correct way to use a Google Apps Marketplace service account to connect to Gmail IMAP and other services

One of the features of our Marketplace app makes use of accessing the user's Gmail account via IMAP. We are using the google-api-java-client and google-oauth-java-client libraries and code similar to this example in the java-gmail-imap project as follows:
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_ID)
.setServiceAccountScopes(Arrays.asList(GMAIL_SCOPE))
.setServiceAccountPrivateKey(PRIVATE_KEY)
.setServiceAccountUser(emailAddress)
.build();
credential.refreshToken();
We are then using code based on the examples at https://code.google.com/p/google-mail-oauth2-tools to make the IMAP connection e.g.
IMAPStore imapStore = OAuth2Authenticator.connectToImap("imap.googlemail.com",
993, emailAddress, credential.getAccessToken(), false);
The majority of the time this appears to work correctly, however we are seeing that for a small but significant number of requests the call to Google made by refreshToken() fails with an HTTP 500 error and an HTML response where the JSON would normally be returned e.g.
<p class="large"><b>500.</b> <ins>That's an error.</ins></p>
<p class="large">The server could not process your request.
<ins>That's all we know.</ins></p>
We were advised by a developer advocate at Google that we refresh tokens are not supported for service accounts and we should be using an approach like in this example.
However, it seems like without the call to refreshToken then accessToken is not populated on the credential object and then this results in a NullPointerException when we call OAuth2Authenticator.connectToImap
From the source for GoogleCredential it did seem like executeRefreshToken() is overridden to handle service accounts i.e. instead of performing a refresh it simply requests a new token, and then this bit of code in Credential then handles populating the access token:
TokenResponse tokenResponse = executeRefreshToken();
if (tokenResponse != null) {
setFromTokenResponse(tokenResponse); ....
We were unsure whether we need to enclose our call to refreshToken() in a retry loop to work around the intermittent 500 errors or whether we need to make other changes to our code to follow the recommended approach for this scenario.
Can anyone advise?
I use the java-gmail-imap example code in production (but it is only used to display an inbox in our University portal, there isn't much interaction that would require me to reuse the same refresh token for instance).
Depending on your usage, I wonder if in your case some kind of throttling is coming into play (I've read in places that Gmail can occasionally throttle access).
Elsewhere I've seen Google APIs talk about making retries using an exponential backoff algorithm.
You have to be a little careful when comparing the usage of OAuth 2.0 with the other Google Service APIs and Gmail. Gmail is special in that it uses XOAUTH2. That said I've seen other Google API's that appear to need the refreshToken call. The documentation is a bit unclear and says things like "Refresh the access token, if necessary" (as you say it doesn't seem to work without this step but I haven't done any experimentation with re-using refresh tokens via credential.setRefreshToken(String refreshToken)).
I'd be interested to hear how you get on.

Resources