I am using WIF with WS Federation so that my ASP.NET application can authenticate against an STS (Thinktecture IdentityServer). In my RP I would like to pragmatically set the cookie persistence based off of the claims of the user.
Watching the traffic in Fiddler I can see the WIF FedAuth cookie is first set when the STS token is posted to the RP. Before the cookie is set I would like to intercept some event and either set the cookie to be persistent (or not) depending on the current claims.
I understand that I can set the cookie persistence in the web.config, however this behavior needs to be conditional based off of the user.
<wsFederation ... persistentCookiesOnPassiveRedirects="true" />
My first approach was to try handling the various SessionSecurityTokenCreated events but these events never seemed to be fired. Am I adding the handlers incorrectly? Or is there a better way of doing this?
protected void Application_Start()
{
...
FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenCreated +=
new EventHandler<SessionSecurityTokenCreatedEventArgs>(SessionAuthenticationModule_SessionSecurityTokenCreated);
FederatedAuthentication.WSFederationAuthenticationModule.SessionSecurityTokenCreated +=
new EventHandler<SessionSecurityTokenCreatedEventArgs>(WSFederationAuthenticationModule_SessionSecurityTokenCreated);
}
//This never seems to fire...
void SessionAuthenticationModule_SessionSecurityTokenCreated(object sender,
SessionSecurityTokenCreatedEventArgs e)
{
if (e.SessionToken.ClaimsPrincipal.HasClaim("someClaim", "someValue"))
e.SessionToken.IsPersistent = true;
else
e.SessionToken.IsPersistent = false;
}
//This never seems to fire either...
void WSFederationAuthenticationModule_SessionSecurityTokenCreated(object sender,
SessionSecurityTokenCreatedEventArgs e)
{
if (e.SessionToken.ClaimsPrincipal.HasClaim("someClaim", "someValue"))
e.SessionToken.IsPersistent = true;
else
e.SessionToken.IsPersistent = false;
}
Interesting to note: if I add a handler for SessionAuthenticationModule_SessionSecurityTokenReceived this event seems to fire. Here I can re-issue the cookie and set IsPersistent = true but this doesn't get fired until after the cookie is first set and I would prefer to do this when the cookie is first issued.
After testing a bit: If I Reissue the cookie in SessionAuthenticationModule_SessionSecurityTokenReceived then SessionAuthenticationModule_SessionSecurityTokenCreated will be fired. I just can't seem to find out why this is not fired on the initial creation of the cookie when the token is first POSTed to the RP.
The source of my problem was:
a) I was using a custom WSFederationAuthenticationModule.
b) I wasn't wiring up the events in the Global.asax using the name of the custom module.
Assuming my web.config has this in it:
<system.webServer>
// ...
<add name="MyCustomWSFederationAuthenticationModule"
type="MyLib.MyCustomWSFederationAuthenticationModule, Thinktecture.IdentityModel, Version=1.0.0.0, Culture=neutral"
preCondition="managedHandler" />
<add name="SessionAuthenticationModule"
type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
preCondition="managedHandler" />
// ...
</system.webServer>
And assuming "MyCustomWSFederationAuthenticationModule" is the name of the custom fed-auth module. Then I just had to fix the name of the method handler (with nothing in app start).
protected void Application_Start()
{
//Nothing here.
}
//This never seems to fire either...
void MyCustomWSFederationAuthenticationModule_SessionSecurityTokenCreated(object sender,
SessionSecurityTokenCreatedEventArgs e)
{
if (e.SessionToken.ClaimsPrincipal.HasClaim("someClaim", "someValue"))
e.SessionToken.IsPersistent = true;
else
e.SessionToken.IsPersistent = false;
}
Related
I am using session value to identify the language that user chosen.
LoadMultilingual() is the method that I will call for all pages to assign the value for label.
I realized that the session value wont store value for long time, by default it store 20 minutes, so when user click any button that will reload the multilingual function, it will prompt an error.
I set the time out for session value for 60 minutes, when I try to click a button at around 45 minutes, it throw me this value.
the code below is how I declare the timeout for session
<sessionState
mode="InProc"
cookieless="true"
timeout="60" />
The code below is the code form C# that user choose their preferred language at Login page..
protected void Page_Load(object sender, EventArgs e)
{
Session["index"] = null;
Session["counter"] = null;
Session["LoginMsg"] = null;
if (Session["Lang"] == null)
{
Session["Lang"] = Request.UserLanguages[0];
}
if (Session["login"] == null)
{
Session["login"] = 1;
}
if (!IsPostBack)
{
LoadMultilingual();
if ((String)Session["Lang"] == "zh-TW")
{
ddLang.SelectedIndex = 1;
}
else
{
ddLang.SelectedIndex = 0;
}
}
Session["stop"] = null;
}
protected void ddLang_SelectedIndexChanged(object sender, EventArgs e)
{
Session["Lang"] = ddLang.SelectedValue;
LoadMultilingual();
}
The code below is the code from C# that use session for multilingual.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DisableLinkBtn();
LoadMultilingual();
}
}
private void LoadMultilingual()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(Session["Lang"].ToString());
rm = new ResourceManager("SOMaintenance.App_GlobalResources.Lang", Assembly.GetExecutingAssembly());
ci = Thread.CurrentThread.CurrentCulture;
Site1 site = this.Master as Site1;
if (site != null)
{
Label SensorTemperatureLabel = site.FindControl("label1") as Label;
string header = validate.getError("userheader", (String)Session["Lang"]);
SensorTemperatureLabel.Text = header;
}
if ((String)Session["Lang"] == "zh-TW")
{
GridView1.Columns[0].HeaderText = "用户账号";
GridView1.Columns[1].HeaderText = "用户名称";
GridView1.Columns[2].HeaderText = "用户职位";
}
btnAdd.Attributes.Add("title", rm.GetString("btnAdd", ci));
btnSearch.Attributes.Add("title", rm.GetString("btnSearch", ci));
}
Anyone has any idea why it is not working? my code work perfectly find within 40 minutes, the things is I already set the session timeout for 60 minutes.
The error is pointing to session["Lang"] this value. (its only appear around 45 minutes and after, I tried on 25minutes it's ok)
If someone visits the page before you've initialize Session["Lang"] then they'll get a null reference exception. So you need to initialize it. The best place to do that is in the Session_Start event in your global application class (global.asax).
protected void Session_Start(object sender, EventArgs e)
{
Session["Lang"] = //add your code here to figure out the language
}
By initializing it in the session start, you can ensure the variable will always be initialized (unless you later set it to null).
Also, you have cookieless authentication enabled. Do not do that! That's very insecure, as sensitive data will now be stored in the URL..
After tested all the method for THREE days. I have found out the solution.
For those which to extend Session in asp.net can use this method.
Step 1 - set session time out at web.config
Copy and paste the code below to web.config
<authentication mode="Forms">
<forms timeout="60"/>
</authentication>
<sessionState
mode="InProc"
cookieless="true"
timeout="60" />
Please take note, by default session value only last for 20 minutes, as code above, I have set it to 60 minutes.
Step 2 - configuration IIS
If you publish your web application to IIS, go to IIS Manager.
Start --> IIS Manager --> Sites --> Default Web site --> ASP
(If you didnt create a new one for your web app, else just click on the one you use for your web app)
then find Session Properties --> time-out set it to 01:00:00 it means 60 minutes.
By default you should see 00:20:00
After that click Apply on your right hand side.
Sample screen as below
After you done the step above, u can laugh your web application, then problem SOLVED!!!
Have a nice day and hope it help!
First time login to the asp.net application,stored some session value
Eg: Session["Test"]="Saving Sesison";
Logout the application
When opened the browser for the second time,need to retain the same session value.
Eg: Session["Test"]="Saving Sesison";
How can i do that,can anyone help me with some solution to proceed further please.
if (!Page.IsPostBack)
{
if (Session["Test"] == null)
{
Binding data to repeater control(with out filter)
}
else
{
//Get Session value (To maintain session value across the browser)
var cookieSession = Request.Cookies["Test"]; //While opening the browser for the 2nd time,this line is getting null for all the browsers,but session is getting value for firefox & Chrome not for IE { Session["Test"] }
if (cookieSession != null &&!String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value;
}
Binding data to repeater control(with filter using session value)
}
}
//On Drop down selection.
protected void Dropdown_SelectedIndexChanged(object sender, EventArgs e)
{
Binding data to repeater control(based on the dropdown selected value)
Session["Test"] = Dropdown.SelectedItem.Text.ToString(); //To maintain the Dropdown selection all over the app
// Set it
if (Session["Test"] == null)
{
Session["Test"] = Guid.NewGuid().ToString();
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
}
ASP.NET Session scope is for only particular session only. So its not possible to have that kind of functionality.
But you can use Cache in same way and it will be there until you make it null or time period exceeds. But beware of fact that it will be there for every browser. So either you need to use different key(Unique key) not like 'test'
You have a few options. Though sessions should be sticky between a browser being re-launched assuming it's not in private/incognito mode. If you're finding the session is timing out too quickly you can extend it in Web.config
<system.web>
<sessionState timeout="10080" mode="InProc" />
</system.web>
Where timeout is in minutes. Note: If you're are debugging stopping and starting the debugger will reset your sessions. So will any kind of re-deployment of the application on IIS. If this is an issue for you, you should check out using something like the SQL session state provider: http://msdn.microsoft.com/en-us/library/vstudio/h6bb9cz9(v=vs.100).aspx
Another method of dealing with this is to store some kind of token in a cookie (again, only works if the browser is not in incognito/private mode, and the user data hasn't been flushed).
// Set it
if (Session["Test"] == null)
{
Session["Test"] = Guid.NewGuid().ToString();
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
// Get it
var cookieSession = Request.Cookies["Test"];
if (cookieSession != null && !String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value;
}
As a note using the SQL session state provider while is one of the more persistent storages there can be some serious overhead requirements. It's easy to rack up a couple of gigs worth of sessions that are being tracked.
In my experience a combination of cookies and the session provider seem to work best if you need to be very certain that some things are sticking to a users experience on the site.
Edit
So the issue with your drop down selection saver is it's always false and should never set the cookie.
protected void Dropdown_SelectedIndexChanged(object sender, EventArgs e)
{
//Binding data to repeater control(based on the dropdown selected value)
// add to Session
Session["Test"] = Dropdown.SelectedItem.Text.ToString();
// Add Cookie
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
Now to get your data back out, put this code in the actions/controllers to run BEFORE you try to access Session["Test"]
var cookieSession = Request.Cookies["Test"];
if (cookieSession != null && !String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value; // Should contain the selected text from the drop down
}
Currently I am working on a Facebook app and it's developed by using ASP.NET.
This app works fine with IE(7,8 and 9) FF and Chrome.
The first page is default.aspx and it will handle the authentication then redirect to home.aspx
Now the only issue it has is that Safari doesn't accept cross-domain cookies. I've changed the web.config file and add it in order to avoid the use of cookies.
After that, the URL comes to
http://www.testdomain.com/(S(gvsc2i45pqvzqm3lv2xoe4zm))/default.aspx
It just can't be redirect from default.aspx to home.aspx automatically...
Anyone got a clue?
Or, is there anyway that i can deal with Safari with ASP.Net session in Facebook app?
Tons of thanks
PS. The code from default.aspx page
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (!string.IsNullOrEmpty(Request.Params["signed_request"]))
{
string signed_request = Request.Params["signed_request"];
if (!string.IsNullOrEmpty(signed_request))
{
// split signed request into encoded signature and payload
string[] signedRequestParts = signed_request.Split('.');
string encodedSignature = signedRequestParts[0];
string payload = signedRequestParts[1];
// decode signature
string signature = decodeSignature(encodedSignature);
// calculate signature from payload
string expectedSignature = hash_hmac(payload, Facebook.FacebookApplication.Current.AppSecret);
if (signature == expectedSignature)
{
// signature was not modified
Dictionary<string, string> parameters = DecodePayload(payload);
if (parameters != null)
{
string UserId = parameters["user_id"];
Session.Add("UserId", _SystemUser.SystemUserId);
Session.Add("Username", _SystemUser.Username);
Response.Redirect("Home.aspx?user_id=" + UserId);
}
}
}
}
if (!String.IsNullOrEmpty(Request["error_reason"])) // user denied your request to login
{
logger.Debug("Error Reason: " + Request["error_reason"]);
//User denied access
}
if (String.IsNullOrEmpty(Request["code"])) // request to login
{
string url1 = String.Format("https://www.facebook.com/dialog/oauth?client_id={0}&redirect_uri={1}&scope={2}", Facebook.FacebookApplication.Current.AppId, callbackUrl, ext_perms);
Response.Redirect(url1);
}
}
}
When using cookieless sessions, ASP.Net will automatically redirect any requests without a session ID in the URL to the same page, but with a new SessionID in the URL. However, it redirects as a GET request, and thus does not forward on any POSTED parameters ... so after the redirect your "parameters" variable, from the decoded signed_request, will be missing because the page will no longer have the signed_request POSTed parameter.
There are two possible solutions to this (that I know of):
Intercept the initial redirect in Global.ascx, and instead do your own redirect with the new SessionID in the URL ... BUT, do this as a self-posting form in Javascript where the form also has a signed_request param with the value of the signed_request.
Turn cookie sessions back on, and in your first page redirect out of FB to a page. In this page set a Session variable (which will get ASP.Net to set a session cookie), and then redirect back into FB.
You may/will also need some code to handle any app_data, if this is on a tab page too.
Sorry I can't be more useful code wise. I've written my own handlers for my job, but my workplace now owns that code! I'm never sure how much is OK to share.
I used cookieless session, but as the initial page was getting refreshed, the Facebook "signed_request" POSTed to the landing page was lost.
As a workaround, I added an HTTPModule to override EndRequest() event. In the event if the page is "initial page" & contained "signed_request" POSTed, the value is added as querystring. In the page we would check the querystring value and set it into session, to be used in the application.
The EndRequest is as below:
void context_EndRequest(object sender, EventArgs e)
{
HttpContext cntxt = HttpContext.Current;
const string paramname = "signed_request";
const string initialPage= "/startapp.aspx";
if ((String.Compare(cntxt.Request.Url.AbsolutePath, initialPage, true) == 0) && (!String.IsNullOrEmpty(cntxt.Request[paramname])))
{
string strQuerySignedReq = paramname+"=" + cntxt.Request[paramname];
if (cntxt.Response.RedirectLocation.Contains(".aspx?"))
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "&" + strQuerySignedReq;
else
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "?" + strQuerySignedReq;
}
}
The initial page - "startapp.aspx", load event would be:
protected void Page_Load(object sender, EventArgs e)
{
signed_request = Request.QueryString["signed_request"];
}
The disadvantage of the code is that EndRequest() would execute for all requests. Also, only relative url should be used for links. I have had several annoying experiences on cookies and Facebook, due to various security levels on different browsers. Hence, I can live with the disadvantages. Hope this helps!
I know this is an old question, but I had exactly the same problem and found a solution.
The solution here works if you're using a SQL Server in your application.
Using cookieless to store your SessionId in the URL will avoid the cookie problem, but still missing the Session issue in Safari.
Well, you'll need to set a SQL SessionState, this will make your application communicate with your Database to store the Sessions. This will work for facebook canvas apps in Safari.
Setting this is simple:
Registering: run aspnet_regsql.exe (in C:/Windows/Microsoft.NET/Framework/'Framework version'/)
Check parameters in https://msdn.microsoft.com/en-us/library/ms229862.aspx (the main ones are -S –ssadd)
In the same path, there is a InstallSqlState.SQL script. Run it on your Database Server.
Now, set this tag in your Web.Config file:
<configuration>
<system.web>
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="120" cookieless="true" />
</system.web>
</configuration>
And the magic is done!
There is something to remember. You can't do WebRequests to facebook from Server side to request for access tokens, because facebook redirects the calls to the "Valid OAuth redirect URIs", and completely ignores the SessionId parameters in the Request URI. You still can make WebRequests to APIs, but the authentication will need to be assyncronous, using Javascript.
Is it possible to use mixed cookieless sessions with cookie sessions?
I've an application that captured user details and then redirect for payment to an ssl page. I was wondering if this is possible?
http://www.mydomain.com/confirm.aspx
redirects to
https://www.mydomain.com/(S(za1tw2l2k02jer4fiskzlovd))/payment.aspx
Note: the session Id in the latter url.
So in essence, we use the standard cookie session for the majority of the application but when we transfer to an ssl page we pass the SessionId to the https url to pick up the session. I've tried this locally but it starts a new session.
Am I missing a trick?
Thanks
I've found a solution that seems to work
When transfering between http and https i've the following:
As you can see I'm passing the session id manually to the https page.
protected void btnPurchase_Click(object sender, EventArgs e)
{
// Confirm puchase code **
string sslPaymentPath = string.Format("https://{0}/payment.aspx?sid={1}", Request.Url.DnsSafeHost, Session.SessionID);
Response.Redirect(sslPaymentPath);
}
Upon reaching the ssl page, asp.net sees the request as a new session so I use the Start_Session method in the global.asax to abandon the newly created session and add a new session cookie with the session id passed in from the query string. Because the AquireSessionState which populates the session keyValue pair has already been run by this point I need to redirect the page back to itself to repopulate those values.
It seems to work really well :)
void Session_Start(object sender, EventArgs e)
{
bool isPaymentPage = (Request.Path.ToLower().IndexOf("payment.aspx") != -1);
// Code to load session over ssl. When changing between two sessions
if (isPaymentPage && Request.QueryString["sid"] != null && Request.IsSecureConnection)
{
string passedSessionId = Request.QueryString["sid"];
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", passedSessionId));
Response.Redirect(Request.Url.LocalPath, true);
}
}
Also with regard to somebody clicking on an external link whilst browsing the ssl purchase.aspx page i've written following in the global.asax to redirect traffic back to standard none ssl pages if it's not the payment page.
void Application_BeginRequest(object sender, EventArgs e)
{
bool isPaymentPage = (Request.Path.ToLower().IndexOf("payment.aspx") != -1);
// In the case someone has navigated away from the payment page redirect them back to the none secure protocol.
if (!isPaymentPage && Request.IsSecureConnection)
{
bool isAxdResource = (Request.Path.ToLower().IndexOf(".axd") != -1);
if (!isAxdResource)
{
string url = Request.Url.AbsoluteUri.ToLower().Replace("https://", "http://");
Response.Redirect(url,true);
}
}
}
Hope somebody finds this useful, I was stuck for a while trying to come up with a nice solution.
My inspiration came from this url.
I am working with asp.net website project that some of pages need authentication. I am using asp.net membership.
I read some answers. e.g. make all of those pages in folder and create inner web.config that describe the privilege. This is one way solve the problem but I need way that is more fixable and effective.
If you don't want to hard code this in web.config(s) you will need to implement a "Base Page" type control.
Your base page class should inherit from System.Web.UI.Page, and would need to have a method you could call to say "User must be logged in" or "User must be in role x", and if the user isn't in that role, redirect to the login page (you can get this by calling FormsAuthentication.LoginUrl).
Your actual pages should inherit from this class rather than from System.Web.UI.Page directly. Then, in something like Init, or at the top of Page_Load, call
base.UserMustBeLoggedIn();
or
// Replace "AccessRole" with the name of your role
base.UserMustBeInRole("AccessRole");
And let the base page handle this.
If you would rather have the access rights stored in a database, then you could move all the processing to the base page, and in a suitable place in the page lifecycle, check the current URL against your database table, check the users role/authentication against the requirements and redirect as required.
Note that you can create page level security in the web config like so:
<configuration>
<location path="LockedPage.aspx">
<system.web>
<authorization>
<!-- Deny access to anonymous users -->
<deny users="?"/>
</authorization>
</system.web>
</location>
</configuration>
More information is available on MSDN: The Location Element and The Authorization Element.
You can try this code,
In the master Page load event write this code,
add a property
public bool m_bLoginRequired = true;
public bool IsLoginRequired
{
get { return m_bLoginRequired; }
set { m_bLoginRequired = value; }
}
try
{
// Response.Cache.SetCacheability(HttpCacheability.ServerAndNoCache);
Response.Cache.SetNoStore();
if (IsLoginRequired==true)
{
if ( Session.IsNewSession || HttpContext.Current.Session["Username"] == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage("Session Expired");
Response.End();
}
}
}
catch (Exception ex)
{
throw (ex);
}
now in Login page you need to write this code
FormsAuthentication.SetAuthCookie(this.txt_UserName.Text.Trim(), false); FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, this.txt_UserName.Text.Trim(), DateTime.Now, DateTime.Now.AddMinutes(10), false, "HR");
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
cookie.Name = "jay";
Session["UserName"] = txt_UserName.Text.Trim();
Response.Cookies.Add(cookie);
txt_UserName.Text = "";
txt_Password.Text = "";
Response.Redirect("HomePage2.aspx");
now you ave to add pageinit event in the login page
protected void Page_PreInit(object sender, EventArgs e)
{
Master.IsLoginRequired = false;
}
if you want that the user can access an un authorized page then
in the pageinit event of that page
set the Master.IsLoginRequired=false;
also specify the loginurl in the web.config file.