1
votes

I use an external provider in order to authenticate user in my ASP.NET MVC app without any problem. However, I also need to authorize users in order to prevent them from direct access or expired access (session for 2 min). I had used ASP.NET Identity before, but this time I do not need to keep neither users nor roles on the table and for this reason I need a quick and good workaround for this problem. So, how can I prevent a user accessing the In dex page of my app without authenticating by the provider that I use. Similarly I also need to check if there is more than 2 minutes after user's last action and in such sitıuation I need to redirect user to Login page. I tried to use OWIN Cookie, but unfortunately I cannot logout user by using at least 10 different approach :(

Startup:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),

            //other options
            ExpireTimeSpan = TimeSpan.FromMinutes(1)
            //Provider = new CookieAuthenticationProvider(),
            //CookieName = "MyCookieName",
            //CookieHttpOnly = true
        });
    }
}

Controller:

[HttpGet]
public ActionResult Login(string code)
{
    //At this stage I want to force user to sign out, but none of the following methods work

    //method 1
    HttpContext.GetOwinContext().Authentication.SignOut("ApplicationCookie");

    //method 2
    var ctx = Request.GetOwinContext();
    var authManager = ctx.Authentication;
    authManager.SignOut("ApplicationCookie");
    //or        
    //authManager.SignOut();

    //method 3
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

    //method 4 (using only one of them at a time)
    Request.GetOwinContext().Authentication.SignOut();
    Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
    HttpContext.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);


    //check session
    var isAuthenticated = HttpContext.GetOwinContext().Authentication.User.Identity.IsAuthenticated; // >>> always returns true
    string tc = HttpContext.GetOwinContext().Authentication.User.Identity.Name; // >>> always returns name value


    //if user is authenticated via OAuth2.0
    if (user.isAuthenticated)
    {
        var claims = new[] {
        new Claim(ClaimTypes.Name, user.Name)
    };

        var identity = new ClaimsIdentity(claims, "ApplicationCookie");

        //// Add roles into claims
        //var roles = _roleService.GetByUserId(user.Id);
        //if (roles.Any())
        //{
        //    var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
        //    identity.AddClaims(roleClaims);
        //}

        var context = Request.GetOwinContext();
        var authManager = context.Authentication;

        authManager.SignIn(new AuthenticationProperties
        { IsPersistent = false }, identity); // ??? I am not sure if IsPersistent should be true ? 

        return View();
    }

    // login failed
    return RedirectToAction("Account", "Login");
}
4

4 Answers

1
votes

Finally I have fixed the problem by using OWIN cookie authentication. Here is the code for those who might need to use OWIN cookie authentication on ASP.NET MVC.

On the other hand, I would really like to integrate JWT to my ASP.NET MVC project, but unfortunately was not able to do. However, many thanks and voted up the answers that are also helpful for me.

Startup:

public void Configuration(IAppBuilder app)
{
    ConfigureAuth(app);
}

public void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        LogoutPath = new PathString("/Account/LogOff"),
        ExpireTimeSpan = TimeSpan.FromMinutes(5),
        SlidingExpiration = true,
        Provider = new CookieAuthenticationProvider(),
        CookieName = "YOUR_COOKIE_NAME",
        CookieHttpOnly = true,
        // !!! Using this setting "Always" causing "302 Redirect..." error while ddebugging >>>
        CookieSecure = CookieSecureOption.SameAsRequest 
    });
}

AccountController:

public ActionResult Login()
{
    //authenticate user
    var user = db.GetUser("John");

    if (user != null)
    {
        var claims = new[] {
            new Claim(ClaimTypes.Name, user.Name),
            new Claim(ClaimTypes.Email, user.Email)
            //you can add more claims
        };

        var identity = new ClaimsIdentity(claims, "ApplicationCookie");

        // Add roles into claims
        var roles = _roleService.GetByUserId(user.Id);
        if (roles.Any())
        {
            var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
            identity.AddClaims(roleClaims);
        }

        var context = Request.GetOwinContext();
        var authManager = context.Authentication;

        authManager.SignIn(new AuthenticationProperties
            { IsPersistent = true }, identity);

        return RedirectToAction("Index", "Home");
    }
    // login failed. 
    return RedirectToAction("Login", "Account");
}
0
votes

You need to set the [Authorize] attribute on the action and/or controller.

And about the session only last for 2 minutes. You can put the timestamp in a session cookie when the user logins and then build a middleware to check the session value each time an action is made. If the session value is older than 2 minutes log out the user.

How to use session:

In Startup file add:

services.AddSession(options =>
{
   options.Cookie.SecurePolicy = CookieSecurePolicy.Always; 
});

and

app.UseSession();
//middleware for checking the 2 minute limit
app.UseMiddleware<SignoutMiddleware>();

Add the session wherever your user gets logged in:

HttpContext.Session.SetString(subjectId, DateTime.Now);

Middleware:

public class SignoutMiddleware
{
    private readonly RequestDelegate _next;

    public SignoutMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {

        var sessionExpire = context.Session.GetString(context.User.GetSubjectId());
        //Do some logic here
        await _next.Invoke(context);
    }
}

As for what your code currently does for login you probably needs to change some. But there should be many tutorials if you just google some :)

0
votes

You could use a Entity Framwork, JSON Web Token (JWT) and Claims. It is really easy to limit the amount of time (days, hours, minutes) you want a user to have access to a section of your Controllers with JWTs.

You can limit the amount of time a JWT has access for by using the Expires object in SecurityTokenDescriptor. So in your case I would do the following:

var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Id.ToString())
                }),
                Expires = DateTime.UtcNow.AddMinutes(2),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

There is several great FULL examples done by Jason Watmore for .NET Core with Role Based Auth and Secure Auth For Hashed Password in Database. Not sure what library you're using though so if this does not help you it would help me to help you if you specify.

0
votes

I'm AmirReza that you talking before.

Edit

In order for MVC to understand anything about your JWT you basically have to tell it :-) . First, install the Jwt package from nuget:

Install-Package Microsoft.Owin.Security.Jwt Then open up your Startup.cs file and add a new funtion that will tell MVC how to consume JWT. At basics your Startup will look something like:

using System.Configuration;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;

[assembly: OwinStartupAttribute(typeof(TOMS.Frontend.Startup))]
namespace TOMS.Frontend {
 public partial class Startup 
  { 
    public void Configuration(IAppBuilder app) 
     { 
      ConfigureAuth(app);
      ConfigureOAuthTokenConsumption(app);
     }

   private void ConfigureOAuthTokenConsumption(IAppBuilder app) 
    { 
     var issuer = ConfigurationManager.AppSettings["Issuer"];
     var audienceId = ConfigurationManager.AppSettings["AudienceId"];
     var audienceSecret =  TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

You will notice that I am placing the issuer, audienceId and audienceSecret in my Web.config file. (Those values should match the ones on your Resource Server). Also, you might want to ensure you have an updated System.IdentityModel.Tokens.Jwt running:

Update-package System.IdentityModel.Tokens.Jwt With those set, you may decorate your controller Action with the [Authorize] attribute and play ball.

UPDATE By The way, if you wish to add the values in your web.config file in order to retrieve them as I did above; simply add them under the AppSettings:

<configuration> <appSettings> <add key="Issuer" value="YOUR_ISSUER" /> <add key="AudienceId" value="YOUR_AUDIENCEID" /> <add key="AudienceSecret" value="YOUR_AUDIENCESECRET" /> </appSettings> </configuration>