DotNetOpenAuth 4.3 and Google - OpenID 2.0 + OAuth 1.0 deprecated - asp.net-mvc-4

If you want to cut to the chase, the question is: what is the best/official way to use DotNetOpenAuth with Google in asp.net mvc 5?
About a year ago, I used OAuth (DotNetOpenAuth oAuth and OpenID) pretty much as it came out of the box for asp.net MVC 4 (as it is in the sample project). Since then I used it successfully for google, facebook, yahoo and microsoft. However, recently I have been having intermittent problems with users signing into google. I have tried upgrading to MVC 5 and DotNetOpenAuth 4.3, but I get the same.
When I looked at the google docs I found this:
Important: Google has deprecated its support for OAuth 1.0. If you are
using OpenID 2.0 + OAuth 1.0, we recommend that you switch to Google+
Sign-In. Google+ Sign-In provides the OAuth 2.0 authentication
mechanism with rich social features and access to additional Google
desktop and mobile features. It supports all Google users and
transparent migration. For details, see the Migration of Google
authentication.
I could very well be mistaken, by I thought that out-of-the-box asp.net mvc 4 DotNetOpenAuth uses OpenID 2.0 (I use minimumRequiredOpenIdVersion="V20") + OAuth 1.0. I can see in the DotNetOpenAuth source that there is an OAuth 2.0 library under 'product', but I am not sure how to use this. Also, I am a bit nervous about Auth 2.0 as what I have read is not very complementary and it seems that it is easier to shoot oneself in the foot (might be unfounded, but it seems to be a recurring theme).
For Google+ I found these instructions which seem pretty straightforward, but that is almost a year ago, so I am wondering if this is still the best way to go. I also found this git repository implementing Google oauth2. Still, I would like to know whether this is still relevant as it is all from some time ago.
So, the question is - what is the best/official way to use DotNetOpenAuth with Google in asp.net mvc5? Hopefully I haven't missed anything obvious, in which case just a pointer to some links will be fine.
Update
I found this question and this question which are related. I guess that I will go with the google auth2 from git unless I am told otherwise.
Resolution
I did the following: -
Followed the steps in the link provided by the accepted answer. It is this link.
It's important to keep using SSL after login and not drop back to HTTP, your login cookie is just as secret as your username and password…redirecting back to HTTP after you’re logged in won’t make the current request or future requests much faster.
Got the latest DotNetOpenAuth.GoogleOAuth2 on Nuget.
I looked at the recommendation from this msdn blog (by the same author) about how to best to secure the site. Basically, the recommendation is to add the following which will force all pages to HTTPS:
filters.Add( new System.Web.Mvc.RequireHttpsAttribute() );
Ultimately what this means is that the whole site is HTTPS. Since making those changes, the site has been running fine.

This is how you use DotnetOpenAuth with Google/OAuth2.
First, reference the DotnetOpenAuth.Ultimate package from Nuget.
Then create a provider class and the profile model class
public class GoogleClient : WebServerClient
{
private static readonly AuthorizationServerDescription GoogleDescription =
new AuthorizationServerDescription
{
TokenEndpoint = new Uri( "https://accounts.google.com/o/oauth2/token" ),
AuthorizationEndpoint = new Uri( "https://accounts.google.com/o/oauth2/auth" ),
ProtocolVersion = ProtocolVersion.V20
};
public const string ProfileEndpoint = "https://www.googleapis.com/oauth2/v1/userinfo";
public const string ProfileScope = "https://www.googleapis.com/auth/userinfo.profile";
public const string EmailScope = "https://www.googleapis.com/auth/userinfo.email";
public GoogleClient()
: base( GoogleDescription )
{
}
}
public class GoogleProfileAPI
{
public string email { get; set; }
private static DataContractJsonSerializer jsonSerializer =
new DataContractJsonSerializer( typeof( GoogleProfileAPI ) );
public static GoogleProfileAPI Deserialize( Stream jsonStream )
{
try
{
if ( jsonStream == null )
{
throw new ArgumentNullException( "jsonStream" );
}
return (GoogleProfileAPI)jsonSerializer.ReadObject( jsonStream );
}
catch ( Exception ex )
{
return new GoogleProfileAPI();
}
}
}
Then, in your login page (login controller) have this code:
private static readonly GoogleClient googleClient = new GoogleClient
{
ClientIdentifier = "client_id",
ClientCredentialApplicator = ClientCredentialApplicator.PostParameter( "client_secret" )
};
// Page_Load of login page if WebForms
// Login action of the Account controller if MVC
IAuthorizationState authorization = googleClient.ProcessUserAuthorization();
if ( authorization == null )
{
// Kick off authorization request
// Google will redirect back here
Uri uri = new Uri( "http://your.application.address/login" );
googleClient.RequestUserAuthorization( returnTo: uri,
scope: new[] { GoogleClient.ProfileScope, GoogleClient.EmailScope } );
}
else
{
// authorization. we have the token and
// we just go to profile APIs to get email (and possibly other data)
var request =
WebRequest.Create(
string.Format( "{0}?access_token={1}",
GoogleClient.ProfileEndpoint,
Uri.EscapeDataString( authorization.AccessToken ) ) );
using ( var response = request.GetResponse() )
{
using ( var responseStream = response.GetResponseStream() )
{
var profile = GoogleProfileAPI.Deserialize( responseStream );
if ( profile != null &&
!string.IsNullOrEmpty( profile.email ) )
FormsAuthentication.RedirectFromLoginPage( profile.email, false );
}
}
}

Here is the recommended way to use Google authentication as well as a few other social integrations:
http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
In order to use oauth2 (assuming your using MVC)
Enable the Google OpenID provider
Open the App_Start\Startup.Auth.cs file and remove the comment characters in //app.UseGoogleAuthentication(); to enable Google authentication.
Under Use another service to log in, click Google. The user is then redirected to the google site where you will enter your credentials.
If you don't have this file or folder "app_start", then you probably created a 'blank' project, instead of an "internet" project when you first created the solution. It's much easier (if planning on using external logins) to select 'internet application' when you first begin. Not sure what editor your using, but Visual Studio 2012/2013 make this ridiculously easy!
If your going to use OpenID which is now the recommended way, here is a great starting point: https://developers.google.com/accounts/docs/OpenID#settingup
Lastly, if you have access to NUGET through your editor like (Visual studio) , you'll find these tasks, like adding oAuth-1/2 or openId have been made very easy..
Here is a last link that would get you off in the right direction if the above doesn't really fit your build... With a few more details, I would be more than happy to help guide you to the best solution. One thing I can say is that oauth2 IS still very relevant and used in many applications today, and you wouldn't be wrong implementing this while starting a new project today - it would be the right way to go (or at least one of the right ways to go)... Hope some of this helps and isn't just going down a path you have already been down.
Hope all is well.

Related

How to do 3-legged OAuth login with One Tap sign-in on Android

I'm trying to implement 3-legged OAuth login with Google's One Tap sign-in for Android. This is needed to access Gmail from the server side.
The app needs to get a server auth code and pass it to the server which stores it. The server will later exchange it for a refresh token and access token and use it to pull email.
I use the following code (error checking removed for brevity) which gets me a token, but it's NOT a refresh token. What I need is a server auth code that I can exchange for access and refresh tokens like.
// Show the one tap sign in account selector
List<String> scopes = new ArrayList<>(List.of("https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email",
..., ... // other scopes come here
));
BeginSignInRequest signInRequest = BeginSignInRequest.builder()
.setGoogleIdTokenRequestOptions(BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.associateLinkedAccounts(LINKED_SERVICE, scopes) // what is this LINKED_SERVICE?
.setSupported(true)
.setServerClientId(SERVER_CLIENT_ID)
.setFilterByAuthorizedAccounts(false)
.build())
.setAutoSelectEnabled(false)
.build();
SignInClient oneTapClient = Identity.getSignInClient(activity);
oneTapClient.beginSignIn(signInRequest).addOnCompleteListener(new OnCompleteListener<BeginSignInResult>() {
#Override
public void onComplete(#NonNull Task<BeginSignInResult> task) {
if (!task.isSuccessful())
return;
BeginSignInResult signInResult = task.getResult();
PendingIntent signInIntent = signInResult.getPendingIntent();
activity.startIntentSenderForResult(signInIntent.getIntentSender(), REQ_SIGN_IN, null, 0, 0, 0));
}
});
Then in the intent receiver:
// Handle response from the one tap account selector
public void onResult(Activity activity, int requestCode, int resultCode, Intent data) {
// Assume requestCode==REQ_SIGN_IN and resultCode is OK (for brevity)
SignInCredential cred = oneTapClient.getSignInCredentialFromIntent(data);
String idTokenString = cred.getGoogleIdToken();
// Now what? This is an Oauth token for client-side use.
// How to I get a server auth code that can be exchanged for a refresh and access tokens at the server-side?
}
Is there a way to get the server auth code?
Comment:
Until now I was using the GoogleSignIn API in the following manner, but it's being phased out and not sure when it's going to stop working (documentation states March 31, 2023).
// Start the OAuth sign in
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope("https://www.googleapis.com/auth/userinfo.profile"), new Scope("https://www.googleapis.com/auth/userinfo.email"))
.requestServerAuthCode(SERVER_CLIENT_ID)
.requestEmail()
.build();
Intent intent = GoogleSignIn.getClient(activity, gso).getSignInIntent();
activity.startActivityForResult(intent, REQ_SIGN_IN);
The result is returned to the activity (again, no error checking)
public void onResult(Activity activity, int requestCode, int resultCode, Intent data) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount acct = task.getResult();
String authCode = acct.getServerAuthCode(); // Server will exchange this for access and refresh tokens
// Pass this authCode to the server
}
New Google Sign-In API using Identity.getSignInClient(activity) doesn't support 3-legged OAuth.
The documentation states:
For new apps we recommend using Google Identity Services instead of the
Google Sign-In API for sign-in and sign-up, unless you need
authorization, Server-Side Access (!!!), or custom OAuth scopes.
Old Google Sign-in is scheduled to be deprecated at March 31, 2023. However, until now (September 2022) there is no sign that the new API will add support.
Linked account sign-in uses access and refresh tokens issued by another, non-Google partner site or platform. Data is shared from the partner platform TO Google using OAuth2. In this scenario the partner platform defines the scope names, and Google requests and requests and manages the access and refresh tokens issued by the partner platform -- your app does not manage the
credentials. An example of this might be playing music or video managed by a different company on a Google Home hub.
From your comments it sounds like you are looking to request Google scopes and work with Google Account user data? In your example code, you are using authentication scopes only (email, profile) and simply using One Tap to obtain an ID token would be the most straightforward. Should you need to work with Google scopes, for Android you'll currently need to use the older Google Sign-in APIs. Somewhat confusingly, the sign-in options for user authentication are using newer Android API's while authorization to work with Google Account user data remains on the older Google Sign-in APIs.

How can I ask for Google Drive access from asp.net API

Using ng-gapi (Angular), I'm already able to ask the users for permission to access their Google Drive. But I would like to try something different.
I would like to achieve the same result (open popup asking for permission), but at this time from a ASP.NET Core API. Kind the same way that I already do the login:
new ChallengeResult(
GoogleDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = Url.Action("...", "...", new { ... })
});
But this time, I know that I need to add Scope, discoveryDocs, client_id, etc... I just don't know how.
You can use Google.Apis.Auth.AspNetCore3 for authenticating in .NET Core 3.1, it is the Google maintained and recommended library. The Google.Apis.Auth.AspNetCore3.IntegrationTests is a good example (it's just an ASP.NET Core 3 Web Application) of how to use the library and shows all of its features, including authorization and using incremental scopes.

Authenticate Google access token with ASP.NET Core backend server

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 ...)

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/

Azure Mobile Services vs MVC4 SimpleMembership

When using an ASP.Net MVC4 site, it's very easy to add OAuth authentication with SimpleMembership.
OAuthWebSecurity.RegisterTwitterClient(consumerKey,consumerSecret);
When using Azure Mobile Services on a client device, it's very easy to add OAuth authentication.
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Twitter);
The problem is that these are two different data stores. I need to users to be able to register and/or login from either the app or the site. What is the best/easiest way to provide integrated OAuth authentication from devices and an ASP.Net site? Are there samples available?
I was only able to achieve this with Twitter and Facebook logins when Azure Mobile Services and MVC SimpleMembership were in play. Please see this thread which admittedly has a lot to look through, but it does explain my findings in pretty good detail.
http://social.msdn.microsoft.com/Forums/en-US/azuremobile/thread/d54d28c6-6941-4af5-b116-dc8c51820498
Sorry I couldn't give you any code, because my stated goal was to not write any authentication/security code for this integration.
Nate
I just finished posting a sample that uses ASP.NET MVC4 simple membership to authenticate to an Azure Mobile Service (via Facebook, in my example) at http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/25/exposing-authenticated-data-from-azure-mobile-services-via-an-asp-net-mvc-application.aspx. The post contains a lot of details, but the idea is that if you can get the provider access token (from Facebook or Google, for example), you can format it and send to the backing mobile service. In the snippet below, the facebook token was stored in the session state, and is retrieved by a method that ensures that the user is logged in.
if (MobileService.CurrentUser == null)
{
var accessToken = Session["facebooktoken"] as string;
var token = new JObject();
token.Add("access_token", accessToken);
return MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token).ContinueWith<bool>(t =>
{
if (t.Exception != null)
{
return true;
}
else
{
System.Diagnostics.Trace.WriteLine("Error logging in: " + t.Exception);
return false;
}
});
}