I am using gmail api to read mail from mail account. But to access mail i have to authorize by selecting or login the mail. If it's only one mail and it's already logged in, it can auto authorize without have select the mail. But if multiple account logged in i have to select the mail to authorize. My goal is to authorize multiple gmail account without selecting the mail. Because this mail reading function will run in crone job. I use following code for authorization.
Thanks in advance.
UserCredential credential;
using (FileStream stream = new FileStream(userfilepath, FileMode.Open, FileAccess.Read))
{
String FolderPath = Convert.ToString(ConfigurationManager.AppSettings["CredentialsInfo"]);
String FilePath = Path.Combine(FolderPath, "APITokenCredentials");
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = cid,
ClientSecret = csecret
},
Scopes = new[] { GmailService.Scope.MailGoogleCom },
DataStore = new FileDataStore(FilePath, true)
});
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = accesstoken,
ExpiresInSeconds = 3600,
Issued = DateTime.Now
};
credential = new UserCredential(flow, usermail, token);
// Create Gmail API service.
var refreshResult = credential.RefreshTokenAsync(CancellationToken.None).Result;
GmailService service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
In your case the best option is using a Service Account, these types of accounts are intended for non-human access.
Examples:
Running workloads on virtual machines (VMs).
Running workloads on on-premises workstations or data centers that call Google APIs.
Running workloads which are not tied to the lifecycle of a human user.
Documentation
C# Google API Github
.NET Gmail API Documentation
Related
I get credentials using code
static string[] Scopes = { "https://www.googleapis.com/auth/userinfo.email" };
private static UserCredential GenerateCredential()
{
UserCredential credential;
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
return credential;
}
How to get email from this credential? I've tried code
private string GetEmailFromCredentials(UserCredential credential)
{
var plusService = new PlusService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My Application",
});
var me = plusService.People.Get("me").Execute();
var useremail = me.Emails.FirstOrDefault().Value;
return useremail;
}
but it looks like that People.Get("me") is not possibe anymore. I'm getting error "Google.Apis.Requests.RequestError
Legacy People API has not been used in project 618254727025 before or it is disabled"
solution is to get access token and try https://www.googleapis.com/oauth2/v2/userinfo?access_token=
In your scopes variable. Try and just use the value "email" not the
full https address. Scope keywords in the web link are separated by spaces. Here is a way that I do this to obtain the scopes: profile email openid.
To test this approach, you can manually paste the below weblink into a browser after obtaining the access code:
https://www.googleapis.com/oauth2/v2/userinfo?access_token=[PASTE ACCESS CODE HERE]&[PASTE VALUE FROM THE BELOW VARIABLE authorizationRequest HERE]
fyi: I was ammending the demonstration code available: https://github.com/googlesamples/oauth-apps-for-windows.
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile%20email&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
authorizationEndpoint,
System.Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
You can use the users.getprofile it will return the email address of the user who is currently authenticated.
request
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My Application",
});
var user = service.Users.GetProfile("me").Execute;
response
{
"emailAddress": "xxxx1#gmail.com",
"messagesTotal": 8394,
"threadsTotal": 1494,
"historyId": "605503"
}
People me
The correct usage of people.get is "people/me"
var request = plusService.People.Get("people/me")
request.PersonFields("emailAddresses");
var response = request.Execute();
I have been stuck on this issue for several days. And finally, thanks to this link (Check if user is already logged in), I learned that the parameter input, "user", to be the key issue. This "user" should be the windows login user (you can use Enviroment.Username), not the programmer or APP user. The GoogleWebAuthorizationBroker.AuthorizeAsync uses this username to save its credential in the location:
C:\Users[username]\AppData\Roaming\Google.Apis.Auth\Google.Apis.Auth.OAuth2.Responses.TokenResponse-[username]
(something like this).
So if you feed "user" to AuthorizeAsync, the credential saving could be a problem, and your app will hang or lag seriously. And, later when you want to use the cred file to get userinfo and email, it will be problematic (lag seriously). In my case, user info will be all missing, leaving only an email address. Also, you have to include the required two scopes: "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile". Hope these helps.
Add 'https://www.googleapis.com/auth/userinfo.email' in scopes.
In callback you will code. Get tokens json from this code using
oauth2Client.
This json contains id_token which is basically a jwt
token, parse it u will get email.
I am try to send emails using GMAIL OAuth protocol but the GoogleWebAuthorizationBroker.AuthorizeAsync method hangs.
I try to run this from server (it is a web.api project that receive the send email information from the client through a htttp POST, then my service should connect to Google Gmail and send the email) Then the service act as a proxy between the Gmail and the client.
I got the client secret stuff from google console for Web Applications.
I use Visual Studio 2013 C# NET4.5. The service has access to the directory where I store the DataStore.
The code that i use is the following and hang over GoogleWebAuthorizationBroker.AuthorizeAsync
DbGoogleDataStore is the same implementation of DataStore as Google (I planning to create my data store in the future in the database, for that I separate it)
public void SendMailUsingOauthProtocol(SmtpConfigurationSettings settings, SmtpSendMessageInputModel filter)
{
var msg = new AE.Net.Mail.MailMessage() // MailMessage
{
Subject = filter.Subject,
Body = filter.Body64,
From = new MailAddress(settings.Username),
};
msg.To.Add(new MailAddress(filter.To));
msg.ReplyTo.Add(new MailAddress(filter.To));
var msgStr = new StringWriter();
msg.Save(msgStr);
// Get from settings the Client secret
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(settings.AutClientSecret);
writer.Flush();
stream.Position = 0;
//ClientSecrets secret = GoogleClientSecrets.Load(stream).Secrets;
var storeFilePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
storeFilePath = storeFilePath + #"\GmailOauth\";
Debug.WriteLine(storeFilePath);
IDataStore fileStore = DbGoogleDataStore.Factory(storeFilePath, true);
// Create the credential.. this step go authenticate with the user if it is necessary
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
stream,
Scopes,
"user",
CancellationToken.None,
fileStore
).Result;
//Create GMAIL API Service
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
var result = service.Users.Messages.Send(new Message { Raw = Base64UrlEncode(msgStr.ToString()) }, "me").Execute();
}
Please, Someone has a idea about how to do this?
I'm using Google's Oauth 2.0 to upload videos to Youtube via our server.
My client ID is a "service account". I downloaded the json key and added it to my solution.
Here is the relevant code:
private async Task Run(string filePath)
{
UserCredential credential;
var keyUrl = System.Web.HttpContext.Current.Server.MapPath("~/content/oauth_key.json");
using (var stream = new FileStream(keyUrl, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
When I run it, I get this error: At least one client secrets (Installed or Web) should be set.
However, in my json there is no "client secret":
{
"private_key_id": "9d98c06b3e730070806dcf8227578efd0ba9989b",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIICdQIBADANBgkqhk etc,
"client_email": "546239405652-8igo05a5m8cutggehk3rk3hspjfm3t04#developer.gserviceaccount.com",
"client_id": "546239405652-8igo05a5m8cutggehk3rk3hspjfm3t04.apps.googleusercontent.com",
"type": "service_account"
}
so I assume I overlooked something.
Maybe I can't use the "service account" ? don't know...
The solution that uses json file is quite similar.
Here is sample that create VisionService using GoogleCredential object created from json file with ServiceAccountCredential.
GoogleCredential credential;
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(VisionService.Scope.CloudPlatform);
}
var service = new VisionService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "my-app-name",
});
this sample require two NuGet packages:
Google.Apis.Vision.v1
Google.Apis.Oauth2.v2
I managed to get a service account to work with a P12 file, But would like to know how to use with the JSON file, Or just value from the JSON file to create the certificate.
To get the token
private static String GetOAuthCredentialViaP12Key()
{
const string serviceAccountEmail = SERVICE_ACCOUNT_EMAIL;
var certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
var scope = DriveService.Scope.Drive + " https://spreadsheets.google.com/feeds";
var credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { scope }
}.FromCertificate(certificate) );
if (credential.RequestAccessTokenAsync(CancellationToken.None).Result == false)
{
return null;
}
return credential.Token.AccessToken;
}
And this is how I used the token I got
// Initialize the variables needed to make the request
OAuth2Parameters parameters = new OAuth2Parameters {AccessToken = token};
GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
Not an expert on C# but it looks like you were trying to use the service account to do the OAuth2 web server flow, which shouldn't work.
You probably want to use ServiceAccountCredential instead.
For more information about different Google OAuth2 flows, please refer to the doc for web server, service account, etc.
I have been using the old version of Google Calendar GData API (v1, v2) since November 2011 in my ASP.NET Applications, allowing Users to retrieve and/or create Calendar Events after submitting their usernames and passwords , and this was working perfectly till 17th of November 2014 just before Google decided to shut down this version of API as announced Calendar GData API / Google Calendar Connectors deprecation
Now I am stuck with the new version of Google APIS Calendar (v3) which forces me to use different scenario of Authentication Process instead of the Traditional one. I don't mind at all using this version of Calendar API as it supports all the needed features but now i don't know how to handle multiple users authentication to use their User Client's ID and Secret which are registered per each user Code Console.
So my question is : Is there any way to let user sign in with his/her normal credentials (Either by Username/Password or Google+ Sign UP feature) and bypassing the process of creating API Project, Enabling the needed APIs and creating new User credentials inside the Console through ASP.net code?
Any Sample code made in C# ASP.net is highly appreciated .
EDIT: Here is my code of Authentication I use
public static CalendarService Authenticate()
{
CalendarService service;
GoogleAuthorizationCodeFlow flow;
string json_File = System.Configuration.ConfigurationManager.AppSettings["Authentication_Path"];
string store_path = System.Configuration.ConfigurationManager.AppSettings["FileStore_Path"];
string url = System.Configuration.ConfigurationManager.AppSettings["Authent_URL"];
using (var stream = new FileStream(json_File, FileMode.Open, FileAccess.Read))
{
flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new FileDataStore(store_path),
ClientSecretsStream = stream,
Scopes = new[] { CalendarService.Scope.Calendar }
});
}
var uri = url;
var result = new AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync("TRAININGCALENDAR", CancellationToken.None).Result;
if (result.Credential == null)
{
GoogleCalendar_Bus.Main_Authentication(url, "", "");
}
// The data store contains the user credential, so the user has been already authenticated.
service = new CalendarService(new BaseClientService.Initializer
{
ApplicationName = "Calendar API Sample",
HttpClientInitializer = result.Credential
});
if (result.Credential != null)
{
service = new CalendarService(new BaseClientService.Initializer
{
ApplicationName = "Calendar API Sample",
HttpClientInitializer = result.Credential
});
}
return service;
}
No you need to use Oauth2. When they authenticate you just save the refresh token this will allow you to then get a new access token and you will have access again. You will need to make your own implementation of Idatastore to store these refresh tokens in the database.
The code for creating an implementation of a Idatastore that stores to the Database is to extensive to post here but you can see a basic example here: DatabaseDataStore.cs
Then you can use it like this.
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = _client_id
,ClientSecret = _client_secret }
,scopes
,Environment.UserName
,CancellationToken.None
,new DatabaseDataStore(#"LINDAPC\SQL2012", "LindaTest", "test123", "test", "test")).Result;
Update: Now that I can see your code.
Make sure you have the latested .net client lib. Google.Apis.Calendar.v3 Client Library
your code is using FileDataStore this is what you will need to change. You need to make your own implementation of Idatastore similar to the one I have created DatabaseDatastore.
Your code looks different from how I normally do it.
string[] scopes = new string[] {
CalendarService.Scope.Calendar , // Manage your calendars
CalendarService.Scope.CalendarReadonly // View your Calendars
};
try
{
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore("Daimto.GoogleCalendar.Auth.Store")).Result;
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
This may be due to the fact that you aren't using the most up to date client lib, you can find a sample console application for Google Calendar here unfortunately it also uses FileDatastore you will have to edit it to use DatabaseDataStore. The authentication sample project can be found here Google-Dotnet-Samples/Authentication/ it shows how you can create your own implementation of Idatastore.
I am still working on the tutorial to go along with that sample project I hope to have it completed soon.
Does .NET support SMTP authentication via OAuth protocol? Basically, I would like to be able to send emails on users' behalves using OAuth access tokens. However, I couldn't find a support for this in the .NET framework.
Google provides some samples for this in other environments but not .NET.
System.Net.Mail does not support OAuth or OAuth2. However, you can use MailKit's (note: only supports OAuth2) SmtpClient to send messages as long as you have the user's OAuth access token (MailKit does not have code that will fetch the OAuth token, but it can use it if you have it).
The first thing you need to do is follow Google's instructions for obtaining OAuth 2.0 credentials for your application.
Once you've done that, the easiest way to obtain an access token is to use Google's Google.Apis.Auth library:
var certificate = new X509Certificate2 (#"C:\path\to\certificate.p12", "password", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential (new ServiceAccountCredential
.Initializer ("your-developer-id#developer.gserviceaccount.com") {
// Note: other scopes can be found here: https://developers.google.com/gmail/api/auth/scopes
Scopes = new[] { "https://mail.google.com/" },
User = "username#gmail.com"
}.FromCertificate (certificate));
bool result = await credential.RequestAccessTokenAsync (CancellationToken.None);
// Note: result will be true if the access token was received successfully
Now that you have an access token (credential.Token.AccessToken), you can use it with MailKit as if it were the password:
using (var client = new SmtpClient ()) {
client.Connect ("smtp.gmail.com", 587, SecureSocketOptions.StartTls);
// use the access token
var oauth2 = new SaslMechanismOAuth2 ("username#gmail.com", credential.Token.AccessToken);
client.Authenticate (oauth2);
client.Send (message);
client.Disconnect (true);
}
I got it working by using Microsoft.Identity.Client and MailKit.Net.Smtp.SmtpClient like this using Office 365 / Exchange Online. App registration requires API permissions SMTP.Send.
var options = new PublicClientApplicationOptions
{
ClientId = "00000000-0000-0000-0000-000000000000",
TenantId = " 00000000-0000-0000-0000-000000000000",
RedirectUri = "http://localhost"
};
var publicClientApplication = PublicClientApplicationBuilder
.CreateWithApplicationOptions(options)
.Build();
var scopes = new string[] {
"email",
"offline_access",
"https://outlook.office.com/SMTP.Send" // Only needed for SMTP
};
var authToken = await publicClientApplication.AcquireTokenInteractive(scopes).ExecuteAsync();
//Test refresh token
var newAuthToken = await publicClientApplication.AcquireTokenSilent(scopes, authToken.Account).ExecuteAsync(cancellationToken);
var oauth2 = new SaslMechanismOAuth2(authToken.Account.Username, authToken.AccessToken);
using (var client = new SmtpClient())
{
await client.ConnectAsync("smtp.office365.com", 587, SecureSocketOptions.StartTls);
await client.AuthenticateAsync(oauth2);
var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse(authToken.Account.Username));
message.To.Add(MailboxAddress.Parse("toEmail"));
message.Subject = "Test";
message.Body = new TextPart("plain") { Text = #"Oscar Testar" };
await client.SendAsync(message, cancellationToken);
await client.DisconnectAsync(true);
}
Based on this example:
https://github.com/jstedfast/MailKit/blob/master/ExchangeOAuth2.md
Just adding to the above answer. I also spend lot of time to find out things for sending email using gmail oAuth2 with mailkit in .net. As I am using this to send email to my App users. Thanks to mailkit developers.
Now we need:
Authorization code
Client ID
Client Secret
Refresh Token
Access Token
You can directly get the Client Id and Client Secret from google console by creating your project.
Next you can enable gmail app from the Google Developers OAuth Playground by using your own OAuth credentials in left top setting button.
After that Select and Authorize the API https://mail.google.com/.
Now you can directly refresh token by this http POST request https://developers.google.com/oauthplayground/refreshAccessToken. you will find the parameter in there.
Now you can directly use this code in your C# code using MailKit:
using (var client = new SmtpClient())
{
client.Connect("smtp.gmail.com", 587, SecureSocketOptions.StartTls);
var oauth2 = new SaslMechanismOAuth2(GMailAccount, token.AccessToken);
client.Authenticate(oauth2);
await client.SendAsync(mailMessage);
client.Disconnect(true);
}
Now you will be able to send email through your gmail account from server side.
Using MailKit as referenced in the other answers, I was hitting an authentication issue requiring more scopes to be requested from Gmail. For anyone experiencing "Authentication Failed error" with either of the other answers, this answer uses the Gmail API instead in order to avoid requesting more scopes.
Using some pieces from this answer: https://stackoverflow.com/a/35795756/7242722
Here's a complete example which worked for me:
var fromAddress = new MailboxAddress(fromName, fromEmail);
var toAddress = new MailboxAddress(toName, toEmail);
List<MailboxAddress> ccMailAddresses = new List<MailboxAddress>();
if (ccEmails != null)
foreach (string ccEmail in ccEmails)
ccMailAddresses.Add(new MailboxAddress(string.Empty, ccEmail));
var message = new MimeMessage();
message.To.Add(toAddress);
message.From.Add(fromAddress);
message.Subject = subject;
var bodyBuilder = new BodyBuilder();
bodyBuilder.HtmlBody = body;
bodyBuilder.TextBody = HtmlUtilities.ConvertToPlainText(body);
message.Body = bodyBuilder.ToMessageBody();
foreach (MailboxAddress ccMailAddress in ccMailAddresses)
message.Cc.Add(ccMailAddress);
GoogleAuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientId = <ClientId>,
ClientSecret = <ClientSecret>
},
});
TokenResponse tokenResponse = await authorizationCodeFlow.RefreshTokenAsync("id", <RefreshToken>, CancellationToken.None);
UserCredential credential = new UserCredential(authorizationCodeFlow, "id", tokenResponse);
var gmailService = new GmailService(new BaseClientService.Initializer()
{
ApplicationName = <AppName>,
HttpClientInitializer = credential,
});
Google.Apis.Gmail.v1.Data.Message gmailMessage = new Google.Apis.Gmail.v1.Data.Message();
gmailMessage.Raw = Utilities.Base64UrlEncode(message.ToString());
var result = gmailService.Users.Messages.Send(gmailMessage, "me").Execute();