I have created my own Authorize attribute called Authorise...
Imports System.Security.Principal
<AttributeUsage(AttributeTargets.Method Or AttributeTargets.[Class], Inherited:=True, AllowMultiple:=True)>
Public Class AuthoriseAttribute
Inherits AuthorizeAttribute
Public Overrides Sub OnAuthorization(filterContext As AuthorizationContext)
Dim CookieName As String = FormsAuthentication.FormsCookieName
If Not filterContext.HttpContext.User.Identity.IsAuthenticated OrElse filterContext.HttpContext.Request.Cookies Is Nothing OrElse filterContext.HttpContext.Request.Cookies(CookieName) Is Nothing Then
HandleUnauthorizedRequest(filterContext)
Return
End If
Dim AuthCookie = filterContext.HttpContext.Request.Cookies(CookieName)
Dim AuthTicket = FormsAuthentication.Decrypt(AuthCookie.Value)
Dim Roles As String() = AuthTicket.UserData.Split(","c)
Dim UserIdentity = New GenericIdentity(AuthTicket.Name)
Dim UserPrincipal = New GenericPrincipal(UserIdentity, Roles)
filterContext.HttpContext.User = UserPrincipal
MyBase.OnAuthorization(filterContext)
End Sub
End Class
I've done this so I can use the roles parameter on the attribute, like this...
<Authorise(Roles:="Admin")>
This works perfectly on my pages that require authorisation. However, on my main page, which does not require authorisation (and therefore does not have the Authorise attribute) I would like to display different items depending on whether the user is (a) logged in and (b) whether they are an admin or not. For example...
#If HttpContext.Current.User.Identity.IsAuthenticated Then
' Display a welcome message (this works)
#If HttpContext.Current.User.IsInRole("Admin") Then
' Display a settings link (this does not work)
End If
End If
The "welcome message" part fires but the "settings link" part does not. This makes sense because this view does not have the Authorise attribute.
How can I check the IsInRole on pages that don't have the Authorise attribute?
I don't have a proper solution for this. Just a work around that may help before someone posts the proper solution.
I use, [Authorize] attribute for the actions but whenever I am in a partial view, I do a manual 'OnAuthorization'.
public class Authorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
....
}
public static void ManualOnAuthorization(HttpContext context)
{
if (context.User.Identity.IsAuthenticated && context.User.Identity.AuthenticationType == "Forms")
{
FormsIdentity fIdent = (FormsIdentity)context.User.Identity;
var user = new CustomUser(fIdent.Ticket.UserData);
var ci = new CustomIdentity(user);
var p = new CustomPrincipal(ci);
HttpContext.Current.User = p;
Thread.CurrentPrincipal = p;
}
}
}
I have put it in Authorize class and use it as following in a partial view.
#if(User.Identity.IsAuthenticated)
{
Authorize.ManualOnAuthorization(HttpContext.Current);
if (User.IsInRole("Admin"))
{
}
}
Related
Title- asp.net-mvc5 Auto logout How to make form auto logout after sometime in asp.net-mvc5 and redirect automatically to login page
You need to create a session variable on the Login method.
The session will be created by Session["Userid"]=Userid;. Then you need to create custom attribute to check session timeout.
Steps you need to follow are:
Create a session variable in login() (Post method)
Create a class file in your MVC project.
Copy and paste below code in that file.
public class SessionTimeOutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Controller controller = filterContext.Controller as Controller;
HttpContext httpContext = HttpContext.Current;
var rd = httpContext.Request.RequestContext.RouteData;
string currentAction = rd.GetRequiredString("action");
string currentController = rd.GetRequiredString("controller");
if (HttpContext.Current.Session["UserId"] == null)
{
filterContext.Result = new RedirectResult("~/Account/Login?ReturnUrl=" + currentController + "/" + currentAction);
return;
}
base.OnActionExecuting(filterContext);
}
}
add [SessionTimeOut] attribute on each controller.
[SessionTimeOut]
public class ControllerName : Controller
{
You should add Statup.cs file.
1. Add Statup Class your project from new item lists.
2. Add following line in ConfigureService.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting =
false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddAuthorization();
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// we do this because we trust the network
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x =>
{
x.Cookie.Name = "WriteSomeThings";
x.Cookie.SecurePolicy = CookieSecurePolicy.Always;
x.Cookie.SameSite = SameSiteMode.Strict;
x.Cookie.HttpOnly = true;
x.Cookie.IsEssential = true;
x.SlidingExpiration = true;
x.ExpireTimeSpan = TimeSpan.FromHours(8);//For Auto Logout
x.LoginPath = "/User/LogOn";
x.LogoutPath = "/User/LogOff";
x.AccessDeniedPath = "/Home/AccessDenied";
});
}
x.ExpireTimeSpan = TimeSpan.FromHours(8) => This line allow us to logout automatically after 8 hours.
If you need full user management check this video
https://youtu.be/912q3TEF25U
Software development template with role-based user management using ASP.NET MVC 5. Try it for free
I need some advice on how to proceed with the mvc app I'm building. On my page I type out who is logged in to the page. This I first did by creating a base class where I created a user class which contained the users name and a image representing the user. Then I passed this class on to my views. But I also need to pass other models to my views depending on what view I'm in. Sure I could build a class that contain all different models I need to use on each page but there should be a easy way to pass name and image values across the pages and be persistant? I tried TempData together with TempData.Keep() but that was not persistant. How can I pass theses values between pages?
public ActionResult Validate(AccountModels.LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var mu = _repo.GetUser(Membership.GetUser().ProviderUserKey.ToString());
TempData["Name"] = mu.Name;
TempData["Image"] = mu.Image;
TempData.Keep();
FormsAuthentication.RedirectFromLoginPage(model.UserName, model.RememberMe);
}
}
return View("Index");
}
As #Jyoti said, you could use of Keep() method.
To make it easy to work with TempData, I wrote these methods in my BaseController, and I use it in every controller when I need to transfer data between actions or between view and controller.
protected TReturnType GetTempDataValue<TReturnType>(PsmConstants.TempDataKey sessionName, bool peekData =false )
{
object value = peekData ? TempData.Peek(sessionName.ToString()) : TempData[sessionName.ToString()];
return (TReturnType) value;
}
protected void RemoveTempData(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()) && TempData[sessionName.ToString()] == null) return;
TempData[sessionName.ToString()] = null;
}
protected void SetTempDataValue(PsmConstants.TempDataKey sessionName, object value)
{
if(TempData.ContainsKey(sessionName.ToString()))
TempData[sessionName.ToString()]=null;
TempData[sessionName.ToString()] = value;
}
protected void KeepTempDataValue(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()))
TempData.Keep(sessionName.ToString());
}
And this is the Keys enumeration :
public enum TempDataKey
{
PageError = 1,
PageInfo = 2
}
And this is, the usage of these methods(Set value and Get value from TempData):
SetTempDataValue(PsmConstants.TempDataKey.PageError , 'your error message' );
var originalValues = GetTempDataValue<MyModel>(PsmConstants.TempDataKey.Info, true);
Use session instead of Temp if it is not working.but i think it should work.
TempData["Name"] = mu.Name;TempData["Image"] = mu.Image;TempData.Keep();
How you are passing this into other models,Please share the source code so that it will easy to identify.
I'm aware that the authentication on the webservicehost class does not adhere fully to authentication standards (returns 403 forbidden rather than prompting for another set of credentials when the user enters incorrect credentials).
I'd still like to implement this basic authentication (username and password at the start of the session, HTTPS unnecessary - see picture below) as it suits my needs for a small home project.
The code I have for myService is as follows:
Imports System.IO
Imports System.Text
Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.ServiceModel.Channels
<ServiceContract()>
Public Class myService
<OperationContract(), WebGet(UriTemplate:="/xml/{argument1}/{argument2}")>
Public Function XML(argument1 As String, argument2 As String) As Stream
requestCounter += 1
Console.WriteLine("xml data request at " & DateTime.Now.ToString() & ", request count= " & requestCounter)
Console.WriteLine(WebOperationContext.Current.IncomingRequest.UserAgent.ToString())
Return _ReturnXML("<xmlresponse><data><argument1>" & argument1 & "</argument1><argument2>" & argument2 & "</argument2></data><server><serverlivesince>" & serverStart.ToString() & "</serverlivesince><pageservetime>" & DateTime.Now.ToString() & "</pageservetime><requestcount>" & requestCounter & "</requestcount></server></xmlresponse>")
'returns the first two parameters, and the time and date
End Function
Private Shared Function _ReturnXML(_result As String) As Stream
Dim data = Encoding.UTF8.GetBytes(_result)
WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml; charset=utf-8"
WebOperationContext.Current.OutgoingResponse.ContentLength = data.Length
Return New MemoryStream(data)
End Function
End Class
I then have similar code to return HTML as well as accept other parameter combinations.
In my Main class I've instantiated and opened this service as:
Dim varWebService = New WebServiceHost(GetType(MyWebService), New Uri("http://0.0.0.0/"))
varWebService.Open()
Could anyone provide me with code to implement this simple authentication? Or point me to a thorough tutorial? Thanks for any help
You can write a custom WebServiceHost by inheriting from it and change some default parameters like below.
The only change in your code would be
Dim varWebService = New AuthenticatedWebServiceHost(GetType(MyWebService), New Uri("http://0.0.0.0/"))
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IdentityModel;
using System.IdentityModel.Selectors;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Security;
using System.ServiceModel.Description;
namespace StackOverflow
{
public class AuthenticatedWebServiceHost : WebServiceHost
{
public AuthenticatedWebServiceHost(Type type, Uri url)
{
IDictionary<string, ContractDescription> desc = null;
base.InitializeDescription(type, new UriSchemeKeyedCollection());
base.CreateDescription(out desc);
var val = desc.Values.First();
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
base.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
base.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNamePasswordValidator();
base.AddServiceEndpoint(val.ContractType, binding, url);
}
//Possible next question:
//"How can I get the name of the authenticated user?"
public static string UserName
{
get
{
if (OperationContext.Current == null) return null;
if (OperationContext.Current.ServiceSecurityContext == null) return null;
if (OperationContext.Current.ServiceSecurityContext.PrimaryIdentity == null) return null;
return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;
}
}
public class CustomUserNamePasswordValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
//Your logic to validate username/password
if (userName != password)
throw new SecurityAccessDeniedException();
}
}
}
}
The answer provided by I4V worked like a charm, converted to VB and copied below in case anyone else needs it in future after spending many hours hunting the web.
The Line to call it is as per the code provided by I4V.
Dim varWebService = New AuthenticatedWebServiceHost(GetType(MyWebService), New Uri("http://0.0.0.0/"))
VB.Net Code
Imports System.IdentityModel.Selectors
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Security
Imports System.ServiceModel.Web
Public Class AuthenticatedWebServiceHost
Inherits WebServiceHost
Public Sub New(ByVal type As Type, ByVal url As Uri)
Dim desc As IDictionary(Of String, ContractDescription) = Nothing
MyBase.InitializeDescription(type, New UriSchemeKeyedCollection())
MyBase.CreateDescription(desc)
Dim val = desc.Values.First()
Dim binding As WebHttpBinding = New WebHttpBinding()
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic
MyBase.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom
MyBase.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = New CustomUserNamePasswordValidator()
MyBase.AddServiceEndpoint(val.ContractType, binding, url)
End Sub
Public Shared ReadOnly Property UserName As String
Get
If OperationContext.Current Is Nothing Then Return Nothing
If OperationContext.Current.ServiceSecurityContext Is Nothing Then Return Nothing
If OperationContext.Current.ServiceSecurityContext.PrimaryIdentity Is Nothing Then Return Nothing
Return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
End Get
End Property
Public Class CustomUserNamePasswordValidator
Inherits UserNamePasswordValidator
Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
If userName <> password Then Throw New SecurityAccessDeniedException()
End Sub
End Class
End Class
Shaydo, you are the best! Thank you! That is what I searched for for weeks!
I expanded the vb Code in order to use it with https:
VB.NET:
Public Class AuthenticatedWebServiceHost
Inherits WebServiceHost
Public Sub New(ByVal type As Type, ByVal url As Uri, MyThumbprint As String)
Dim desc As IDictionary(Of String, ContractDescription) = Nothing
MyBase.InitializeDescription(type, New UriSchemeKeyedCollection())
MyBase.CreateDescription(desc)
Dim val = desc.Values.First()
Dim binding As WebHttpBinding = New WebHttpBinding()
'binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly
binding.Security.Mode = BasicHttpsSecurityMode.TransportWithMessageCredential
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic
MyBase.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom
MyBase.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = New CustomUserNamePasswordValidator()
MyBase.Credentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint, MyThumbprint)
MyBase.AddServiceEndpoint(val.ContractType, binding, url)
End Sub
Public Shared ReadOnly Property UserName As String
Get
If OperationContext.Current Is Nothing Then Return Nothing
If OperationContext.Current.ServiceSecurityContext Is Nothing Then Return Nothing
If OperationContext.Current.ServiceSecurityContext.PrimaryIdentity Is Nothing Then Return Nothing
Return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
End Get
End Property
Public Class CustomUserNamePasswordValidator
Inherits UserNamePasswordValidator
Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
If userName <> password Then
Console.WriteLine("Error: Access denied")
Throw New SecurityAccessDeniedException()
End If
End Sub
End Class
End Class
I am using ASP.NET MVC. I want to redirect to login page when session expires. How can I achieve this? If I am doing an AJAX call to a method in controller then if my session expires in that situation also I want to redirect to login page.
you could do this by 3 ways:
Create a filter to your actions and apply it programming a code in OnActionExecuting (before the action been executed), http://www.asp.net/mvc/tutorials/understanding-action-filters-cs
Create a base class (inheriting from Controller class) and make your controllers inherits from this one. In this class you could overwrite a method called OnActionExecuting, like the filter.
Don't use Session for Authentication, you can use Forms authentication and keep it simple to use, look this: http://weblogs.asp.net/fredriknormen/archive/2008/02/07/asp-net-mvc-framework-using-forms-authentication.aspx
In my opinion, the solution 3 is better than other. I hope it works for you!
because it's possible to copy the security-cookie of the Forms-Authentication use it to simulate a registered user I use the following attribute to bind the authentication to the current session lifetime.
To make the Attribute work you have to set session["user"] = MyUser on login and call session.abandom() on logout.
I don't know if the redirect works with ajax calls - that's something you have to try.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class CheckUserSessionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
var user = session["User"];
if (((user == null) && (!session.IsNewSession)) || (session.IsNewSession))
{
//send them off to the login page
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content("~/Account/LogOff");
session.RemoveAll();
session.Clear();
session.Abandon();
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
}
}
This answers is heavily based on Michaels except it works ;-)
I changed it to take a delegate for checking if the session has ended so it can work in different apps which might have different ways of determining this and also the login page might be different in other apps. In the Global.asax.cs Application_Start() the code I have in my app is
CheckUserSessionAttribute.CheckSessionAlive = session => (session.GetUser() != null);
CheckUserSessionAttribute.LoginUrl = "~/Account/Login";
Then the attribute class is as follows
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class CheckUserSessionAttribute : ActionFilterAttribute
{
public static String LoginUrl { get; set; }
public delegate bool CheckSessionDelegate(HttpSessionStateBase session);
public static CheckSessionDelegate CheckSessionAlive;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
if ((CheckSessionAlive == null) || (CheckSessionAlive(session)))
return;
//send them off to the login page
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content(LoginUrl);
session.RemoveAll();
session.Clear();
session.Abandon();
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.HttpContext.Response.Redirect(loginUrl, false);
filterContext.Result = new EmptyResult();
}
}
From your controller just add the [CheckUserSession] attribute above the class or the individual actions.
Another plausible solution could be found in here:
Overriding Controller Methods for session Timeout handling
For our web app I want to let developers create accounts using our api, when an account is created the URI has a subdomain. To accomplish this, do I have to have two separate API's because the URL is different.
For account creation: api.example.com/v1
For account usage: subdomain.example.com/api/v1/
we are using .net mvc3, can this be done with one set of api's and routes?
You should set up routing to get to the relevant controller+action as usual, and inside an action (or much better - using an ActionFilter), examine the subdomain and set the appropriate code that determines the who the user is, for further handling.
Example of a filter would be:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SubdomainFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int? userID = null;
Uri uri = filterContext.HttpContext.Request.Url;
if (uri != null)
{
string domain = uri.Host;
// do whatever you need analyzing the 'domain' variable... like getting the user id according to the subdomain. Let's say we discoveered that the user id is 1.
userID = 1;
}
if (filterContext.ActionParameters.ContainsKey("Subdomain"))
filterContext.ActionParameters["SubdomainUser"] = userID;
base.OnActionExecuting(filterContext);
}
}
and then your action would be:
[SubdomainFilter]
public virtual ActionResult GetUserName(int? userID)
{
...
}