Save Claims value for logged user in MVC 5 app - oauth

I need to save New Claims value to my SQL Database [AspNetUserClaims] table which I suppose can be done like this (please correct if I am wrong as I'm new to MVC).
The User logs in using OAuth from Facebook. Once logged in, he fills in some form that collects user data as well as some uploads etc using a wizard type setup. Once the multi step wizard is completed in the View, at the controller after POST these fields are saved to the database. I need to save some of the fields data to the [AspNetUserClaims] table which I suppose is possible in this way:
//Post Fields
var db = new ApplicationDbContext();
var Post = new PostModel();
Post.PostTitle = model.Post.PostTitle;
Post.PostBrief = model.Post.PostBrief;
db.PostModel.Add(Post);
db.SaveChanges();
// User Claims Data
var claims = new List<System.Security.Claims.Claim>();
claims.Add(new System.Security.Claims.Claim("FullName", model.UserClaims.FullName));
claims.Add(new System.Security.Claims.Claim("Email", model.UserClaims.Email));
System.Security.Claims.ClaimsIdentity i = (System.Security.Claims.ClaimsIdentity)HttpContext.GetOwinContext().Authentication.User.Identity;
i.AddClaims(claims);
//How to make the changes to User Claims, save to the database?

Related

How to store in cookie a list with objects MVC C#

I need to store the shopping list items in cookie.
I have the products page where each product has an "Add" button where it should be added to cookie.
I run into a problem, I observed that I can store in the cookie only one product. If the user wants to add the second product to the basket, the first product will be overwrite with the newly added product.
I didn't find any solution on the net which can help me, my question is: I can store a list of objects in the cookie.
I tried this way:
string myObjectJson = new JavaScriptSerializer().Serialize(cartItem);
var cookiea = new HttpCookie("myObjectKey", myObjectJson)
{
Expires = DateTime.Now.AddYears(1)
};
When I need the cookie value, I can get this way. It works:
var tttt = new JavaScriptSerializer().Deserialize<ShoppingCartVM>(myObjectJson);
How should I resolve the problem if the cookie already contains an item, to add a the new item to the same cookie, meaning the cookie had to store a list of objects, and when I need it to return me the entire list of the objects?
Could anyone help me on this? Thanks in advance!
You really shouldn't use Cookies for storing shopping cart items. Session is much better in this case. Cookies are stored on the client side whereas Session is stored on server. When you use cookies, you send whole serialized shopping cart back and forth between the client and server, that's not necessary. Also, Cookies's size is limited and Session does allow you to store complex objects.
To save the shopping cart, use (in controller):
ShoppingCartVM cart; // create instance of the shopping cart
this.Session["ShoppingCart"] = cart;
To get the values, use:
if (this.Session["ShoppingCart"] !== null)
{
ShoppingCartVM cart = (ShoppingCartVM)Session["NeededByDateTime"];
}
If you need something persistent. To make sure that the user will find their products in the cart even after couple months. You should consider saving their shopping cart in database.

ASP.NET MVC Action Parameters Not Binding when form sits idle

I am experiencing a strange issue with my mvc 5 web application that I can't seem to figure out. The Application is an internal web app I've built for my organization. Primarily it is accessed by employees who use domain connected computers but also will access via mobile devices and tablets. I wanted to provide the former an automatic login experience through windows auth and AD (without having to enter credentials if they were already signed on to the domain) I also wanted to be able to provide all other users with a custom login screen rather than the browser native prompt. To implement this I created a separate web app which authenticates the windows users and sends an encrypted cookie back to the main app with the user's roles. Non windows based browsers are presented with a login page in the main app that authenticates against AD and retrieves the user's roles. For each type of login the roles are than converted to claims and a federated token is created for the user.
My problem is that when a user logs in via the redirect to the windows auth app a strange issue is occuring. Any form that I submit whether it be standard form submit or an AJAX post has to be submitted within a minute of loading the page otherwise the parameters sent to the controller action do not bind (null). If a user logins in via the custom login page this problem doesn't exist.
Here is the code that performs the initial authentication in global.asax:
Protected Sub Application_AuthenticateRequest()
Dim user As System.Security.Principal.IPrincipal = HttpContext.Current.User
If user Is Nothing Then
'First check if an authentication cookie is has been generated from the windows login
'authentication app
Dim authCookie As HttpCookie = Request.Cookies(".ConnectAUTH")
If Not authCookie Is Nothing Then
' Extract the roles from the cookie, and assign to our current principal, which is attached to the HttpContext.
Dim ticket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Dim claims As New List(Of Claim)
For Each role In ticket.UserData.Split(";"c)
claims.Add(New Claim(ClaimTypes.Role, role))
Next
Dim claimIdent As New ClaimsIdentity(claims, "Custom")
claimIdent.AddClaim(New Claim(ClaimTypes.WindowsAccountName, ticket.Name))
claimIdent.AddClaim(New Claim(ClaimTypes.NameIdentifier, ticket.Name))
Dim claimPrinc As New ClaimsPrincipal(claimIdent)
Dim token = New SessionSecurityToken(claimPrinc)
Dim sam = FederatedAuthentication.SessionAuthenticationModule
sam.WriteSessionTokenToCookie(token)
HttpContext.Current.User = New ClaimsPrincipal(claimIdent)
Return
Else 'User hasn't been authenticated
Dim ConnectBaseURL = Request.Url.GetLeftPart(UriPartial.Authority) & "/"
Dim mvcPath As String = Request.Url.ToString.Replace(ConnectBaseURL, "")
'If user is requesting the login page then let them authenticate through there
If mvcPath.ToUpper.Contains("ACCOUNT/LOGIN") Or mvcPath.ToUpper.Contains("ACCOUNT/LOGUSERIN") Or mvcPath.ToUpper.Contains("SIGNALR") Then Exit Sub
'for brevity i will omit the code below:
' Basically it checks whether the browser is windows based if so then it redirects the user to
' a windows login authenticator which authenticates the user against Active Directory, builds and sends
' an encrypted cookie with a list of roles/groups that the user belongs to. When this function detects that cookie
' it decrypts it, sets up a claims identity, add the user roles and creates a federated authentication token
'If the the browser is not windows based then it redirects the user to a custom login page which is tied to an MVC
'action that will also authenticate the user against active directory and and set up the claims identity ...
End If
End If
End Sub
Here is the code that authenticates the user if they are redirected to the custom login page:
<AllowAnonymous>
<HttpPost>
<ValidateAntiForgeryToken>
<OutputCache(NoStore:=True, Duration:=0, Location:=OutputCacheLocation.None, VaryByParam:="None")>
Public Function LogUserIn(model As User, returnURL As String) As ActionResult
Try
If ModelState.IsValid Then
If Membership.ValidateUser(model.UserName, model.PassWord) Then
'Call helper function to get all user roles and convert them to claims
Dim userClaims As List(Of Claim) = New LDAPHelper().GetUserGroups(model.UserName)
userClaims.Add(New Claim(ClaimTypes.WindowsAccountName, model.UserName))
userClaims.Add(New Claim(ClaimTypes.NameIdentifier, model.UserName))
userClaims.Add(New Claim(ClaimTypes.Name, model.UserName))
Dim claimIdent As New ClaimsIdentity(userClaims, "Custom")
Dim claimPrinc As New ClaimsPrincipal(claimIdent)
Dim token = New SessionSecurityToken(claimPrinc)
Dim sam = FederatedAuthentication.SessionAuthenticationModule
sam.WriteSessionTokenToCookie(token)
If returnURL Is Nothing Then
Return Redirect("~/")
Else
Return Redirect(returnURL)
End If
Else
ModelState.AddModelError("LoginFailure", "The username/password combination was invalid")
End If
End If
Return Nothing
Catch ex As Exception
ModelState.AddModelError("LoginFailure", ex.Message)
Return Nothing
End Try
End Function
I've tried eliminating the forms cookie from the equation by not sending it from the windows auth app and just hardcoding the claims and token creation after being redirected back to the main app. The HttpContext.Current.User object stays set as a valid claimsPrincipal each time Application_AuthenticateRequest is hit. I've implemented a Custom AuthorizeAttribute and the user is always authenticated and authorized. The funny thing is if i hit the submit button on the form again immediately after the parameters passed through as null, it works. I've scoured online for a similar problem - nothing - i'm hoping someone on here has an idea.
My answer might be completely unrelated but I'm justtrying to help. We had something similar. When we switched from a web api in MVC4 to MVC5. We had an AuthorizationAttribute that checked the token. We read it, set the principal but when we hit the controller action, the principal was gone (in mVC5 - in the previous version this worked). The short story was that it had to do with the async nature of MVC5. We changed our way to implement another attribute that implemented IAuthenticationFilter (which is where mVC5 expects the principal to be set).
What this has to do with you ? I think you also have a problem with async and that you need to figure out in which event MVC5 expects you to set the principal (Application_AuthenticateRequest might not be the place). Anything else might be set on the wrong thread and be gone when you arrive in the controller.

Is TempData the best place to store a saved successfully/unsuccessfully message?

I'm implementing an ASP.NET MVC post/redirect/getpattern in an Azure website. When a user creates a new entity they are redirected from the create view to the edit view with the new object id as part of the url.
The entity has quite a few fields so it's not uncommon to save multiple times and to reassure the user that their data is being saved we are showing a 'Saved successfully' message using javascript.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Branch branch, int orgs)
{
if (ModelState.IsValid)
{
// model is valid, save it and redirect to edit
_branchRepository.Save(branch);
TempData["Message"] = new NotificationViewModel(NotificationSeverity.Success,
"Saved",
"Saved successfully");
return RedirectToAction("Edit", new { id = branch.Id });
}
// model is invalid, don't save it, let them have another go
TempData["Message"] = new NotificationViewModel(NotificationSeverity.Warning,
"I'm sorry, Dave.",
"I'm afraid I can't do that.");
ModelState.Clear();
return View("Edit", branch);
}
My understanding of TempData is that any data stored in TempData will be around for the life the current request and the next request only (or until the item is removed explicitly) and is the best place to put data you want to pass another view that you will be redirecting to.
Is TempData the best place for this message?
Note: I've read that if you're load balancing your webservers that you have to have Sticky Sessions enabled. Does Azure turn on Sticky Sessions automatically or do you need to manually configure this?
Storing validation message betweeen requests when using PRG pattern in TempData is in my opinion the most popular usage of TempData. Additionally you can write some action filters that will automatically store all modelstate in tempdata if you return Redirect result and move that data from tempdata to modelstate/viewstate if you return view in Get phase.
In MVC Contrib there are two such filters:
http://mvccontrib.codeplex.com/SourceControl/latest#src/MVCContrib/Filters/ModelStateToTempDataAttribute.cs
http://mvccontrib.codeplex.com/SourceControl/latest#src/MVCContrib/Filters/TempDataToViewData.cs
Data stored in TempData is removed after end of the request in which it was read until you call TempData.Keep(key).

Deployd : Most secure, most elegant way to get all objects in a specific collection created by the logged-in user?

I think the title pretty much says it all... Brand new to Deployd, so any pointers about how best to go about this are appreciated.
To get the objects in a collection created by the user (I assume you're using the javascript library dpd.js):
// Get the current user:
var currentUser;
dpd.users.me(function(result, error) {
currentUser = result;
});
// query your collection with your currentUser id as parameter
dpd.yourcollection.get({creator:currentUser.id}, function(result) {
// Do something with the result
console.log(result);
});
Your collection should have a property "creator" that contains the id of the user who created the object (*).
Then, to secure your backend, go to the dashboard, in the ON_GET tab of your collection and secure it with this code:
cancelUnless(isMe(this.creator), "You have to be the creator to view this item", 401);
More info about cancellUnless() and isMe() here:
http://docs.deployd.com/docs/collections/reference/event-api.md#s-cancelIf%28%29,%20cancelUnless%28%29-764
The good practice to secure your collections is to allow queries only if user is logged:
cancelUnless(me,"You have to be connected to view this item", 401);
Users collections should be particularly well secured (allow ON_PUT only by admin or something like that).
*: to automatically store the currentUserId in the creator property, you could also add this in the ON_POST event in the dashboard:
this.creator = me.id;
More info here: http://docs.deployd.com/docs/collections/reference/event-api.md#s-me-764
As of version 0.8.9, event ONBEFOREREQUEST exists and you could just put this code in there:
cancelUnless(me);
query.creator = me.id;
This means that for every request sent to that endpoint, creator key would be queried to have the currently logged in user's id. If there's not currently logged in user, the request is canceled.

How to update a claim when using Session Authentication Module (SAM)

I'm using the Session Authentication Module to store the user claims in the authentication cookie.
What is the recommended approach to update the user's claims whilst they are logged in? An example would be if they update their profile (First Name/Last Name) and we want to update the associated claims.
I don't want to log the user out when this happens so DeleteSessionTokenCookie is not an option.
Set a new session cookie with SAM.WriteSessionTokenToCookie.
Leastprivilege's answer is correct.
But in response to Vijay V's comment, here's example code on how to update the cookie after adding new claims to your claims principal:
var sam = FederatedAuthentication.SessionAuthenticationModule;
if (sam != null)
{
var token = new SessionSecurityToken(claimsPrincipal);
sam.WriteSessionTokenToCookie(token);
}
Pulled the code from here.

Resources