Using Google OAuth to secure web services in aspnet core - authentication

I'm getting lost in OAuth and OpenIDConnect and aspnet core middleware. Any help on this would be appreciated.
I have multiple UIs (web, native apps) that use the same set of web services, and I'd like to ensure only authenticated users can access the web services. My organization uses Google accounts, so I'd like to use Google authentication restricted to the organization domain.
The web site is properly requiring authentication, following this sample. What I need now is to have the web site (AngularJS 4) invoke my back end web services with an auth token that I can verify with Google.
The back end services are written with aspnet core. I've tried using these approaches: Google middleware and Google OpenIDConnect but these still 1) assume there is a UI that can prompt an unauthorized user to log in, and 2) appear to be cookie-based, and I won't have cookies for the web service calls.
I don't want to prompt the user to log in, since the "user" in this case is a software client. Either they're authenticated or not already. I just need to get the authentication token, validate it, and carry on.
This appears to be the same question, which hasn't been answered yet, either.
Any suggestions are appreciated. Also, suggestions or tips on having native apps do the same!

Got it working. As mentioned, I was getting lost, and the OpenIDConnect, though referenced in several areas as a solution, was a red herring for the web services. Here's what is working for me now, with as complete steps as I can provide (some cleanup required):
Add authentication to the UI following these directions
Obtain the JWT token as shown in the first segment here
On each web service call, include the JWT token in the headers:
Name: Authentication
Value: Bearer {token value}
Install the JwtBearer NuGet package
In the ConfigureServices method of Startup in the web service, after you AddMvc():
services.AddAuthorization(options =>
{ // this policy needed only if you want to restrict to accounts within your domain. otherwise, don't use options. or use whatever options work for you.
options.AddPolicy("hd",
policy => policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "hd" &&
("https://accounts.google.com".Equals(c.Issuer) ||
"accounts.google.com".Equals(c.Issuer, StringComparison.CurrentCultureIgnoreCase)) &&
c.Value == "yourdomain.com"
)));
});
In the Configure method, before you UseMvc():
JwtBearerOptions jwtOptions = new JwtBearerOptions();
jwtOptions.Audience = "{the OAuth 2.0 client ID credential from google api developer console}";
jwtOptions.Authority = "https://accounts.google.com";
jwtOptions.TokenValidationParameters = new TokenValidationParameters();
jwtOptions.TokenValidationParameters.ValidIssuers = new List<string>()
{
"https://accounts.google.com",
"accounts.google.com"
};
app.UseJwtBearerAuthentication(jwtOptions);
Perhaps there is a more appropriate way to do this...if there is, I'm interested in trying it out. For now, this is working.

I will try to help.
First you need to look at OpenID Connect (which is built on top of OAuth 2.0) remembering that OAuth 2.0 NOT an Authentication protocol.
1) assume there is a UI
No UI is required for login assuming you are using Google services. You only need to check for the existence of and validate the Access Token, Identity Token (and perhaps the refresh token). If there is no Token, assume the user is NOT Authenticated and redirect them to the Authentication Server with a Authorization Request.
If there is a valid Access Token and Refresh Token, then you can assume the user is Authenticated.
You can also inspect the Access Token for proper "Scopes" to determine if they are Authorized for your specific application.
If you are using Google for Authorization Server, you can validate the the hd parameter within Identity Token has the desired Domain.
BTW: No cookies involved.
Hope that helps.

Related

Azure AD Client Credentials and Interactive in same ASP.NET Core API

I have an ASP.NET Core (3.1) web API with a couple of client UI apps. Authentication is via Azure AD, everything is working, using:
services.AddAuthentication()
.AddAzureADBearer(options => Configuration.Bind("AzureAD", options))
I also want to allow machine to machine API access using the Client Credentials flow. I can also get this working using the same app registration.
However, I need a way to validate which flow the request is using, as I want to expose functionality using Client Credentials API to API requests that I don't want interactive users to have access to.
What is the best way to make this work?
I have created a separate app registration in AAD that the Client Secret for the Client Credentials grant is on, and I have it adding permissions (as roles) to the token. And in the app registration for the API, I have granted permission to the Client Credentials app registration. But if I obtain a token with this flow, I can't authenticate. I have found that changing the scope in the token request to match the scope on the API app registration gives me a token that allows me to access the API, but then it is missing the app roles.
One the interactive token there are some user specific claims. So one workaround would be to check for the presence of these claims and disallow the functionality I want to restrict if they are present, but this seems a little hacky.
What else can I do? Is there a way to make both login flows work? Or another option that I've missed?
In case anyone else needs to get this working, I got it working by switching from:
services.AddAuthentication()
.AddAzureADBearer(options => Configuration.Bind("AzureAD", options))
to:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.MetadataAddress = $"https://login.microsoftonline.com/mydomain.onmicrosoft.com/.well-known/openid-configuration";
options.TokenValidationParameters.ValidateAudience = false;
});
There are some additional steps too, as mentioned in the question. I created a separate app registration in AAD, and in the app registration for the API granted permission to the new app registration. In the new app registration I had to edit the manifest to get the scope I wanted included as a role (scopes are only assigned to user tokens, not tokens obtained with the client credentials grant).
With the token working that has the role data, for requests to my restricted endpoint I can just check that it's there:
public bool ValidateScope(string scopeName)
{
return _httpContextAccessor.HttpContext.User.IsInRole(scopeName);
}
bool authorised = _clientCredentialsService.ValidateScope("restricted");
if (!authorised)
{
throw new UnauthorizedAccessException("Attempt to access restricted functionality as a regular user");
}
(I have a filter that picks up this exception and bubbles it up to the consumer as a 403).
If anyone else is doing this you can see I've set ValidateAudience to false, so you probably want to add some policies if you do this.

Securing API using OIDC Resoource server

I have 2 .NET solutions: a webapp, an API.
When user browses the webapp he needs to be authenticated and thus facing this:
return builder.AddOpenIdConnect("contonso", options =>
{
options.Authority = configuration["Contonso:Oidc:Authority"];
options.ClientId = configuration["Contonso:Oidc:ClientId"];
options.ClientSecret = configuration["Contonso:Oidc:ClientSecret"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.ProtocolValidator.RequireState = true;
options.UsePkce = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
}
Which works finely as I get all infos : code, token, id_token once user authenticated.
I copied code above into my API and changed clientId/clientSecret to point to my resource server.
From my webapp I call the API adding an Authorization : Bearer token (received previously).
However in API solution, I get redirected each time to the login page, whatever I pass in my authorization header.
Do I need to change something in my API code in order to make the token "transit" to the resource server and just validate token received?
Cheers,
APIs behave differently to web back ends. The API should never redirect the client and should just validate JWT access tokens instead:
Return a 200 error when the JWT is valid
Return a 401 error when it is not
You use different .Net middleware in the API, by calling AddJwtBearer. Here is a Curity code example that does this.
The main thing is to implement the standards based behaviour, which will then work with any programming language and Authorization Server.
API CODE
There are two main approaches to API JWT validation, and you are using the first type:
Use a JWT framework (such as ASP.Net Core)
Use a JWT library
When you are new to securing APIs using OAuth, it can be useful to understand what code looks like for the library approach, as in this class of mine, which shows the steps an API should use:
Download the token signing public key from the Authorization Server
Give the public key to a library, which will then cryptographically validate the JWT
Also check the issuer, audience and algorithm are as expected
.Net Core will do this for you automatically though, so use the Curity code example approach, since you will write less code.
As always RTFM was the good way. I needed to call the introspection endpoint to validate my token. Explained here also : https://connect2id.com/products/server/docs/api/token-introspection
I then used a Nuget package doing that and it works fine.
Anyways thanks for your help and lightning Gary :)

Dotnet core security oauth and bearer

I'm trying to secure a small dot net core mvc and api application and I've gotten turned around and need a little direction.
I've got to use ADFS 3.0 Server2012 R2 as the source of login/password.
I have to use versioning in the API. (Microsft.aspnetcore.mvc.versioning)
I don't want to send a login/password to API, just a bearer token.
I configured cookieauthentication and OAuth against the ADFS endpoint and it works fine for the mvc ui, but I don't know how/what to do to get the API to work with httpclient from the mvc ui controller to the API.
Long ago I used IdentityServer 1 or maybe 2 and used bearer tokens but I couldn't figure out how to create a token in the OnCreatingTicket in the OAuth event and not sure where to store it. I tried a claim, but it didn't work so it might be malformed or simply wrong.
I am unsure if my issue warrants using something like IdentityServer since the site is small and i don't need a user store, everything is in LDAP / ADFS.
Can I register three middleware peices, build a token from the oauth authentication, store it somewhere like a claim and pass it through the httpclient where its verified?
app.UseCookieAuthentication(option);
app.UseJwtBearerAuthentication(bearer); //api
app.UseOAuthAuthentication(adfsOption); //mvc ui
inside the adfsOption build a token...
Everything I try gets
Message "A security error occurred"
The answer to my question is yes, its very straight forward. I'll post a gist later. My primary problem which was high jacking me was the httpclient didn't like my dev cert for ssl.
I'm still unsure if putting the token in a claim is ok. The cookie and oauth invalidate it every 15 minutes and refresh it, but randomly getting a refresh token would be very difficult.
adding this very bad code allowed it to check my token and it worked as expected.
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };

What are the correct authentication settings for an on-premise ADFS flow?

I've been reading Vittorio Bertocci's blog to try to get up to speed on using ADFS to manage authentication and claims in either an MVC app or WebApi service. It looks like it's getting to be very approachable.
I am now trying to build out a POC using ADFS to do common claims resolution for internal sites/services in our enterprise. Our users would be on the internal network along with our endpoints. Right now we use Windows Integrated auth by default and each site does the work of looking up a user’s name, email, and other AD details and inspecting the claims principal for roles via IsInRole. The claims we get with integrated auth includes just a SamIdentifier and a bunch of group SIDs. I’d like ADFS to do that work for us but still give our users a challenge-free experience. Long term, we will likely add support for non-domain-joined devices on some sites/services, so that is another motivation to explore ADFS.
So I've set up a simple sample app in VS2013 using Organizational Accounts (On Premise) that will dump out a current user's claims, configured the metadata endpoint and audience uri, communicated that info along with the claims I'd like mapped to my ADFS admin (2012, btw), and deployed my site to a development server. So my host is still IIS, though I hope to use Owin middleware to set up authentication rather than web.config (WIF-style).
Given that IIS is my host, how do I configure authentication for my site: anonymous? And my web.config should specify "None" for the authentication mode and deny="?" for authorization, correct?
The other question I have that Vittorio didn't get into in his post about on-premise adfs was the nature of the bearer token and whether or not we need to explicitly configure the middleware to use cookies. My startup config looks like this right now:
public void ConfigureAuth(IAppBuilder app)
{
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] }
});
}
It looks like this middleware is expecting JWT tokens (given that there is a JwtSecurityTokenHandler on the class). Is there any configuration we need to do on the ADFS side to issue JWT tokens? My understanding is that I'll receive a SAML token by default.
And should we expect to use the CookieAuthentication middleware to manage the token or will the browser just keep including it for the life of the session?
Thanks, all!
UPDATE:
So based on Vittorio's help below and some further research, I now have a simple website with just one page protected with an [Authorize] attribute. My startup class's ConfigureAuth method now looks like this:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] }
});
}
We've added my website as a relying party trust in ADFS and created a half dozen claims rules. Everything seems correct so far, but I'm still struggling. I hit the protected "claims" page and get a 401 response with a WWW-Authenticate:Bearer header. So far, so good.
But that's it. How does the browser know where to get authenticated and receive a token? If I was proving out the separate client scenario, my client would be configured with the location of the token authority, but in this simple website scenario, I'm clearly missing something.
UPDATE 2:
I wonder if the implementation for on-premise ADFS just isn't ready yet? Or perhaps the documentation just isn't there yet - or both...
I pulled out all the Owin packages and reverted to using the WSFederationAuthenticationModule and SessionAuthenticationModule, along with all the web.config settings in system.identityModel and system-identityModel.services that have been around a while. Basically, I made the solution look like the one you get from VS2013 when you selected Organizational Accounts --> On Premise. Everything works beautifully and I have all my configured claims coming from ADFS. I see the initial 302 redirect to ADFS, the challenge-response, and ultimately have a SAML token serialized into a secure session cookie. On the website, I echo back the claims like so:
var user = User as ClaimsPrincipal;
ViewBag.Claims = user.Claims;
return View();
This is why I suspect the middleware is incomplete: when you use that new template in VS2013, the wizard goes to the federation metadata endpoint you specify and builds out all the web.config settings by reading that xml and, in addition, sets some intelligent defaults. That's sort of what I expected to happen in the Owin middleware - it should have everything it needs to know since I pass in the same metadata endpoint. I was hoping that "magic" would replace using the FAM/SAM modules and all the accompanying config.
1) If you are configuring a web UX app, that is something meant to be consumed through browser redirects, you want to use http://www.cloudidentity.com/blog/2014/04/29/use-the-owin-security-components-in-asp-net-to-implement-web-sign-on-with-adfs/. You'll see that the cookie middleware does come into play in that case.
2) If you are configuring a web API, as in something that is consumed by a rich client or another server, or in general anything that is not a browser roundtripping, see http://www.cloudidentity.com/blog/2013/10/25/securing-a-web-api-with-adfs-on-ws2012-r2-got-even-easier/. In that case you do not need cookies, given that there is no session -every single call must carry the token.
HTH
V.
As Vittorio said you need to differentiate if you create a web page with web api or web api only. Follow his blog posts they are great!!
If you host a webapi only project in an IIS you need to set the authentication to "forms authentication".
This works also if your web api is covered behind a web application proxy. make sure that you configure your endpoint (published web application) not to preauthenticate. the value for "preauthenticate" should be "pass through".
bg Andrej

Firebase custom OAuth authentication

FireBase appears to support OAuth-based authentication from Facebook, Twitter, Google, GitHub. What about a different OAuth provider? It would be great if there were a generic "OAuth" option where in addition to specifying an API Key and Secret you specified whatever other information was needed.
Is it possible to use FireBase's existing OAuth-based authentication modules without rolling my own custom OAuth authentication module for FireBase, by possibly forking from one of the "built-in" OAuth mechanisms? Which one is the most generic, if so?
I also struggled for a while now with this, and here's how I've done it for my project. Run a node.js express server that will have the role to:
get the req from your frontend app
redirect user to oauth page
return to node.js in case of success/error and compute the token needed for firebase in order to successfully login the user.
res with a cookie containing this token and redirect the user back to frontend app to complete the process.
You will have to run the node server on a different vps in order for your app to work but you'll probably need it anyway if you have a bigger app that needs to run private stuff on the backend and not everything upfront.
Firebase has 5 OAuth-based authentication, Facebook, Twitter, Google, GitHub and Custom Auth for now. Using Custom Authentication Firebase gives you complete control over user authentication by allowing you to authenticate using secure JSON Web Tokens (JWTs). So you can specify additional options to API Key and Secret. For example:
var logInAndThen = function(options) {
var secret = '********************';
var tokenGenerator = new FirebaseTokenGenerator(secret);
var token = tokenGenerator.createToken(credentials[options.userType ||
'admin'
};