I have been working on a web application that is setup on IIS 7 as one of several applications and web services operating under the DefaultWebSite. When developing on localhost, my FormsAuthentication cookie persists; however, when published on the server the cookie(s) are not persistent. I am using SQLServer sessions, and have validation keys in the web config to rule out application pool recycling. I have been trying to figure this out for a while now with no luck...please help!
Here is some code below:
<forms name=".OPTFORMSTEST" loginUrl="~/Secure/Login.aspx" defaultUrl="~/Default.aspx" timeout="240" path="/" slidingExpiration="false" protection="All" />
Public Shared Function DoLogin(ByVal strUsername As String, ByVal isPersistent As Boolean)
Dim authTicket As FormsAuthenticationTicket
Dim authCookie As HttpCookie
Dim strUserData As String = strUsername
Dim intTimeoutPersist As Integer = 43200 '(30 days)
Dim intTimeoutNonPersist As Integer = 300 '(5 hours)
Dim intRtn As Integer = 1
Dim strCookiePath As String = Current.Request.Url.AbsolutePath.Remove(Current.Request.ApplicationPath.Length)
Try
'set cookie timout period and create auth ticket based on isPersistent
If isPersistent Then
'create a persistent ticket
authTicket = New FormsAuthenticationTicket(1, strUsername, _
DateTime.Now(), _
DateTime.Now.AddMinutes(intTimeoutPersist), _
True, strUserData)
Else
'create a temp ticket
authTicket = New FormsAuthenticationTicket(1, strUsername, _
DateTime.Now(), _
DateTime.Now.AddMinutes(intTimeoutNonPersist), _
False, strUserData)
End If
'create encrypted string for user data
Dim strEncr As String = FormsAuthentication.Encrypt(authTicket)
'create cookie
authCookie = New HttpCookie("OPTFORMSTEST", strEncr)
'set cookie expiration based on the auth ticket
If isPersistent Then
authCookie.Expires = authTicket.Expiration
End If
Current.Response.Cookies.Add(authCookie)
Catch ex As Exception
intRtn = -1
End Try
Return intRtn
End Function
After some experimenting, I found out that the RedirectFromLoginPage method was creating another cookie when used from the production box. I replaced with a Response.Redirect and only one cookie was created and it was persistent.
Related
My CreateUserWizard sends an email when I press a button. The email is send via app password as follows
Protected Sub NewUserWizard_SendingMail(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MailMessageEventArgs) Handles NewUserWizard.SendingMail
'Get the UserId of the just-added user
Dim newUser As MembershipUser = Membership.GetUser(NewUserWizard.UserName)
Dim newUserId As Guid = CType(newUser.ProviderUserKey, Guid)
'some more code...
Dim obj As SecurePassword = New SecurePassword()
Dim google As GoogleOauth = New GoogleOauth()
' Replace <%VerificationUrl%> with the appropriate URL and querystring
e.Message.Body = e.Message.Body.Replace("<%VerificationUrl%>", fullUrl)
Select Case ConfigurationSettings.AppSettings("WhichSMTP")
Case "gmail"
'no need to do anything
Try
obj.SendViaGmail(e.Message.Subject, e.Message.Body, e.Message.To.ToString())
Catch ex As Exception
Response.Write("Error sending email " & ex.Message)
End Try
End Select
End Sub
Secure.vb
Public Function SendViaGmail(subject As String, body As String, recipients As String)
Dim fromEmail As String = ConfigurationSettings.AppSettings("ContactEmail")
Dim smtpClient As Mail.SmtpClient = New Mail.SmtpClient("smtp.gmail.com") With {
.Port = 587,
.DeliveryMethod = SmtpDeliveryMethod.Network,
.Credentials = New NetworkCredential(fromEmail, "APP_PWD"),
.EnableSsl = True
}
Dim mailMessage As MailMessage = New MailMessage With {
.From = New Mail.MailAddress(fromEmail),
.Subject = subject,
.Body = body,
.IsBodyHtml = True
}
mailMessage.[To].Add(recipients)
If smtpClient IsNot Nothing Then
smtpClient.Send(mailMessage)
End If
End Function
I keep getting smtp server requires a secure connection or the client was not authenticated,...
also, I actually receive the email after which it just crashed the application. I have setup 2-factor authentication on my gmail account, generated app password and tried everything given on this post including re-ordering of credentials etc..nothing works!!!
The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.7.0 Authentication Required.
UPDATE
I have also just found a setting in web.config (I have replaced this password with app password) - result is the same
<system.net>
<mailSettings>
<smtp deliveryMethod="Network" from="vl#gmail.com">
<network defaultCredentials="false" enableSsl="true" host="smtp.gmail.com" port="587" userName="vl#gmail.com" password="****" />
</smtp>
</mailSettings>
</system.net>
First remove .DeliveryMethod = SmtpDeliveryMethod.Network, from your code, then make sure that the From Email address is the email address of the user that the apps password matches.
I have tested this and it works.
Private Function SendViaGmail(subject As String, body As String, recipients As String) As String
Dim smtpClient As Mail.SmtpClient = New Mail.SmtpClient("smtp.gmail.com") With {
.Port = 587,
.Credentials = New NetworkCredential(FromEmail, GoogleAppPassword),
.EnableSsl = True
}
Dim mailMessage As MailMessage = New MailMessage With {
.From = New Mail.MailAddress(FromEmail),
.Subject = subject,
.Body = body,
.IsBodyHtml = True
}
mailMessage.[To].Add(recipients)
If smtpClient IsNot Nothing Then
smtpClient.Send(mailMessage)
Return "Message sent."
End If
Return "Error: failed to create smtpClient."
End Function
I have a login form where I should save into cookie the authentication in case the user close and reopen the browser.
I tried with
<authentication mode="Forms" />
With this code, I save the cookie into login
Dim ticket As FormsAuthenticationTicket = New FormsAuthenticationTicket(Login1.UserName, True, 600)
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))
and here I read the cookie
Dim FormsAuthCookie As HttpCookie = Request.Cookies(FormsAuthentication.FormsCookieName)
Dim usr As String = FormsAuthCookie.Value
Dim Ticket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(FormsAuthCookie.Value)
usr = Ticket.Name & ""
The FormsAuthookie.value is always NULL, cannot understand what is wrong
I'm using WebForms with forms authentication. I'm connecting my application with an rest api token mechanism.
My problem is that I want to ask for my new access token using my refreshtoken.
I currently do this job in Global.asax Application_AuthenticateRequest method.
When I obtain the accesstoken i update the ticket but i am redirected to the login page.
I've try to use Response.Redirect and i am redirected to the original url but i lost the state of the page. It's as if I has reloaded the page. Somebody know what i'm doing wrong?
Below is my global.asax code in VB.net:
Thanks!
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
Try
If Request.Cookies(FormsAuthentication.FormsCookieName) IsNot Nothing Then
Dim authCookie As HttpCookie = (Request.Cookies(FormsAuthentication.FormsCookieName))
If Not String.IsNullOrEmpty(authCookie.Value) Then
Dim ticket = FormsAuthentication.Decrypt(authCookie.Value)
If ticket.Expired Then
'reauth cookie Is My refreshtoken
If Request.Cookies("reAuthCookie") IsNot Nothing Then
Dim funciones As New Funciones.Usuarios
Dim reAuthCookie As HttpCookie = Request.Cookies("reAuthCookie")
If Not String.IsNullOrEmpty(reAuthCookie.Value) Then
Dim refreshToken As String = reAuthCookie.Value(0).ToString
Dim login As Entidades.Login = funciones.renovarAccessToken(refreshToken)
Dim ticketExpiration As Date
ticketExpiration = Date.Now.AddSeconds(CDbl(login.Expires_in) - 20)
Dim userData As String = Newtonsoft.Json.JsonConvert.SerializeObject(login)
ticket = New FormsAuthenticationTicket(1, login.Username, DateTime.Now,
ticketExpiration, True,
userData, FormsAuthentication.FormsCookiePath)
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
HttpContext.Current.Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))
Response.Cookies.Remove("reAuthCookie")
reAuthCookie.Expires = Now.AddMonths(2)
reAuthCookie.Path = "/"
reAuthCookie.Value = login.Refresh_token
Response.Cookies.Add(reAuthCookie)
End If
End If
End If
End If
Else
If Request.Cookies("reAuthCookie") IsNot Nothing Then
Dim funciones As New Funciones.Usuarios
Dim reAuthCookie As HttpCookie = Request.Cookies("reAuthCookie")
If Not String.IsNullOrEmpty(reAuthCookie.Value) Then
Dim refreshToken As String = reAuthCookie.Value.ToString
Dim login As Entidades.Login = funciones.renovarAccessToken(refreshToken)
Dim ticketExpiration As Date
ticketExpiration = Date.Now.AddSeconds(CDbl(login.Expires_in) - 20)
Dim userData As String = Newtonsoft.Json.JsonConvert.SerializeObject(login)
Dim ticket = New FormsAuthenticationTicket(1, login.Username, DateTime.Now,
ticketExpiration, True,
userData, FormsAuthentication.FormsCookiePath)
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))
Response.Cookies.Remove("reAuthCookie")
reAuthCookie.Expires = Now.AddMonths(2)
reAuthCookie.Path = "/"
reAuthCookie.Value = login.Refresh_token
Response.Cookies.Add(reAuthCookie)
End If
End If
End If
Catch ex As Exception
Throw ex
End Try
End Sub
I got the following error
{"The specified domain either does not exist or could not be
contacted. "}
at the line
Dim adResults = adSearch.FindOne.Path
Can anyone suggest why it is ? Seeing the below code
Dim ID As FormsIdentity = DirectCast(User.Identity, FormsIdentity)
Dim ticket As FormsAuthenticationTicket = ID.Ticket
Dim adTicketID As String = ticket.Name
Dim adSearch As New DirectorySearcher
adSearch.Filter = ("(userPrincipalName=" & adTicketID & ")")
Dim adResults = adSearch.FindOne.Path
Dim adResultsDirectory As New DirectoryEntry(adResults)
Dim found As Boolean = False
For Each entry In adResultsDirectory.Properties("memberOf")
Response.Write(entry)
Response.Write("<br/>")
If entry = "CN=GroupName,CN=UserGroup,DC=my,DC=domain,DC=com" Then
found = True
End If
Next
If Not (found) Then
Response.Redirect("login.aspx")
End If
Where is your domain specified?
First parameter for DirectoryEntry should be your AD server, something like this: LDAP://adserver.
Here is the code that I am using for checking whether user is authenticated in AD:
Dim dsDirectoryEntry As New DirectoryEntry("LDAP://" & domain, userName, password)
Dim dsSearch As New DirectorySearcher(dsDirectoryEntry)
Dim dsResults As SearchResult = dsSearch.FindOne()
If dsResults IsNot Nothing Then
Return True
Else
Return False
End If
Domain I am reading from configuration, userName and password are from login form input.
Can anyone give me an example (or point me in the right direction) on how to store custom data in an ASP.NET Membership cookie?
I need to add some custom properties like UserID and URLSlug to the cookie and be able to retrieve the information in the same way one would retrieve the Username.
Edit:
I used Code Poet's example and came up with the following.
When I set a breakpoint at Dim SerializedUser As String = SerializeUser(userData) the value of userData is right. It has all the properties I expect it to have.
The problem I'm now running into is that when I get to Dim userdata As String = authTicket.UserData (breakpoint), the value is "". I'd love to figure out what I'm doing wrong.
Here's the code.
Imports System
Imports System.Web
Imports System.Web.Security
Namespace Utilities.Authentication
Public NotInheritable Class CustomAuthentication
Private Sub New()
End Sub
Public Shared Function CreateAuthCookie(ByVal userName As String, ByVal userData As Domain.Models.UserSessionModel, ByVal persistent As Boolean) As HttpCookie
Dim issued As DateTime = DateTime.Now
''# formsAuth does not expose timeout!? have to hack around the
''# spoiled parts and keep moving..
Dim fooCookie As HttpCookie = FormsAuthentication.GetAuthCookie("foo", True)
Dim formsTimeout As Integer = Convert.ToInt32((fooCookie.Expires - DateTime.Now).TotalMinutes)
Dim expiration As DateTime = DateTime.Now.AddMinutes(formsTimeout)
Dim cookiePath As String = FormsAuthentication.FormsCookiePath
Dim SerializedUser As String = SerializeUser(userData)
Dim ticket = New FormsAuthenticationTicket(0, userName, issued, expiration, True, SerializedUser, cookiePath)
Return CreateAuthCookie(ticket, expiration, persistent)
End Function
Public Shared Function CreateAuthCookie(ByVal ticket As FormsAuthenticationTicket, ByVal expiration As DateTime, ByVal persistent As Boolean) As HttpCookie
Dim creamyFilling As String = FormsAuthentication.Encrypt(ticket)
Dim cookie = New HttpCookie(FormsAuthentication.FormsCookieName, creamyFilling) With { _
.Domain = FormsAuthentication.CookieDomain, _
.Path = FormsAuthentication.FormsCookiePath _
}
If persistent Then
cookie.Expires = expiration
End If
Return cookie
End Function
Public Shared Function RetrieveAuthUser() As Domain.Models.UserSessionModel
Dim cookieName As String = FormsAuthentication.FormsCookieName
Dim authCookie As HttpCookie = HttpContext.Current.Request.Cookies(cookieName)
Dim authTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Dim userdata As String = authTicket.UserData
Dim usersessionmodel As New Domain.Models.UserSessionModel
usersessionmodel = DeserializeUser(userdata)
Return usersessionmodel
End Function
Private Shared Function SerializeUser(ByVal usersessionmodel As Domain.Models.UserSessionModel) As String
Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim mem As New IO.MemoryStream
bf.Serialize(mem, usersessionmodel)
Return Convert.ToBase64String(mem.ToArray())
End Function
Private Shared Function DeserializeUser(ByVal serializedusersessionmodel As String) As Domain.Models.UserSessionModel
Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim mem As New IO.MemoryStream(Convert.FromBase64String(serializedusersessionmodel))
Return DirectCast(bf.Deserialize(mem), Domain.Models.UserSessionModel)
End Function
End Class
End Namespace
Here's where I create all the magic. This method is in a "BaseController" class that inherits System.Web.Mvc.Controller
Protected Overrides Function CreateActionInvoker() As System.Web.Mvc.IActionInvoker
If User.Identity.IsAuthenticated Then ''# this if statement will eventually also check to make sure that the cookie actually exists.
Dim sessionuser As Domain.Models.UserSessionModel = New Domain.Models.UserSessionModel(OpenIdService.GetOpenId(HttpContext.User.Identity.Name).User)
HttpContext.Response.Cookies.Add(UrbanNow.Core.Utilities.Authentication.CustomAuthentication.CreateAuthCookie(HttpContext.User.Identity.Name, sessionuser, True))
End If
End Function
And here's how I try and retrieve the info.
Dim user As Domain.Models.UserSessionModel = CustomAuthentication.RetrieveAuthUser
First of all ASP.Net Membership providers don't write any cookies, Authentication cookies are written by FormsAuthentication.
And secondly, why interfere into authentication cookie at all. You can do this in a seperate cookie altogether. Here's how you can do that.
Writing the keys-values into cookie.
//create a cookie
HttpCookie myCookie = new HttpCookie("myCookie");
//Add key-values in the cookie
myCookie.Values.Add("UserId", "your-UserId");
myCookie.Values.Add("UrlSlug", "your-UrlSlug");
//set cookie expiry date-time, if required. Made it to last for next 12 hours.
myCookie.Expires = DateTime.Now.AddHours(12);
//Most important, write the cookie to client.
Response.Cookies.Add(myCookie);
Reading the keys-values from cookie.
//Assuming user comes back after several hours. several < 12.
//Read the cookie from Request.
HttpCookie myCookie = Request.Cookies["myCookie"];
if (myCookie == null)
{
//No cookie found or cookie expired.
//Handle the situation here, Redirect the user or simply return;
}
//ok - cookie is found.
//Gracefully check if the cookie has the key-value as expected.
if (!string.IsNullOrEmpty(myCookie.Values["UserId"]))
{
string UserId= myCookie.Values["UserId"].ToString();
//Yes UserId is found. Mission accomplished.
}
if (!string.IsNullOrEmpty(myCookie.Values["UrlSlug"]))
{
string UrlSlug = myCookie.Values["UrlSlug"].ToString();
//Yes key2 is found. Mission accomplished.
}
If at all you need to disturb the
authentication cookie, though not
advisable, This is how you may do it.
Writing the keys-values into cookie.
//create a cookie
HttpCookie myCookie = FormsAuthentication.GetAuthCookie("UserName", true);
//Add key-values in the cookie
myCookie.Values.Add("UserId", "your-UserId");
myCookie.Values.Add("UrlSlug", "your-UrlSlug");
//set cookie expiry date-time, if required. Made it to last for next 12 hours.
myCookie.Expires = DateTime.Now.AddHours(12);
//Most important, write the cookie to client.
Response.Cookies.Add(myCookie);
Reading the keys-values from cookie.
//Assuming user comes back after several hours. several < 12.
//Read the cookie from Request.
HttpCookie myCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (myCookie == null)
{
//No cookie found or cookie expired.
//Handle the situation here, Redirect the user or simply return;
}
//ok - cookie is found.
//Gracefully check if the cookie has the key-value as expected.
if (!string.IsNullOrEmpty(myCookie.Values["UserId"]))
{
string UserId= myCookie.Values["UserId"].ToString();
//Yes UserId is found. Mission accomplished.
}
if (!string.IsNullOrEmpty(myCookie.Values["UrlSlug"]))
{
string UrlSlug = myCookie.Values["UrlSlug"].ToString();
//Yes key2 is found. Mission accomplished.
}
Depending on the scenario, using a separate cookie might be a viable option, but in my opinion is sub optimal for several reasons including the simple fact that you have to manage multiple cookies as well as managing the lifetime of the cookie.
The most reliable strategy for incorporating custom information into your forms ticket is to leverage the userData field of the ticket. That is exactly what it is there for.
You can easily store custom data in the userData field of the ticket.
There are a few concerns to be aware of regarding the size of the data to be stored in the ticket that are explained here
And here is a small class that can help in the task of storing custom data in your forms ticket.