Google calendar API - How to make a request without having to prompt a login? - google-api

I have a simple question:
I am developing a website that needs full authorisation to make requests to a Google calendar. I manage to do all the requests I need with javascript from a web server and it works, but I need to be logged into my google account for it to work. This caused a problem for other users that use my website because the request doesn't work if they are not logged into MY google account.
I understand why it doesn't work, my question is How can I do for my website to get full access granted for use the google calendar without having to log into my google account, even better if nobody had to log into a Google account to perform the task??

The form of login you are currently using is called Oauth2. It requires that a user authenticate access.
What you should be using is a service account. Service accounts are pre authorized. You will need to share your personal calendar with the service account then it will be able to access it.
The only drawback is that service account authentication is not supported by JavaScript you will need to switch to a server sided language like node.js for example.
'use strict';
const {google} = require('googleapis');
const path = require('path');
/**
* The JWT authorization is ideal for performing server-to-server
* communication without asking for user consent.
*
* Suggested reading for Admin SDK users using service accounts:
* https://developers.google.com/admin-sdk/directory/v1/guides/delegation
*
* See the defaultauth.js sample for an alternate way of fetching compute credentials.
*/
async function runSample () {
// Create a new JWT client using the key file downloaded from the Google Developer Console
const client = await google.auth.getClient({
keyFile: path.join(__dirname, 'jwt.keys.json'),
scopes: 'https://www.googleapis.com/auth/drive.readonly'
});
// Obtain a new drive client, making sure you pass along the auth client
const drive = google.drive({
version: 'v2',
auth: client
});
// Make an authorized request to list Drive files.
const res = await drive.files.list();
console.log(res.data);
return res.data;
}
if (module === require.main) {
runSample().catch(console.error);
}
// Exports for unit testing purposes
module.exports = { runSample };
Code ripped from smaples jwt

Related

What URL do I use to send users to google oauth2 consent screen

I am trying to write a simple application to access google's api using user authentication tokens and html requests, however I am struggling to find what URL I send users too in order for them to select a profile and sign in.
URL I send users too in order for them to select a profile and sign in.
The thing is you are confusing authorization and authentication. Oauth2 a user can authorize you to access their data, it has nothing to do with logging in to your application that's OpenID connect.
However what you are probably looking for is the oauth2 consent screen This is the screen where the user consents to your application accessing their data.
https://accounts.google.com/o/oauth2/auth?client_id={clientid}&redirect_uri={redirectURI}&scope={scope}&response_type=code
Remember this is only the first step if they consent then you will be given an authorization code your application must then exchange the authorization code for an access token which you can use to access the api.
You may find this video helpful in understanding the fill Oauth2 dance. Understanding Google OAuth 2.0 with curl
If you are looking to login a user and check their profile something like this would be better
[GoogleScopedAuthorize(PeopleServiceService.ScopeConstants.UserinfoProfile)]
public async Task UserProfile([FromServices] IGoogleAuthProvider auth)
{
var cred = await auth.GetCredentialAsync();
var service = new PeopleServiceService(new BaseClientService.Initializer()
{
HttpClientInitializer = cred
});
var request = service.People.Get("people/me");
request.PersonFields = "names";
var person = await request.ExecuteAsync();
return View(person);
}
The full tutorial and companion video can be found here Asp .net core 3 and Google login

How to check if user is still logged in google?

I am using Google OAuth through Laravel Socialite to authenticate all the users in my web app.
I need the user session to end as soon as possible if user logs out of his google account.
I am trying to set up a middleware that would perform regular checks if user is still signed in with google. But I can't find a way to ask "Is user#example.com still the current user on google?"
I tried to get \Socialite::driver('google')->user() in the middleware but that doesn't seem to work without doing a redirect to google beforehand. I would like this check to be as quick and unobtrusive as possible. It should also work during a background ajax call.
It seems that it would be trivial using the client side authentication as there is gapi.auth2.init().isSignedIn.get(). However, that would mean I have to ask users for two authorizations (server side and client side) which seems wrong.
Looking at the docs at google, I see nothing that would let me check their authentication status apart from reauthenticating. Access token won't expire on logout... Is there a workaround?
It turns out that we can actually use gapi javascript in tandem with Socialite - just use the same client token. I didn't suspect that I will just get all the info without logging in separately for browser session, but it works.
I added the following code to the end of my master blade template to check state for authorized users.
#auth
<script>
var currentUserEmail = '{{Auth::user()->email}}'; // user "sameness" criterion
var googleClientId = '{{env('GOOGLE_ID')}}'; // the same oauth client id
</script>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="{{mix('js/checkGoogleAuth.js')}}"></script>
#endauth
The script checkGoogleAuth is a simple then, I copied the google tutorial and shortened it:
var auth2; // The Sign-In object.
var googleUser; // The current user.
/**
* Initializes Signin v2 and sets up listeners.
*/
var initSigninV2 = function() {
auth2 = gapi.auth2.init({
client_id: googleClientId,
scope: 'profile'
});
// Listen for sign-in state changes.
auth2.isSignedIn.listen(checkState);
auth2.currentUser.listen(checkState);
};
var checkState = function (user) {
//if signed out or changed user
if (!auth2.isSignedIn.get() || currentUserEmail != auth2.currentUser.get().getBasicProfile().getEmail())
$('.logout-username').click(); //click logout button
};
gapi.load('auth2', initSigninV2); //launch it
I hope it helps someone else as well!

How to use Google API auth with flutter for obtaining Client-side Web Application credentials?

I am trying to use googleapis_auth flutter package to obtain credentials, but stick with var scopes = [...]; what does I need to fill in the var scopes
import "package:googleapis_auth/auth_browser.dart";
...
var id = new ClientId("....apps.googleusercontent.com", null);
var scopes = [...];
// Initialize the browser oauth2 flow functionality.
createImplicitBrowserFlow(id, scopes).then((BrowserOAuth2Flow flow) {
flow.obtainAccessCredentialsViaUserConsent()
.then((AccessCredentials credentials) {
// Credentials are available in [credentials].
...
flow.close();
});
});
Auth scopes express the permissions you request users to authorize for your app. You can get details about what scopes you might need to add to your auth req here
But, googleapis_auth package is meant to be used with web projects only, it can not be used in flutter project.
If you want to implement google auth, try firebase_auth which allows google authentication using firebase backend.

Google Oauth Error: redirect_uri_mismatch

I'm trying to use google Oauth 2 to authenticate with google calendar API for a web server running on AWS EC2.
When I generated the credentials I selected 'OAuth Client ID' and then 'Web Application'. For the Authorised redirect URIs I have entered:
http://ec2-XX-XX-XX-XXX.eu-west-1.compute.amazonaws.com
(I've blanked out the IP of my EC2 instance). I have checked this is the correct URL that I want the callback to go to.
The link that is generated in the server logs is of the form:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=XXXXXXXXXXXX-XXXXXXXXXXXXXX.apps.googleusercontent.com&redirect_uri=http://localhost:47258/Callback&response_type=code&scope=https://www.googleapis.com/auth/calendar.readonly
When I follow the link I get the error
'Error: redirect_uri_mismatch'.
I've read this SO question and have checked that I am using HTTP and there is no trialing '/'
I suspect that the URL generated should not have 'localhost' in it but I've reset the client_secret.json several times and each time I restart tomcat with the new client secret I still get a link with localhost but just over a different port.
Locally, I had selected Credentials type of 'other' previously and was not given an option for the Authorised redirect URI. I did try this for the EC2 instance but this won't give me the control I want over the redirect URI and sends the redirect over localhost.
Google throws redirect_uri_mismatch when the uri (including ports) supplied with the request doesn't match the one registered with the application.
Make sure you registered the Authorised redirect URIs and Authorised JavaScript origins on the web console correctly.
This is a sample configuration that works for me.
In case you are seeing this error while making API call from your server to get tokens.
Short Answer 👇 - What solved my problem
use string postmessage in place of actual redirectUri that you configured on cloud console.
Here is my initilization of OAuth2 client that worked for me.
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
My Case
On the frontend, I am using react to prompt the user for login with google with the authentication-code flow. On success, this returns code in the payload that needs to be posted to the google API server to get token - Access Token, Refresh Token, ID Token etc.
I am using googleapis package on my server. Here is how I am retrieving user info from google
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
/*
get tokens from google to make api calls on behalf of user.
#param: code -> code posted to backend from the frontend after the user successfully grant access from consent screen
*/
const handleGoogleAuth = (code: string) => {
oauthClient.getToken(code, async (err, tokens: Auth.Credentials) {
if (err) throw new Error()
// get user information
const tokenInfo = await oauthClient.verifyIdToken({
idToken: tokens.id_token
});
const {email, given_name, family_name, email} = tokenInfo.getPayload();
// do whatever you want to do with user informaton
}
}
When creating a Oath client ID, DO NOT select web application, Select "Other". This way, the Redirect URI is not required.

Gmail API suddenly stopped working with [Error: unauthorized_client]

Where I work we use Google Apps for Work. For the last 9 months we've been using the Gmail API (~2,000 requests per day) to pull in new emails for our support email accounts.
This is how we originally set it up:
Go to https://console.developers.google.com/project/
Click on the project (or create a new one)
Click on API's & Auth
Click on Credentials
Click on Create new Client ID
Click on Service account
Download a JWT (json) for the account.
Follow the node.js quickstart guide with an installed/native type token for the same account, and authorize it through the console. The JWT tokens did not work unless we did this step, once for each account.
We did this for each of our individual support email accounts to avoid having to turn on domain wide delegation for any of them in the admin console. We were then able to authenticate with the tokens using the officially supported npm library googleapis, similar to this:
var google = require('googleapis');
var jwtClient = new google.auth.JWT(
token.client_email,
null,
token.private_key,
['https://www.googleapis.com/auth/gmail.readonly'],
'supportemail#mycompany.com'
);
jwtClient.authorize(function(err, tokens) {
if (err) {
return cb(err);
}
var gmail = google.gmail('v1');
var requestOptions = {
auth: jwtClient,
userId: 'me',
id: messageId,
format: 'raw'
};
gmail.users.messages.get(requestOptions, function(err, response) {
if (err) {
return cb(err);
}
// do stuff with the response
});
});
Like I said, we used this for a long time and never had any issues. Yesterday around 10am MST every one of the accounts stopped being able to authenticate at the same time, with jwtClient.authorize() suddenly returning the error [Error: unauthorized_client].
I tried doing the same thing with a new token on a new service account (the web interface to get the token has changed quite a bit in the last 9 months), and it returns the same error.
The version of googleapis that we were using was 0.9.7, but we can't get JWT authentication to work on the newest version either.
We opened a ticket with the Google APIs support team, but the support person we spoke with had never read the Gmail API specs before and was ultimately unable to help us, so he redirected us here in order to get in touch with the API engineering support team.
We have noticed that authentication works if we enable the scope for domain wide delegation in the admin console, but we would prefer not to do that. We don't need to impersonate the accounts and would prefer to use an individual JWT for each account.
It turns out that the auth flow we were using was never supported, and probably was broken due to a bugfix on Google's part.
In the question comments #Brandon Jewett-Hall and #Steve Bazyl recommended that we use the installed app auth flow instead, as it allows for indefinite refreshing of access tokens and is supported.
More information about the different auth flows can be found in the Google API docs.

Resources