Is it possible to configure web.config to authorize a page to be only read locally (similar in concept to the RemoteOnly feature for error messages).
You can check this on the Page that you wish to. Here is an example code that I write and check if the user is local or not.
override protected void OnInit(EventArgs e)
{
if (!IsUserLocal())
{
Response.Redirect("~/");
return;
}
base.OnInit(e);
}
public bool IsUserLocal()
{
string userHostAddress = Request.ServerVariables["REMOTE_HOST"].ToString();
if (string.IsNullOrEmpty(userHostAddress))
{
return false;
}
return (((userHostAddress == "127.0.0.1") || (userHostAddress == "::1")) || (userHostAddress == LocalAddress()));
}
public string LocalAddress()
{
IServiceProvider provider = (IServiceProvider)HttpContext.Current;
HttpWorkerRequest wr = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
return wr.GetLocalAddress();
}
Related
I'm integrating our asp.net MVC application with SAML2 Authentication. And using Kentor.AuthServices as module as described at kentor.AuthServices Configuration
Everithing works fine. But next step is to add usage of second service provider (which configured to use another auth mechanisms on server side) only for specified range of pages.
First, how to configure it via web.config to add second SP (not the second IdP in scope of first SP) with different entityId.
And Second, how to switch programmatically to second SP? I assume that it should happend in global.asax file in method Application_BeginRequest, but how?
Using two different SP instances in the same application is a quite rare scenario. But if you are really sure you need it, it can be achieved.
You will have to use the Kentor.AuthServices.Owin package and do the configuration in code - web.config won't do. Register two instances of the middleware. Each one will have their own configuration, including their own SP EntityID. Also make sure to change the ModulePath of at least one of them so that they get different endpoint addresses.
To challenge an authentication from either one, set the right authentication scheme in the challenge (typically in a ChallengeResult returned from a controller)
Self-answering.
Here is a workaround for multiple SP for MVC or HttpModule package, switching is based on specified range of URLs. In my case different SP realize different amount of security factors.
First, implementing custom IOptions and CookieHandler, with ability to switch to correct instance. In the web.config file, two kentor.authServices sections must be defined. In my case only "entityId" attribute differs.
public class CustomOptions : IOptions
{
private IOptions options1Factor;
private IOptions options2Factor;
private Func<bool> _checkIsSecure;
public CustomOptions(Func<bool> checkIsSecure)
{
_checkIsSecure = checkIsSecure;
AddOption(out options2Factor, "kentor.authServices1");
AddOption(out options1Factor, "kentor.authServices");
}
private void AddOption(out IOptions options, string sectionName)
{
var sp = new SPOptions((KentorAuthServicesSection)ConfigurationManager.GetSection(sectionName));
options = new Options(sp);
KentorAuthServicesSection.Current.IdentityProviders.RegisterIdentityProviders(options);
KentorAuthServicesSection.Current.Federations.RegisterFederations(options);
}
public SPOptions SPOptions
{
get
{
if (_checkIsSecure())
return options2Factor.SPOptions;
return options1Factor.SPOptions;
}
}
public IdentityProviderDictionary IdentityProviders
{
get
{
if (_checkIsSecure())
return options2Factor.IdentityProviders;
return options1Factor.IdentityProviders;
}
}
public KentorAuthServicesNotifications Notifications
{
get
{
if (_checkIsSecure())
return options2Factor.Notifications;
return options1Factor.Notifications;
}
}
}
public class CustomCookieHandler : CookieHandler
{
private Func<bool> _checkIsSecure;
private CookieHandler _originalCookieHandler1Factor;
private CookieHandler _originalCookieHandler2Factor;
public CustomCookieHandler(Func<bool> checkIsSecure)
{
_checkIsSecure = checkIsSecure;
_originalCookieHandler1Factor = new ChunkedCookieHandler()
{
Name = "commonAuth",
RequireSsl = false
};
_originalCookieHandler2Factor = new ChunkedCookieHandler()
{
Name = "securedAuth",
RequireSsl = false
};
}
public override string MatchCookiePath(Uri baseUri, Uri targetUri)
{
if (_checkIsSecure())
return _originalCookieHandler2Factor.MatchCookiePath(baseUri, targetUri);
return _originalCookieHandler1Factor.MatchCookiePath(baseUri, targetUri);
}
protected override void DeleteCore(string name, string path, string domain, HttpContext context)
{
if (_checkIsSecure())
_originalCookieHandler2Factor.Delete();
else
_originalCookieHandler1Factor.Delete();
}
protected override byte[] ReadCore(string name, HttpContext context)
{
if (_checkIsSecure())
return _originalCookieHandler2Factor.Read();
return _originalCookieHandler1Factor.Read();
}
protected override void WriteCore(byte[] value, string name, string path, string domain, DateTime expirationTime, bool secure, bool httpOnly, HttpContext context)
{
if (_checkIsSecure())
_originalCookieHandler2Factor.Write(value, true, expirationTime);
else
_originalCookieHandler1Factor.Write(value, true, expirationTime);
}
}
In Global.asax file setting static properties to custom implementations. No more modifications needed.
protected void Application_Start()
{
FederatedAuthentication.FederationConfiguration.CookieHandler = new CustomCookieHandler(CheckIsSecure);
Kentor.AuthServices.Mvc.AuthServicesController.Options = new CustomOptions(CheckIsSecure);
}
private bool CheckIsSecure()
{
if (HttpContext.Current == null)
return false;
var mainHost = "http://host.local"; // host url
var sp = new [] { "/Home/Secure" }; // array of URLs which must be secured with other SP
var request = HttpContext.Current.Request;
var isSecured = sp.Any(x => x.Equals(request.Path, StringComparison.InvariantCultureIgnoreCase));
if (!isSecured && request.Path.Equals("/AuthServices/SignIn", StringComparison.InvariantCultureIgnoreCase))
{
var returnUrl = request.QueryString["ReturnUrl"];
isSecured = !string.IsNullOrEmpty(returnUrl) &&
sp.Any(x => x.Equals(returnUrl, StringComparison.InvariantCultureIgnoreCase));
}
if (!isSecured && request.Path.Equals("/AuthServices/Acs", StringComparison.InvariantCultureIgnoreCase))
{
var _r = new HttpRequestWrapper(request).ToHttpRequestData();
isSecured = _r != null && _r.StoredRequestState != null && _r.StoredRequestState.ReturnUrl != null
&& sp.Any(x => x.Equals(_r.StoredRequestState.ReturnUrl.ToString(),
StringComparison.InvariantCultureIgnoreCase));
}
if (!isSecured && !string.IsNullOrEmpty(request.Headers["Referer"]))
{
var referer = request.Headers["Referer"];
isSecured = sp
.Select(x => string.Format("{0}/{1}", mainHost.TrimEnd('/'), x.TrimStart('/')))
.Any(x => x.Equals(referer, StringComparison.InvariantCultureIgnoreCase));
}
return isSecured;
}
I have an ancient Classic ASP website that used to use Windows authentication for access control but this is no longer appropriate. I thought I might try and wrap this website in an ASP.NET MVC 5 app, so I:
created a new website complete with ASP.NET Identity
created a user
copied the Classic ASP website into the root
viewed the classic ASP website
Now what I need to do is require authorization for all .asp pages so that only authorized users can see them. What's the best way of doing this? Maybe I could do something with OWIN?
Crispin
Following this example I created an HttpModule that came out like this:
public class ClassicAspAuthorization : IHttpModule
{
private MyEventHandler _eventHandler = null;
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(OnBeginRequest);
}
public delegate void MyEventHandler(Object s, EventArgs e);
public event MyEventHandler MyEvent
{
add { _eventHandler += value; }
remove { _eventHandler -= value; }
}
public void OnBeginRequest(Object s, EventArgs e)
{
HttpApplication app = s as HttpApplication;
if (app.Request.CurrentExecutionFilePathExtension.EndsWith(".asp") == true && blnIsAuthenticated() == false)
{
app.Context.Response.Redirect("/Account/Login");
}
if (_eventHandler != null)
{
_eventHandler(this, null);
}
}
and the boolean (blnIsAuthenticated) method that determined whether or not the user was authenticated was derived from a Stackoverflow answer where I removed the lines:
var identity = new ClaimsIdentity(claims, authenticationType, ClaimTypes.Name, ClaimTypes.Role);
var principal = new ClaimsPrincipal(identity);
System.Threading.Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
and replaced this with my own claims checking to establish if the user was authenticated. An appropriate boolean value was returned.
I am failing this part of the certification:
If signed out of App, but signed into the App Center, the App should launch when launching from the App Center without asking for credentials
I am basing my application on intuits "HelloIntuitAnywhere" c# demo which uses DotNetOpenAuth.
protected void Page_Load(object sender, EventArgs e)
{
#region OpenId
// Hide Connect to Quickbooks widget and show Sign in widget
IntuitInfo.Visible = false;
IntuitSignin.Visible = true;
// If Session has keys
if (HttpContext.Current.Session.Keys.Count > 0)
{
// If there is a key OpenIdResponse
if (HttpContext.Current.Session["OpenIdResponse"] != null)
{
// Show the Sign in widget and disable the Connect to Quickbooks widget
IntuitSignin.Visible = false;
IntuitInfo.Visible = true;
}
// Sow information of the user if the keys are in the session
if (Session["FriendlyIdentifier"] != null)
{
friendlyIdentifier.Text = Session["friendlyIdentifier"].ToString();
}
if (Session["FriendlyName"] != null)
{
friendlyName.Text = Session["FriendlyName"].ToString();
}
else
{
friendlyName.Text = "User Didnt Login Via OpenID, look them up in your system";
}
if (Session["FriendlyEmail"] != null)
{
friendlyEmail.Text = Session["FriendlyEmail"].ToString();
}
else
{
friendlyEmail.Text = "User Didnt Login Via OpenID, look them up in your system";
}
}
#endregion
#region oAuth
// If session has accesstoken and InvalidAccessToken is null
if (HttpContext.Current.Session["accessToken"] != null && HttpContext.Current.Session["InvalidAccessToken"] == null)
{
// Show oAuthinfo(contains Get Customers Quickbooks List) and disable Connect to quickbooks widget
oAuthinfo.Visible = true;
connectToIntuitDiv.Visible = false;
}
#endregion
}
How do I identify that the user is already signed in to the App Center and by-pass my login screen/section?
I have this piece of code (from OpenIdHandler.aspx.cs) that works EXCEPT I want to to not redirect to the intuit login page when the user is not logged in. I want to display the login button.
public partial class OpenIdHandler : System.Web.UI.Page
{
/// <summary>
/// Action Results for Index, uses DotNetOpenAuth for creating OpenId Request with Intuit
/// and handling response recieved.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event Args.</param>
protected void Page_Load(object sender, EventArgs e)
{
//OpenId Relying Party
OpenIdRelyingParty openid = new OpenIdRelyingParty();
var openIdIdentifier = ConfigurationManager.AppSettings["openid_identifier"];
var response = openid.GetResponse();
if (response == null)
{
// Stage 2: user submitting Identifier
Identifier id;
if (Identifier.TryParse(openIdIdentifier, out id))
{
try
{
IAuthenticationRequest request = openid.CreateRequest(openIdIdentifier);
FetchRequest fetch = new FetchRequest();
fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Contact.Email));
fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.FullName));
request.AddExtension(fetch);
request.RedirectToProvider();
}
catch (ProtocolException ex)
{
throw ex;
}
}
}
else
{
if (response.FriendlyIdentifierForDisplay == null)
{
Response.Redirect("/OpenIdHandler.aspx");
}
// Stage 3: OpenID Provider sending assertion response
Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
FetchResponse fetch = response.GetExtension<FetchResponse>();
if (fetch != null)
{
Session["OpenIdResponse"] = "True";
Session["FriendlyEmail"] = fetch.GetAttributeValue(WellKnownAttributes.Contact.Email);
Session["FriendlyName"] = fetch.GetAttributeValue(WellKnownAttributes.Name.FullName);
}
//Check if user disconnected from the App Center
if (Request.QueryString["disconnect"] != null && Request.QueryString["disconnect"].ToString(CultureInfo.InvariantCulture) == "true")
{
Session["Flag"] = true;
Response.Redirect("CleanupOnDisconnect.aspx");
}
else
{
Response.Redirect("Default.aspx");
}
}
}
}
When the user is redirected to your application from the App Center, use OpenID to fetch the user's email address and sign them into your app. Since the user has already authorized the connection, you will also be able to fetch the realmId.
https://developer.intuit.com/docs/0025_quickbooksapi/0010_getting_started/0020_connect/0011_from_the_intuit_app_center/implement_openid_in_your_app
My fix is two fold:
In the Intuit application manager I set the App URL: mywebsite.com/OpenIdHandler.aspx
I was basing my application on the intuit demo... In Global.asax.cs I removed the redirect which was causing all my problems since no matter what I would be redirected to default.aspx (which in turn redirects me to login).
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
//Response.Redirect("Default.aspx"); //<-----
}
Thanks.
I have enabled Basic Authentication in IIS7 for my site and followed this link to create handler for basic authentication requests.
The problem is that no matter what credentials user enters, the site keeps returning 401, even if entering correct credentials. This is just a test and credentials are checked against hardcoded values.
Here is relevant code:
public class BasicAuthenticationHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest+=context_BeginRequest;
context.AuthenticateRequest += context_AuthenticateRequest;
}
void context_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
TryAuthenticate(application);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
TryAuthenticate(application);
}
private static void TryAuthenticate(HttpApplication application)
{
if (!Authenticate(application.Context))
{
application.Context.Response.Status = "401 Unauthorized";
application.Context.Response.StatusCode = 401;
application.Context.Response.AddHeader("WWW-Authenticate", "Basic");
application.CompleteRequest();
}
}
private static bool Authenticate(HttpContext context)
{
if (context.User!=null && context.User.Identity.IsAuthenticated)
{
return true;
}
if (!context.Request.Headers.AllKeys.Contains("Authorization"))
return false;
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
IPrincipal principal;
if (TryGetPrincipal(authHeader, out principal))
{
context.User = principal;
return true;
}
return false;
}
private static bool TryGetPrincipal(string[] creds, out IPrincipal principal)
{
if (creds[0] == "Administrator" && creds[1] == "SecurePassword")
{
principal = new GenericPrincipal(
new GenericIdentity("Administrator"),
new string[] { "Administrator", "User" }
);
return true;
}
if (creds[0] == "BasicUser" && creds[1] == "Password")
{
principal = new GenericPrincipal(
new GenericIdentity("BasicUser"),
new string[] { "User", "SystemUser" }
);
return true;
}
else
{
principal = null;
return false;
}
}
When client enters correct credentials (i.e. "BasicUser", "Password"), GenericPrincipal object is created and assigned to HttpContext's User property. Looking into Request.IsAuthenticated tells that it's true.
And this is why I don't understand is why client receives 401 again and again and again.
I'm not sure how all the pipeline works - may be basic authentication goes further to some IIS HttpModule which also serves the request? Or may be code is incomplete and context_BeginRequest needs to be extended? (I know that in case of Forms authentication type, you do something like Response.Redirect(goodguy.aspx))
Anyway, any help/questions are appreciated.
Forgot to mention that in web.config I also placed
<system.webServer>
<modules>
<add name="BasicAuthenticationHttpModule" type="Analytics.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Apparently implemements it's own Basic authentication. Thus our module could authenticate request succussfully, it would get passed to built-in IIS module, which rejected authentication. It is really helpful not to copy paste, but also to think yourself indeed. So to answer my question - disable all authentication on IIS except Anonymous.
I wrote a custom url rewriting http module that's work perfectly in Integrated managed pipline app pool.
but when I turn it to Classic, it stops working.
protected virtual void BaseModuleRewriter_AuthorizeRequest(
object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender;
Rewrite(app.Request.Path, app);
}
protected void Rewrite(string requestedPath,HttpApplication app)
{
if (requestedPath.IndexOf('.') == -1)
{
Page page =PageManager.GetPageByAlias(requestedPath.EndsWith("/") ? requestedPath.Substring(0, requestedPath.Length - 1) : requestedPath);
if (page != null)
{
app.Context.RewritePath(page.RelativeUrl == null ? "/Default.aspx?PageId=" + page.Serial : page.RelativeUrl.Substring(1), false);
//app.Session["ThisPage"] = page;
app.Response.StatusCode = 200;
}
else
{
app.Response.StatusCode = 404;
}
}
}
I should explain that "Page" is a database entity that has "RelativeUrl" and "AliasAddress" property.
any Idea what's wrong?