Saml2 Single Logout (SingleLogoutServiceResponseUrl) with Sustainsys and Identity Server 4 - asp.net-core-mvc

I am using Sustainsys Saml2 with Identity Server 4. A customer has asked me if we support support SAML Single Logout.
They have asked for:
Single Logout Request URL
Single Logout Response URL
From what I can see this is probably supported by Sustainsys given the following properties exist.
var idp = new Sustainsys.Saml2.IdentityProvider(new EntityId("https://sso.acme.com"), opt.SPOptions)
{
MetadataLocation = "/metadata/sso-meta.xml",
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = true,
SingleLogoutServiceResponseUrl = "INSERT",
SingleLogoutServiceBinding = Saml2BindingType.HttpRedirect
};
I have two questions:
I can only see one property which matches their request - the SingleLogoutServiceResponseUrl (I don't see a property for the SingleLogoutServiceRequestUrl). How do I configure the Single logout request Url?
How do I determine what the values are for these Url's?
Thanks

Outbound logout requests are sent to the SingleLogoutUrl configured on the Idp. The SingleLogoutResponseUrl is a special one - it's only used when responses should be sent to a different endpoint on the Idp than requests. Normally they are the same and if SingleLogoutResponseUrl is not set, the SingleLogoutUrl is used for both responses and requests.
Ask the Idp people for those.
And as an additional note: You're loading metadata. Then everything should already be in the metadata and you can shorten your code to
var idp = new Sustainsys.Saml2.IdentityProvider(new
EntityId("https://sso.acme.com"), opt.SPOptions)
{
MetadataLocation = "/metadata/sso-meta.xml",
AllowUnsolicitedAuthnResponse = true,
};

Related

Read Session Value In .Net Core Web Api [duplicate]

This question already has answers here:
Accessing Session Using ASP.NET Web API
(13 answers)
Closed 3 years ago.
In my Web api when a user login successfully I set session with some values like
HttpContext.Session.SetObject("CurrentUserID", user.Id);
HttpContext.Session.SetObject("CurrentUserRoles",user.Roles);
and just return token and some values to save in cookie
return Ok(new
{
Id = user.Id,
Username = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Token = tokenString,
role = user.Roles
});
But when the client hit api action which has this line
List<string> userRolesList = HttpContext.Session.GetObject<List<string>>("CurrentUserRoles");
Then always get null value even I have added session inside Startup >Configure
like
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
and ConfigureService also
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds( 60 * 60);
options.Cookie.HttpOnly = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
but does work still... Please help.
HTTP is a stateless protocol. Sessions are fake state, enabled by both a server-side and client-side component. The client-side component is a cookie: specifically a Set-Cookie response header. In order for the session to be restored on the next request, the value of this Set-Cookie response header must be sent back via the Cookie request header with each request. A web browser (the client) will do all this automatically, including persisting the cookie locally. However, a thin client like HttpClient, Postman, etc. will not. You would need to independently persist the cookie from the response header and then attach it to each request via the Cookie header in order to maintain the session between requests.
That said, this is a major reason why APIs typically do not, and honestly should not make use of sessions. It's simply a pattern that doesn't make much sense in an API context, and only adds a potential point of failure, since clients must pay attention to the cookie headers, and take manual actions to handle the cookies.

Running IdentityServer4 and API and a Web App from a single 'site'

We have a legacy system written in Core MVC 1, using IdentityServer4 for API access and Identity for user management. The site includes a set of API controllers as well, which a mobile application connects to for authentication and data.
We have been having general stability issues, which we have not been able to get to the bottom of. We have decided to upgrade the system to the latest version of MVC Core and in the process IdentityServer4 requires an upgrade.
The problem is that the authentication pipeline has changed dramatically between versions (Core MVC 1 - 2 and Identity 1 - 2) and we are unable to determine a configuration that works.
In short we need:
Cookie Authentication for web site access
OAuth 2 password grant flow for app access
However, despite this setup working on the legacy version, it does not seem to want to play ball on the newer setup. It seems we can have one or the other, but not both. There doesn't appear to be any example projects available anywhere that demonstrate such a setup.
I understand this setup is not ideal in that these systems should be split out, and I am going to be making a recommendation as such. I have seen hints of routing api requests through a pipeline setup for Bearer authentication using MapFrom but haven't managed to determine a working setup.
UPDATE: Startup.cs
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
var AuthServerConfig = new IdentityServerConfig(Configuration.GetSection("IdentityServer"));
var IdentityCert = AuthServerConfig.GetCerttificate();
var IdentityConfig = services.AddIdentityServer()
.AddInMemoryIdentityResources(AuthServerConfig.GetIdentityResources())
.AddInMemoryApiResources(AuthServerConfig.GetApiResources())
.AddInMemoryClients(AuthServerConfig.GetClients())
.AddAspNetIdentity<ApplicationUser>();
//to secure the API
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = AuthServerConfig.Settings.RootUrl;
options.RequireHttpsMetadata = false;
options.ApiName = AuthServerConfig.Settings.Scope;
});
And in the Configure Method we have:
app.UseIdentityServer();
app.UseAuthentication();
The stage we are at now is that IdentityServer seems to be operational in that a token can be requested. You can call into an API endpoint and gain access so long as that endpoint had the following attribute:
[Authorize(AuthenticationSchemes = "Bearer")]
However, we want the API to be authenticated using both Identity Cookies as well as Bearer tokens, as there is a swagger UI for querying the API when logged in.
Using just the [Authorize] attribute will allow it to be accessed via cookies, but not access tokens through Postman (401)
Not sure if this is solved but what i personally done to enable API to be authenticated via cookie and bearer token is implement both schemes:
e.g.
services.AddAuthentication()
.AddCookie(options => {
options.LoginPath = "/Account/Unauthorized/";
options.AccessDeniedPath = "/Account/Forbidden/";
})
.AddJwtBearer(options => {
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});
In the api controller i use [Authorize(AuthenticationSchemes = AuthSchemes)]
[Authorize(AuthenticationSchemes = AuthSchemes)]
public class TodoController: Controller
More details on different schemes and usages of them you can have a look here:
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?tabs=aspnetcore2x
Hope that helps.

IdentityServer 3: How to validate the ClaimsIdentity in ASP.NET WebAPI

I use the IdentityServer 3 with the ResourceOwner Flow.
It seems to work. I can see in fiddler the token is accessed and validated by my WebAPI.
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://server/IdentityServer/",
ValidationMode = ValidationMode.ValidationEndpoint,
RequiredScopes = new[] { "MyFramework" },
SigningCertificate = Certificate.Get(),
DelayLoadMetadata = true
});
If I configure:
ValidationMode = ValidationMode.ValidationEndpoint
There is an extra request.
With ValidationMode.Local there is not. (This is good because IdentityServer is embedded in the WebAPI).
But if I use ValidationMode.Local, there are TWO requests to the IdentityServer.
http://server/IdentityServer/.well-known/openid-configuration
http://server/IdentityServer/.well-known/jwks
Why is this reccessary? The IdentityServer is embedded and this information is accessible in-memory. Even if the setting is "ValidationMode.ValidationEndpoint" this two calls do not appear.
But back to my primary question:
How can I validate the ClaimsIdentity in my WebAPI?
I have an "AuthenticationHandler" (ASP.NET-DelegatingHandler) that handles the validation for my old basic-authentication.
I figured out I can use the HttpContext.Current.User to access the IPrincipal.
Now I check it is of the type "ClaimsIdentity" and the Identity.IsAuthenticated==true.
Is this a correct approach?
And how can I check the User-ID?
On the server-side the ID of the user is put in the "sub"-claim (aka 'Subject').
Do I have to access this specific property to check which user is logged in?
I could access it like this:
ClaimsIdentity.Claims.FirstOrDefault(c => c.Type.Equals("sub"));
BUT only with the setting "ValidationMode.ValidationEndpoint".
Then the AuthenticationType of the IPrincipal is "Bearer" and the 'sub'-claim is present.
With the setting "ValidationMode.Local" the AuthenticationType is "JWT" and the claim is not present.
Why does the ValidationMode changes the IPrincipal? Do I have to check this for different cases?

CSRF for Ajax request across applications in one Domain

We have MVC 4 application which is hosted on Web Farm. Site has 5 applications hosted under one domain. These applications communicate with each other. We are implementing Cross Site Request Forgery for our application. We have added AntiForgeyToken(#Html.AntiForgeryToken()) on Layout page. When we try to post data actions across applications using Ajax request, we are facing below exception-
Exception:
The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
For Ajax request we have added “__RequestVerificationToken” value into prefilter as shown below-
Client side implementation:
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.type.toLowerCase() == "post") {
if ($('input[name="__RequestVerificationToken"]').length > 0)
jqXHR.setRequestHeader('__RequestVerificationToken',
$('input[name="__RequestVerificationToken"]').val());
}});
On Server side we validated this token as shown below-
string cookie = "";
Dictionary<string, object> cookieCollection = new Dictionary<string, object>();
foreach (var key in HttpContext.Current.Request.Cookies.AllKeys)
{
cookieCollection.Add(key, (HttpContext.Current.Request.Cookies[key]));
}
var res= cookieCollection.Where(x => x.Key.Contains("RequestVerificationToken")).First();
cookie = ((System.Web.HttpCookie)(res.Value)).Value;
string formToken = Convert.ToString(HttpContext.Current.Request.Headers["__RequestVerificationToken"]);
if(string.IsNullOrEmpty(formToken))
{
//To validate HTTP Post request
AntiForgery.Validate();
}
else
{
//To validate Ajax request
AntiForgery.Validate(cookie, formToken);
}
Other configurations which we have done are as below-
We have machine key in config which is same for all applications as well as on all web servers.
We have set AntiForgeryConfig.CookieName = "__RequestVerificationToken" + "_XYZ" cookie name across all applications which is mandatory to access token across applications.
Changes which we tried to resolve this issue-
We tried to post “__RequestVerificationToken” inside each ajax request’s data so that we can access it using Request.Form but no success on this.
We have verified that Content-Type header is appearing with each request.
Please suggest if you have any other way to implement CSRF functionality for Ajax POST requests across multiple applications.

Https and Http only on .net webapi v2 actions

I have been working on a project with webapi 2 using oauth 2 (openid connect to be precise) bearer tokens to grant access. Now the whole idea is that the bearer tokens are only secure if used with a secure connection.
Until now I have simply not allowed http calls to the webserver which kinda worked since no one could do a http call with a bearer token.
We now have some endpoints that need to be avaible over http (no bearer token/authenticaiton required) and we are going to enable http of course. Now my question is, what is normal in these situations?
Would I have an attribute that I can put on all actions that only accept https?
Can I make that the default behaviour and only put attribute on those that are okay on http?
What is the advice on, is it our responsibility that no one use a oauth token over a non secure line or is the user of the api ?
I believe the right way to do this is to add global action filter which forces you to use HTTPs on all controllers/actions on your Web API. The implementation for this HTTPs action filter can be as the below:
public class ForceHttpsAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var html = "<p>Https is required</p>";
if (request.Method.Method == "GET")
{
actionContext.Response = request.CreateResponse(HttpStatusCode.Found);
actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
UriBuilder httpsNewUri = new UriBuilder(request.RequestUri);
httpsNewUri.Scheme = Uri.UriSchemeHttps;
httpsNewUri.Port = 443;
actionContext.Response.Headers.Location = httpsNewUri.Uri;
}
else
{
actionContext.Response = request.CreateResponse(HttpStatusCode.NotFound);
actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
}
}
}
}
Now you want to register this globally on WebApiConfig class as the below:
config.Filters.Add(new ForceHttpsAttribute());
As I understand from your question, the number of controllers you want to call them over https are greater than controllers over http, so you want to add override attribute to those http controllers [OverrideActionFiltersAttribute]
Do not forget to attribute your anonymous controllers with [AllowAnonymous] attribute.
But my recommendation is to keep all the communication over https and you just allow anonymos calls.
You can read more about enforcing https on my blog here: http://bitoftech.net/2013/12/03/enforce-https-asp-net-web-api-basic-authentication/
Hope this helps.
Firstly I think you definitely have to make best efforts to ensure the security of that token and so the server should enforce SSL.
We are using web api v1 (infrastructure restrictions :() and we have a global DelegatingHandler that enforces SSL on all requests except for certain uris that are on a whitelist (not the prettiest solution but it works for now).
In web api 2 I reckon you could have a global FilterAttribute to enforce the SSL connectivity and then use the new attribute override feature http://dotnet.dzone.com/articles/new-filter-overrides-feature to create your exceptions - all theory though ! :)
Hope that helps
Garrett

Resources