How to populate UserInfo object in DNN Authentication providers? - oauth-2.0

I am trying to create an authentication provider in DotNetNuke 7.4 which supports LinkedId. I have used the source package for the Facebook provider from the DnnPlatform GIT as the base and have modified it for LinkedIn's oAuth. I am able to connect via LinkedIn and get the auth token but the code fails on
OAuthClient.GetCurrentUser<LinkedInUserData>();
due to LinkedInUserData being null. the specific logged error is
DotNetNuke.Services.Exceptions.Exceptions - ~/Default.aspx?tabid=55&error=An unexpected error has occurred
System.ArgumentNullException: Value cannot be null.
Parameter name: value
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at DotNetNuke.Services.Authentication.OAuth.OAuthClientBase.GetCurrentUser[TUserData]()
at DotNetNuke.Authentication.LinkedIn.Login.GetCurrentUser() in c:\Websites\dnndev74_2\DesktopModules\AuthenticationServices\LinkedIn\Login.ascx.cs:line 103
Below are the 3 classes that are in play, there is a lot of inheritance going on so I'm having trouble understanding the mechanism for how LinkedInUserData gets populated in the first place. On a note. when I take the facebook codebase from GIT and install it as a provider on my local, and try to register with facebook account, I get the same error. However, if I install the provider through the CMS it runs fine or use the dll that comes with the dnn 7.4 install, facebook works. So I am lead to believe there is something fundamentally flawed with the GIT code..
LinkedInClient.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
public class LinkedInClient : OAuthClientBase
{
#region Constructors
public LinkedInClient(int portalId, AuthMode mode)
: base(portalId, mode, "LinkedIn")
{
base.AuthorizationEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/authorization");
base.RequestTokenEndpoint = new Uri("https://api.linkedin.com/uas/oauth/requestToken?scope=r_emailaddress");
base.TokenMethod = HttpMethod.POST;
base.TokenEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/accessToken");
base.MeGraphEndpoint = new Uri("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,formatted-name,picture-url)?format=json");
base.AuthTokenName = "LinkedInUserToken";
base.OAuthVersion = "2.0";
base.LoadTokenCookie(string.Empty);
}
#endregion
protected override TimeSpan GetExpiry(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return new TimeSpan(0, 0, Convert.ToInt32(tokenDictionary["expires_in"]));
}
protected override string GetToken(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return Convert.ToString(tokenDictionary["access_token"]);
}
}
}
LinkedInUserData.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
[DataContract]
[Serializable]
public class LinkedInUserData : UserData
{
#region Overrides
public override string FirstName
{
get { return LinkedInFirstName; }
set { }
}
public override string LastName
{
get { return LinkedInLastName; }
set { }
}
public override string Email
{
get { return emailAddress; }
set { }
}
public override string ProfileImage
{
get { return LinkedInPictureUrl; }
set { }
}
#endregion
[DataMember(Name = "first-name")]
public string LinkedInFirstName { get; set; }
[DataMember(Name = "last-name")]
public string LinkedInLastName { get; set; }
[DataMember(Name = "picture-url")]
public string LinkedInPictureUrl { get; set; }
[DataMember(Name = "email-address")]
public string emailAddress { set; get; }
}
Login.cs
namespace DotNetNuke.Authentication.LinkedIn
{
public partial class Login : OAuthLoginBase
{
protected override string AuthSystemApplicationName
{
get { return "LinkedIn"; }
}
public override bool SupportsRegistration
{
get { return true; }
}
protected override UserData GetCurrentUser()
{
return OAuthClient.GetCurrentUser<LinkedInUserData>();
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
loginButton.Click += loginButton_Click;
registerButton.Click += loginButton_Click;
OAuthClient = new LinkedInClient(PortalId, Mode);
loginItem.Visible = (Mode == AuthMode.Login);
registerItem.Visible = (Mode == AuthMode.Register);
}
protected override void AddCustomProperties(NameValueCollection properties)
{
base.AddCustomProperties(properties);
properties.Add("LinkedIn", OAuthClient.GetCurrentUser<LinkedInUserData>().Link.ToString());
}
private void loginButton_Click(object sender, EventArgs e)
{
AuthorisationResult result = OAuthClient.Authorize();
if (result == AuthorisationResult.Denied)
{
UI.Skins.Skin.AddModuleMessage(this, Localization.GetString("PrivateConfirmationMessage", Localization.SharedResourceFile), ModuleMessage.ModuleMessageType.YellowWarning);
}
}
}
}

Mark,
I wrote a DNN provider for Linkedin a couple years ago. Comparing my code to yours, the first thing mine does is redirect the user in order to get an access token. The base url for obtaining the access token and permission from the user is: www.linkedin.com/uas/oauth2. I must pass my linked API key, a redirect Url and a few other pieces of data which comes from the provider settings.
My redirect Url is the same as the default portal login page which loads my provider. Once the user allows LinkedIn to allow my application access and verifies he is logged in, the redirect back to my provider will look for the LinkedInAuthToken cookie in the Request.Cookies. Once I verify the token is valid, I can make an additional API call to the /v1/people/ API to get user data to complete any kind of auto-registration or auto profile updates to DNN.
It seems like your provider is immediately attempting the user data lookup API call before obtaining the oauth access cookie.
The code for my LinkedIn provider is not open source, but I suppose I could get permission from my work to make it public. Message me if you are interested in it.

Related

Custom Authorize Attribute in Web API

I want to create my custom authorization in web api controller to check the roles of the user and if its active user. So far this is my code and I don't know yet how/what to override in this codes.
Thanks! your help is appreciated :D
using Avanza.Conference.Persistence;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace Avanza.Conference.Core.Extensions
{
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
ApplicationDbContext _context = new ApplicationDbContext(); // my entity
public override void OnAuthorization(HttpActionContext actionContext)
{
//Sample on what to do here??
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
//Code to handle unauthorized request
var challengeMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
throw new HttpResponseException(challengeMessage);
}
private bool AuthorizeRequest(HttpActionContext actionContext)
{
//Sample on what to do here??
return true;
}
}
}
Here is the sample you required, this check the request contains the authenticationtoken then only allow to execute the request. you can here check your session is available to check user logged in or not.
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization({
System.Web.Http.Controllers.HttpActionContext actionContext)
private readonly string Resource {get; set; }base.OnAuthorization(actionContext);
if (actionContext.Request.Headers.GetValues("authenticationToken") != null)
string authenticationToken =public Convert.ToStringCustomAuthorize(
string resource, string actionContext.Request.Headers.GetValues("authenticationToken").FirstOrDefault()action);
//authenticationTokenPersistant{
// it is saved in someResource data= storeresource;
// i will compare the authenticationToken sent byAction client= withaction;
// authenticationToken persist in database against specific user, and act accordingly}
public override ifvoid OnAuthorization(authenticationTokenPersistant != authenticationToken)
{
HttpContextSystem.CurrentWeb.ResponseHttp.AddHeader("authenticationToken",Controllers.HttpActionContext authenticationTokenactionContext);
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized");{
actionContext.Response = actionContext.Requestbase.CreateResponseOnAuthorization(HttpStatusCode.ForbiddenactionContext);
return;
}
//Check your post authorization logic using Resource HttpContext.Current.Response.AddHeader("authenticationToken",and authenticationToken);Action
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "Authorized");
//Your logic here to return return;
authorize or unauthorized response }
actionContext.Response =
actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed);}
actionContext.Response.ReasonPhrase = "Please provide valid inputs";
}

MVC Get/Impersonate Windows User In Repository

I have an intranet application that uses the Windows username and passes that to a procedure to return data.
I'm using dependency injection, but I don't believe I have the method to get the username separated properly.
I'm trying to keep this secure by not passing in the username as a parameter, but I also want to be able to impersonate (or bypass my GetWindowsUser() method) and send in another username so I can test results for other users.
One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository.
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
return _dropDownDataRepository.MyList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
public HttpResponseMessage MyList()
{
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
//Grab the windowsUser from the method
var windowsUser = utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
INTERFACE
public interface IDropDownDataRepository : IDisposable
{
HttpResponseMessage MyList();
}
UTILITY CLASS
public class Utility
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UPDATE 1
In addition to what Nikolai and Brendt posted below, the following is also needed to allow Web Api controllers work with the session state.
Accessing Session Using ASP.NET Web API
Abstract the Utility class and inject it into the repository.
Then you can stub or mock for testing.
public interface IUtility
{
string GetWindowsUser();
}
public class TestUtility : IUtility
{
public string GetWindowsUser()
{
return "TestUser";
}
}
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private IUtility _utility;
public DropDownDataRepository(IUtility utility)
{
_utility = utility;
}
}
EDIT
Also the repository should not return an HTTPResponseMessage type it should just return a List<T> of the domain model you're accessing.
i.e.
public List<Model> MyList()
{
//Grab the windowsUser from the method
var windowsUser = _utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
return sourceQuery
}
Then move the JSON portion to the controller.
One idea I had for this was to set a session variable in another page
with another (impersonated) username, then check if that session
variable exists first before grabbing the actual user name, but I
couldn't figure out how to access the session variable in the
repository.
Potentially, if you add in a dependency to session, you need to isolate it, e.g.
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private ISession session;
public DropDownDataRepository(ISession session)
{
this.session = session;
}
public HttpResponseMessage MyList()
{
var myUserName = this.session.UserName;
// ... etc
With ISession being something like:
public interface ISession
{
string UserName { get; }
}
Implemented as:
public class MySession : ISession
{
public string UserName
{
get
{
// potentially do some validation and return a sensible default if not present in session
return HttpContext.Current.Session["UserName"].ToString();
}
}
}
Of course there is the potential to decouple this MySession class from HttpContext if desired.
With regards to this:
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
Yes, anytime you create a new object you are tightly coupling them together, which will give you issues, for example, if you try to unit test it in isolation.
In this instance you could extract an IUtility interface from Utility:
public class Utility : IUtility
{
string GetWindowsUser();
}
Then:
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private IUtility utility;
public DropDownDataRepository(IUtility utility)
{
this.utility = utility;
// .... etc
Then you have removed the depenedency between Utility and DropDownDataRepository, and can substitute in another type or mock with ease.
I got a lot of help from Nikolai and Brent and got most of the way there with their posted answers, but ended up figuring out the complete answer on my own. The problems I was having were related to not being able to access session variables in a WebAPI. So, I'm sure there are cleaner solutions to this, but I definitely improved what I had and came up with the following code, which works.
This answer was needed to allow access to the session variable in Web Api - Accessing Session Using ASP.NET Web API
GLOBAL.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
UnityConfig.RegisterComponents();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
//Added to allow use of session state in Web API
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
//Added to allow use of session state in Web API
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
protected void Session_Start(Object sender, EventArgs e)
{
//Default set the session variable to none
Session["_impersonatedUser"] = "none";
}
protected void Session_End(Object sender, EventArgs e)
{
//Reset the session variable to blank
Session["_impersonatedUser"] = "";
}
}
UNITY.config
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<IDropDownDataRepository, DropDownDataRepository>();
container.RegisterType<IUtilityRepository, UtilityRepository>();
container.RegisterType<ISessionRepository, SessionRepository>();
//MVC5
//Unity.MVC5 NuGet Package
DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));
//WEB API
//Unity.WebApi NuGet Package
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
}
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
var sourceQuery = _dropDownDataRepository.MyList();
//JSON stuff moved to controller
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
protected override void Dispose(bool disposing)
{
_dropDownDataRepository.Dispose();
base.Dispose(disposing);
}
}
DROPDOWNDATA REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
private IUtilityRepository _utilityRepository;
private ISessionRepository _sessionRepository;
//Dependency Injection of Utility and Session
public DropDownDataRepository(IUtilityRepository utilityRepository, ISessionRepository sessionRepository)
{
_utilityRepository = utilityRepository;
_sessionRepository = sessionRepository;
}
//Changed to a list here
public List<MyProcedure> MyList()
{
string windowsUser;
//Check the session variable to see if a user is being impersonated
string impersonatedUser = _sessionRepository.ImpersonatedUser;
//Grab the windowsUser from the Utility Repository
windowsUser = _utilityRepository.GetWindowsUser();
if (impersonatedUser != "none")
{
windowsUser = impersonatedUser;
}
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.MyProcedure(windowsUser)
select p).ToList();
return sourceQuery;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
DROPDOWNDATA INTERFACE
public interface IDropDownDataRepository : IDisposable
{
//Changed to list here
List<MyProcedure> MyList();
}
UTILITY REPOSITORY
public class UtilityRepository : IUtilityRepository
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UTILITY INTERFACE
public interface IUtilityRepository
{
string GetWindowsUser();
}
SESSION REPOSITORY
public class SessionRepository : ISessionRepository
{
public string ImpersonatedUser
{
get
{
return HttpContext.Current.Session["_impersonatedUser"].ToString();
}
}
}
SESSION INTERFACE
public interface ISessionRepository
{
string ImpersonatedUser { get; }
}

Is Glimpse.MVC able to work with HttpContext.Current.Session variables?

I'm wondering if the package Glimpse.Mvc5 (1.9.0) http://getglimpse.com/ is able to work with HttpContext.Current.Session variables.
For example when I store the current language in session HttpContext.Session['CurrentUILanguage'], if I enable Glimpse, it always shows me that Session is null, otherwise when glimpse is disabled everything works great.
Here is the sample code of my viemodel:
public class VmCulture
{
public int Id { get; set; }
public string Language { get; set; }
public string Symbol { get; set; }
public CultureInfo CultureInfo { get { return new CultureInfo(Symbol); } }
}
public class SiteSession
{
public static VmCulture CurrentUICulture
{
get
{
return (VmCulture)HttpContext.Current.Session["CurrentUICulture"];
// here I get it Null when enabling Glimpse
}
set
{
Thread.CurrentThread.CurrentUICulture=new CultureInfo(value.Symbol);
Thread.CurrentThread.CurrentCulture = new CultureInfo(value.Symbol);
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(value.Symbol);
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(value.Symbol);
HttpContext.Current.Session["CurrentUICulture"] = value;
}
}
}
Update 1: Added the usage of SiteSession.CurrentUICulture
Suppose there is a BaseController class where I use the protected override void Initialize(RequestContext requestContext) for checking each request and persisting the current language.
Here is a sample code (simplified):
protected override void Initialize(RequestContext requestContext)
{
if (requestContext.HttpContext.Session["Languages"] == null)
{
//1. Get cultures from DB and add them to Session["Languages"]
//2. Get from Request the culture parameter and add it to Session["CurrentUICulture"]
//3. Set the current culture through SiteSession.CurrentUICulture = currentCulture;
}
else
{
//1. Check whether the current language did not change
//2. Set the current culture through SiteSession.CurrentUICulture = currentCulture;
}
base.Initialize(requestContext);
}

How to implement IIdentity for a custom User object in ASP.NET MVC?

In my ASP.NET MVC app, I'm trying to create a custom HttpContent.User object. I've started by creating a Member class, which implements IPrincioal.
public class Member : IPrincipal
{
public string Id { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role) { throw new NotImplementedException(); }
...
}
Then at authentication time I set HttpContext.User to an instance of a Member class:
FormsAuthentication.SetAuthCookie(email, false);
HttpContext.User = member;
Then later I want to check if the user is authenticated, like so:
if (User.Identity.IsAuthenticated) { ... }
That's where I'm stuck. I'm not sure what I need to do for the public IIdentity Identity property on the instance of the Member. So that I can use the HttpContext.User object something like this:
IsAuthenticated = HttpContext.User.Identity.IsAuthenticated;
ViewBag.IsAuthenticated = IsAuthenticated;
if (IsAuthenticated) {
CurrentMember = (Member)HttpContext.User;
ViewBag.CurrentMember = CurrentMember;
}
A Principal is not something you can just set once when writing the auth cookie and forget later. During subsequent requests, the auth cookie is read and the IPrincipal / IIdentity is reconstructed before executing an action method. When that happens, trying to cast the HttpContext.User to your custom Member type will throw an exception.
One option would be to intercept in an ActionFilter, and just wrap the standard implementation.
public class UsesCustomPrincipalAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var systemPrincipal = filterContext.HttpContext.User;
var customPrincipal = new Member(systemPrincipal)
{
Id = "not sure where this comes from",
};
filterContext.HttpContext.User = customPrincipal;
}
}
public class Member : IPrincipal
{
private readonly IPrincipal _systemPrincipal;
public Member(IPrincipal principal)
{
if (principal == null) throw new ArgumentNullException("principal");
_systemPrincipal = principal;
}
public string Id { get; set; }
public IIdentity Identity { get { return _systemPrincipal.Identity; } }
public bool IsInRole(string role)
{
return _systemPrincipal.IsInRole(role);
}
}
This way, you're not losing anything that comes out of the box with the default IPrincipal and IIdentity implementations. You can still invoke IsAuthenticated on the IIdentity, or even IsInRole(string) on the IPrincipal. The only thing you're gaining is the extra Id property on your custom IPrincipal implementation (though I'm not sure where this comes from or why you need it).

HttpModules life in ASP.NET MVC

I find a code about counting online users in Asp.Net. I add it to my MVC project but its not working. There is a custom Httpmodule that has a Init() function and it is being called in every request. where is the problem.
init() must be run one time for all application lifecycle , but it is running on every request.
This code run well on asp.net but because of init() method run in every request it is not working on MVC.
public class OnlineUsersModule : IHttpModule
{
private static Int32 _sessionTimeOut = 20; // Set Default to 20 Minutes
private static List<OnlineUserInfo> _onlineUsers = null;
public static List<OnlineUserInfo> OnlineUsers
{
get
{
CleanExpiredSessions();
return _onlineUsers;
}
}
private static void CleanExpiredSessions()
{
_onlineUsers.RemoveAll(delegate(OnlineUserInfo user)
{
return user.SessionStarted.AddMinutes(_sessionTimeOut) < DateTime.Now;
});
}
#region IHttpModule Members
public void Init(HttpApplication context)
{
_onlineUsers = new List<OnlineUserInfo>();
// Get the Current Session State Module
SessionStateModule module = context.Modules["Session"] as SessionStateModule;
module.Start += new EventHandler(Session_Start);
}
private void Session_Start(object sender, EventArgs e)
{
HttpRequest Request = HttpContext.Current.Request;
HttpApplicationState Application = HttpContext.Current.Application;
HttpSessionState Session = HttpContext.Current.Session;
// Get Session TimeOut
_sessionTimeOut = HttpContext.Current.Session.Timeout;
Application.Lock();
OnlineUserInfo user = new OnlineUserInfo();
user.SessionId = Session.SessionID;
user.SessionStarted = DateTime.Now;
user.UserAgent = !String.IsNullOrEmpty(Request.UserAgent)
? Request.UserAgent : String.Empty;
user.IPAddress = !String.IsNullOrEmpty(Request.UserHostAddress)
? Request.UserHostAddress : String.Empty;
if (Request.UrlReferrer != null)
{
user.UrlReferrer = !String.IsNullOrEmpty(Request.UrlReferrer.OriginalString)
? Request.UrlReferrer.OriginalString : String.Empty;
}
else
{
user.UrlReferrer = String.Empty;
}
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
user.CurrentUser = HttpContext.Current.User;
}
// Add the New User to Collection
_onlineUsers.Add(user);
Application.UnLock();
}
public void Dispose()
{
}
#endregion
}
public class OnlineUserInfo
{
public String UserAgent { get; set; }
public String SessionId { get; set; }
public String IPAddress { get; set; }
public String UrlReferrer { get; set; }
public DateTime SessionStarted { get; set; }
public IPrincipal CurrentUser { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("UserAgent = {0} | ", UserAgent);
sb.AppendFormat("SessionId = {0} | ", SessionId);
sb.AppendFormat("IPAddress = {0} | ", IPAddress);
sb.AppendFormat("UrlReferrer = {0} | ", UrlReferrer);
sb.AppendFormat("SessionStarted = {0}", SessionStarted);
return sb.ToString();
}
}
Also I think there is one more problem. when i add breakpoint to init() method, after push F10 it goes to start of init() means there is other threads that try to run init() is it a problem?
HttpModules live in a pool. The ASP.NET process creates and initializes a (configurable) number of them when your app starts up and places it in a pool.
Then every time a request comes in an instance is taken from the pool and assigned to service the request. There is no initialization at this time. When the processing of the request is completed, the instance is placed back in the pool for later use.
Under heavy load the system can decide to create more instances of HttpModules
HTH
Maybe you are using Cassini and recompiling your application every time by hitting F5 which creates the illusion that the Init method is called on every request.

Resources