how to get email after using google OAuth2 in C#? - c#

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.

Related

GMail API auto authorization for read mail

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

Get account name or email when using Google Drive Rest API in C# app

I'm new to C# and I'm creating a WPF C# app. I want use Google Drive Rest API to send backup file to Google Drive. But this isn't problem. When I wrote Android app in Java, getting account name was very easy. But in C# and my desktop app, I don't now how do this. I read many topic and Google documentation, but I didn't find answer.
Here is my code for Helper class. I only want to get the account name or address and display it in the main window.
I found a solution with Google+ API, but now it doesn't work because Google+ doesn't exist.
Is there any way to do this?
UserCredential credential;
public DriveService GetGoogleDriveService()
{
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 = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/menadzer_zespolow_token.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
}
public String GetAccountEmail()
{
// code to get account name or email there
return accountNameOrEmail;
}

Refresh token for Google Storage Client

I must be stupid I think. Using OAuth.
Have the following piece of code
// Authenticate with Google
using (MemoryStream stream =
new MemoryStream(GetSecrets()))
{
string credPath = "token.json";
this.userCredential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
Works fine and sets this.userCredential as expected
Now I want to create a StorageClient
But StorageClient cannot be created directly from UserCredential.
The only way I can see is to create from the AccessToken
GoogleCredential googleCredential = GoogleCredential.FromAccessToken(this.userCredential.Token.AccessToken);
return StorageClient.Create(googleCredential);
Problem is this token expires after an hour.
However, in C# I cannot for the life of me find a way to pass in the refresh token (which I have in the UserCredential) and have it refresh itself as needed
It seems possible in Java though, using the .Builder
Does anyone know if this is possible ?
The simplest way of achieving this is to create the StorageService yourself, and then pass that to the StorageClientImpl constructor:
var service = new StorageService(new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = StorageClientImpl.ApplicationName,
});
StorageClient client = new StorageClientImpl(service);
We might consider adding an overload of Create that takes an IConfigurableHttpClientInitializer instead of a GoogleCredential, in the future.

Is there a way to reuse Google.Apis.Auth.OAuth2.UserCredential object to authorize SpreadsheetsService?

I'm trying to use the Google Drive and Spreadsheets APIs from a C# console app. I'd like to authorize both services using user credentials with a FileDataStore so that I don't have to reauth my app every single time it runs. Below is how I'm authorizing my Drive service object:
var userCredential = GoogleWebAuthorizationBroker.AuthorizeAsync
(
new ClientSecrets
{
ClientId = "[clientID]",
ClientSecret = "[clientSecret]"
},
new []
{
"https://www.googleapis.com/auth/drive",
"https://spreadsheets.google.com/feeds"
},
"[userName]",
CancellationToken.None,
new FileDataStore("MyApp.GoogleDrive.Auth.Store")
).Result;
var driveService = new DriveService
(
new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = "MyApp",
}
);
For the Spreadsheets service, I'm authorizing as prescribed by this guide, but every time I run my app, I have to open a browser to the given auth URL and manually copy in the access token to get it to work.
Is there a way to auth once, obtain the user credentials as above, and use them with both services? Note, I'm authorizing with both the Drive and the Spreadsheets scope, so I don't think there's a problem with that.
I've tried to make it work like this, but I keep getting 400 Bad Request errors when I attempt to insert rows into my spreadsheet:
var auth = new OAuth2Parameters
{
ClientId = "[clientID]",
ClientSecret = "[clientSecret]",
RedirectUri = "[redirectUri]",
Scope = "https://www.googleapis.com/auth/drive https://spreadsheets.google.com/feeds" ,
AccessToken = userCredential.Token.AccessToken,
RefreshToken = userCredential.Token.RefreshToken,
TokenType = userCredential.Token.TokenType,
};
var requestFactory = new GOAuth2RequestFactory(null, "MyApp", auth);
var spreadsheetsService = new SpreadsheetsService("MyApp")
{
Credentials = new GDataCredentials(userCredential.Token.TokenType + " " + userCredential.Token.AccessToken),
RequestFactory = requestFactory,
};
Is there a way to auth once, obtain the user credentials as above, and use them with both services?
Yes. Provided you have included all scopes and have requested offline access, then you'll get a refresh token which you can store and reuse to get access tokens as needed. Obv you need to consider the security implications.
A 400 bad request doesn't sound like an OAuth issue. I think you have two questions/issues here and it might be worth starting a new thread. Include the http request/response for the 400 in your question.

My Issue with Google Authentication

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.

Categories

Resources