Authentication with Google Calendar API using Server Account and .NET - google-calendar-api

I'm using Google Calendar API V3, with OAuth2 & .NET.
my authentication is by Service Account, since i need to run it as a service, without user interface.
I've managed, after a lot of struggle to authenticate with the credentials, but for some reason i can't create an event on my calendar (& yes, i shared it with my self...).
i found a lot of questions regarding some same issues, but all in php, which i don't really know or think it will help me.
i seek for some help. Thanks
String serviceAccountEmail = "XXX#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.MachineKeySet |X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
Event e = new Event()
{
Summary = "Appointment1",
Location = "42.138679, -88.045519",
Start = new EventDateTime()
{
DateTime = DateTime.Now,
TimeZone = "Asia/Jerusalem"
},
End = new EventDateTime()
{
DateTime = DateTime.Now.AddDays(1),
TimeZone = "Asia/Jerusalem"
},
};
Event createdEvent = service.Events.Insert(e, "primary").Execute();

For some reason, the Insert event to a "primary" calendar, didn't do the job.
Instead, i wrote the following code, which allowed me to write the event.
var list = service.CalendarList.List().Execute().Items;
service.Events.Insert(e, list[0].Id).Execute();
This is my solution for this problem, I also agree with Craig here that the API is not well organized. (saying this after working with the amazing API of maps).

Perhaps try specifying the user programmatically, rather than sharing the calendar with your service account e-mail? The following worked in my application:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = "youremail#yourdomain.com",
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// ... Define the event
service.Events.Insert(e, "primary").Execute();

Related

How to send email using service account details only in ASP.NET Core console application?

I have gmail API service account details = client id and service account. How can I just send an email from one id to other without OAuth?
I want to authorize this email sending process with the service account credentials only.
Is there a nuget package that can help fulfill this requirement?
How can I just send an email from one id to other without OAuth?
I assume what you mean is how to send an email with out poping up the Oauth2 consent screen.
Using a service account will allow you to do that, once you have configured the permissions properly in your google workspace account. You grant the service account to preform actions on behalf of one of your domain users. This way the service account can send emails as that user without the user having to consent to that access because you have pre authorized it via google workspace.
The following code will show you how to authorize your application to use a service account.
class Program
{
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static void SendMail()
{
try
{
string ApplicationName = "Gmail API .NET Quickstart";
const string serviceAccount = "xxxx#xxxx-api.iam.gserviceaccount.com";
var certificate = new X509Certificate2(#"c:\XXXX.p12", "notasecret", X509KeyStorageFlags.Exportable);
var gsuiteUser = "YourDomain#YourDomain.com";
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
{
User = gsuiteUser,
Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(CancellationToken.None).Result)
throw new InvalidOperationException("Access token failed.");
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var mailMessage = new MailMessage();
mailMessage.From = new MailAddress("se#Yourdomain.com");
mailMessage.To.Add("ddddd#hotmail.com");
mailMessage.ReplyToList.Add("se#Yourdomain.com");
mailMessage.Subject = "test";
mailMessage.Body = "<h1>sdf</h1>";
mailMessage.IsBodyHtml = true;
//foreach (System.Net.Mail.Attachment attachment in email.Attachments)
//{
// mailMessage.Attachments.Add(attachment);
//}
var mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mailMessage);
var gmailMessage = new Message
{
Raw = Base64Encode(mimeMessage.ToString())
};
Message message1 = new Message();
UsersResource.MessagesResource.SendRequest sendRequest = service.Users.Messages.Send(gmailMessage, "me");
var s = sendRequest.Execute();
Console.WriteLine("Message delivered!");
}
catch (Exception ep)
{
Console.WriteLine(ep.ToString());
}
}
The trick is to remember to set up the domain wide delegation properly and to decide which user the service account is going to be impersonating and to remember to add that email
without google workspace
If you do not have a google workspace account then you can not use service accounts. You may want to consider going though the smtp server instead.

C#: Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"invalid_client", Description:"The OAuth client was not found.", Uri:""

I am trying to integrate Google calendar in my web application. I have successfully integrate Google calendar and able to read events from my Visual Studio IIS Express server. But when I publish and upload my application on web server I have start receiving error
Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"invalid_client", Description:"The OAuth client was not found.", Uri:""
My Code
var certificate = new X509Certificate2(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/key.p12"), "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(/*My Service Account */)
{
Scopes = new string[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Test",
});
EventsResource.ListRequest request = service.Events.List("primary");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
// List events.
Events events = request.Execute();
I have solve my problem by using .JSON key instead of .P12 key.
I have download new .JSON key from google API console. and change little bit of code as bellow.
GoogleCredential credential;
using (var stream = new FileStream(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/key.json"), FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(new string[] { CalendarService.Scope.Calendar });
}
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Test",
});
EventsResource.ListRequest request = service.Events.List("primary");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
// List events.
Events events = request.Execute();
For more detail please visit link from comment section. Thank you DaImTo.

AndroidPublisher library on asp.net gives error No application was found for the given package name

I am using Google.Apis.AndroidPublisher.v2 to get Inappproducts: list and inapp purchases details. I have created a new service account in my app console and using .p12 key as required. Here is my code:
String serviceAccountEmail = "iap-119#abc.iam.gserviceaccount.com";
var fullPath = System.Web.Hosting.HostingEnvironment.MapPath(#"~/App_Data/key.p12");
var certificate = new X509Certificate2(fullPath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
ApplicationName = "abc",
HttpClientInitializer = credential
});
var request = service.Inappproducts.List("com.Its.abc");
var purchaseState = request.Execute();
I got following error on request.Execute();
Message[No application was found for the given package name.] Location[packageName - parameter] Reason[applicationNotFound] Domain[global]
However my package name is correct and same package name is uploaded on play store.
Thanks,
I have found the solution. Need to add service account as a linked account on "API Access" page on Google Play Console. Also add service account email as a user in developer console with required permissions.

Creating Google Drive DriveService with existing access token

I am using ASP.NET Web API and Google.Apis.Drive.v2 Client Library for .NET to upload files to users Drive.
All examples of using the Drive Client Library for .NET require a authentication flow. But how should I create the DriveService when I already know the access token?
Despite the fact that have been 2 years since the question has been asked, today I've encountered the same situation and my solution is:
var valid_token = "Pass_the_valid_token_here";
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = valid_token,
ExpiresInSeconds = 3600,
Issued = DateTime.Now
};
var fakeflow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "fakeClientId",
ClientSecret = "fakeClientSecret"
}
});
UserCredential credential = new UserCredential(fakeflow, "fakeUserId", token);
var serviceInitializer = new BaseClientService.Initializer()
{
//ApplicationName = "Storage Sample",
HttpClientInitializer = credential
};
DriveService service = new DriveService(serviceInitializer);
Update
You could create your own custom token but the issue with this is going to be that the client library will not be able to refresh your access without the refresh token.
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = valid_token,
ExpiresInSeconds = 3600,
Issued = DateTime.Now
};
var authorization = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "lientId",
ClientSecret = "ClientSecret"
}
});
var credential = new UserCredential(authorization, "user", token);
The issue you are going to have with this is that the client library is not going to be able refersh the access token after it has expired since you are not supplying a refresh token its only going to work for an hour.
The answer from Svetoslav Georgiev has so far worked well for me - Can't thank you enough. Google really don't help themselves with the lack of .Net (Asp Core) samples etc. Anway, one problem I did run into was that of referer restriction, so a addition/slight modification to the answer - Once you have the "service" and want to say upload a file, you need to set the referer on a buried HttpClient property...
FilesResource.CreateMediaUpload uploadRequest;
byte[] byteArray = Encoding.UTF8.GetBytes(html);
using (var stream = new MemoryStream(byteArray))
{
uploadRequest = service.Files.Create(fileMetadata, stream, "text/html");
uploadRequest.Service.HttpClient.DefaultRequestHeaders.Referrer = new Uri($"{baseUrl}");
uploadRequest.Fields = "id";
var progress = uploadRequest.Upload();
if (progress.Exception != null)
{
throw progress.Exception;
}
var file = uploadRequest.ResponseBody;
.... do what you will with file ....
}

How can i use an already authenticated session for google calendar?

I've implemented simple Google+ authentication on my MVC5 app and I'd like to access their google calendar. How do I do this using the MVC identity system and my already authenticated user?
Dim authGOps = New GooglePlusAuthenticationOptions() With {
.Caption = "Google+",
.ClientId = "MYCLIENTRID",
.ClientSecret = "MYCLIENTSECRET",
.Provider = New GooglePlusAuthenticationProvider() With {
.OnAuthenticated = Async Function(context)
context.Identity.AddClaim(New Claim(GooglePlusAccessTokenClaimType, context.AccessToken))
End Function
}
}
authGOps.Scope.Add("https://www.googleapis.com/auth/calendar")
app.UseGooglePlusAuthentication(authGOps)
Getting the calendar service:
Dim calendarService = New CalendarService(New Google.Apis.Services.BaseClientService.Initializer() With {
WHAT GOES HERE TO AUTHENTICATE USING MY OLD AUTH CEDENTIALS?
}
So I as well would love to use the Service as it's documented almost everywhere, but I found a workaround to at least getting the data and not having to login again.
Make sure to Nuget Json.Net to deserialize and strongly type. Otherwise you'll get a Json string to manage.
It's in C#, but I'm sure the translation won't be too difficult. Hope it helps!
var claimsIdentity = User.Identity as ClaimsIdentity;
var claims = claimsIdentity.Claims;
var accessTokenClaim = claims.FirstOrDefault(x => x.Type == GooglePlusAccessTokenClaimType);
if (accessTokenClaim != null)
{
string calendarUrl = "https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=" + Uri.EscapeDataString(accessTokenClaim.Value);
using(var client = new HttpClient())
{
var response = await client.GetAsync(calendarUrl);
var calendarList = JsonConvert.DeserializeObject<CalendarList>(await response.Content.ReadAsStringAsync());
}
}

Resources