MVC 5 Gmail API Integration - oauth-2.0

I'm trying to use the GMail API in an MVC 5 project, but I seem to having difficulties on how to achieve that using the Owin Middleware for Authentication
I'm able to login via a Google account, and I can also get the user token as such
var googleOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "xxx",
ClientSecret = "yyy",
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = async ctx =>
{
ctx.Identity.AddClaim(new Claim("urn:tokens:google:accesstoken", ctx.AccessToken));
}
},
};
app.UseGoogleAuthentication(googleOptions);
I get the access token as I would expect, but Google Quicktart Tutorial found here seems to suggest a very different way to accomplish the Authentication
Is there a way I can use this AccessToken to create the objects required in the tutorial ?
Or are these two completely different?

Related

How to make secure authentication for .NET Core Web API?

I am developing an app with .NET Core Web API, Entity Framework and React. I've been reading a lot recently about possible authentication techniques for my API and I've discovered that plain JWT is not entirely secure, so at first I decided to use OpenID Connect with IdentityServer 4. I understand the idea behind OAuth 2.0 and OpenID Connect is to hide user credentials during login process and to involve external authentication provider in issuing an access token, but I don't want to rely on such services because not everyone have an account on Facebook etc. I consider this as an optional way to login. I want to give users an ability to sign in with just login and password. So what is the best (secure) way to accomplish this in modern web apps?
Having project 1 as Client App, project 2 as API Resources and project 3 as Authorization Service (IdentityServer4), I consider following scenarios:
A user is able to create an account on Authorization Service which is responsible for issuing a token required to get access to API Resources through Client App. Authorization Service is registered as authorization provider only for my Client App.
Get authorization token from Authorization Service using resource owner password grant - this one is not recommended by the specs but in my case since user must provide credentials to Authorization Service anyway and I will be hosting every project I can't see any problem.
Don't bother with OAuth and implement authorization mechanism using ASP.NET Core Identity + bearer token authentication.
Any ideas or recommendations highly apprecieated.
I use the JwtBearer package, wire it up in your Startup.cs Configure method like
.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["AppSettings:AuthConfig:SecretKey"])),
ValidateIssuer = true,
ValidIssuer = Configuration["AppSettings:AuthConfig:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["AppSettings:AuthConfig:Audience"],
ValidateLifetime = true,
}
})
and my login action on my User controller looks like
[HttpPost]
public string Post([FromBody]LoginRequest request)
{
var contact = dbContext.Contacts.Where(c => c.Active && c.Email == request.Email).Select(c => new { c.Id, c.PasswordHash }).SingleOrDefault();
if (contact == null || !Security.PasswordHash.ValidatePassword(request.Password, contact.PasswordHash))
{
return string.Empty;
}
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(appSettings.AuthConfig.SecretKey));
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, contact.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
var jwt = new JwtSecurityToken(
issuer: appSettings.AuthConfig.Issuer,
audience: appSettings.AuthConfig.Audience,
claims: claims,
notBefore: now,
expires: now.AddDays(30),
signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256));
jwt.Payload.Add("roles", dbContext.ContactRoles.Where(cr => cr.ContactId == contact.Id).Select(ur => ur.Role.Name).ToArray());
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
I use a JWT package for Angular on the client, there may be something similar for React.

How do I get OpenId Connect access token and refresh tokens in ASP.NET MVC 5?

I am using the Microsoft.Security.Owin.OpenIdConnect to implement Single Sign-On in my ASP.NET MVC 5 app. This is the code I am using:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
ClientId = "id",
ClientSecret = "secret",
MetadataAddress = "https://accounts.google.com/.well-known/openid-configuration",
RedirectUri = "http://localhost:****",
ResponseType = "code id_token",
Scope = "openid email profile",
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie
});
However, I want to get the access token, and if necessary the refresh token, to use for API codes. There is very little documentation for how to do this. Apparently I need to use AcquireTokenByAuthorizationCode, but I can only find this function in an ActiveDirectory assembly, which doesn't seem like it is something that would be used for OpenId. How do I set the options so I can get the access token to use in APIs?
In your ResponseType, put token as well

GoogleOAuth2AuthenticationProvider AccessToken not getting validated

I am using the GoogleOAuth2AuthenticationOptions class for authentication in my MVC5 Web App (SPA Template). Given below is the code
var g = new GoogleOAuth2AuthenticationOptions
{
ClientId = "clientid",
ClientSecret = "secret",
//CallbackPath="",
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = async ctx =>
{
ctx.Identity.AddClaim(new Claim("urn:tokens:google:accesstoken", ctx.AccessToken));
}
}
};
// restrict the retrieved information to just signin information
g.Scope.Add("openid");
app.UseGoogleAuthentication(g);
The token I get is something like this
ya29.LgAibra6cNLEKCEAAADLJxUOviZRgv9JSm-jrB-lNp16nomUijNrVAbcdDkI60Vg-A9yjFN4abcd_C8b4
I am using this token in subsequent calls to a MVC WebAPI which uses OAuthBearerTokens for security. I send the access token through the header in my WebAPI call from my MVC Web app
app.UseOAuthBearerTokens(OAuthOptions);
The javascript generated on the client contains a much larger token which works with my MVC WebAPI. Does anyone know how to fix this, plus why is the javaScript token different? I suspect this has something to do with the SPA template itself but I am not sure.

ASP.Net MVC 5 Google Authentication with Scope

I'm trying to get ASP.Net MVC 5 Google OAuth2 authentication working correctly.
When I set pass in a GoogleOauth2AuthenticationOptions without any scope, then I'm able to log in successfully.
var googlePlusOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = googleClientId,
ClientSecret = googleClientSecret,
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = async ctx =>
{
ctx.Identity.AddClaim(new Claim("urn:tokens:googleplus:accesstoken", ctx.AccessToken));
}
},
};
app.UseGoogleAuthentication(googlePlusOptions);
Then this call will return an ExternalLoginInfo object with all the properties set
ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
When I add any scope though, then I don't get any login info returned. It's just null.
var googlePlusOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = googleClientId,
ClientSecret = googleClientSecret,
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = async ctx =>
{
ctx.Identity.AddClaim(new Claim("urn:tokens:googleplus:accesstoken", ctx.AccessToken));
}
},
};
googlePlusOptions.Scope.Add(YouTubeService.Scope.Youtube);
app.UseGoogleAuthentication(googlePlusOptions);
Then the call to get external info just returns null.
ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
In the Google dev console, I have the following APIs turned on..
Analytics API
BigQuery API
Google Cloud SQL
Google Cloud Storage
Google Cloud Storage JSON API
Google+ API
Google+ Domains API
Identity Toolkit API
YouTube Analytics API
YouTube Data API v3
Something about adding scope to the options is breaking GetExternalLoginInfoAsync.
If anyone's still having trouble with this with the latest Microsoft
OWIN middleware (3.0.0+)...
I noticed from Fiddler that by default, the following scope is sent to accounts.google.com:
scope=openid%20profile%20email
If you add your own scope(s) via GoogleOAuth2AuthenticationOptions.Scope.Add(...), then the scope becomes:
scope=YOUR_SCOPES_ONLY
Therefore, you need to add the default scopes too (or at least, this fixed the issue for me):
var googlePlusOptions = new GoogleOAuth2AuthenticationOptions {
...
};
// default scopes
googlePlusOptions.Scope.Add("openid");
googlePlusOptions.Scope.Add("profile");
googlePlusOptions.Scope.Add("email");
// additional scope(s)
googlePlusOptions.Scope.Add("https://www.googleapis.com/auth/youtube.readonly");
So, I figured this out, with a lot of help from http://www.beabigrockstar.com/blog/google-oauth-sign-asp-net-identity. It turns out that the built in Google authentication provider for MVC is openId only. That's why adding a scope broke it. Using Fiddler, I was able to see the GET request to accounts.google.com, which included "scope=openid" in the querystring.
By switching to the GooglePlusOAuth2 provider in the link above, or on Nuget https://www.nuget.org/packages/Owin.Security.GooglePlus and using the provider name of "GooglePlus", I was able to succesfully add the scopes and still get back the login info from GetExternalLoginInfoAsync.
The changes Google has made to their auth mechanisms have been reflected in version 3.0.0 of Microsoft Owin middleware. As you have identified correctly, one of the changes have been moving the OAuth endpoint to Google+ (https://www.googleapis.com/plus/v1/people/me).
So, the key is to:
upgrade the OWIN middleware to version 3.0.0
enable Google+ API for your app in Google Developers Console

How do I work with Google Analytics oAuth in a WebAPI?

I am building an extension for open source ASP.NET CMS Umbraco where I want to fetch the analytic's from the user's account once they have authorised via oAuth.
The example MVC 4 snippet over on the Google API .NET wikiw page for oAuth seems to only work with a controller and not a WebAPI controller as far as I can tell, is this right?
AuthorizationCodeMvcApp(this, new AppFlowMetaData()).AuthorizeAsync(cancellationToken);
The first parameter in the example expects it to be a regular MVC Controller
https://code.google.com/p/google-api-dotnet-client/source/browse/Src/GoogleApis.Auth.Mvc4/OAuth2/Mvc/AuthorizationCodeMvcApp.cs
So my question is really, how do I work with oAuth with a WebAPI in mind, as I want to return stats back from the API as JSON from the WebAPI so I can use a client side library such as AngularJS to bind the JSON returned to the HTML view?
I would love for any ideas, feedback or suggestions on how I could solve this please.
Thanks,
Warren :)
I have looked into your problem and the i have tested the service account solution. It's tricky to setup but when it runs it works.
This is the code I used in a webapi controller :
String serviceAccountEmail = "805395301940-cu3nhkuqi4ipa3453o276bar5u2e70lq#developer.gserviceaccount.com";
var cert = HttpContext.Current.Server.MapPath("/key.p12");
var certificate = new X509Certificate2(cert, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { AnalyticsService.Scope.Analytics }
}.FromCertificate(certificate));
var service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential
});
//var ga = service.Data.Ga.Get("ga:31335471", "2013-01-01", "2013-01-31", "ga:visits");
// Not Working Currently in Beta
//var ga = service.Data.Realtime.Get("ga:31335471", "ga:activeVisitors");
var ga = service.Management.Profiles.List("~all", "~all");
return ga.Execute();

Resources