Google Domain Shared Contacts API Using OAuth 2.0 for Server to Server Applications - Credential params - google-api

I'm trying to get the "Google Domain Shared Contacts API" described here:
https://developers.google.com/admin-sdk/domain-shared-contacts/
Working using "OAuth 2.0 for Server to Server Applications" described here:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount
The recommendation from the OAuth page is to use the provided Google client library... I'm using the Java library. But the Shared-Contacts API doesn't have an example that uses this library, and I'm having trouble figuring out how to use the library with the Shared-Contacts API.
I am able to make the example for the OAuth to work for me... It uses Google Cloud Storage. Here's a snippet of the code:
String STORAGE_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write";
try {
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
String p12Content = Files.readFirstLine(new File(keyFile), Charset.defaultCharset());
// Build service account credential.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
.setServiceAccountPrivateKeyFromP12File(new File(keyFile))
.build();
// Set up and execute Google Cloud Storage request.
String URI;
URI = "https://storage.googleapis.com/" + BUCKET_NAME;
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
HttpRequest request = requestFactory.buildGetRequest(url);
HttpResponse response = request.execute();
content = response.parseAsString();
} catch (IOException e) {
System.err.println(e.getMessage());
}
} catch (Throwable t) {
t.printStackTrace();
}
return content;
It's a request to get a listing of what's in a certain bucket on GCS. It calls a specific URL using the Credentials object, where the Credentials object does the work of the OAuth, using a key file I downloaded. There's other steps involved for getting it to work (setting the service account email, etc), which I did. It returns an xml string containing what is inside the bucket, and it works fine for me.
I then tried changing the URI to this string:
URI = "https://www.google.com/m8/feeds/contacts/myGoogleAppsDomain.com/full";
and I changed the STORAGE_SCOPE variable to be this string:
STORAGE_SCOPE = "http://www.google.com/m8/feeds/";
Hoping it would then return an xml-string of the shared-contacts. But instead, it returns this error:
403 Cannot request contacts belonging to another domain
I believe I'm getting this error because I'm not specifying the "hd" parameter when I do the authentication request... However, I'm unsure how I can specify the "hd" parameter using the GoogleCredential object (or the other parameters, except for "scope")... Can someone help me with that?

I think the issue here is that you are not specifying which user you want to impersonate in the domain (and you haven't configured the security settings in your domain to authorize the service account to impersonate users in the domain).
The doubleclick API auth documentation has good examples on how to do this. You can use their sample and replace the scopes and API endpoint:
https://developers.google.com/doubleclick-publishers/docs/service_accounts#benefits

Related

Google Admin Setttings API connection for .NET

I've been working with the Google directory API for quite some time now.
However, I need to update SSO settings in the admin settings section of Google. Yes, they say it will be deprecated at some point, but according to a google employee, it's going to be a while before a new API is available and then the old one will be removed.
First, if there is a NUGET package out there, please let me know. I can't seem to find anything that works with the admin settings API: https://developers.google.com/admin-sdk/admin-settings/
My first attempt is getting the SSO settings in Google.
I can use postman to pull this information so I know the API works.
However, I'm running into two issues:
How can I authenticate using the service certificate that I use in the apis.google.directory class?
Anticipating, how do I request access to the admin settings? In directory API, I have the scope enum to select from. If I'm making a manual connection to the API I assume I'll need to call this by hand?
Code
var certificate = new X509Certificate2(serviceAccountCertPath,
serviceAccountCertPassword,
X509KeyStorageFlags.Exportable);
// below the scopes are going to get in my way, right? What is the scope process I need to do for this manually?
credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser,
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryOrgunit},
User = _szAdminEmail
}.FromCertificate(certificate));
// I'm not seeing anyway to call the above credentials
using (HttpClient client = new HttpClient())
{
// client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
client.BaseAddress = new Uri(#"https://apps-apis.google.com/a/feeds/domain/2.0/[mydomain]/sso/general");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//client.DefaultRequestHeaders.
HttpResponseMessage response = client.GetAsync("api/Values").Result; // Blocking call!
var products = response.Content.ReadAsStringAsync().Result;
return products.ToString();
}
The admin settings API does not appear to support service account authentication you will need to use Oauth2. Admin Settings Oauth
Your not going to be able to use it very easily using the Google .net client library as that library was designed for use with the Google discovery apis. I dont think the Admin Settings API is a discovery api. You might be able to use the old gdata library for it I am not sure if one exists I have not been able to find it on nuget. If you do find it the old gdata library doesn't support oauth2 which means that you will need to use the new library for that and plug in the gdata library after.
I have only done this before using the Google contacts api I have a tutorial here on how i did it it may help you here
Auth
string clientId = "xxxxxx.apps.googleusercontent.com";
string clientSecret = "xxxxx";
string[] scopes = new string[] { "https://www.googleapis.com/auth/contacts.readonly" }; // view your basic profile info.
try
{
// Use the current Google .net client library to get the Oauth2 stuff.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, "test"
, CancellationToken.None
, new FileDataStore("test")).Result;
// Translate the Oauth permissions to something the old client libray can read
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.AccessToken = credential.Token.AccessToken;
parameters.RefreshToken = credential.Token.RefreshToken;
RunContactsSample(parameters);
If you cant find the gdata library for it you may have better luck just using the library for authencation and then code the rest of the calls yourself. It returns xml not json.

Google Drive API's file Export endpoint is failing with API Key authentication?

Has anything changed recently with the Google Drive APIs and specifically the Export function, which would cause it to fail while using API Key access after 27-Mar-2018?
I have a Windows Service that creates and sends daily course emails for an educational group. The source content for each email is stored in a Google Drive, as a Google Doc, so that the faculty can update the course content easily.
This has been working flawlessly for the past year, but suddenly stopped working about 27-Mar-2018. Since then, I can retrieve the file details;
_googleDriveHtmlContent.LoadFile(
fileId
);
But not the contents. When I Export the file as HTML, I immediately get a DownloadStatus.Failed from the ProgressChanged handler;
var request = _driveService.Files.Export(
fileId,
"text/html"
);
I'm using API keys for security, rather than OAuth, since it's a UI-less service. To do this I need to mark the file folders as publicly accessible - specifically I'm using "Accessible to everyone, with link." This has been working great.
I've updated to the latest API v3 libraries through NuGet, with no change in behavior.
Using Google's API Explorer, I'm seeing a similar behavior.
I can retrieve my file successfully using the API Explorer with the get endpoint.
https://developers.google.com/drive/v3/reference/files/get
fileId 1AIuGhzXsNuhhi0PMA1pblh0l5CCDaa1nPj8t_dasi_c
Authentication: API key (uses a "demo API key")
But with the export endpoint, I get an Internal Error (500)-
https://developers.google.com/drive/v3/reference/files/export
fileId 1AIuGhzXsNuhhi0PMA1pblh0l5CCDaa1nPj8t_dasi_c
mimeType: text/html
Authentication: API key (uses a "demo API key")
Changing the Authentication in the API Explorer to OAuth 2.0, and approving access, then returns a successful 200 result with the file HTML. However I'm unable to do that since I'm accessing the API via a UI-less service.
Has anything changed recently with the Google Drive APIs and specifically the Export function, which would cause it to fail while using API Key access after 27-Mar-2018?
Its possible but its most likely a stealth change that you will not get any official word on. Not that long ago i saw someone posting a similar question they were using an API key to update a Google sheet and it suddenly stopped working.
IMO if google has changed this its probably a good thing. API keys are meant for accessing public data. Setting a document to public is a really bad idea if anyone did manage to find the file ID of your document they would then be able to update your document.
Suggestion:
What you should be using is a Service account. Service accounts are dummy users by creating service account credentials on Google developer console and then taking the service account email address you can share the file on Google Drive with the service account granting it access to said file without the need of making the file public.
You havent specified what language you are using but you said you were making a windows service so i am going to assume you are using .net. Here is an example of service account authencation with the Google .net client library.
public static DriveService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath, string[] scopes)
{
try
{
if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
throw new Exception("Path to the service account credentials file is required.");
if (!File.Exists(serviceAccountCredentialFilePath))
throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
if (string.IsNullOrEmpty(serviceAccountEmail))
throw new Exception("ServiceAccountEmail is required.");
// For Json file
if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
{
GoogleCredential credential;
using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
// Create the Analytics service.
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive Service account Authentication Sample",
});
}
else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
{ // If its a P12 file
var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create the Drive service.
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive Authentication Sample",
});
}
else
{
throw new Exception("Unsupported Service accounts credentials.");
}
}
catch (Exception ex)
{
throw new Exception("CreateServiceAccountDriveFailed", ex);
}
}
}
code ripped from serviceaccount.cs. Assuming that you were already using the Google .net client library the service this method returns will be the same drive service you were using with an api key.
Once you have granted your service account access to the file it will be able to access the file when ever it needs there is no authentication needed as you have preauthorized it by sharing the file with it.

Silent login using Username in Azure Active Directory in Xamarin

I am developing app using Xamarin Forms. I have created a directory on azure portal. As i see references over internet , active directory authentication uses Microsofts login page to log in.
I want to create native login form and pass user name to active directory and authenticate it.
Is it possible to pass user credentials programatically and authenticate user?
How can i pass user credentials?
I have tried following but i got "(411) Length required" exception
var request = WebRequest.Create(string.Format(#"https://login.microsoftonline.com/{0}/oauth2/token?client_id=5e811f4f-4fa4-451e-a439-ca05cabc02d7&grant_type=password&username=02atul.com#gmail.com&password=userpassword&scope=openid", tenant));
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
using (HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Debug.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if (string.IsNullOrWhiteSpace(content))
{
Debug.WriteLine("Response contained empty body...");
}
else {
Debug.WriteLine("Response Body: \r\n {0}", content);
}
}
}
My username is my email id. Is it correct user name? As i am getting bad request error now. What's missing?
Technically you could use username and password flow which is described in more detail here with ADAL.
However, you need to revisit your scenario and understand if it really accomplishes what the Azure Active Directory Platform is for.
Users use OAuth2 based authentication to get the security and safety of only having to share their passwords with trusted identity providers like MS, FB, Google, etc... In general, the safety conscious person will NOT want to type in a password into your random website, and trust that you do not abuse that information. If you want to use AAD, you should also use our login experiences, as this is really what the customer is paying for in our service in many regards.
EDIT: If ADAL no longer supports this flow, you can simply generate the http requests yourself:
POST: https://login.microsoftonline.com/xxxxx.onmicrosoft.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
resource={resource}&client_id={clientId}&grant_type=password&username={userName}&password={password}&scope=openid&client_secret={clientSecret}

Google APIs for Web sign in and query user data

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

Retrieve blog feeds using google oauth 2.0 and scribe

I used scribe to connect to google using oAuth 2.0 and successfully got the access token. When i am trying to get the blogs i always get the below error
Unauthorized
Must authenticate to use 'default' user
Below is the code snippet
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET_KEY);
oauthParameters.setScope("http://www.blogger.com/feeds");
oauthParameters.setOAuthToken(ACCESS_TOKEN);
oauthParameters.setOAuthTokenSecret(ACCESS_TOKEN_SECRET); // this is null
BloggerService myService = new BloggerService("blogger");
try {
myService.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
} catch (OAuthException e) {
e.printStackTrace();
}
final URL feedUrl = new URL("http://www.blogger.com/feeds/default/blogs");
Query query = new Query(feedUrl);
Feed resultFeed = myService.getFeed(query, Feed.class);
Not able to get the feeds here and displays the Unauthorized error as mentioned above.
Jason has answered your question, I believe.
You do not want to use the library code above to access blogger. Use the new API, and use OAuth2 https://developers.google.com/blogger/docs/3.0/using#auth
I'm not sure what scribe is, but with OAuth2, you need to indicate what type of application you're building, It might be one that runs from a web server, or an installed application. What's you've determined that, you can follow the appropriate documentation for the java client library linked to above to get the access token and retrieve the data.

Resources