Google Contacts API (contacts.readonly) - google-contacts-api

I'm developing a C Library to access my google contacts information to be used in a C command line app (for personal use only).
I'm trying to authenticate with the scope: https://www.googleapis.com/auth/contacts.readonly but the answer is always "invalid_scope".
Any suggestion?
EDIT:
Some more information about my problem.
I created a Google Project and enabled some APIs (Contacts API and People API).
I'm using curl and a JSON library to communicate with Google APIs.
The code that I'm using is:
#define GOOGLE_AUTH_URL "https://accounts.google.com/o/oauth2/device/code"
#define GOOGLE_AUTH_POST "client_id="GOOGLE_AUTH_CLIENT_ID"&scope=email profile https://www.googleapis.com/auth/contacts.readonly"
int main(void) {
char * res = handle_url(GOOGLE_AUTH_URL,GOOGLE_AUTH_POST); // use curl to make a POST
if (res==NULL) {
Report("Error");
return -1;
}
cJSON *obj = cJSON_Parse(res);
printf("Result=%s\n",cJSON_Print(obj));
return 0;
}
... and the result is:
Result={
"error": "invalid_scope"
}
If I change the define to:
#define GOOGLE_AUTH_POST "client_id="GOOGLE_AUTH_CLIENT_ID"&scope=email profile https://www.googleapis.com/auth/contacts"
the result is:
Result={
"verification_url": "https://www.google.com/device",
"expires_in": 1800,
"interval": 5,
"device_code": "AH-1Ng2lAE01qw5HFlGQqT02q7dtmAE6WmKJ_FkH0mO4enJMybvCvRzXnazvyUm22-sJR51ZtKkIJjOT-QhO0PJBUZpCdLrOEw",
"user_code": "JNXW-WQPJ"
}

Google wants developers to explicitly enable the APIs that they intend to use. This limits the potential damage if your OAuth credentials are compromised. So the Contacts API may need to be enabled for your project.
Visit this link and select the project from the page header to confirm that the API is enabled for your project: https://console.developers.google.com/apis/api/contacts.googleapis.com/overview

As Graeme said, first you need to enable the API. Second, in your OAuth client setting page (Credential -> OAuth 2.0 Client IDs section), you also need to explicitly add this scope https://www.googleapis.com/auth/contacts.readonly.
I guess the reason why you can use this scope (https://www.googleapis.com/auth/contacts) is because you already added this scope on the OAuth client setting page.

Related

Serve dynamic content with Firebase Hosting/Functions in EU

I would like to serve a Next.js app in europe using Firebase Hosting & Functions capabilities.
I do understand from the doc that:
If you are using HTTP functions to serve dynamic content for Firebase
Hosting, you must use us-central1
and that
Firebase Hosting supports Cloud Functions in us-central1 only
It's pretty clear: you must use us-central. But my main target is Europe..
I've read the following on the Cloud Functions locations guide:
For HTTP and callable functions, we recommend that you first set your
function to the destination region, or closest to where most expected
customers are located, and then alter your original function to
redirect its HTTP request to the new function (they can have the same
name). [Solution 1] If clients of your HTTP function support
redirects, you can simply change your original function to return an
HTTP redirect status (301) along with the URL of your new function.
[Solution 2] If your clients do not handle redirects well, you can
proxy the request from the original function to the new function by
initiating a new request from the original function to the new
function. The final step is to ensure that all clients are calling the
new function.
I've highlighted what seems to be two solutions to my initial problem:
Solution 1
Have a us-central1 function that send a 301 redirection to https://europe-west1-[myProject].cloudfunctions.net/[myEuropeanFunction]
Have a europe-west1 function that does the job (in my case, serve the Next.js app)
Happily using Firestore located in europe-west1
This would only work if clients of the HTTP function support redirects. In my case, it's fine: all browsers support redirection.
exports.nextServer = functions
.https
.onRequest((req, res) => {
res.set('location', 'https://europe-west1-<my-project>.cloudfunctions.net/nextServerEurope');
res.status(301).send()
});
exports.nextServerEurope = functions
.region('europe-west1')
.https
.onRequest((req, res) => {
return server.prepare().then(() => nextjsHandle(req, res));
});
The issue with that solution is that the URL changes in the browser to https://europe-west1-.cloudfunctions.net/nextServerEurope :-/
Solution 2
Have a us-central1 function that initiate a new/proxy request to the europe-west1 function
Have the same europe-west1 function that does the job (in my case, serve the Next.js app)
Still happily using Firestore located in europe-west1
By proxy request (as suggested in the guide), it would mean using a lib like axios I suppose. I know there are some libraries to perform proxy request available for node as well.
However, with that solution, the first issue I can think of is the unnecessary delay introcuded by passing by the us endpoint:
client -> us endpoint -> eu endpoint -> do stuff -> us endpoint -> client
Billing wise, I'm wondering what would be the impact..
I know that two services from different regions calling each others can increase the latency and the billing (egress).
With the first solution, there's no egress traffic as it's only a redirection to the european endpoint. But the redirection itself is not a valid solution in my case.
It's unclear for me what would be the additional billing cost with the second solution (beside the latency cost): is the traffic for the proxy request from us to eu going to be expensive?
To wrap-up:
The solution 1 is easy but leads to a non-transparent redirection
The solution 2 seems ok but it requires extra http request which leads to extra-latency (and potentially extra billing)
In the end, both solutions doesn't seem quite okay.
Therefore my question:
How do you serve in Europe dynamic content using Firebase Hosting and Functions?
Firebase Hosting only supports Cloud Functions in Us-Central as you mentioned and as stated in the Firebase Hosting Official Documentation.
I have created a Feature Request in Public Issue Tracker to support other regions when using Firebase Hosting with Cloud Functions. Please note, there is no ETA when this will be implemented.
So as #Doug Stevenson suggest, you can use Firebase Hosting with Cloud Run instead to serve your Dynamic Content.
Just to update. As of August 2022.
Finally, latency issue can be solved easily for now.
Firebase Hosting rewrites to CF3 are able to be done to any CF3
region, not just us-central1.
Reference: Feature Request Ticket

Trying to get Google Drive to work with PCL Xamarin Forms application

I’m using Xamarin Forms to do some cross platform applications and I’d like to offer DropBox and GoogleDrive as places where users can do backups, cross platform data sharing and the like. I was able to get DropBox working without doing platform specific shenanagins just fine, but Google Drive is really giving me fits. I have my app setup properly with Google and have tested it with a regular CLI .NET application using their examples that read the JSON file off the drive and create a temporary credentials file – all fine and well but getting that to fly without access to the file system is proving elusive and I can’t find any examples on how to go about it.
I’m currently just using Auth0 as a gateway to allow users to provide creds/access to my app for their account which works dandy, the proper scope items are requested (I’m just using read only file access for testing) – I get an bearer token and refresh token from them – however when trying to actually use that data and just do a simple file listing, I get a 400 bad request error.
I’m sure this must be possible but I can’t find any examples anywhere that deviate from the slightest of using the JSON file downloaded from Google and creating a credentials file – surely you can create an instance of the DriveService object armed with only the bearer token...
Anyway – here’s a chunk of test code I’m trying to get the driveService object configured – if anyone has done this or has suggestions as to what to try here I’d very much appreciate your thoughts.
public bool AuthenticationTest(string pBearerToken)
{
try
{
var oInit = new BaseClientService.Initializer
{
ApplicationName = "MyApp",
ApiKey = pBearerToken,
};
_googleDrive = new DriveService(oInit);
FilesResource.ListRequest listRequest = _googleDrive.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
//All is well till this call to list the files…
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute().Files;
foreach (var file in files)
{
Debug. WriteLine(file.Name);
}
}
catch (Exception ex)
{
RaiseError(ex);
}
}

VimeoUpload not re-authenticating After Deletion of App Access on Vimeo.com

I was able to connect and upload videos using the library but when I deleted the app connection on Vimeo.com (as a test) the app didn't authorize again.
the upload looks like it's working but nothing is uploaded as the app is no longer connected.
I deleted the app on the phone and restarted but it still won't re-authorize the app.
This comes up in the output:
Vimeo upload state : Executing
Vimeo upload state : Finished
Invalid http status code for download task.
And this is in OldVimeoUpload.swift: ( didn't include the actual access code!)
import Foundation
class OldVimeoUpload: VimeoUpload
{
static var VIMEO_ACCESS_TOKEN :String! // = "there's a string of numbers here"
static let sharedInstance = OldVimeoUpload(backgroundSessionIdentifier: "") { () -> String? in
return VIMEO_ACCESS_TOKEN // See README for details on how to obtain and OAuth token
}
// MARK: - Initialization
override init(backgroundSessionIdentifier: String, authTokenBlock: AuthTokenBlock)
{
super.init(backgroundSessionIdentifier: backgroundSessionIdentifier, authTokenBlock: authTokenBlock)
}
}
It looks like the access token number is commented out. I deleted the 2 forward slashes to see if that would fix it but it didn't.
I spoke too soon.
It sounds like you went to developer.vimeo.com and created an auth token. Used it to upload videos. And then went back to developer.vimeo.com and deleted the auth token.
The app / VimeoUpload will not automatically re-authenticated in this situation. You've killed the token and the app cannot request a new one for you. You'll need to create a new auth token and plug it into the app.
If this is not accurate and you're describing a different issue let us know.
If you inspect the error that's thrown from the failing request I'm guessing you'll see it's a 401 unauthorized related to using an invalid token.
Edit:
Disconnecting your app (as described in your comment below) has the same effect as deleting your auth token from developer.vimeo.com.
Also, VimeoUpload accepts a hardcoded auth token (as you see from the README and your code sample). It will not automatically re-authenticate, probably ever.
If you'd like to handle authentication in your app check out VimeoNetworking or VIMNetworking. Either of those libraries can be used to create a variety of authentication flows / scenarios. Still, if a logged in user disconnects or deletes their token, you will need them to deliberately re-authenticate (i.e. you will need to build that flow yourself). In that case, the user has explicitly stated that they don't want the app to be able to access information on their behalf. It would go against our security contract with them to automatically re-authenticate somehow.
Does that make sense?

How do I use FIDO U2F to allow users to authenticate with my website?

With all the recent buzz around the FIDO U2F specification, I would like to implement FIDO U2F test-wise on a testbed to be ready for the forthcoming roll out of the final specification.
So far, I have a FIDO U2F security key produced by Yubico and the FIDO U2F (Universal 2nd Factor) extension installed in Chrome. I have also managed to set up the security key to work with my Google log-in.
Now, I'm not sure how to make use of this stuff for my own site. I have looked through Google's Github page for the U2F project and I have checked their web app front-end. It looks really simple (JavaScript only).
So is implementing second factor auth with FIDO as simple as implementing a few JavaScript calls? All that seems to be happening for the registration in the example is this:
var registerRequest = {
appId: enrollData.appId,
challenge: enrollData.challenge,
version: enrollData.version
};
u2f.register([registerRequest], [], function (result) {
if (result.errorCode) {
document.getElementById('status')
.innerHTML = "Failed. Error code: " + result.errorCode;
return;
}
document.location = "/enrollFinish"
+ "?browserData=" + result.clientData
+ "&enrollData=" + result.registrationData
+ "&challenge=" + enrollData.challenge
+ "&sessionId=" + enrollData.sessionId;
});
But how can I use that for an implementation myself? Will I be able to use the callback from this method call for the user registration?
What you are trying to do is implement a so called "relying party", meaning that your web service will rely on the identity assertion provided by the FIDO U2F token.
You will need to understand the U2F specifications to do that. Especially how the challenge-response paradigm is to be implemented and how app ids and facets work. This is described in the spec in detail.
You are right: The actual code necessary to work with FIDO U2F from the front end of you application is almost trivial (that is, if you use the "high-level" JavaScript API as opposed to the "low-level" MessagePort API). Your application will however need to work with the messages generated by the token and validate them. This is not trivial.
To illustrate how you could pursue implementing a relying party site, I will give a few code examples, taken from a Virtual FIDO U2F Token Extension that I have programmed lately for academic reasons. You can see the page for the full example code.
Before your users can use their FIDO U2F tokens to authenticate, they need to register it with you.
In order to allow them to do so, you need to call window.u2f.register in their browser. To do that, you need to provide a few parameters (again; read the spec for details).
Among them a challenge and the id of your app. For a web app, this id must be the web origin of the web page triggering the FIDO operation. Let's assume it is example.org:
window.u2f.register([
{
version : "U2F_V2",
challenge : "YXJlIHlvdSBib3JlZD8gOy0p",
appId : "http://example.org",
sessionId : "26"
}
], [], function (data) {
});
Once the user performs a "user presence test" (e.g. by touching the token), you will receive a response, which is a JSON object (see spec for more details)
dictionary RegisterResponse {
DOMString registrationData;
DOMString clientData;
};
This data contains several elements that your application needs to work with.
The public key of the generated key pair -- You need to store this for future authentication use.
The key handle of the generated key pair -- You also need to store this for future use.
The certificate -- You need to check whether you trust this certificate and the CA.
The signature -- You need to check whether the signature is valid (i.e. confirms to the key stored with the certificate) and whether the data signed is the data expected.
I have prepared a rough implementation draft for the relying party server in Java that shows how to extract and validate this information lately.
Once the registration is complete and you have somehow stored the details of the generated key, you can sign requests.
As you said, this can be initiated short and sweet through the high-level JavaScript API:
window.u2f.sign([{
version : "U2F_V2",
challenge : "c3RpbGwgYm9yZWQ/IQ",
app_id : "http://example.org",
sessionId : "42",
keyHandle: "ZHVtbXlfa2V5X2hhbmRsZQ"
}], function (data) {
});
Here, you need to provide the key handle, you have obtained during registration.
Once again, after the user performs a "user presence test" (e.g. by touching the token), you will receive a response, which is a JSON object (again, see spec for more details)
dictionary SignResponse {
DOMString keyHandle;
DOMString signatureData;
DOMString clientData;
};
You the need to validate the signature data contained herein.
You need to make sure that the signature matches the public key you have obtained before.
You also need to validate that the string signed is appropriate.
Once you have performed these validations, you can consider the user authenticated. A brief example implementation of the server side code for that is also contained in my server example.
I have recently written instructions for this, as well as listing all U2F server libraries (most of them bundles a fully working demo server), at developers.yubico.com/U2F. The goal is to enable developers to implement/integrate U2F without having to read the specifications.
Disclaimer: I work as a developer at Yubico.

How to authenticate with Chrome sync XMPP servers?

I need to get the currently opened tabs of a Google Chrome user in my Java application (not on the same machine). Chrome sync is enabled so the current tabs are synced with Google servers.
According to the documentation of Chrome sync it is done via XMPP. So I guess it should be possible to connect to the Google XMPP server (xmpp.google.com), e.g. via Smack (Java library for XMPP), authenticate and listen for protobuf messages that indicate a tab session change.
Of course the login credentials of the user or the "client_id" Chrome uses to identify clients are available.
But I'm having a hard time getting behind the authentication method that is used to connect to the XMPP server – I can't figure out how it's done in the Chromium source code and there's no documentation available besides the very low-level comments in the code.
The libjingle library Google uses for it's XMPP based services is only available for C++ and not well maintained/documented.
So is there anyone who has done something like that before and who can give any advice/hints on how the authentication process works?
I'm not sure chrome sync uses xmpp, at least on the level when it has to exchange info with client. It uses 'protocol buffers' Google technology. The protocol is given by using .proto protocol description files and you can convert it to your language's objects by using special compiler.
The sync server seems to rest at https://clients4.google.com/chrome-sync and client sends POST requests with the binary body where typed ClientToServerMessage message is placed.
Here's the output from when first connecting to sync server.
The first output Python object is a pprint of 'environ' WSGI variable where HTTP headers are placed too. The second object (after '====' ) is actual protocol message.
{'CONTENT_LENGTH': '54',
'CONTENT_TYPE': 'application/octet-stream',
'GATEWAY_INTERFACE': 'CGI/1.1',
'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate,sdch',
'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8',
'HTTP_AUTHORIZATION': 'GoogleLogin auth=MKhiqZsdz2RV4WrUJzPltxc2smTMcRnlfPALTOpf-Xdy9vsp6yUpS5cGuND0awqrYVUK4lhOJlh6OMsg093eBRghGGIgvWUTzU8PUvquy_c8Xn4sRiz_3tVJcke5eXi3q4qFDa6iVuEbT_0QhyPOjIQyeDOKRpZzMR3rpHsAs0ptFiTtUeTHsoIeUFT9nZPYzkET4-yHbDAp45_dxWdb-U6DPg24',
'HTTP_CONNECTION': 'keep-alive',
'HTTP_HOST': 'localhost:8080',
'HTTP_USER_AGENT': 'Chrome MAC 0.4.21.6 (130497)-devel',
'PATH_INFO': '/chrome-sync/dev/command/',
'QUERY_STRING': 'client_id=SOME_SPECIAL_STRING',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '59031',
'REQUEST_METHOD': 'POST',
'SCRIPT_NAME': '',
'SERVER_NAME': 'vian-bizon.local',
'SERVER_PORT': '8080',
'SERVER_PROTOCOL': 'HTTP/1.0',
'SERVER_SOFTWARE': 'gevent/1.0 Python/2.6',
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x100416140>,
'wsgi.input': <gevent.pywsgi.Input object at 0x102a04250>,
'wsgi.multiprocess': False,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'https',
'wsgi.version': (1, 0)}
'==================================='
share: "MY_EMAIL_WAS_HERE#gmail.com"
protocol_version: 30
message_contents: GET_UPDATES
get_updates {
caller_info {
source: NEW_CLIENT
notifications_enabled: false
}
fetch_folders: true
from_progress_marker {
data_type_id: 47745
token: ""
notification_hint: ""
}
}
debug_info {
events {
type: INITIALIZATION_COMPLETE
}
events_dropped: false
}
This happens for OAuth based authentication. You can see the OAuth token in HTTP_AUTHORIZATION field. The OAuth token is given to you when you interact with HTML dialog 'Google Account Login'. I'm not sure but seems like the API to get an access token for Google services is available publicly.
If you are looking for XMPP auth instead, please see the description of X-GOOGLE-TOKEN auth mechanism here:
Authenticate to Google Talk (XMPP, Smack) using an authToken
For the X-OAUTH2 authorization, you can access the info here: https://developers.google.com/talk/jep_extensions/oauth
And a sample here: http://pits.googlecode.com/svn/trunk/xmpp.c
Note that you can add XMPP stream flow to the Chrome log files populated on each run of the browser - chrome_debug.log. To enable this, run Chrome with following options: --enable-logging --v=2