How to create object of Google DriveService using OAuthParameters - google-drive-api

I am able to authorize my application using oAuth ( I get Access token and secret in return). I use those to instantiate an object of OauthParameter. My question is as I have added scope for "google drive" during authentication. How can I create an object of DriveService, just the way PicasaService is being created.
PicasaService service;
GOAuthRequestFactory requestFactory;
string Picasa_CONSUMER_KEY = "<CONSUMER KEY>";
static string Picasa_CONSUMER_SECRET = "<CONSUMER SECRET>";
public static string Picasa_SCOPE = "http://picasaweb.google.com/data/https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
string Picasa_CALLBACK_URL = "<CALLBACK URL>";
This is how I am creating an object of PicasaService
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = Picasa_CONSUMER_KEY,
ConsumerSecret = Picasa_CONSUMER_SECRET,
Token = "token received during authentication",
TokenSecret = "secret received during authentication"
};
requestFactory = new GOAuthRequestFactory("XYZ", "XYZ APP", parameters);
service = new PicasaService(requestFactory.ApplicationName);
service.RequestFactory = requestFactory;
Now how can I "parameters" to instantiate an object of DriveService??
As I already have the scope of Google Drive, and received token, secret during the authentication process, I don't want to do oAuth separately for google drive.
Thanks
Sujit

Related

How to set up google api in asp.net mvc to access docs

About 6 months ago I set up a web application in the google developers console so that employees of our internal web site could initiate emails which would read a template doc in a google account, merge some fields and then download a pdf version of it to email out.
Now we have to move those template docs to a different google managed domain/user account so I've made copies of the documents in the new account and updated our references with the new doc ids.
In addition, the email I had when I originally created this application in the google dev console is going away as of the first of the year. So I also have to recreate the app under a new account.
I've done that and matched all the settings of the original app. However, when I try to access a document I get the error Google.Apis.Auth.OAuth2.Responses.TokenResponseException: 'Error:"unauthorized_client", Description:"Unauthorized", Uri:""'
I had followed this page in setting up the original user authentication. I know there was a ton of trial and error before I actually got it working and I must be forgetting something. I'm wondering if it's tied to needing to reauthenticate the new app. Although I'm specifying the new clientid and clientsecret from the new app, I don't get the popup asking me to give permission to the app. I would expect with the new credential info that it would open that window asking me to give permission. Here's that file for reference. Any ideas?
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData => new AppFlowMetadata();
}
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override IAuthorizationCodeFlow Flow => flow;
public override string GetUserId(Controller controller)
{
return "userid";
}
}
public class GoogleController : TECWareControllerBase
{
private readonly IGoogleCredentialService _gservice;
public GoogleController(IGoogleCredentialService gservice)
{
_gservice = gservice;
}
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
_gservice.SaveRefreshToken(result.Credential.Token.RefreshToken);
return View();
}
return new RedirectResult(result.RedirectUri);
}
}
I finally found a way to get this working.
First off in this method
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
I had to change the FileDataStore("Drive.Api.Auth.Store") key to something else like FileDataStore("GoogleAuth")
That forced the authentication to fire up.
Unfortunately, google then complained about an invalid redirect uri. The following code returned a redirect uri of http://localhost:11224/AuthCallback/IndexAsync which didn't even exist in my web application's Authorized redirect uris. It should have been http://localhost:11224/MVC/AuthCallback/IndexAsync. So in the url result's redirect url I changed it to what it should have been which allowed me to complete the authorization. Now I can access the documents in the authenticated account.
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);

Azure Mysql HTTP REST API . Get JSON Web Token

I'm trying to connect to my Azure Mysql via http rest api (https://learn.microsoft.com/en-us/rest/api/mysql/) without success. The problem is that i can't get the JSON Web Token from my Web App. Situation:
Azure Web App ----- rest api ----> Azure MySql
I guess i need to 'register' this Mysql Server resource in active directory but seems i can't do it.
I followed this tutorial (https://blogs.msdn.microsoft.com/jpsanders/2017/03/17/accessing-azure-app-services-using-azure-ad-bearer-token-2) but i have the same problem : i can't register MySql in Azure Active Directory .
So, how can i obtain a JSON Web Token for Mysql HTTP REST API ?
Thanks!
-------- AD PROPIETARY ROLE FOR MYSQL RESOURCE (NOT MYSQL SERVER) --
---------------- CODE ----------------------------------------------
//
// https://blogs.msdn.microsoft.com/jpsanders/2017/03/17/accessing-azure-app-services-using-azure-ad-bearer-token-2/
//
public static class AzureActiveDirectory
{
// the AD Authority used for login. For example: https://login.microsoftonline.com/myadnamehere.onmicrosoft.com
public static string authority = "";
// the Application ID of this app. This is a guid you can get from the Advanced Settings of your Auth setup in the portal
public static string clientId = "";
// the key you generate in Azure Active Directory for this application
public static string clientSecret = "";
// the Application ID of the app you are going to call.This is a guid you can get from the Advanced Settings of your Auth setup for the targetapp in the portal
public static string resource = "";
static public async Task<AuthenticationResult> GetS2SAccessTokenForProdMSAAsync()
{
var task = await GetS2SAccessToken(authority, resource, clientId, clientSecret);
return task;
}
static async Task<AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
}
AzureActiveDirectory.authority = "https://login.microsoftonline.com/********/";
AzureActiveDirectory.clientId = "********";
AzureActiveDirectory.clientSecret = "********";
AzureActiveDirectory.resource = "https://management.azure.com/";
try
{
AuthenticationResult token = await AzureActiveDirectory.GetS2SAccessTokenForProdMSAAsync();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "Bearer " + token.AccessToken);
var resp = await client.GetAsync("https://management.azure.com/subscriptions/*******/resourceGroups/MYSQL/providers/Microsoft.DBforMySQL/servers/shoplister/firewallRules?api-version=2017-12-01");
Console.WriteLine(resp.StatusCode.ToString());
Console.WriteLine();
}
catch (Exception e) { Console.WriteLine(e); }
--------------- AFTER CHANGES NOW GETTING UNAUTHORIZED ------------
I'm compiling the important points from our discussion in the comments that led to a solution:
Use https://management.azure.com as the resource identifier when acquiring the access token
Use https://login.microsoftonline.com/tenant-id-here/ as the authority (you can also use a verified domain name instead of the id). This defines which AAD tenant you authenticate against
The access token must be attached with new AuthenticationHeaderValue("Bearer", token.AccessToken) in C#, so that the resulting header is Authorization: Bearer tokengoeshere
Finally make sure you have granted permissions to the right app. There can be apps with an identical or similar name.

Google Drive API authentication without user interaction

I'm looking for a Google Drive API authentication scheme that will give a service application (on a server) rights to create shared documents in a Drive folder, without user interaction.
Google's current unique name for the specific authentication scheme I should use for this is probably a sufficient answer to this question.
Although the document creation will occur in response to a user action, the documents will not be permanently associated with those users and I do not wish to require any user to present a Google account. Instead, I wish the user to be able to access the document via an "Anyone with the link can edit"-type URL displayed on a web page after the document is created.
This is intended to automatically generate documents for multiple generally anonymous people to collaborate, and all documents will be stored in a single folder.
There's a good chance this is a duplicate of this question: Google Drive API username + password authentication. Unfortunately, the accepted answer doesn't contain enough information for me to find my way now that the links it references are dead.
It may also be a duplicate of other questions that have accepted but unclear answers, such as: .NET Google Docs API Authentication (without user interaction), How do I authenticate Google Calendar API v3 without user interaction?, and Drive API doc upload from a server without user interaction.
Authenticating as a service account was the approach I needed.
The Google SDK actions were simply misleading. When I provided some incorrect values it fell back to user-based authentication (automatically opening a web browser to request interactive credentials). I incorrectly interpreted this to mean that the service account functionality was implemented as a long-term key approved by and in the context of a specific interactive user, or something similar.
No user interaction was necessary, however the .p12 certificate was required, rather than whatever credentials the default .json file provided (which I had tried using in a number of ways). Here's the code I used:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using Google.Apis.Http;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using GData = Google.Apis.Drive.v2.Data;
public class Drive
{
private const string GoogleDocMimeType = "application/vnd.google-apps.document";
/// <summary>
/// Creates a drive service, authenticated using information found in the Google Developers Console under "APIs & auth / Credentials / OAuth / Service account"
/// </summary>
/// <param name="svcEmail">The service account "Email address"</param>
/// <param name="certPath">The path to the service account "P12 key" file</param>
public Drive(string svcEmail, string certPath)
{
Service = AuthenticateService(svcEmail, certPath);
}
private DriveService Service
{
get;
set;
}
/// <summary>
/// Creates a "Google Doc" and shares it with anyone with the link
/// </summary>
/// <param name="title"></param>
/// <returns>The drive FileId, accessible at https://docs.google.com/document/d/FileId </returns>
public async Task<string> CreateShared(string title)
{
var fileId = await CreateDocument(title);
await ShareFile(fileId);
return fileId;
}
private async Task<string> CreateDocument(string title)
{
var file = new GData.File
{
Title = title,
MimeType = GoogleDocMimeType
};
file = await Service.Files.Insert(file).ExecuteAsync();
return file.Id;
}
private async Task ShareFile(string fileId)
{
Permission permission = new Permission
{
Type = "anyone",
Role = "writer",
WithLink = true
};
var a = Service.Permissions.Insert(permission, fileId);
await a.ExecuteAsync();
}
private static DriveService AuthenticateService(string svcEmail, string certPath)
{
string[] scopes = new[] { DriveService.Scope.DriveFile };
X509Certificate2 certificate = new X509Certificate2(certPath, "notasecret", X509KeyStorageFlags.Exportable);
var init = new ServiceAccountCredential.Initializer(svcEmail) { Scopes = scopes };
IConfigurableHttpClientInitializer credential = new ServiceAccountCredential(init.FromCertificate(certificate));
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Document Management Service",
});
}
}
And here's an experimental consumer:
internal class Program
{
private const string svcEmail = "serviceaccountid#developer.gserviceaccount.com";
private const string certPath = #"credentials\projectname-fingerprintprefix.p12";
private readonly static Drive drive = new Drive(svcEmail, certPath);
private static void Main(string[] args)
{
string id = drive.CreateShared("my title").Result;
Console.WriteLine(id);
}
}
This seems to use Google Drive storage in an isolated, application/project-specific data repository. According to other posts, there is no way to get an interactive Drive UI view on that. I don't know if if it uses my personal storage quota, etc. But, this is the best approach I have so-far and I'll answer those questions for myself (not here) next.

System.web name space does not exist error

I am trying to use Gmail API for windows phone 8.1 app.This is the code which I got from https://developers.google.com/gmail/api/quickstart/quickstart-cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Compilation;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Routing;
using System.Web.SessionState;
using Newtonsoft.Json;
// TODO(class) Reorder, this gets messy with alt+shift+F10
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util;
using Google.Apis.Gmail;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Oauth2;
using Google.Apis.Oauth2.v2;
using Google.Apis.Oauth2.v2.Data;
using Google.Apis.Plus.v1;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Auth.OAuth2.Flows;
using System.Threading;
namespace GmailQuickstart
{
/// <summary>
/// This is a minimal implementation of GMail demonstrating:
/// - Using the Google+ Sign-In button to get an OAuth 2.0 refresh token.
/// - Exchanging the refresh token for an access token.
/// - Making GMail API requests with the access token, including
/// getting a list GMail threads.
/// - Disconnecting the app from the user's Google account and revoking
/// tokens.
/// </summary>
/// #author class#google.com (Gus Class)
public class Signin : IHttpHandler, IRequiresSessionState, IRouteHandler
{
// These come from the APIs console:
// https://code.google.com/apis/console
public static ClientSecrets secrets = new ClientSecrets()
{
ClientId = "YOUR_CLIENT_ID",
ClientSecret = "YOUR_CLIENT_SECRET"
};
// Configuration that you probably don't need to change.
static public string APP_NAME = "GMail .NET Quickstart";
// Stores token response info such as the access token and refresh token.
private TokenResponse token;
// Used to peform API calls against Google APIs.
private PlusService ps = null;
private GmailService gs = null;
/// <summary>Processes the request based on the path.</summary>
/// <param name="context">Contains the request and response.</param>
public void ProcessRequest(HttpContext context)
{
// Redirect base path to signin.
if (context.Request.Path.EndsWith("/"))
{
context.Response.RedirectPermanent("signin.ashx");
}
// This is reached when the root document is passed. Return HTML
// using index.html as a template.
if (context.Request.Path.EndsWith("/signin.ashx"))
{
String state = (String)context.Session["state"];
// Store a random string in the session for verifying
// the responses in our OAuth2 flow.
if (state == null)
{
Random random = new Random((int)DateTime.Now.Ticks);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 13; i++)
{
builder.Append(Convert.ToChar(
Convert.ToInt32(Math.Floor(
26 * random.NextDouble() + 65))));
}
state = builder.ToString();
context.Session["state"] = state;
}
// Render the templated HTML.
String templatedHTML = File.ReadAllText(
context.Server.MapPath("index.html"));
templatedHTML = Regex.Replace(templatedHTML,
"[{]{2}\\s*APPLICATION_NAME\\s*[}]{2}", APP_NAME);
templatedHTML = Regex.Replace(templatedHTML,
"[{]{2}\\s*CLIENT_ID\\s*[}]{2}", secrets.ClientId);
templatedHTML = Regex.Replace(templatedHTML,
"[{]{2}\\s*STATE\\s*[}]{2}", state);
context.Response.ContentType = "text/html";
context.Response.Write(templatedHTML);
return;
}
if (context.Session["authState"] == null)
{
// The connect action exchanges a code from the sign-in button,
// verifies it, and creates OAuth2 credentials.
if (context.Request.Path.Contains("/connect"))
{
// Get the code from the request POST body.
StreamReader sr = new StreamReader(
context.Request.InputStream);
string code = sr.ReadToEnd();
string state = context.Request["state"];
// Test that the request state matches the session state.
if (!state.Equals(context.Session["state"]))
{
context.Response.StatusCode = 401;
return;
}
// Use the code exchange flow to get an access and refresh token.
IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = new string[] { PlusService.Scope.PlusLogin, GmailService.Scope.GmailReadonly}
});
token = flow.ExchangeCodeForTokenAsync("", code, "postmessage",
CancellationToken.None).Result;
// Create an authorization state from the returned token.
context.Session["authState"] = token;
// Get tokeninfo for the access token if you want to verify.
Oauth2Service service = new Oauth2Service(
new Google.Apis.Services.BaseClientService.Initializer());
Oauth2Service.TokeninfoRequest request = service.Tokeninfo();
request.AccessToken = token.AccessToken;
Tokeninfo info = request.Execute();
string gplus_id = info.UserId;
}
else
{
// No cached state and we are not connecting.
context.Response.StatusCode = 400;
return;
}
}
else if (context.Request.Path.Contains("/connect"))
{
// The user is already connected and credentials are cached.
context.Response.ContentType = "application/json";
context.Response.StatusCode = 200;
context.Response.Write(JsonConvert.SerializeObject("Current user is already connected."));
return;
}
else
{
// Register the authenticator and construct the Plus service
// for performing API calls on behalf of the user.
token = (TokenResponse)context.Session["authState"];
IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = new string[] { PlusService.Scope.PlusLogin, GmailService.Scope.GmailReadonly }
});
UserCredential credential = new UserCredential(flow, "me", token);
bool success = credential.RefreshTokenAsync(CancellationToken.None).Result;
token = credential.Token;
ps = new PlusService(
new Google.Apis.Services.BaseClientService.Initializer()
{
ApplicationName = ".NET Quickstart",
HttpClientInitializer = credential
});
gs = new GmailService(
new Google.Apis.Services.BaseClientService.Initializer()
{
ApplicationName = ".NET Quickstart",
HttpClientInitializer = credential
});
}
// Perform an authenticated API request to retrieve the list of
// people that the user has made visible to the app.
if (context.Request.Path.Contains("/mail"))
{
// List the GMail threads for the current user.
IList<Google.Apis.Gmail.v1.Data.Thread> threadFeed =
gs.Users.Threads.List("me").Execute().Threads;
string jsonContent =
Newtonsoft.Json.JsonConvert.SerializeObject(threadFeed);
context.Response.ContentType = "application/json";
context.Response.Write(jsonContent);
return;
}
// Disconnect the user from the application by revoking the tokens
// and removing all locally stored data associated with the user.
if (context.Request.Path.Contains("/disconnect"))
{
// Perform a get request to the token endpoint to revoke the
// refresh token.
token = (TokenResponse)context.Session["authState"];
string tokenToRevoke = (token.RefreshToken != null) ?
token.RefreshToken : token.AccessToken;
WebRequest request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/revoke?token=" +
token);
WebResponse response = request.GetResponse();
// Remove the cached credentials.
context.Session["authState"] = null;
// You could reset the state in the session but you must also
// reset the state on the client.
// context.Session["state"] = null;
context.Response.Write(
response.GetResponseStream().ToString().ToCharArray());
return;
}
}
/// <summary>
/// Implements IRouteHandler interface for mapping routes to this
/// IHttpHandler.
/// </summary>
/// <param name="requestContext">Information about the request.
/// </param>
/// <returns></returns>
public IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
var page = BuildManager.CreateInstanceFromVirtualPath
("~/signin.ashx", typeof(IHttpHandler)) as IHttpHandler;
return page;
}
public bool IsReusable { get { return false; } }
}
}
So I keep getting this error System.web,System.Web.Compilation namespaces are not available.So What can I do to fix this?
The error is correct. System.Web is not available to Windows Phone apps (either Silverlight or Runtime). If the Gmail API depends on it then it is probably designed for the full .Net Framework and not for Windows Phone. Instead you'll need to connect directly to the Gmail web API. It looks like google has docs for it at https://developers.google.com/gmail/api/v1/reference/
See .NET API for Windows Phone for .Net classes available in WP8 Silverlight apps.

Create a C# .net DriveService object using existing access token

In my application, I already have the access token retrieved and now I want to use to access files using DriveService? Is this possible to do with the sdk? I am able to do this for the Google Spreadsheets service just fine, using OAUTH2PARAMETERS and GOAuth2RequestFactory (code below). Can I do something similar to instantiate DriveService object in C# sdk for web apps?
OAuth2Parameters parameters = new OAuth2Parameters
{
TokenType = "Bearer",
AccessToken = <access_token>,
};
var requestFactory = new GOAuth2RequestFactory("svc", "appname", parameters);
SpreadsheetsService service = new SpreadsheetsService("svc");
service.RequestFactory = requestFactory;