Authenticate Google access token with ASP.NET Core backend server - asp.net-core

I have Angular2 on client and ASP.NET Core on server side. I use JavaScriptServices (aspnetcore-spa template).
For authentication I use OpenIddict and I follow example here.
Now I am on the server side in Controller class method and I would like to validate id_token because this is suggested on this side:
Important: Do not use the Google IDs returned by getId() or the user's
profile information to communicate the currently signed in user to
your backend server. Instead, send ID tokens, which can be securely
validated on the server.
And I would also like to register user (save email, profile ...) in my database through ASP.NET Core identity.
I would like to use Google API client Library for .NET to get user information and store refresh_token. Years ago I manage to do it with PHP, but I can't figure it out with .NET.
I download nuget packages: Google.Apis, Google.Apis.OAuth2.v2, Google.Apis.Plus.v1.
I am not sure which nuget package I need for this, which class should I use, how to set Google ServerKey and how to get user information from information which I get from gapi.signin2 button.
In simple:
How can I validate id_token from .NET with Google .NET Client library?

I found solution here. It is old, but it works.
var googleInitializer = new BaseClientService.Initializer();
googleInitializer.ApiKey = this.config["Authentication:Google:ServerKey"];
Oauth2Service ser = new Oauth2Service(googleInitializer);
Oauth2Service.TokeninfoRequest req = ser.Tokeninfo();
req.AccessToken = request.AccessToken; //access token received from Google SignIn button
Tokeninfo userinfo = await req.ExecuteAsync();
I didn't figure it out how to get Display name and picture on server. But it can be done on client:
onGoogleLoginSuccess(user: gapi.auth2.GoogleUser)
{
console.log("basic profile", user.getBasicProfile());
}
If someone knows more updated solution or how to retrieve basic user profile on server, please share it.
In addition I can use Google+, but careful because Google Account is not Google+ Account. I didn't have + account and get error:
Google.Apis.Requests.RequestError Not Found [404] Errors [
Message[Not Found] Location[ - ] Reason[notFound] Domain[global] ]
in code:
var plusService = new PlusService(googleInitializer);
Person me = await plusService.People.Get(userinfo.UserId).ExecuteAsync();
but it is possible to get all user information (picture, display name, first name, last name, birthday ...)

Related

What's the best way of web api authentication to Microsoft Graph to access emails in the service account mailbox?

I am trying to figure out which way would be better for my application. I need to perform an automatic email import triggered from ASP.NET Core Web API using Microsoft Graph mailbox access. According to the documentation, there are two options to go for:
Get access on behalf of the user (https://learn.microsoft.com/en-us/graph/auth-v2-user?view=graph-rest-1.0)
Get access without the user (https://learn.microsoft.com/en-us/graph/auth-v2-service?view=graph-rest-1.0)
As the import is automatically triggered by Azure Function timer I do not want to open a popup for the user credentials. So I considered to go with the second option and create a service user to do this, but then I saw a point about Admin consent in the documentation and I got a bit confused. Does this mean that if nobody is going to accept the app rights to access emails it will not be able to do so? What would be easier/preferred way to implement this kind of functionality?
--Does this mean that if nobody is going to accept the app rights to access emails it will not be able to do so?
no, that means after admin consent the api permission, then you can access any users' emails in your tenant.
--What would be easier/preferred way to implement this kind of functionality?
as you said that you are using Azure timer function to to auto import the mails, so you shouldn't get access to graph api on behalf of a user, you have to access the api on behalf the application.
But you have to check if the graph api you want to call support being accessed on behalf of the application. Let's see this api. You may notice that it supports Application api permission.
Then pls note that you have to using client credential flow to generate access token to call the graph api, you can't use other flows. Here's sample code for using client credential flow with graph SDK to call graph api.
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "azure_ad_appid";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var inboxMessages = await graphClient.Users["user_id"]
.MailFolders["inbox"]
.Messages.Request().GetAsync();

How can I delete Azure B2C users directly from my app?

I'm building an ASP .Net Core web App. I use Azure ADB2C for user authentication and I would like to have an admin user, which could delete other users. I can delete users from Azure Active Directory via Azure portal, but I would like to do it directly from the app. I have created an admin account in my Active Directory tenant, and gave it global administrator permissions.
I tried to use Graph API, but I can't get it to work. I created an IAuthenticationProved according to instructions on this website:
https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS.
Then I created GraphServiceClient and tried to delete user (https://learn.microsoft.com/en-us/graph/api/user-delete?view=graph-rest-1.0&tabs=csharp), but I got error:
System.AggregateException: 'Returning 0 accountsts and 0 broker accountsdata provider for login.microsoftonline.com. Success? True.)'
AuthenticationException: Code: authenticationChallengeRequired
Message: Authentication challange is required.
My code looks like this:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("<My Client ID>")
.WithRedirectUri("<My Redirect Uri>")
.WithClientSecret("<My Client secret>")
.Build();
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/User.ReadWrite.All");
scopes.Add("https://graph.microsoft.com/Directory.ReadWrite.All");
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(confidentialClientApplication,scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
graphClient.Users[<UserId>]
.Request()
.DeleteAsync()
.Wait();
Could you tell me what I'm doing wrong? Or maybe there is some other way to do it? Thank you in advance!
Your approach to use Graph API is the way to go. But the problem you are facing there is because your selected auth provider (AuthorizationCodeProvider) is not matching with the scope needed here to delete user. Looking at your code, you want 'Authorization Code' flow which means it would need a delegated scope. As mentioned in permission requirement for delete user, the 'Delegated' scope needed to delete user is Directory.AccessAsUser.All which I don't see in the code snippet you shared.
Additionally, suggest you a quick read to Choose a Microsoft Graph authentication provider based on scenario.
NOTE: To add the required delegated permission, the application registration must be either of the first two types types shown below. The third option won't give you that.
For the above you will be able to add Directory.AccessAsUser.All delegated permission.

Save information immediately after Google login in Azure Mobile Services (.NET Back-end)

What I basically want to be able to do is authenticate to azure mobile services (using google or some other provider), and immediately save some of the user information (i.e. email address) on the server.
I know I could call a custom method from the app after authentication, but I was hoping to have some hook to do this straight after the google login on the server side.
Is this possible? How do I do it?!
This is currently only possible in the .NET runtime. If using the Node runtime, you will not be able to do this.
For the .NET runtime, you would want to create a class which inherits from GoogleLoginProvider (I'll call mine CustomGoogleLoginProvider), and then you'll need to override the CreateCredentials method:
public override ProviderCredentials CreateCredentials(ClaimsIdentity claimsIdentity)
{
// grab any information from claimsIdentity which you would like to store
// If you need the access token for use with the graph APIs, you can use the following
string providerAccessToken = claimsIdentity.GetClaimValueOrNull(ServiceClaimTypes.ProviderAccessToken);
// use the access token with HttpClient to get graph information to store
return base.CreateCredentials(claimsIdentity);
}
Then in your WebApiConfig.cs, add the following to the Register() method, immediately after the options object is created:
options.LoginProviders.Remove(typeof(GoogleLoginProvider));
options.LoginProviders.Add(typeof(CustomGoogleLoginProvider));
The CreateCredentials() method gets called immediately before a Mobile Services token is created. At this point, the Google token has been validated, and the claimsIdentity has been populated with whatever Google sent back.
Some information will be available in the claimsIdentity by default, but you may also have information which requires you to call through to Google. You can only do this if you set the proper scopes configured.
If you did want to go the custom API route, you would just need to make a call from your controller:
ServiceUser user = (ServiceUser)this.User;
GoogleCredentials creds = (await user.GetIdentitiesAsync()).OfType<GoogleCredentials>().FirstOrDefault();
string accessToken = creds.AccessToken;
The Node version of getIdentities() is documented here.

getting gmail calendars by oauth credentials in c# dot net framework 2.0

I want to read google calendars through oauth credentials for my desktop application.
I am done with following things
registered with google api console
got client id ,client secret key
Now as I was looking for some examples,which tells me that
this can be achieved with dot net framework 4.0 with google calendar api v3.
But for some reasons i need to stick to dot net framework 2.0.
So how i can achieve this?
I am done with reading calendars with username and password but now need to read through oauth.
And As I was able to read contacts by oauth I am sure there will be some way to do this for calendars.
Code for conatcts :
RequestSettings ObjectRequestSetting= new RequestSettings("appname",
"consumerkey", "consumersecretkey", "user", "domain");
ContactsRequest objContactReq = new ContactsRequest(ObjectRequestSetting);
ContactsService objService = new ContactsService("appname");
To connect to OAuth2 with out using googles client librarys is a little tricky but it can be done. I normaly use this method becouse i dont like having to release third party dll's with my applications.
The first thing you need is to build the URL that will get you the AutenticationCode.
public static Uri GetAutenticationURI(Autentication MyAutentication)
{
List postData = new List();
postData.Add("client_id=<strong>{Client ID}</strong>");
postData.Add("redirect_uri=<strong>{Redirect URI}</strong>");
postData.Add("scope=<strong>{Scope}</strong>" );
postData.Add("response_type=code");
return new Uri("https://accounts.google.com/o/oauth2/auth" + "?" + string.Join("&", postData.ToArray()));
}
The URI it returns should look something like this https://accounts.google.com/o/oauth2/auth?client_id=.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/calendar.readonly&access_type=offline&approval_prompt=force&response_type=code
You need to have a webbrowser control on your form someplace. Where ever you want to call it do something like this. It will open the screen for the user to authorise your aplication.
wbAuthenticate.Url = Autentication.GetAutenticationURI(myAutentication);
When the wbAuthenticate_DocumentCompleted returns you need to rip the Autentication code out of the body of HTML. Don't bother trying to rip it from the title this isn't always corect.
Once you have an AutenticationCode you need to exchange it for a refreshtoken and a accesstoken. The access token is the one you use for all your calls to the API. You use the refreshtoken to get a new access token back after it expires which is normaly in an hour. The only thing you need to save for next time is the RefreshToken.
I have code for all that in a blog post but its pritty big and i'm not sure about spaming that amount of code here. http://daimto.com/google-api-and-oath2/

How to identify a Google OAuth2 user?

I used Facebook login to identify users. When a new user comes, I store their userID in my database. Next time they come, I recognized their Facebook ID and I know which user it is in my database.
Now I am trying to do the same with Google's OAuth2, but how can I recognize the users?
Google sends me several codes and tokens (access_token, id_token, refresh_token), however none of them are constant. Meaning if I log out and log back in 2 minutes later, all 3 values have changed. How can I uniquely identify the user?
I am using their PHP client library: https://code.google.com/p/google-api-php-client/
As others have mentioned, you can send a GET to https://www.googleapis.com/oauth2/v3/userinfo, using the OAuth2 bearer token you just received, and you will get a response with some information about the user (id, name, etc.).
It's also worth mentioning that Google implements OpenID Connect and that this user info endpoint is just one part of it.
OpenID Connect is an authentication layer on top of OAuth2. When exchanging a authorization code at Google's token endpoint, you get an access token (the access_token parameter) as well as an OpenID Connect ID token (the id_token parameter).
Both these tokens are JWT (JSON Web Token, https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token).
If you decode them, you'll get some assertions, including the id of the user. If you link this ID to a user in your DB, you can immediately identify them without having to do an extra userinfo GET (saves time).
As mentioned in the comments, these tokens are signed with Google's private key and you may want to verify the signature using Google's public key (https://www.googleapis.com/oauth2/v3/certs) to make sure they are authentic.
You can see what's in a JWT by pasting it at https://jwt.io/ (scroll down for the JWT debugger). The assertions look something like:
{
"iss":"accounts.google.com",
"id":"1625346125341653",
"cid":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"aud":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"token_hash":"WQfLjdG1mDJHgJutmkjhKDCdA",
"iat":1567923785,
"exp":1350926995
}
There are also libraries for various programming languages to programatically decode JWTs.
PS: to get an up to date list of URLs and features supported by Google's OpenID Connect provider you can check that URL: https://accounts.google.com/.well-known/openid-configuration.
I inserted this method into google-api-php-client/src/apiClient.php:
public function getUserInfo()
{
$req = new apiHttpRequest('https://www.googleapis.com/oauth2/v1/userinfo');
// XXX error handling missing, this is just a rough draft
$req = $this->auth->sign($req);
$resp = $this->io->makeRequest($req)->getResponseBody();
return json_decode($resp, 1);
}
Now I can call:
$client->setAccessToken($_SESSION[ 'token' ]);
$userinfo = $client->getUserInfo();
It returns an array like this (plus e-mail if that scope has been requested):
Array
(
[id] => 1045636599999999999
[name] => Tim Strehle
[given_name] => Tim
[family_name] => Strehle
[locale] => de
)
The solution originated from this thread: https://groups.google.com/forum/#!msg/google-api-php-client/o1BRsQ9NvUQ/xa532MxegFIJ
It should be mentioned, that the OpenID Connect API returns no id attribute anymore.
It's now the sub attribute which serves as a unique user identification.
See Google Dev OpenID Connect UserInfo
"Who is this?" is essentially a service; you have to request access to it as a scope and then make a request to the Google profile resource server to get the identity. See OAuth 2.0 for Login for the details.
Altough JWTs can be validated locally with the public key, (Google APIs Client Library downloads and caches they public keys automatically) checking the token on Google's side via the https://www.googleapis.com/oauth2/v1/tokeninfo endpoint is necessary to check if the access for the applicaton has been revoked since the creation of the token.
Java version
OAuth2Sample.java