Testing with HTTP - c#

I'm trying to get an action to fire but getting the following error...
ArgumentException: IDX20108: The address specified '[PII is hidden.
For more details, see https://aka.ms/IdentityModel/PII.]' is not valid
as per HTTPS scheme. Please specify an https address for security
reasons. If you want to test with http address, set the RequireHttps
property on IDocumentRetriever to false. (Parameter 'address')
How do I set the RequireHttps property to false?
The line triggering the error is...
await HttpContext.ChallengeAsync("Schema", new AuthenticationProperties() {});

Related

No authenticationScheme was specified error even when JWT Authentication Specified

I'm new to AzureAD authentication. I setup my Web API with below settings in startup.cs
services.AddAuthentication(sharedopt => sharedopt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("AzureAd", options =>
{
options.Audience = Configuration.GetValue<string>("AzureAd:Audience");
options.Authority = Configuration.GetValue<string>("AzureAd:Instance")
+ Configuration.GetValue<string>("AzureAd:TenantId");
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidIssuer = Configuration.GetValue<string>("AzureAd:Issuer"),
ValidAudience = Configuration.GetValue<string>("AzureAd:Audience")
};
});
I am expecting my Client App (Angular) will attach Authorization header in its requests and thus it will get access to API
But when I execute the Web API and trying to open any API with Authorize, it triggers this error
InvalidOperationException: No authenticationScheme was specified, and
there was no DefaultChallengeScheme found. The default schemes can be
set using either AddAuthentication(string defaultScheme) or
AddAuthentication(Action configureOptions).
I already specified JWTBearerDefaults.AuthenticationScheme. Still why its not accepting?
Please remove the first "AzureAd" parameter from AddJwtBearer call.
TLDR: When you call AddAuthentication you set the default scheme to JwtBearerDefaults.AuthenticationScheme which is string "Bearer".
This tells the authentication middleware to authenticate all requests (unless specified otherwise e.g. via Authorize attribute with schemes) to use a set of handlers and configurations organized by the shceme name "Bearer".
However you didn't register that scheme. Your call to AddJwtBearer registers a scheme named "AzureAd" instead of "Bearer".
Authentication middleware cannot find the matching scheme and hence the error.
If you don't specify the "AzureAd" parameter, below version of AddJwtBearer is invoked:
builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, configureOptions);
As we can see, it registers the JwtBearer authentication with scheme "Bearer" matching your default scheme.
You might have missed to register
services.ConfigureApiAuthentication(Configuration); in the startup class
Make sure you don't have an [Authorize] attribute above your Controller class definition or any associated methods for which you don't want any Authentication. I copy/pasted a Controller class and didn't notice it had an [Authorize] attribute above the class definition. I removed that and the problem was resolved.

Redirect after cookie validation fails in ASP.NET Core

I have implemented a cookie validator as described in the ASP.NET docs:
public static class CookieValidator
{
public static async Task ValidateAsync(CookieValidatePrincipalContext context)
{
...
if (invalidCookie)
{
context.RejectPrincipal();
await context.HttpContext.Authentication.SignOutAsync("MyCookieScheme");
}
}
}
It seems to be working correctly and gets into the invalidCookie block, rejecting the principal and signing out. After that I would like to redirect to a different URL. How do I have it redirect if I invalidate the cookie?
It should already return a 302 and add a 'location' header to your response with either the 'AccessDeniedPath' or 'LoginPath' when you reject principal, however lets say you want to add a header change the status code, or other customization.
In the invalidCookie block you can set custom headers with the following.
context.httpContext.Response.Headers["forceRedirect"] = httpContext.Request.Host.ToString();
Then when you get the response back on the client you need to check if this header is present, if it is then you redirect to the link given. This allows you to at least set headers, however if you changed the status code in the same place the framework will still override the status code when you reject principal (after you are out of the ValidateAsync function).
In order to do this, and stop the framework from overriding your custom response you have to declare an onRedirectToAccessDenied funtion.
In startup.cs: ConfigureServices
services.ConfigureApplicationCookie(options => { //this just makes checks against the cookie, so if the user deletes the cookie then ValidateAsync never fires
// Cookie settings ...
options.Events.OnRedirectToAccessDenied = CookieValidator.overrideRedirect;
options.Events.OnValidatePrincipal = CookieValidator.ValidateAsync;
});
Then in CookieValidator:
It is worth noting that if you do this then other rejections will execute this code too. Like if you also have a Roles authorization then when the user fails that authorization it will call this. This can be frustrating if you want the code to return different responses based upon why auth failedI am solving this by using policies and having them return the custom responses because it knows exactly why it failed then have overrideRedirect empty so the framework doesn't change my response.
internal static async Task overrideRedirect(RedirectContext<CookieAuthenticationOptions> context) {
...
//In here you can customize the response anyway you want and it will not be changed
}
It should be noted that ValidateAsync only is called if the cookie is present in the request, so if the user deletes the cookie then they can skip this validation. In my case I got around this by validating the cookie here, but still had other auth code (i added policy requirements in an IAuthorizationHandler) later that verified that the user had a claim. If the user deleted their cookie then the claim wouldn't be there and they would still lose access.

OpenIdProvider.GetRequest() returns null

As somewhat of a continuation of this question, I'm having problems with dotnetopenauth.
I navigate to my relying party code and create the request, however when my provider receives the request OpenIdProvider.GetRequest() returns null. I went through the code and as far as I can tell, this is because the openid payload (request.form) is not being delivered by my relying party; but I can't figure out why this is.
Code:
Relying Party:
public ActionResult Authenticate(string RuserName = "")
{
UriBuilder returnToBuilder = new UriBuilder(Request.Url);
returnToBuilder.Path = "/OpenId/Authenticate";
returnToBuilder.Query = null;
returnToBuilder.Fragment = null;
Uri returnTo = returnToBuilder.Uri;
returnToBuilder.Path = "/";
Realm realm = returnToBuilder.Uri;
var response = openid.GetResponse();
if (response == null) {
if (Request.QueryString["ReturnUrl"] != null && User.Identity.IsAuthenticated) {
} else {
string strIdentifier = "http://localhost:3314/User/Identity/" + RuserName;
var request = openid.CreateRequest(
strIdentifier,
realm,
returnTo);
var fetchRequest = new FetchRequest();
request.AddExtension(fetchRequest);
request.RedirectToProvider();
}
} else {
switch (response.Status) {
case AuthenticationStatus.Canceled:
break;
case AuthenticationStatus.Failed:
break;
case AuthenticationStatus.Authenticated:
//log the user in
break;
}
}
return new EmptyResult();
}
Provider:
public ActionResult Index()
{
IRequest request = OpenIdProvider.GetRequest();
if (request != null) {
if (request.IsResponseReady) {
return OpenIdProvider.PrepareResponse(request).AsActionResult();
}
ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request;
return this.ProcessAuthRequest();
} else {
//user stumbled on openid endpoint - 404 maybe?
return new EmptyResult();
}
}
public ActionResult ProcessAuthRequest()
{
if (ProviderEndpoint.PendingRequest == null) {
//there is no pending request
return new EmptyResult();
}
ActionResult response;
if (this.AutoRespondIfPossible(out response)) {
return response;
}
if (ProviderEndpoint.PendingRequest.Immediate) {
return this.SendAssertion();
}
return new EmptyResult();
}
Logs:
RP: 1) http://pastebin.com/Pnih3ND7 2) http://pastebin.com/eBzGun9y
Provider: http://pastebin.com/YAUTBzHk
Interestingly enough the RP log says that localhost is untrusted...yet I added it to the whitelisted hosts in my web.config, and it was "working" yesterday...
EDIT: Okay, this is weird. Yesterday I was stepping through the DNOA source trying to find out what the problem is. I enabled log4net and it created the log file and left it blank. Today I set up log4net again - it logged fine but I had an error that didn't make sense (see above). I also wasn't able to step into the DNOA source. I removed and re-added the reference to dotnetopenauth.dll, and then my "original error" with the whitelisted hosts went away, I was able to step into the source, but the log file was blank again. And I stil have the problem with request.form not being populated...
EDIT2: Both my controllers are named "OpenIdController" (both on the RP and EP). My RP is running on localhost:1903, and my endpoint is running on localhost:3314.
EDIT3: After I made the changes you suggested things started working. The RP performs the discovery fine, but I have an issue when it actually makes the request.
The line IRequest i_request = OpenIdProvider.GetRequest(); works fine, but when I try to cast it: IAuthenticationRequest iR = (IAuthenticationRequest)i_request; it gives me the following error:
System.InvalidCastException was unhandled by user code
Message=Unable to cast object of type 'DotNetOpenAuth.OpenId.Provider.AutoResponsiveRequest' to type 'DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest'.
Source=Portal
StackTrace:
at Portal.Controllers.OpenIdController.Index() in Controllers\OpendIdController.cs:line 35
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
This code is a bit of a hodge-podge between the two samples I found relevant. I want to set up an SSO type environment so the majority of the code I'm using is from \DotNetOpenAuth-4.1.0.12182\Samples\OpenIdWebRingSsoProvider\Code\Util.cs (ProcessAuthenticationChallenge function). However, since that function expects an IAuthenticationRequest but OpenIdProvider.GetRequest returns an AutoResponsiveRequest I figured I'd be able to cast it in order to use the properties and methods of the IAuthenticationRequest class. Obviously I was incorrect.
I'm not quite sure how to approach things at this point. Should I be using the sample code from the OpenIdProviderMVC sample? The key thing is that the login work like a single sign on, and the user is never actually prompted to enter an OpenId. I will only ever have one endpoint as well (although I will have multiple RP's).
Here's the link to the most recent RP logs: http://pastebin.com/enpwYqq3
EDIT4: I did what you suggested, and made some progress. My EP recieves the response and processes it as far as I can tell, but when it redirects back to the realm url it errors out.
012-10-10 13:55:01,171 (GMT-4) [25] ERROR DotNetOpenAuth.Messaging - Protocol error: An HTTP request to the realm URL (http://localhost:1903/) resulted in a redirect, which is not allowed during relying party discovery.
What exactly is the function of the Realm as opposed to the ReturnTo? Using the sample code, the Realm ends up being http://localhost:1903/ and the ReturnTo ends up being http://localhost:1903/OpenId/Authenticate which seems fine. Why does the EP need to make a request to the realm? I'd have thought that it should simply be sending the assertion to the returnTo once it finished processing. If I manually set the Realm to http://localhost:1903/OpenId/Authenticate then relyingParty.GetResponse() returns null.
I do have my application set up to redirect when someone accesses the base url (http://localhost:1903) - what code should I have running there to intercept the DNOA EP request?
New Logs:
RP: http://pastebin.com/L9K5Yft4
EP: http://pastebin.com/kBPWiUxp
I've also updated the code at the beginning of the question to better reflect the changes I've made.
EDIT5: Does the realm have to be the actual base URL of the application? That is, (http://localhost:1903)? Given the existing architecture in place it is difficult to remove the redirect - I tried setting the realm to the base OpenId controller (http://localhost:1903/OpenId) and testing manually did generate the XRDS document. However, the application seems to freeze, and the EP log reveals the following error:
2012-10-10 15:17:46,000 (GMT-4) [24] ERROR DotNetOpenAuth.OpenId - Attribute Exchange extension did not provide any aliases in the if_available or required lists.
Your RP has very suspiciously odd code. While it is normal (in fact required) for the return_to and realm to both have the same Uri authority, the fact that the user-supplied identifier that you're passing in as the first parameter to OpenIdRelyingParty.CreateRequest has the same host and port as your relying party is very odd. Normally the identifier you pass in would be a URL hosted by the provider. Now, I don't know if port 3314 is your RP or your OP, but either way, one of these port numbers in your RP code looks wrong.
Secondly, discovery on the user identifier fails with a null reference exception (according to v2 of your RP logs). That would prevent the login request from ever reaching your Provider. The fact that your Provider is getting called but with a non-existent OpenID request suggests that http://localhost:3314/OpenId/ is actually your OP Endpoint (the URL of your OpenID Provider's action method that reads OpenID requests). That would be inappropriate. The URL you should pass to your OpenIdRelyingParty.CreateRequest method's first parameter should, again, be a user's OpenID URL -- not an OP Endpoint. Check out the OpenIdProviderMvc sample's User controller for an example of how to set up a user OpenID URL. Then use that URL as the first arg to CreateRequest and I think you'll be good.
Thirdly, once your Provider receives a non-null request, you can't always cast it to IAuthenticationRequest. Not all OpenID messages are authentication messages. Some are part of the underlying OpenID protocol. If you look at the OpenIdProviderMvc sample's OpenID controller, you should notice that there are conditional casts to deal with the different message types. You should have similar message handling in your controller.
Since you're going for the SSO scenario, the significant difference in your controller would presumably be:
your controller never responds with a redirect to a login page, but rather "magically" figures out who the user is.
your controller should check the IAuthenticationRequest.Realm property against a whitelist of the RPs included in your SSO web ring, and only provide positive assertions when the Realm qualifies. This mitigates the attack where once your server is set up, anyone could set up a site that quietly uses your OpenID Provider to identify a user of a random Internet web site if they belong to your org, which would violate their privacy.
Fourthly, the HTTP request that the OP is sending to your RP's "realm" URL is part of a process OpenID calls "RP discovery". It's there to mitigate "open redirector" attacks. You should adjust your RP's base URL to not redirect, but rather return an XRDS document when the RP discovery request comes in. You can still redirect for the normal browser case. You can see an example of how to do this in the OpenIdRelyingPartyMvc sample's HomeController.
As you can see from the relying party log:
ERROR DotNetOpenAuth.Messaging - Protocol error: The URL 'http://localhost:3314/OpenId/' is rated unsafe and cannot be requested this way.
Your Provider is hosted on localhost, which on a production server is not a safe OpenID. So by default localhost is disabled. You can allow it for local testing by adding localhost to your whitelist by adding this to your web.config file (with the appropriate configSections at the top):
<dotNetOpenAuth>
<messaging>
<untrustedWebRequest>
<whitelistHosts>
<add name="localhost" />
</whitelistHosts>
</untrustedWebRequest>
</messaging>
</dotNetOpenAuth>

Handling confidentiality in application

I have an asp.net mvc3 application where each logged users may have access to some specific data.
For exemple, "user A" have acces to "Client 1" but not "Client 2", while "user B" have access to "Client 2" but not "Client 1".
If user a acces to http://myApp/Clients/2, we will throw a custom exception, say ConfidentialityException.
From that, we can trap it in global.asax Application_Error. But from that point, I wonder what would be the best practice :
Returning an error page with http 403 code (how ?)
Just returning an error page.
Let it crash.
other suggestion ?
My preffered solition is the first one (error page with 401), but I don't see how to set the http code from Application_Error.
Edit
I changed 401 status code to 403, since it's not an authentification error, but confidentiality. 403 seems more appropriate according to w3c.
To set the status code you can use the following:
HttpContextBase.Response.StatusCode = 401;
However, if you're using MVC you can simply set the result to be an HttpUnauthorizedResult, which will set the http status code for you.

Facebook C# SDK OAuth Exception "ClientID required"

This question is, I think, similar to my previous one.
Using the latest C# Facebook SDK on .NET 4 I get an Exception with the message "ClientID required" with the following code on the last line:
var app = new DefaultFacebookApplication();
app.AppId = "appId";
app.AppSecret = "secret";
var fb = new FacebookWebContext(app);
fb.IsAuthenticated();
App ID and secret are properly set. The stack trace of the exception is the following:
System.Exception occurred
Message=ClientID required. Source=Facebook StackTrace:
at Facebook.FacebookOAuthClient.BuildExchangeCodeForAccessTokenParameters(IDictionary`2 parameters, String& name, String& path)
at Facebook.FacebookOAuthClient.ExchangeCodeForAccessToken(String code, IDictionary`2 parameters)
at Facebook.FacebookSession.get_AccessToken()
at Facebook.FacebookSession.get_Expires()
at Facebook.Web.FacebookWebContext.IsAuthenticated()
at Piedone.FacebookTest.Authorize() InnerException:
On the client side I'm using the JS SDK, initialized as following:
FB.init({
appId: appId,
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true, // parse XFBML
oauth: true // enable OAuth 2.0
});
The users gets properly logged in with the JS login() method, as the alert in the following piece of code runs:
FB.login(function (response) {
if (response.authResponse) {
alert("logged in");
} else {
alert('User cancelled login or did not fully authorize.');
}
}, { scope: scope });
In the app settings on Facebook both the "Forces use of login secret for OAuth call and for auth.login" and "Encrypted Access Token" are turned on. As far as I know all this should enable the use of the OAuth 2 authentication.
Anybody has an idea what am I doing wrong? There really can't be any error in these few lines of code...
Thanks in advance for any help!
Edit:
The AccessToken property of FacebookWebContext throws the same error and HttpContext.CurrentNotification does:
CurrentNotification '(_facebookWebContextCache.HttpContext).CurrentNotification' threw an exception of type 'System.PlatformNotSupportedException' System.Web.RequestNotification {System.PlatformNotSupportedException}
This operation requires IIS integrated pipeline mode.
Since I must run the program from Visual Studio with its Development Server (as I'm currently developing the application) there is no way anything can be done about the latter exception, I suppose. Actually I also tried with Webmatrix's IIS express, but the problem persists.
It's also interesting, that in the FacebookWebContext the settings (app id, secret) are correctly set as well, the user Id and the signed request is also there...
Edit 2:
I also get the same error when using the SDK source. It looks that AccessToken and in the Session the Expires property throw the exception. I don't know if this is connected to the httpcontext issue above.
One more solution is add facebook settings to you web or app congfig
<facebookSettings appId="appid" appSecret="secret" />
after that create Auth class
var oauth = new FacebookOAuthClient(FacebookApplication.Current);
And it wil work as well
Finally I managed to solve the problem, but most likely this is a bug in the SDK.
The cause
The problem is that the FacebookApplication.Current is empty, as it does not get populated with data set in the FacebookWebContext ctor. This leads to the problem of the access token: in FacebookSession.AccessToken on line 119 FacebookOAuthClient is instantiated with FacebookApplication.Current, that of course is practically empty. So FacebookOAuthClient is throwing the exception as it doesn't get the application settings.
The solution
The workaround is to simply explicitly set the current FacebookApplication together with the instantiation of FacebookWebContext:
var app = new DefaultFacebookApplication();
app.AppId = "appId";
app.AppSecret = "secret";
var fb = new FacebookWebContext(app);
FacebookApplication.SetApplication(app); // Note this is the new line

Categories