Google APIs for Web sign in and query user data - google-api

I'm trying to implement a process which combines Google sign-in on client side (Web page) with server side verification and query user data (Java server).
What I did:
In Google developer console, added an OAuth 2.0 client IDs credential.
Implemented the sign-in on the web page and got the ID token after successful login.
Implemented the authentication with a backend server as explained here:
https://developers.google.com/identity/sign-in/web/backend-auth. This part also works and I can verify the authentication and get the user's e-mail address.
What I need to do now is getting the user's profile information, i.e. first and last name and access the app folder, to store relevant application data.
This is my server side code. I marked the part where I need help:
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(HTTP_TRANSPORT, JSON_FACTORY)
.setAudience(Arrays.asList(service.getClientId()))
.build();
GoogleIdToken idToken = null;
try {
idToken = verifier.verify(token); // token is the ID token received from the client
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
payload.getEmail() <== This works
/*
Here I need to query Google API per the available application scopes: profile, app storage etc.
*/
}
Is it possible to use the API at this stage? If not, can I request access token here? Should I use the Client ID or do I need a different type of credential (like API key or Service account)?

ID Token represents authentication, not authorization. So you won't be able to access Google APIs just with ID Token.
In order to make requests to Google APIs from server side, do following on client side.
var auth2 = gapi.auth2.getAuthInstance();
auth2.grantOfflineAccess({
scope: 'SCOPES_COMES_HERE'
}).then(function(resp) {
// send `resp.code` to server to exchange it with
// credentials (access_token, refresh_token
});
The code is the key to exchange with access_token.
You might be inclined to implement authentication and authorization at the same time, but Google's recommendation is to separate them and request permissions as they are needed (incremental authorization). Leave the current code and add above + server side that handles code to exchange with access_token.
Detailed doc: https://developers.google.com/identity/sign-in/web/server-side-flow

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

Google Social Login with Auth0 Tokens for [Authorized] API Calls

Anyone here implemented social login through Google for Auth0? I have an issue with the tokens (access and id) being returned after validating with Google.
Here's my code:
var waGoogle = new auth0.WebAuth({
domain: 'testApplication.auth0.com',
clientID: '************',
redirectUri: 'http://localhost:8080/'
})
waGoogle.authorize({
connection: 'google-oauth2',
responseType: 'id_token token'
}, function(err, authResult){
if(err){
console.log('Google Login Error')
console.log(err)
}
});
Google screen shows up, I log in and I am redirected back to my application. From the application, I parse the URL so that I can get the access and id tokens.
let getParameterByName = (name) => {
var match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
var access_token = getParameterByName('access_token')
var id_token = getParameterByName('id_token')
Issue I am having is that none of the tokens allow me to call my APIs (asp.net web api) which are decorated with the [Authorize] attribute. It returns a:
401 (Unauthorized)
I know that my API is working, as using the normal
Username-Password-Authentication
method where I also obtain an access token, my api calls are just pulling through.
Are there any next steps which I need to do after obtaining the access and id_token from Google? Do I need to make an additional call to Auth0 to obtain the proper access token to be able to call my web api?
Thanks.
The token you are looking for is called an IdP (Identity Provider) Token. This is different from the one issued to you after logging in. There are pretty good instructions on the Auth0 site that walk you through the process of getting that token.
Here is the overview of IdP tokens
Here is a step-by-step guide to calling the Identity Provider
The tl;dr:
To access the IdP token you need to call the Auth0 management API from your server. For that, your server will need a management token. Then use that token to access the endpoint /api/v2/users/[USER_ID]. In the object sent back from Auth0, look for the google identity and extract that token.
Also note, you should probably keep that token on your server if you can. If you can keep those power tokens away from your client your users will be happy.

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.

How to exchange Google one-time authorization code for a refresh token without callback (intranet)?

I'm working on a intranet-based application and I want to use Google services. Currently I have successfully implemented Google Authentication with "Sign-In for Websites" using JavaScript client-side authentication. My users can now sign in or sign up with their Google accounts.
Now I want to use Google API to create and share Google Sheets with my users. These documents will be created with a specific Google account and then shared with my users.
This is why I want to use this server-slide flow to get a one-time authorization code and exchange it for a refresh token:
https://developers.google.com/identity/sign-in/web/server-side-flow
This refresh token will be stored in my database allowing me to user Google services on behalf of this offline user.
Using JavaScript library, I was able to get the one-time authorization code that I send to my server with a AJAX request.
auth2.grantOfflineAccess({'redirect_uri': 'postmessage'}).then(grantOfflineAccessCallback);
var grantOfflineAccessCallback = function(authResult) {
var auth_code = authResult.code;
// Exchange the one-time authorization code for tokens
$.post(...);
}
On server-side I use Google API PHP Client (v2.0.0-RC6) to acquire an access and refresh token.
$this->client = new Google_Client();
$this->client->setClientId($this->clientId);
$this->client->setClientSecret($this->clientSecret);
$this->client->setAccessType('offline');
$this->client->setApprovalPrompt('force');
$response = $this->client->fetchAccessTokenWithAuthCode($oneTimeCode);
I wasn't able to exchange the authorization code.
Client error: `POST https://www.googleapis.com/oauth2/v4/token` resulted in a `400 Bad Request` response:
{
"error": "invalid_request",
"error_description": "Missing parameter: redirect_uri"
}
On this page we can read:
On the server, exchange the auth code for access and refresh tokens.
Use the access token to call Google APIs on behalf of the user.
On the JAVA example code:
REDIRECT_URI: // Specify the same redirect URI that you use with your web
// app. If you don't have a web version of your app, you can
// specify an empty string.
Because the application I working on is an intranet application, I tried to specify an empty string for this redirect_uri parameter before calling fetchAccessTokenWithAuthCode() method:
$this->client->setRedirectUri('');
... result in Redirect URI must be absolute.
Can we use this hybrid server-slide flow without callback URL?
Is there any solution to my problem?
Thanks,
Edit:
redirect_uri is where the user will be redirected to after he signed in. This URL must be registered in the Google Project (developers console). So redirect_uri is NOT the callback...!
Problem is now solved with:
$this->client->setRedirectUri('http://same.url.as.in.developers.console/');

How to reset google oauth 2.0 authorization?

I'm using Google APIs Client Library for JavaScript (Beta) to authorize user google account on web application (for youtube manipulations). Everything works fine, but i have no idea how to "logout" user from my application, i.e. reset access tokens.
For example, following code checks user authorization and if not, shows popup window for user to log into account and permit web-application access to user data:
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: false}, handleAuth);
But client library doesn't have methods to reset authorization.
There is workaround to redirect user to "accounts.google.com/logout", but this
approach is not that i need: thus we logging user off from google account not only from my application, but also anywhere.
Google faq and client library description neither helpful.
Try revoking an access token, that should revoke the actual grant so auto-approvals will stop working. I assume this will solve your issue.
https://developers.google.com/accounts/docs/OAuth2WebServer#tokenrevoke
Its very simple. Just revoke the access.
void RevokeAcess()
{
try{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/revoke?token="+ACCESS_TOKEN);
org.apache.http.HttpResponse response = client.execute(post);
}
catch(IOException e)
{
}
}
But it should be in asyncTask
It depends what you mean by resetting authorization. I could think of a three ways of doing this:
Remove authorization on the server
Go to myaccount.google.com/permissions, find your app and remove it. The next time you try to sign in you have to complete full authorization flow with account chooser and consent screen.
Sign out on the client
gapi.auth2.getAuthInstance().signOut();
In this way Google authorization server still remembers your app and the authorization token remains in browser storage.
Sign out and disconnect
gapi.auth2.getAuthInstance().signOut();
gapi.auth2.getAuthInstance().disconnect();
This is equivalent to (1) but on the client.
Simply use: gapi.auth.setToken(null);
Solution for dotnet, call below API and pass the access token, doc - https://developers.google.com/identity/protocols/oauth2/web-server#tokenrevoke
string url = "https://accounts.google.com/o/oauth2/revoke?token=" + profileToken.ProfileAccessToken;
RestClient client = new RestClient(url);
var req = new RestRequest(Method.POST);
IRestResponse resp = client.Execute(req);

Resources