Link to home
Start Free TrialLog in
Avatar of Jeanette Durham
Jeanette DurhamFlag for United States of America

asked on

asp.net identity 2.+ how to add claims to authorization cookie and remove old cookie

Dear Experts,

I have an asp.net website that uses identity 2.1 (I'm pretty sure > 2.0 anyways as near as I can tell). It is written in visual basic. I ran into a weird issue the other day where people were logging in and getting this viewstate error:

Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

So I followed the advice I found in the microsoft article and added a machine key to the web.config file so that no matter what it (hopefully) shouldn't get that error again.

Once the machine key was changed to one that I set now we had issues with anyone with an already existant authorization cookie on their computer, they were unable to login. I believe it's because the machine key that I set no longer matched the one that was used in their cookie. I'm not entirely clear how, but apparently it's part of how it's encrypted. If they clear their cookies, all is well - then they could login.

So one bit of advice I found searching on the web sounded super simple and super-easy. They recommended that you have some sort of set key in your web.config, like a guid or something and then you add it to your cookie also. Then, when you load the cookie, you can check the key and if it doesn't match your server key, somehow you invalidate the cookie.

I basically spent all day trying to figure out little things, like where do you put the code to put the thing in the cookie? When you do so, is it a claim? At the end of the day we just had to put a message on the page telling users to clear their cookies or call us if they had any issues. I still really want to know how to do this.

Here is the code that I came up with and I was able to determine that the main problem was, it didn't appear to be adding the claim to the cookie. As far as putting in the login portion, that was the only place I could figure to do it, even though I think there's supposed to be some other place you hook it in for the default microsoft asp.net user thing.

This part is supposed to add the server key as a claim to the cookie (afaik).. It is inside the IdentityModels.vb file:
Public Class ApplicationUser
    Inherits IdentityUser

    Public Function GenerateUserIdentity(manager As ApplicationUserManager) As ClaimsIdentity
        ' Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        Dim userIdentity = manager.CreateIdentity(Me, DefaultAuthenticationTypes.ApplicationCookie)
        ' Add custom user claims here

        'I'm going to use this to invalidate any cookies if I make any server change that requires it
        'Will reset everyone only one time, whenever their cookie value doesn't match our server one in our web.config file ~Michael
        'https://stackoverflow.com/questions/19663779/how-to-invalidate-all-cookies-in-a-web-application
        Dim sCookieValidation As String = ConfigurationManager.AppSettings("CookieValidationServerValue")
        userIdentity.AddClaim(New Claim("CookieValidationValue", sCookieValidation))
        Return userIdentity
    End Function
..

Open in new window


And here is my modified login routine on the login.aspx page. It's got a lot of extra features in it, like I'd ignore the phone number part and the part about logging people out if they're logged in (not even sure if that part works). But the key features are where it tries to enumerate the claims, it actually got none of them, even on my test user with no preset cookies. Also, apparently the way I'm invalidating cookies may not be working either because on my test user that actually did have an old (not expired) cookie it ended up making 2 authorization cookies instead of removing the one. The output from my debugging string was:

Signinmanger.passwordsignin result: Success Had claim: False Claim value: , Claim value type:

And then when I stepped through it I couldn't find any claims either.

If anyone can help me figure out how to add the claims and then how to properly invalidate the user's authorization cookie (so this is the one the identity creates on it's own -came with the microsoft website project when you add the users to it) so I can issue them a new one when their's is lacking my server key entirely or the server key doesn't match. Thanks very much!

Jeffrey

In case it helps to know the libraries, here are the libraries referenced at the top of the login.aspx page:
Imports System.Web
Imports System.Web.UI
Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.EntityFramework
Imports Microsoft.AspNet.Identity.Owin
Imports Microsoft.Owin.Security
Imports Owin

Code follows for login function:

    Protected Sub LogIn(sender As Object, e As EventArgs)
        If IsValid = False Then Exit Sub
        Handle_Login()
    End Sub

    Private Async Sub Handle_Login()
        ' Validate the user password
        Dim manager = Context.GetOwinContext().GetUserManager(Of ApplicationUserManager)()
        Dim signinManager = Context.GetOwinContext().GetUserManager(Of ApplicationSignInManager)()
        Dim iResult As IdentityResult
        Dim b2ndSignInAttempt As Boolean = False, sClaimVal As String = "", sClaimValType As String = ""
        Dim sCookieValidation As String = ConfigurationManager.AppSettings("CookieValidationServerValue")

        'Now let's check if they are already logged in - if so, log them out and log in again
        If Context.User.Identity.IsAuthenticated = True Then
            Context.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie)
        End If

        'Check first and see if the user has confirmed their email, disallow login if so
        Dim sEmail As String = Me.Email.Text
        Dim user = manager.FindByName(sEmail)
        If user IsNot Nothing Then
            'Before continuing, verify the user has the correct password before letting them resend
            '  the confirmation email
            'If not, we'll let the signInManager throw the error
            If manager.CheckPassword(user, Password.Text) = True Then
                'Now verify the user has a confirmed email
                If user.EmailConfirmed = False Then
                    ShowErrorMessage("Invalid login attempt. You must have a confirmed email account.")
                    panelEmailConfirm.Visible = True
                    Exit Sub
                End If

                'Another thing we want to check for now is that we have a phone number for this user and if we 
                '  don't let's show a dialog requesting a valid phone number and if not provided with one do
                '  not allow the user to login
                If nz(user.PhoneNumber) = "" Then
                    'check first and see if we've already shown the dialog and if so, take the phone number
                    '  and save it back to the user
                    Dim sPhone As String = Me.fldPhone.Text
                    If sPhone <> "" Then
                        user.PhoneNumber = sPhone
                        iResult = Await manager.UpdateAsync(user)
                        If iResult.Succeeded = False Then GoTo Report_UserUpdate_Failed Else GoTo Phone_Number_Passed
                    End If

                    'show dialog requesting phone number
                    Page.ClientScript.RegisterStartupScript(Me.GetType(), "showModalDlg_Phone", "document.getElementById('MainContent_btnShowDlg_RequestPhone').click();", True)
                    Exit Sub
                End If
Phone_Number_Passed:

                'If we know we have a valid username and password let's update the security stamp
                'This will, in effect, invalidate any other existing sessions and they should be logged out 
                '  Next time they make a web request
Update_Security_Stamp:
                iResult = Await manager.UpdateSecurityStampAsync(user.Id)
                If iResult.Succeeded = False Then
Report_UserUpdate_Failed:
                    ShowErrorMessage("Unknown error has occurred on login.")
                    myFuncs.EmailDebuggingInfo("User attempting to login: " & user.UserName & vbCrLf & "Updating Security Stamp Async func call failed for some unknown reason.")
                    Exit Sub
                End If
            End If
        Else
            'No point in attempting the login since we know thier user doesn't even exist ~Michael 5/29/2018
            GoTo Invalid_Login_Attempt
        End If

        ' This doen't count login failures towards account lockout
        ' To enable password failures to trigger lockout, change to shouldLockout := True
Sign_In_Again:
        Dim result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout:=False)
        Select Case result
            Case SignInStatus.Success

                'we are running into an issue (6/1/2018) where the viewstate/mac address isn't matching
                'what we need to do Is check And make sure the cookie contains our server check value And if it doesn't or is different
                'we need to invalidate the cookie ~Michael 6/1/2018
                Dim claims = Await manager.GetClaimsAsync(user.Id)
                Dim bNoClaim As Boolean = True
                For Each oClaim As System.Security.Claims.Claim In claims
                    If oClaim.Type = "CookieValidationValue" Then
                        sClaimVal = oClaim.ValueType
                        If oClaim.Value <> sCookieValidation Then
                            'we need to get the user's browser to delete their cookie
                            GoTo Remove_User_Authentication_Cookie
                            'bNoClaim = False
                            'Exit Sub
                        End If
                        bNoClaim = True
                    End If
                Next
                If bNoClaim = True Then
Remove_User_Authentication_Cookie:
                    If b2ndSignInAttempt = False Then
                        'Cookie is old and should be invalidated
                        myFuncs.EmailDebuggingInfo("Signinmanger.passwordsignin result: " & result.ToString() & vbCrLf & "Had claim: " & Not bNoClaim & vbCrLf &
                            "Claim value: " & sClaimVal & ", Claim value type: " & sClaimValType & ", Server value: " & sCookieValidation)

                        'maybe if we sign them out and then change their security stamp
                        Context.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie)
                        b2ndSignInAttempt = True
                        GoTo Update_Security_Stamp
                    Else
                        ShowErrorMessage("Your cookies are invalid. Please reset your cookies and sign in again.<br />" &
                                         "If you are still having trouble, please call Ron Creeger at (480) 699-3400. Thanks!")
                        Exit Sub
                    End If
                End If

                'Now, let's move on and go to the requested return url
                IdentityHelper.RedirectToReturnUrl(Request.QueryString("ReturnUrl"), Response)
                Exit Select
            Case SignInStatus.LockedOut
                Response.Redirect("/Account/Lockout")
                Exit Select
            Case SignInStatus.RequiresVerification
                Response.Redirect(String.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={1}",
                    Request.QueryString("ReturnUrl"),
                    RememberMe.Checked),
                    True)
                Exit Select
            Case Else
Invalid_Login_Attempt:
                ShowErrorMessage("Invalid login attempt")
                Exit Select
        End Select
    End Sub

Open in new window

Avatar of Kyle Santos
Kyle Santos
Flag of United States of America image

Hi,

I am following up on your question.  Do you still need help?

If you solved the problem on your own, would you please post the solution here in case others have the same problem?

Regards,

Kyle Santos
Customer Relations
Avatar of Jeanette Durham

ASKER

I never was able to figure it out. What we ended up doing is posting a message on our login page telling people to clear their cookies and call if they need help. I occasionally still see errors come into my email where a user tries to login (that hasn't logged in for awhile) and gets it, but they seem to figure it out.

At this point I do still want to know the answer, but if the cookie invalidation thing is difficult or there is no easy way - no worries there. I still Really want to know how to properly add claims to authorization cookies because I could really use this. I don't think we're ever going to get the mac state error again now that we have a machine key that doesn't change, so the issue with removing cookies isn't really that important. Soon enough they will all expire anyways.

Thanks Kyle!
Jeffrey
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.