Reputation: 17
I am working on a GDrive project which was written over 10 months back. The previous code to get the Drive service object was
private OAuth2Authenticator<AssertionFlowClient> authenticator;
private DriveService GetService(String userEmail)
{
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
{
ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
Scope = DriveService.Scopes.Drive.GetStringValue(),
ServiceAccountUser = userEmail,
};
authenticator = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);
return new DriveService(new BaseClientService.Initializer() { Authenticator = authenticator, ApplicationName = "Random" });
}
With the latest google drive api changes i changed the code to get the drive service object to
private DriveService GetService(String userEmail)
{
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
Scopes = new[] { DriveService.Scope.Drive },
User = userEmail,
}.FromCertificate(certificate));
// Create the service.
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Random",
});
}
I also have the following code to Download a file
public void DownloadFile(string url, string fileName)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//authenticator.ApplyAuthenticationToRequest(request);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
System.IO.Stream received = response.GetResponseStream();
using (System.IO.FileStream file = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
Util.CopyStream(received, file);
}
}
The problem here is that with the latest change to get the drive service object i am not able to call the "ApplyAuthenticationToRequest" call since OAuth2Authenticator is deprecated. Without calling that function i get an exception that "System.Net.WebException: The remote server returned an error: (401) Unauthorized". With the latest changes how can i call the "ApplyAuthenticationToRequest" function so that the call could succeed. This function is also used in 2 other places in the project.Can someone please help?
Upvotes: 0
Views: 524
Reputation: 80
apperently it's also possible with the service
var file = service.Files.Get(fileID).Execute();
var bytes = service.HttpClient.GetByteArrayAsync(file.DownloadUrl).Result;
Upvotes: 4
Reputation: 80
you need to request a token from google.. here's the class to get a token from google
public class GoogleJsonWebToken
{
public const string SCOPE_DRIVE_READONLY = "https://www.googleapis.com/auth/drive.readonly";
public const string SCOPE_DRIVE = "https://www.googleapis.com/auth/drive";
public static dynamic GetAccessToken(string clientIdEMail, string keyFilePath, string scope)
{
// certificate
var certificate = new X509Certificate2(keyFilePath, "notasecret");
// header
var header = new { typ = "JWT", alg = "RS256" };
// claimset
var times = GetExpiryAndIssueDate();
var claimset = new
{
iss = clientIdEMail,
scope = scope,
aud = "https://accounts.google.com/o/oauth2/token",
iat = times[0],
exp = times[1],
};
JavaScriptSerializer ser = new JavaScriptSerializer();
// encoded header
var headerSerialized = ser.Serialize(header);
var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
var headerEncoded = Convert.ToBase64String(headerBytes);
// encoded claimset
var claimsetSerialized = ser.Serialize(claimset);
var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
var claimsetEncoded = Convert.ToBase64String(claimsetBytes);
// input
var input = headerEncoded + "." + claimsetEncoded;
var inputBytes = Encoding.UTF8.GetBytes(input);
// signiture
var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
var cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
var aescsp = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
var signatureBytes = aescsp.SignData(inputBytes, "SHA256");
var signatureEncoded = Convert.ToBase64String(signatureBytes);
// jwt
var jwt = headerEncoded + "." + claimsetEncoded + "." + signatureEncoded;
var client = new WebClient();
client.Encoding = Encoding.UTF8;
var uri = "https://accounts.google.com/o/oauth2/token";
var content = new NameValueCollection();
content["assertion"] = jwt;
content["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
string response = Encoding.UTF8.GetString(client.UploadValues(uri, "POST", content));
var result = ser.Deserialize<dynamic>(response);
return result;
}
private static int[] GetExpiryAndIssueDate()
{
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.UtcNow;
var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;
return new[] { iat, exp };
}
}
and for the download itself i'm using this
public static byte[] GetFile(string fileID, out string contenttype, out string filename)
{
DALParameter dalp = new DALParameter();
var gdEmail = dalp.GetParameter(INMDTO.Parameters.GoogleDriveLogin, INMDTO.ParametersLevel.General, 0);
var credential = GetCertificate();
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "tttt",
});
string path = AppDomain.CurrentDomain.BaseDirectory + "App_Data\\";
string certFile = "gd.p12";
string fullcertpath = Path.Combine(path, certFile);
var email = gdEmail;
var auth = GoogleJsonWebToken.GetAccessToken(email, fullcertpath, GoogleJsonWebToken.SCOPE_DRIVE_READONLY);
var f = service.Files.Get(fileID).Execute();
contenttype = f.MimeType;
filename = f.OriginalFilename;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(f.DownloadUrl));
req.Headers.Add("Authorization", auth["token_type"] + " " + auth["access_token"]);
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
Stream sourceStream = response.GetResponseStream(); // the ConnectStream
byte[] array;
using (var ms = new MemoryStream())
{
sourceStream.CopyTo(ms);
array = ms.ToArray();
}
return array;
}
return null;
}
it might not be the best solution but it works and i didn't really find another way to do it. hope this helps you a little.
Upvotes: 0