Identity Server 4 User management API - asp.net-core

I've managed to get a solution working with a single page application project (ReactJS), an API running on ASP.net Core project and an IdentityServer 4 project.
I want to be able to call an API on the IdentityServer 4 project from the single page application project.
I created a simple controller class in the IdentityServer 4 application, with the authorize attribute. If I call it via Postman, however, I get the HTML for the login page back.
This happens after I already logged in on the API, and I use that same token.
How am I supposed to log in to identity server to make calls to it to manage users?

As stated in the comments, you should definitely add more information to your question. Your controller is part of the identity server's mvc application? Are you using AspnetCore.Identity?
If so, your controller is protected by AspnetCore.Identities's cookie authentication scheme. You need to send the cookie to access the controller. This has nothing to do with identity server as you are on the local MVC application, it's just plain vanilla MVC.
Postman has problems sending cookies, you need the interceptor chrome extension. You also need to login though postman.
This will probably work if the SPA is hosted by the same MVC application. If not you will need to configure your mvc applications to validate access tokens (not just issue them), like this:
// Adds IdentityServer
app.UseIdentityServer();
// Accept access tokens from identity server
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
AuthenticationScheme = "Bearer"
ApiName = "api1"
});
This way you can request access tokens for your local api through identity server, (You also need to configure an api scope for 'api1').
To validate them in your api-controller add this to your controller:
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
[HttpGet]
public string Bearer()
{
return "Access Token Accepted";
}

Related

Can System.Web be used within a class library that targets .net standard 2.0

I have an Asp.net application that uses form authentication. I am building an asp.net core API that will receive a JWT token that has user information included in the payload. This API needs to call asp.net application for serving the user request. In order for asp.net application to serve this request, it needs the form authentication cookie to recognize it as valid logged in user.
In other words, the API needs to generate and send an authentication cookie in the request forwarding stimulating scenario that actual user is making the request.
Note:
The API is being created to expose features of the asp.net application as a public API without user having to log in to the asp.net application UI directly first.
I am creating a class library that target .net standard 2.0 that can be used in my api that takes care of creating cookie part and add it to the request before it forwards the request to my asp.net application. How can I create FormAuthenication cookie in my class library? I found following code and wondering how can I do similar in my library?
string userData = string.Join("|",GetCustomUserRoles());
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // ticket version
username, // authenticated username
DateTime.Now, // issueDate
DateTime.Now.AddMinutes(30), // expiryDate
isPersistent, // true to persist across browser sessions
userData, // can be used to store additional user data
FormsAuthentication.FormsCookiePath); // the path for the cookie
// Encrypt the ticket using the machine key
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
// Add the cookie to the request to save it
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.HttpOnly = true;
No. System.Web is .NET Framework only. It is not included nor compatible with .NET Standard, any version. Additionally, Forms Auth, in general, is deprecated (as part of ASP.NET Membership), so nothing but ASP.NET (.NET Framework) will ever be able to work with it.
Going forward, if you need to share authentication, you must switch to ASP.NET (Core) Identity and utilize the data protection API, rather than the old-style machine key encryption Forms Auth depends on.
For what it's worth though, an API should not be using cookie authentication. Cookies are a non-standard way for clients to interact with an API, and they're also a form of state, whereas APIs should be stateless (REST). Instead, you should be doing something like basic or bearer authentication, using the Authorization header to authorize each request either with user/pass (basic) or token (bearer).

How to integrate Social Login with existing .Net core Web API backend and Angular SPA frontend with working OpenIddict user/password and bearer token

TL;DR
Question: how to implement social login (OAuth2 authorization flow) with an existing SPA/Web API application that is based on identity, user/password, bearer token authentication?
I have an existing application that has:
Backend: .Net Core 2 Web API with Identity and OpenIddict services configured, with a working authentication process based on user/password challenge for bearer token.
Users are stored with Identity (AspNetUsers).
Part of the Startup.cs code
// Register the OpenIddict services.
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
})
.AddServer(options =>
{
options.UseMvc();
options.EnableTokenEndpoint("/connect/token");
options.AllowPasswordFlow();
options.AllowRefreshTokenFlow();
options.AcceptAnonymousClients();
options.RegisterScopes(
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Phone,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles);
})
.AddValidation();
.
Frontend: SPA Angular 7 app that consumes this backend API and token authorization
So basically the current setup is, user inputs user/password to the SPA that invokes the backend /connect/token endpoint that validates the credentials and generates the token for the client.
And now I need to integrate Social Login (OAuth2 Authorization flow) so that
user chooses login with provider,
gets redirected to providers authorization page,
gets redirected back to my application that
needs to create the Identity user and save the Identity UserLoginInfo data and
provide my application token so that the user can login.
I understand the OAuth2 authorization flow that needs to Request an Authorization Code and then Exchange Authorization Code for an Access Token for that provider. I also know that this flow must use backend, once it uses sensitive information like client_secret that can't be stored in client side.
But at some point user needs to interact with frontend, so connecting these parts seems very difficult considering that these are wide used technologies. All practical examples I found on Google were using .Net Core MVC application. I also found this article ASP.NET Core 3.0 Preview 4 - Authentication and authorization for SPAs that seems promising but is still in Preview 4.
I already created the social providers apps and I have client_id, client_secret. Also registered my redirects url's.
What I tried with no success was:
In frontend user chooses login with social provider,
User gets redirected to provider authorization page, authenticates himself and
gets redirected from the provider to my frontend URL (redirect_uri) with the provider's code then
my frontend calls my backend /connect/token existing endpoint passing the selected provider and the received code, the endpoint was programmed to receive the provider and code also, then
my backend calls provider's get AccessToken url posting "grant_type", "authorization_code" "code", code "redirect_uri", "https://same_frontend_host/same/path" "client_id", providerClientId "client_secret", providerSecret and receives a StatusCode: 401, ReasonPhrase: 'Unauthorized' response
What am I doing wrong? It's been a real hard time to get this to work.
What worked but it's not what I need
An implicit 2 step authorization flow using frontend for provider authentication calls and a backend call to get my bearer token and create Identity user. With this setup user made a successful login using a social provider, unfortunately it's not what I need
EDIT:
Made a diagram of what is implemented, it is failing at step 5/6 with StatusCode: 401, ReasonPhrase: 'Unauthorized' and further steps are not completed.
The flow you describe pretty much corresponds to "Authorization Cross Domain Code", an OpenID Connect flow that has never been standardized.
I wouldn't recommend going with such a non-standard option. Instead, consider tweaking your flow to make your JS client exclusively communicate with your own authorization server instead of starting the flow by making the client redirect the user agent to an external provider.
The key idea here is that your own authorization server should initiate the initial communication with the external provider (i.e it should build the authorization request and redirect your users to the external provider's authorization endpoint) and handle the last part: the callback authorization response. For that, I'd recommend going with the OAuth2/OIDC handlers shipping with ASP.NET Core (there are providers for Google, Facebook and many more)
Of course, this doesn't mean your JS client can't send a hint about the external provider the user should use to authenticate. It's something you can easily handle in your authorization controller. Here's an example:
public class AuthorizationController : Controller
{
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly SignInManager<ApplicationUser> _signInManager;
public AuthorizationController(
IAuthenticationSchemeProvider authenticationSchemeProvider,
SignInManager<ApplicationUser> signInManager)
{
_authenticationSchemeProvider = authenticationSchemeProvider;
_signInManager = signInManager;
}
[HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
Debug.Assert(request.IsAuthorizationRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (!User.Identity.IsAuthenticated)
{
// Resolve the optional provider name from the authorization request.
// If no provider is specified, call Challenge() to redirect the user
// to the login page defined in the ASP.NET Core Identity options.
var provider = (string) request.GetParameter("identity_provider");
if (string.IsNullOrEmpty(provider))
{
return Challenge();
}
// Ensure the specified provider is supported.
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
if (!schemes.Any(scheme => scheme.Name == provider))
{
return Challenge();
}
// When using ASP.NET Core Identity and its default AccountController,
// the user must be redirected to the ExternalLoginCallback action
// before being redirected back to the authorization endpoint.
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider,
Url.Action("ExternalLoginCallback", "Account", new
{
ReturnUrl = Request.PathBase + Request.Path + Request.QueryString
}));
return Challenge(properties, provider);
}
// ...
}
}

Using Google OAuth to secure web services in aspnet core

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.

How to set up cookie based authentication with NancyFx and IdentityServer3 (non-API website)

We have an environment with the following:
Standalone IdentityServer3 instance (issues reference tokens, not jwt)
ASP.NET WebAPI resource server
.NET client applications that authenticate against IdSvr (via resource owner flow)
...and now we'd like to start adding an OWIN-hosted web app that will use NancyFx to serve server-rendered pages as well as a couple AngularJS SPAs. This Nancy website will NOT host any APIs, but may consume data from our existing API. I'd like to add authentication in the OWIN pipeline to help secure our Angular applications from being sent down to users who don't have access.
This would be in contrast to sending down the SPA code, and having Angular determine if the user should see anything. In that case we've already exposed the javascript code base, and this we want to avoid.
I'm trying to understand how I should configure this Nancy site to authenticate users against IdentityServer using the implicit flow. I have implemented this authentication scheme in standalone SPAs before (where all authentication was handled by AngularJS code and tokens were stored in HTML5 local storage), but I'm a bit lost on how to properly tackle this within the OWIN pipeline.
I'm thinking that the OWIN cookie authentication middle-ware is the answer, but does that mean the following?
I need to redirect the user to IdentityServer (using the proper url arguments for implicit flow)?
IdentityServer will redirect the user back to my site on a successful login, so is that where I hook into the OWIN Authorization manager to set the appropriate cookie?
...or am I thinking about this all wrong?
For reference, I've read through the following posts, and they're very helpful but I'm not quite seeing the big picture with OWIN. I'm going to experiment with the UseOpenIdConnectAuthentication middle-ware next, but I would appreciate any guidance SO might have here.
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
https://github.com/IdentityServer/IdentityServer3/issues/487
Fundamentally, implementing OpenID Connect authentication in a Nancy app hosted via OWIN is really not different from implementing it in any MVC/Katana app (the Thinktecture team has a sample for this scenario: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Clients/MVC%20OWIN%20Client)
You basically need 3 things: the cookie middleware, the OpenID Connect middleware and the Nancy middleware:
public class Startup {
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
// Set the address of your OpenID Connect server:
Authority = "http://localhost:54541/"
// Set your client identifier here:
ClientId = "myClient",
// Set the redirect_uri and post_logout_redirect_uri
// corresponding to your application:
RedirectUri = "http://localhost:56765/oidc",
PostLogoutRedirectUri = "http://localhost:56765/"
});
app.UseNancy(options => options.PerformPassThrough = context => context.Response.StatusCode == HttpStatusCode.NotFound);
}
}
If you're looking for a functional demo, you can take a look at https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy/Nancy.Client (note: it doesn't use IdentityServer3 for the OIDC server part but it shouldn't make any difference for the client app).

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