Authenticate MVC Application with UseCookieAuthentication and an exisiting Web API OAuth Application - asp.net-mvc

I have created my own web api OAuth authentication server with my customized Microsoft.Owin implementation:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/Auth/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthOptions);
Now I want to use that OAuth authentication in an external mvc application but I've been really confused.
In fact my main concern is authorizing controllers and actions based on my existing OAuth server.
After 2 days research finally I implemented that based on a custom OAuthBearerAuthenticationProvider which is worked based on a cookie created during sign in process. The functionality works correctly But I think (actually I know) something is wrong.
this is my Custom OAuthBearerAuthenticationProvider :
public class ApplicationOAuthBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
if (context == null)
throw new ArgumentNullException("context");
var tokenCookie = context.OwinContext.Request.Cookies["BearerToken"];
if (!string.IsNullOrEmpty(tokenCookie))
{
context.Token = tokenCookie;
}
return Task.FromResult<object>(null);
}
}
and this is my MVC auth startup configure method:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Auth/SignIn")
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new ApplicationOAuthBearerAuthenticationProvider(),
});
}
If someone has related experience please advise me.
UPDATE: I think finally I found a solution.
In my solution during sign in process I create a ticket and protect that inside OAuthBearerOptions which is defined inside Startup.Auth class.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SignIn(LoginPageModel pageModel, string returnUrl)
{
if (!ModelState.IsValid)
{
return RedirectToAction("SignIn", new { returnUrl = returnUrl });
}
try
{
var result = await AuthService.Instance.AuthenticateAsync(pageModel.LoginModel);
CreateIdentity(result);
return RedirectToLocal(returnUrl);
}
catch (Exception ex)
{
return RedirectToAction("SignIn", new { returnUrl = returnUrl });
}
}
private void CreateIdentity(TokenResponseModel result)
{
IDictionary< String, String> data = new Dictionary< String, String>
{
{ "userName", result.Username }
};
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, result.Username));
claims.Add(new Claim(ClaimTypes.Email, result.Username));
if (!String.IsNullOrEmpty(result.ExternalIdentity))
{
claims.Add(new Claim(CustomClaimTypes.ExternalIdentity, result.ExternalIdentity));
}
if (result.Roles != null && result.Roles.Length != 0)
{
foreach (var role in result.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
ClaimsIdentity oAuthIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationProperties properties = new AuthenticationProperties(data);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
AuthenticationManager.SignIn(cookiesIdentity);
}
And the Auth Configure is changed to this:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Auth/SignIn"),
});
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}

Related

How to get Owin identity in Signalr hub

I have an application written in ASP.NET MVC using SignalR with Owin external authentication (steam).
Problem is that I can't obtain any identity information inside SignalR hub. Identity.Name returns empty string, Identity.Claims is empty.
Startup.cs
public class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Home/Index"),
AuthenticationMode = AuthenticationMode.Active
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseSteamAuthentication("API KEY");
}
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
In SteamCallBack
authenticateResult?.Identity.Claims
is not empty, it returns correct Identity.Name provided by Steam.
public async Task<ActionResult> SteamCallback()
{
....
var authenticateResult =
await HttpContext.GetOwinContext().Authentication.AuthenticateAsync("ExternalCookie");
var firstOrDefault = authenticateResult?.Identity.Claims.FirstOrDefault(claim => claim.Issuer == "Steam" && claim.Type.Contains("nameidentifier"));
...
}
Inside Hub all of the following are null/empty
var z = Context.User.Identity.Name;
var b = Context.User.Identity.AuthenticationType;
var x = ((ClaimsIdentity)Context.User.Identity).Claims.ToList();
I solve my problem. I forgot to sign in user using IAuthenticationManager.SignIn method.
Example use of that method:
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);

Asp.Net Mvc "Remember Me" not working on server

I am developing web application with asp.net mvc.
It's seems to work in local, but in a shared hosting, It last for about 10 minutes then logout.
Here is the code:
AccountController.cs
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var context = Request.GetOwinContext();
var authenticationManager = context.Authentication;
authenticationManager.SignIn(new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddDays(90), IsPersistent = true }, identity);
return RedirectToAction("Index", "Dashboard");
Startup.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "social",
CookieSecure = CookieSecureOption.Never
});
}
Solving my problem by adding machine key, hope helps someone

OWIN not working on live server

I am using OWIN to implement OAUTH2 in my MVC WEB API application.
I have setup the startup like this
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
static Startup()
{
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new CustomOAuthProvider(),
RefreshTokenProvider = new CustomOAuthRefreshProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(180),
AllowInsecureHttp = true
};
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseOAuthBearerTokens(OAuthOptions);
}
The CustomOAuthProvider Class contains
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
return Task.Factory.StartNew(() =>
{
var username = context.UserName;
var password = context.Password;
var userService = new Merchants();
MerchantsModel user = userService.Authenticate(username, password);
if (user.MerchantId > 0)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, user.MerchantName),
new Claim(ClaimTypes.Email, user.EmailId),
new Claim("CompanyName", user.CompanyName),
new Claim("UserID", user.MerchantId.ToString()),
new Claim("CreatedOn", DateTime.UtcNow.ToString("dd-MMM-yyyy HH:mm:ss"))
};
ClaimsIdentity oAutIdentity = new ClaimsIdentity(claims, Startup.OAuthOptions.AuthenticationType);
context.Validated(new AuthenticationTicket(oAutIdentity, new AuthenticationProperties() { }));
}
else
{
context.SetError("Invalid Username or Password", "Error");
}
});
}
Now when i test this using POSTMAN it works perfectly on my local server and generates the access_token.
But when i deploy it on the web server it returns the error
{
"error": "unsupported_grant_type"
}
Am i missing something here?
this is the first time i am implementing OAUTH2 in my application.
Do i need to add some more code of config to make it work on the live server.
P.S. The request i send for authenticating is same in both cases:
grant_type=password&username=myusername&password=mypassword

OAuth token bearer extra user information

I am using Web API secured with OAuth Bearer token. When getting the token I want to send extra information to the user, so I tried the following as per this thread:
CustomOAuthProvider.cs:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Other stuff, cut off for brevity
var user = await userManager.FindAsync(context.UserName, context.Password);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
oAuthIdentity.AddClaims(ExtendedClaimsProvider.GetClaims(user));
oAuthIdentity.AddClaims(RolesFromClaims.CreateRolesBasedOnClaims(oAuthIdentity));
var ticket = new AuthenticationTicket(oAuthIdentity, this.CreateProperties(user.UserName, oAuthIdentity));
context.Validated(ticket);
}
private AuthenticationProperties CreateProperties(string userName, ClaimsIdentity oAuthIdentity)
{
var data = new Dictionary<string, string>
{
{ "username", userName },
{ "roles", JsonConvert.SerializeObject(oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray()) }
};
return new AuthenticationProperties(data);
}
But the returned object is always as following:
{
access_token: "theTokenHash"
expires_in: 86399
token_type: "bearer"
}
This is my Startup.cs:
public void Configuration(IAppBuilder app)
{
// AutoMapper
AutoMapperConfig.RegisterMappings();
var httpConfig = new HttpConfiguration();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
ConfigureWebApi(httpConfig);
WebApiConfig.Register(httpConfig);
AutofacConfig.Register(httpConfig);
app.UseWebApi(httpConfig);
httpConfig.EnsureInitialized();
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
var OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
What am I doing wrong here?
Wow never mind, I dug into the full example given in the answer of the link. It seems that adding the extra fields isn't enough. You still have to add the parameters to the context yourself by overriding the TokenEndpoint function:
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}

authenticate both mvc controller and api controller from one login entry

I use Web API2 and MVC5 in the same project with Asp.net Identity 2 for authentication and authorization, for Web APIs I use AngularJs as front end framework,
Now I need to make one login entry for both controllers, MVC controllers and Apicontrollers
this code for my configuration function
public void Configure(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext(TemplateEntities.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = TimeSpan.FromMinutes(5),
LoginPath = new PathString("/Home/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
and this my provider code
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var signInManager = context.OwinContext.Get<ApplicationSignInManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
//userManager.lo
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
var result = await signInManager.PasswordSignInAsync(context.UserName, context.Password, true, shouldLockout: false);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
I used this line to authorize MVC Controllers
var result = await signInManager.PasswordSignInAsync(context.UserName, context.Password, true, shouldLockout: false);
and this to set token cookie for APIs
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
in my GrantResourceOwnerCredentials function in previous provider code
Now the problem is APIs run perfectly with authenticated user
but when decorating MVC Controller action with [authorize] attribute it doesn't run in spite of this line var result = await signInManager.PasswordSignInAsync(context.UserName, context.Password, true, shouldLockout: false); runs successfully

Resources