I am trying to create an ASP.NET MVC site that has specific areas protected by Windows Azure ACS. I want the default area to be unprotected (i.e. allow anonymous users) but only the sub areas to be protected.
I have made this happen by removing the authorization element from the system.web section in my Web.config.
<authorization>
<deny users="?" />
</authorization>
Then adding a protected location for the desired MVC3 area.
<location path="MyArea">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
However, my old code that used to access the IClaimsIdentity and pull attributes off it for processing used to live in Session_Start event of my Global.asax. Now that the site does not require authentication to access the default area, Session_Start happens without authentication taking place.
What event can I wire up to handle the authentication event of WIF?
I have implemented a sliding session timeout using SessionAuthenticationModule_SessionSecurityTokenReceived and have tried adding my user analysis logic on the OnPostAuthenticationRequest event to no avail.
I was able to get the user after first wiring up to the following event:
FederatedAuthentication.ServiceConfigurationCreated
Then within this event I wire up to this event:
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn
However, within this event the session is null and session_start is never called again. So it appears the session is getting crushed when redirecting to the identity provider.
anon -> Application_start
anon -> Session_start
anon -> Navigate to /MyArea
anon -> Redirected to ACS -> Redirected to idP
anon -> Log in
auth -> Redirected to /MyArea
auth -> FederatedAuthentication.WSFederationAuthenticationModule.SignedIn occurs, but session is null!
UPDATE: I still have not found a place where both Session and Authentication exist. I am using Unity to detect the user on demand. I'd love it if there was an event that does it as it happens but my work around still works.
You've got a few options depending on when and how you want to execute your logic (after login, when creating the session token, after receiving it). The SessionAuthenticationModule_SessionSecurityTokenReceived event should work correctly, but subscribing to it can be tricky. This is how you can do it:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
FederatedAuthentication.FederationConfigurationCreated += (s, e) =>
{
FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenCreated += SessionAuthenticationModule_SessionSecurityTokenCreated;
FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenReceived += SessionAuthenticationModule_SessionSecurityTokenReceived;
FederatedAuthentication.WSFederationAuthenticationModule.SessionSecurityTokenCreated += WSFederationAuthenticationModule_SessionSecurityTokenCreated;
FederatedAuthentication.WSFederationAuthenticationModule.SecurityTokenValidated += WSFederationAuthenticationModule_SecurityTokenValidated;
FederatedAuthentication.WSFederationAuthenticationModule.SecurityTokenReceived += WSFederationAuthenticationModule_SecurityTokenReceived;
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += WSFederationAuthenticationModule_SignedIn;
};
}
void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
Debugger.Break();
}
void SessionAuthenticationModule_SessionSecurityTokenCreated(object sender, SessionSecurityTokenCreatedEventArgs e)
{
Debugger.Break();
}
void WSFederationAuthenticationModule_SessionSecurityTokenCreated(object sender, SessionSecurityTokenCreatedEventArgs e)
{
Debugger.Break();
}
void WSFederationAuthenticationModule_SecurityTokenValidated(object sender, SecurityTokenValidatedEventArgs e)
{
Debugger.Break();
}
void WSFederationAuthenticationModule_SecurityTokenReceived(object sender, SecurityTokenReceivedEventArgs e)
{
Debugger.Break();
}
void WSFederationAuthenticationModule_SignedIn(object sender, EventArgs e)
{
Debugger.Break();
}
}
All of this code goes in the Global.asax file, and you want to set up the events after the FederationConfigurationCreated event raises (this is when the SessionAuthenticationModule and WSFederationAuthenticationModule will be available). I've added a Debugger.Break in each event handler. Leave them there and debug your application to see when each event is being triggered. This will allow you to decide when and where you want to add your logic.
use the [Authorize] attribute on the controllers/actions that you want to secure:
[Authorize]
public ActionResult Index()
{
return View();
}
Related
We've got a Xamarin.Forms Android app in which we're displaying progress on a loading page, the progress value being sent by an event from another class.
We're using FreshMvvm which has ViewIsAppearing and ViewIsDisappearing overrides available in the PageModel.
So we're subscribing on ViewIsAppearing, and unsubscribing in ViewIsDisappearing - we're also unsubscribing in a PrepareForDispose method which is intended to ensure the PageModel has cleaned up so that it can be disposed.
Code is below. ProgressManager is supplied by IoC
protected override void ViewIsAppearing (object sender, EventArgs e)
{
base.ViewIsAppearing (sender, e);
ProgressManager.ProgressEvent += ProgressManager_ProgressEvent;
}
protected override void ViewIsDisappearing (object sender, EventArgs e)
{
base.ViewIsDisappearing (sender, e);
RemoveEventHandlers();
}
public override void PrepareForDispose()
{
RemoveEventHandlers();
base.PrepareForDispose();
}
private void RemoveEventHandlers()
{
ProgressManager.ProgressEvent -= ProgressManager_ProgressEvent;
}
The problem is that, when examining object in Profiler, we can see the LoadingPageModel is still in memory, because of the EventArgs created in the ViewIsAppearing (examining the "Paths To Root" in Profiler tells us this).
When I log/debug the app, I can see that RemoveEventHandlers has been called.
So is _ProgressManager.ProgressEvent -= ProgressManager_ProgressEvent;_ failing to remove the handler, or is there another reason that we've still got a reference to the PageModel from the EventArgs?
Edit One possibility is that we're subscribing more than once, but unsubscribing only once. I've checked with debug/logging, and I don't think this is the case. We're subscribing/unsubscribing symmetrically.
It turns out that we were subscribing twice (due to the lifecyle of the page in question at the start of the app lifecycle). Therefore the single unsubscribe was leaving a subscription behind.
Fixing this fixed the problem.
In WP application we need to provide user option to lock app with password.
As I understand WP app lifecycle, I need to put navigation to LockPage in App.Application_Activated, App.Application_Deactivated and start page, but I can not use NavigationService in App class...
I do not want to put navigation code to lock page in each other pages, or there is no other options?
I writed own solution, but may be it is not so elegant as it could be.
App locking logic: User enable app locking with password, we handling Application_Deactivated and Application_Closing events in App class and marking app as locked if user enabled this option. Then, on each page we should put check: is app currently locked and if it is, we should navigate to AppLockedWithPasswordPage. On AppLockedWithPasswordPage we need to check user`s password, if it is correct call NavigationService.GoBack().
So we need to do 6 steps:
You should choose where to save IsAppCurrentlyLocked (bool flag), AppLockPassword (string) and IsUserEnabledAppLockWithPassword (bool flag). I had chosen IsolatedStorageSettings
Create AppLockedWithPassword page, where you need to show TextBox and Button, do not forget to provide option for user to reset AppLock of course with deleting app data
AppLockedWithPasswordPage should prevent BackButton navigation, so preventing it:
// AppLockedWithPasswordPage
protected override void OnBackKeyPress(CancelEventArgs e)
{
// Preventing back key navigation
e.Cancel = true;
}
Check password on button click
// AppLockedWithPasswordPage
private void UnlockAppButton_Click(object sender, RoutedEventArgs e)
{
if (PasswordBox.Password.Equals(IsolatedStorageSettings["AppLockPassword"]))
{
NavigationService.GoBack();
}
else
{
// Say user, that password incorrect, etc...
}
}
In App class find Application_Deactivated (to handle app minimizing (windows button)) and Application_Closing (to handle when user closing app) methods, we should mark app as locked if user enabled this option when this events happens
private void SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking()
{
if ((bool)IsolatedStorageSettings["IsUserEnabledAppLockWithPassword"])
{
IsolatedStorageSettings["IsAppCurrentlyLocked"] = true;
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
And final step, on all pages you want to lock you should add check in OnNavigatedTo method which will navigate to AppLockedWithPasswordPage if app is currently locked
// Create some class, like PagesUtils or so on with check method
private static Uri uriToAppLockedWithPasswordPage = new Uri("pathToAppLockedWithPasswordPage", UriKind.Relative);
public static void NavigateToAppLockedWithPasswordPageIfAppLocked(PhoneApplicationPage page)
{
if ((bool)IsolatedStorageSettings["IsAppCurrentlyLocked"])
{
page.NavigationService.Navigate(uriToAppLockedWithPasswordPage);
}
}
// In each page you want to lock add
protected override void OnNavigatedTo(NavigationEventArgs e)
{
PagesUtils.NavigateToAppLockedWithPasswordPageIfAppLocked();
base.OnNavigatedTo();
}
P.S. of course real code is much better, this is just simple example, I hope it will help you
You should add the check in the Application_Launching and Application_Activated events.
The launching event for when the app is first opened and the activated one for when the user returns to the app after having left to do something else.
Have these events both set a flag and have the base page that all your pages inherit from check for this flag when navigated to. The check should be for if the flag is set, if it is, show the login prompt and then clear the flag after successful password entry.
This approach will handle FAS, FAR & deep linking, in addition to starting the app normally.
Beware Some choosers will trigger the activated event when they return to the app. Add extra handling for these as appropriate / if necessary.
Why not create a start page where the passwords is entered?
For instances you have your MainPage.xaml, create a InsertPasswordPage.xaml reference it on WMAppManifest as the start page:
<DefaultTask Name="_default" NavigationPage="InsertPasswordPage.xaml" />
And insert all the password logic on the InsertPasswordPage.xaml, when the user successfully logins just navigate to your main page ;)
EDIT: As Gambit said if the user pressed the back button he will return to the insert password page, but you can solve this by removing from the backstack the page after the user logged in.
I'm creating a ASP.NET MVC 3.0 website, and have a couple of different database initializations based on whether the site is intended for development, testing, or production. I'm stuck on the testing initialization, as I'm trying to get a test user created. I can get the user to create just fine, however when I try to add some profile values, I get: System.Web.HttpException: Request is not available in this context. Is there a way to add Profile values in a situation where the request isn't going to be available?
Following code is what is being run:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
if (ApplicationServices.GetInitialCatalog() != "tasktracker")
{
Database.SetInitializer(new TaskTrackerDropCreateDatabaseIfModelChanges());
}
else
{
Database.SetInitializer(new TaskTrackerCreateDatabaseIfNotExists());
}
using (var db = new TaskTrackerContext())
{
db.Database.Initialize(false);
}
}
public class TaskTrackerDropCreateDatabaseIfModelChanges : DropCreateDatabaseIfModelChanges<TaskTrackerContext>
{
protected override void Seed(TaskTrackerContext context)
{
// Set up the membership, roles, and profile systems.
ApplicationServices.InstallServices(SqlFeatures.Membership | SqlFeatures.Profile | SqlFeatures.RoleManager);
// Create the default accounts and roles.
if (ApplicationServices.GetInitialCatalog() == "tasktracker_testing")
{
if (Membership.GetUser("testuser", false) == null)
{
Membership.CreateUser("testuser", "password", "testuser#test.com");
MembershipUser user = Membership.GetUser("testuser", false);
user.IsApproved = true;
var profile = ProfileBase.Create("testuser");
profile.SetPropertyValue("FirstName", "test");
profile.SetPropertyValue("LastName", "user");
profile.SetPropertyValue("TimeZone", "US Mountain Standard Time");
profile.Save();
}
}
}
}
Interesting question. Have you looked at using the new Universal Providers? Dunno if you will run into the same httpcontext issue but may be worth a look: http://www.hanselman.com/blog/IntroducingSystemWebProvidersASPNETUniversalProvidersForSessionMembershipRolesAndUserProfileOnSQLCompactAndSQLAzure.aspx
Did you try to do a call of "Initialize()" :
profile.Initialize(username, true)
after your create action to see if the context should be Initialized.
By using Reflector i saw the ProfileBase of Initialize (see below) creates this kind of context from the settings:
public void Initialize(string username, bool isAuthenticated)
{
if (username != null)
{
this._UserName = username.Trim();
}
else
{
this._UserName = username;
}
SettingsContext context = new SettingsContext();
context.Add("UserName", this._UserName);
context.Add("IsAuthenticated", isAuthenticated);
this._IsAuthenticated = isAuthenticated;
base.Initialize(context, s_Properties, ProfileManager.Providers);
}
It seems working here, the SettingsContext() seems taking account of my custom properties declared in the web.config.
Regards,
I come back again because the solution I added with the "Initialize()" function in fact not run really after an other test. So in fact I found a way which runs correctly.
The problem of "request is not available in this context" in application_start in your case could be due to the application mode "Integrated" which is new from II7 instead of the Classic mode.
To see a good explain you ca go on the Mike Volodarsky's blog IIS7 Integrated mode: Request is not available in this context exception in Application_Start .
I copy/paste an extract which could indicate the main reason:
" *This error is due to a design change in the IIS7 Integrated pipeline that makes the request context unavailable in Application_Start event. When using the Classic mode (the only mode when running on previous versions of IIS), the request context used to be available, even though the Application_Start event has always been intended as a global and request-agnostic event in the application lifetime. Despite this, because ASP.NET applications were always started by the first request to the app, it used to be possible to get to the request context through the static HttpContext.Current field.* "
To solve this you can use a workaround that moves your first-request initialization from Application_Start to BeginRequest and performs the request-specific initialization on the first request.
A good example of code is done in his blog :
void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
// Attempt to peform first request initialization
FirstRequestInitialization.Initialize(context);
}
class FirstRequestInitialization
{
private static bool s_InitializedAlready = false;
private static Object s_lock = new Object();
// Initialize only on the first request
public static void Initialize(HttpContext context)
{
if (s_InitializedAlready)
{
return;
}
lock (s_lock)
{
if (s_InitializedAlready)
{
return;
}
// Perform first-request initialization here
//
// You can use your create profile code here....
//---
s_InitializedAlready = true;
}
}
}
It seems like I can get JSON data from a different domain using jQuery's getJSON method (see: http://docs.jquery.com/Getjson). However, this works only for HTTP GET.
What if I needed to POST something and get the JSON response? How would I do that in jQuery/Ajax?
It is not possible to POST requests to a remote server from the client using jQuery alone as of version 1.6.1 in all browsers. If you attempt to make an XHttpRequest of any sort to a server in a different domain than the document, some browsers will simply fail to complete it. The JSONP requests to remote servers are handled by creating a script tag, the src for which is the API url with the query parameters added, including a callback method name. Because scripts can be loaded from any domain, this works, but it limits you to GET requests. The remote host returns the body of the script which is the callback invoked on the resulting javascript object. jQuery typically creates the callback function for you and from it calls the anonymous callback function you supply in the getJSON method parameters.
There are emerging standards, CORS and UMP (see also the comparison), that some browsers support but not in standardized ways (read IE does it differently). There are plugins to provide partial support for those browsers that do support CORS. No idea how well they work and they won't work unless the browser supports it.
The other answers aren't entirely true. This is possible if you have control over the server.
See:
W3C - Cross-Origin Resource Sharing
http://www.w3.org/TR/cors/
Essentially, the client sends a "pre-flight" OPTIONS HTTP request, and, if the correct response is received from the server, it continues with it's regular operations. (There are plenty of examples online... Unless you need me to, I won't get into the details).
I understand this may not work in all scenarios (for example, I'm not sure if IE5/5.5 supports this or not... but I believe IE6 does)... but if you're working on an HTML5 app, and you have control over the server, this could be a possibility for you.
NOTE: Just an aside - Given the option I'd prefer JSONP, of course. Less to go wrong.
EDIT: There seems to be a lot of confusion here, so let me give an example of how one might do this using .NET / WCF (I think some of this came from an article somewhere, and other parts of it were developed in house... so if some of it came from somewhere else, I apologize in advance for not giving the due credit):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace YourNamespaceHere
{
using System;
using System.Web;
using System.Collections;
public class CrossOriginModule : IHttpModule {
public String ModuleName {
get { return "CrossOriginModule"; }
}
public void Init(HttpApplication application) {
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source, EventArgs e) {
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
CrossOriginHandler.SetAllowCrossSiteRequestOrigin(context);
}
public void Dispose()
{
}
}
public class CrossOriginHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
//Clear the response (just in case)
ClearResponse(context);
//Checking the method
switch (context.Request.HttpMethod.ToUpper())
{
//Cross-Origin preflight request
case "OPTIONS":
//Set allowed method and headers
SetAllowCrossSiteRequestHeaders(context);
//Set allowed origin
//This happens for us with our module:
SetAllowCrossSiteRequestOrigin(context);
//End
context.Response.End();
break;
default:
context.Response.Headers.Add("Allow", "OPTIONS");
context.Response.StatusCode = 405;
break;
}
context.ApplicationInstance.CompleteRequest();
}
#endregion
#region Methods
protected void ClearResponse(HttpContext context)
{
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Clear();
}
protected void SetNoCacheHeaders(HttpContext context)
{
context.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
context.Response.Cache.SetValidUntilExpires(false);
context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetNoStore();
}
#endregion
public static void SetAllowCrossSiteRequestHeaders(HttpContext context)
{
string requestMethod = context.Request.Headers["Access-Control-Request-Method"];
context.Response.AppendHeader("Access-Control-Allow-Methods", "GET,POST");
//We allow any custom headers
string requestHeaders = context.Request.Headers["Access-Control-Request-Headers"];
if (!String.IsNullOrEmpty(requestHeaders))
context.Response.AppendHeader("Access-Control-Allow-Headers", requestHeaders);
}
public static void SetAllowCrossSiteRequestOrigin(HttpContext context)
{
string origin = context.Request.Headers["Origin"];
if (!String.IsNullOrEmpty(origin))
context.Response.AppendHeader("Access-Control-Allow-Origin", origin);
else
//This is necessary for Chrome/Safari actual request
context.Response.AppendHeader("Access-Control-Allow-Origin", "*");
}
}
}
And in the Web.config:
...
<system.webServer>
...
<modules runAllManagedModulesForAllRequests="true">
...
<add name="CrossOriginModule" preCondition="managedHandler" type="YOURNANMESPACEHERE.CrossOriginModule, ASSEMBLYNAME" />
</modules>
<handlers>
<add name="CrossOrigin" verb="OPTIONS" path="*" type="YOURNAMESPACEHERE.CrossOriginHandler, ASSEMBLYNAME" />
</handlers>
</system.webServer>
in short: JsonP is a cross-domain technique limited to GET request.
test.php
<?php fire query
$row = array();
while($queryresults){
$row['id'] = '$queryresults['idfield']';
$row['name'] = '$queryresults['namefield']';
$row['marks'] = '$queryresults['marksfield']';
$output[] = $row;
}
echo json_encode( $output ); //json array
?>
document ready
$.getJSON('test.php?query=query,function(data) {
$.each(enq_data, function(i,data){
$('.anydiv').append('<div class="row">'+data.id+data.name+data.marks+'</div>');
});
});
I have an app that I'm upgrading from some beta bits - and my map screen is crashing. So to try to get to the bottom of it - I started a brand new - blank "Win Phone Application".
Referenced Caliburn.Micro (just built from new code last night) Version: caliburnmicro_1296ea635677 (from codeplex)
referenced Microsoft.phone.controls.map.dll
and in the MainPage I added
<Grid>
<Maps:Map />
</Grid>
and I add a bootstrapper to app.xaml
<WP7:PhoneBootStrapper x:Name="bootstrapper" />
when the page runs in the phone emulator - the main page renders and I see a map of the world. if I click anywhere on the page - I get an unhandled exception of "The parameter is incorrect"
if I remove the
from the app.xaml - the map works correctly.
What do you think?
Thanks for any advice?
I have found the answer.
The key here - is that I had this setup and wroking with the Beta Templates - and it stopped working when I moved to the WinPhone RTM Templates in VS2010.
Caliburn does some work on my behalf, that was "ADDED" to the RTM templates - which were conflicting with each other. In the end This problem has/had nothing to do with the Bing Maps control - it just so happens that - that was my first screen - so that's where I was trying to solve the problem.
This was the ever so Not-Helpful exception:
The parameter is incorrect
Which, I'm pretty sure would happen on any screen - if you went to the upgrade path of templates, like I did. So here is what I had to remove - to get everything back to normal. In the new App.Xaml.cs - I removed (by commenting) in the App Ctor ...
// Phone-specific initialization
// InitializePhoneApplication();
// Global handler for uncaught exceptions.
// UnhandledException += Application_UnhandledException;
And then I removed these method bodies, because it's just dead code after removing the InitializePhoneApplication() call from ctor ...
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
// Code to execute if a navigation fails
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// A navigation has failed; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
#region Phone application initialization
// Avoid double-initialization
private bool phoneApplicationInitialized = false;
// Do not add any additional code to this method
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Ensure we don't initialize again
phoneApplicationInitialized = true;
}
// Do not add any additional code to this method
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
#endregion
Special Thanks to Rob for his help solving this mystery!