ASP.NET MVC 5 - Cookies being not created - c#

I am having my controller action that calls a private method that handles a cookie. The problem is that the cookie is not being created at all. I read multiple posts on SO but I haven't found an answer as I think that my handling cookies in this code is correct.
Is there any web.config setting that I need to check in regards to cookies? I also tried different browsers.
I debugged the code and I can see that the cookie is actually set in the code but as soon as I load the page and have a look at cookies the cookie is not there.
private ABHomeModel HandleWhiteBoxCookie(ABHomeModel model)
{
var whiteBox = _whiteBoxService.GetActiveWhiteBox();
if (model.WhiteBox != null)
{
const string cookieName = "whiteBox";
var whiteBoxCookie = HttpContext.Request.Cookies.Get(cookieName);
if (whiteBoxCookie != null)
{
var displayedTimes = Convert.ToInt32(whiteBoxCookie.Value);
if (displayedTimes < 2)
{
displayedTimes++;
var cookie = new HttpCookie(cookieName, displayedTimes.ToString())
{
Expires = new DateTime().AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Set(cookie);
ViewBag.IsWhiteBoxActive = true;
}
else
{
ViewBag.IsWhiteBoxActive = false;
}
}
else
{
var cookie = new HttpCookie(cookieName, "1")
{
HttpOnly = true,
Domain = Request.Url.Host,
Expires = DateTime.Now.AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Add(cookie);
ViewBag.IsWhiteBoxActive = true;
}
model.WhiteBox = whiteBox;
}
return model;
}

My colleague found the issue. It is in regard to setting the domain. As soon as we removed this line:
Domain = Request.Url.Host,
The cookies started working and are now being created.
The full updated code of the method:
private ABHomeModel HandleWhiteBoxCookie(ABHomeModel model)
{
var whiteBox = _whiteBoxService.GetActiveWhiteBox();
if (whiteBox != null)
{
const string cookieName = "whiteBox";
var whiteBoxCookie = HttpContext.Request.Cookies.Get(cookieName);
if (whiteBoxCookie != null)
{
var displayedTimes = Convert.ToInt32(whiteBoxCookie.Value);
if (displayedTimes < 2)
{
displayedTimes++;
var cookie = new HttpCookie(cookieName, displayedTimes.ToString())
{
HttpOnly = true,
Secure = false
};
HttpContext.Response.Cookies.Set(cookie);
ViewBag.IsWhiteBoxActive = true;
}
else
{
ViewBag.IsWhiteBoxActive = false;
}
}
else
{
var cookie = new HttpCookie(cookieName, "1")
{
HttpOnly = true,
Expires = DateTime.Now.AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Add(cookie);
ViewBag.IsWhiteBoxActive = true;
}
model.WhiteBox = whiteBox;
}
return model;
}

Related

CodeVerification Cookie disappears in Edge and Chrome

.NET framework 4.6.1 website using OIDC authentication (Microsoft.Owin.Security.OpenIdConnect 4.1.0)
As part of the authentication I include "code_challenge". The following code is based on this example.
RedirectToIdentityProvider = n =>
{
//ProcessCertificateValidation();
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
if (AppSettingsKey.AuthCodeChallangeEnabled.Enabled)
{
// generate code verifier and code challenge
var codeVerifier = CryptoRandom.CreateUniqueId(32);
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64UrlEncoder.Encode(challengeBytes);
}
// set code_challenge parameter on authorization request
n.ProtocolMessage.Parameters.Add("code_challenge", codeChallenge);
n.ProtocolMessage.Parameters.Add("code_challenge_method", "S256");
// remember code verifier in cookie (adapted from OWIN nonce cookie)
RememberCodeVerifier(n, codeVerifier);
}
if (AppSettingsKey.MultiFactorAuthEnabled.Enabled)
n.ProtocolMessage.AcrValues = authCfg.AcrValues ?? n.ProtocolMessage.AcrValues;
}
logger.Debug("OIDC-Notification: RedirectToIdentityProvider Called");
//if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
logger.Debug(" RequestType=" + OpenIdConnectRequestType.Logout);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
logger.Debug(" IdTokenHint got from n.OwinContext.Authentication.User");
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
logger.Debug(" IdTokenHint=" + n?.ProtocolMessage?.IdTokenHint);
}
return Task.CompletedTask;
},
I confirmed that the "codeVerifierCookie" is sent.
AuthorizationCodeReceived = async n =>
{
logger.Debug("OIDC-Notification: AuthorizationCodeReceived Called");
logger.Debug(" Code=" + n.Code);
logger.Debug(" AuthenticationType=" + n.Options.AuthenticationType);
if (authCfg.DiscoverEndpoints)
{
var disco = await n.Options.ConfigurationManager.GetConfigurationAsync(n.OwinContext.Request.CallCancelled);
authCfg.TokenEndpoint = disco.TokenEndpoint;
authCfg.UserinfoEndpoint = disco.UserInfoEndpoint;
authCfg.EndsessionEndpoint = disco.EndSessionEndpoint;
//authCfg.RevocationEndpoint = disco.RevocationEndpoint;
authCfg.WebKeySetEndpoint = disco.JwksUri;
}
if (AppSettingsKey.AuthCodeChallangeEnabled.Enabled) {
var codeVerifier = RetrieveCodeVerifier(n);
// attach code_verifier
n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
}
var requestMessage = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, authCfg.TokenEndpoint);
requestMessage.Content = new System.Net.Http.FormUrlEncodedContent(n.TokenEndpointRequest.Parameters);
var responseMessage = await n.Options.Backchannel.SendAsync(requestMessage);
responseMessage.EnsureSuccessStatusCode();
var responseContent = await responseMessage.Content.ReadAsStringAsync();
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage message = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage(responseContent);
logger.Debug(" IdToken=" + message.IdToken);
logger.Debug(" AccessToken=" + message.AccessToken);
n.HandleCodeRedemption(message);
},
The issue is that when trying to retrieve the "codeVerifierCookie" it does not exist, when trying to login in Edge or Chrome (on Firefox its there).
Here are the methods used to send, retrieve and get the code verification. CookieManager is configured to be Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager().
private void RememberCodeVerifier(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> n, string codeVerifier)
{
var properties = new AuthenticationProperties();
properties.Dictionary.Add("cv", codeVerifier);
n.Options.CookieManager.AppendResponseCookie(
n.OwinContext,
GetCodeVerifierKey(n.ProtocolMessage.State),
Convert.ToBase64String(Encoding.UTF8.GetBytes(n.Options.StateDataFormat.Protect(properties))),
new CookieOptions
{
SameSite = SameSiteMode.None,
HttpOnly = true,
Secure = n.Request.IsSecure,
Expires = DateTime.UtcNow + n.Options.ProtocolValidator.NonceLifetime
});
}
private string RetrieveCodeVerifier(AuthorizationCodeReceivedNotification n)
{
string key = GetCodeVerifierKey(n.ProtocolMessage.State);
string codeVerifierCookie = n.Options.CookieManager.GetRequestCookie(n.OwinContext, key);
if (codeVerifierCookie != null)
{
var cookieOptions = new CookieOptions
{
SameSite = SameSiteMode.None,
HttpOnly = true,
Secure = n.Request.IsSecure
};
n.Options.CookieManager.DeleteCookie(n.OwinContext, key, cookieOptions);
}
var cookieProperties = n.Options.StateDataFormat.Unprotect(Encoding.UTF8.GetString(Convert.FromBase64String(codeVerifierCookie)));
cookieProperties.Dictionary.TryGetValue("cv", out var codeVerifier);
return codeVerifier;
}
private string GetCodeVerifierKey(string state)
{
using (var hash = SHA256.Create())
{
return OpenIdConnectAuthenticationDefaults.CookiePrefix + "cv." + Convert.ToBase64String(hash.ComputeHash(Encoding.UTF8.GetBytes(state)));
}
}
Why does the "codeVerifierCookie" missing when I try to login from Edge or Chrome? Could it be some default setting or maybe my setup is missing something? Why does it work on Firefox?
Thank you for reading my post and I welcome every and any input on the issue.
Have you used HTTPS when testing your app? Cookies that assert SameSite=None must also be marked as Secure. I think the issue might be related with Same-Site cookies setting.
I find a thread which has the same issue as yours, you can refer to it. Besides, there's a detailed article about working with SameSite cookies in ASP.NET, you can also check it for more information.

Cookies not showing up in global.asax

I'm using cookies to pass user information for authentication as seen in this question. Everything was working fine, until our team upgraded our computers and are now on windows 10. Now my cookie is not found in global.asax.cs's Application_PostAuthenticateRequest.
Here's my code trying to send the cookie:
private void AddUserDataToCookies(User user)
{
var serializeModel = new WebUserSerializeModel
{
FirstName = user.Person.FirstName,
MiddleName = user.Person.MiddleName,
LastName = user.Person.LastName,
CredentialNumber = user.CredentialNumber,
Roles = user.Roles.Select(role => role.Name).ToList(),
Permissions = user.Permissions.Select(perm => perm.PrimaryKey).ToList()
};
var userData = new JavaScriptSerializer().Serialize(serializeModel);
var authTicket = new FormsAuthenticationTicket(1, user.CredentialNumber, DateTime.Now, DateTime.Now.AddMinutes(15), false, userData);
var encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
Secure = true,
HttpOnly = true
};
Response.Cookies.Add(cookie);
var requestCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
}
The cookie shows up in request cookie. But when I try in my global.asax, it doesn't. My global asax code is below.
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
try
{
var authTicket = FormsAuthentication.Decrypt(cookie.Value);
if (authTicket != null)
{
var serializer = new JavaScriptSerializer();
var serializeModel = serializer.Deserialize<WebUserSerializeModel>(authTicket.UserData);
var user = new WebUser(serializeModel.FirstName, serializeModel.LastName)
{
MiddleName = serializeModel.MiddleName,
CredentialNumber = serializeModel.CredentialNumber,
Roles = serializeModel.Roles,
Permissions = serializeModel.Permissions
};
HttpContext.Current.User = user;
}
}
catch (CryptographicException ex)
{
Logger.Error("Error while decrypting cookie post authentication.", ex);
FormsAuthentication.SignOut();
HttpContext.Current.User = null;
}
}
}
Does anyone have any ideas why changing to Windows 10 may have causes this issue? I'm somewhat new to ASP.NET and web development in general.
EDIT - by removing Secure = true when creating my cookie I was able to get it to work. I'm investigating why this is the case before I add an answer and I welcome any insights.
As mentioned in my edit, the problem was that Secure was set to true when creating my cookie but I did not have SSL enabled when running locally, unlike, I guess, on my old workstation. The code in my controller currently looks like:
private void AddUserDataToCookies(User user)
{
var serializeModel = new WebUserSerializeModel
{
FirstName = user.FirstName,
MiddleName = user.MiddleName,
LastName = user.LastName,
CredentialNumber = user.CredentialNumber,
Roles = user.Roles,
Permissions = user.Permissions
};
var userData = new JavaScriptSerializer().Serialize(serializeModel);
var authTicket = new FormsAuthenticationTicket(1, user.CredentialNumber, DateTime.Now, DateTime.Now.AddMinutes(FormsAuthentication.Timeout.Minutes), false, userData, FormsAuthentication.FormsCookiePath);
var encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
HttpOnly = true,
Secure = true
};
if (Request.IsLocal)
{
cookie.Secure = false;
}
Response.Cookies.Add(cookie);
}
Leaving Request.IsLocal is a bit ugly but it's good enough for now until we decide if we want to implement SSL for everyone locally.
At least it was an easy fix.

Request.Cookies["cookieName"] is null in web service method

This is how I'am saving cookie in my code which is a web service method. I tried to change the cookie expire time but still it is not working for me. Is there any issue with Context.Response to write cookie or Context.Request to read it??
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = false)]
public void SaveInDraft(FormRootObject _forms)
{
HttpCookie formCookie = new HttpCookie("UserData");
formCookie.Values.Add("formid", _forms.formid);
foreach (var item in _forms.fielddata)
{
formCookie.Values.Add(item.id, item.value);
}
formCookie.Expires = DateTime.Now.AddYears(1);
Context.Response.Cookies.Add(formCookie);
}
for retrieving this cookie on next page
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = false)]
public FormRootObject getDataFromCookie()
{
FormRootObject form = new FormRootObject();
List<formFielddata> lstFormFieldData = new List<formFielddata>();
var getCookie=Context.Request.Cookies["UserData"];
if (getCookie != null)
{
if (getCookie.Values.Count > 0)
{
foreach (var val in getCookie.Values)
{
formFielddata document = new formFielddata();
if (val.ToString() != "formid")
{
document.id = val.ToString();
document.value = getCookie[val.ToString()];
lstFormFieldData.Add(document);
}
else {
form.formid = getCookie[val.ToString()];
}
}
form.fielddata = lstFormFieldData;
}
}
return form;
}
but my object getCookie is always null
Rajesh, forms are only present on web applications. If you're using plain web api to create your service, no form is going to be presented to your service client.
To create a cookie:
var resp = new HttpResponseMessage();
var cookie = new CookieHeaderValue("session-id", "12345");
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return resp;
To consume cookie:
CookieHeaderValue cookie = Request.Headers.GetCookies("session-id")
I got this by storing cookies as
HttpContext.Current.Response.Cookies.Add(formCookie);
And reading this as
var getCookie=HttpContext.Current.Request.Cookies["UserData"];

Federated Authentication in SharePoint 2013 ( with ADFS) - Operation Timeout when getting FedAuth cookie

We are encountering a bit of an odd issue on our integration code with SharePoint.
We've managed to get the integration working using SAML tokens, see code below.
The problem is that sometimes it times out when getting the FedAuth cookie or making the request to SharePoint.
The timeouts might point to load or network issues but they are reproducible.
The first unit test on the unit test suite gets the cookie without any problem every single time, but it fails on the second.
To make this more mystifying, it seems to work fine whenever I have fiddler capturing traffic.
Even more annoying is the fact that if i run the second unit test and not the first, the test works fine.
It's as if SharePoint refuses to issue another cookie for the same client until a certain amount of time has passed, unless going through Fiddler.
I should add, that I have tried both storing the cookie for subsequent requests and getting it again for each request, it seems to make no difference.
Any help would be appreciated.
public static ClientContext CreateClientContext(SharePointClaimsConnection connection)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
logger.DebugFormat("Create Client Context for connection: {0}", connection);
ClientContext context = new ClientContext(connection.WebUrl);
try
{
if (connection.SecurityTokenServiceEndPoint != null && !String.IsNullOrEmpty(connection.Realm))
{
CookieInfo token = GetToken(connection);
if (token == null)
{
lock (syncRoot)
{
token = GetToken(connection);
if (token == null)
{
token = GetFedAuthCookie(connection);
if (token != null)
{
tokens[connection] = token;
}
}
}
}
if (token != null)
{
context.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.WebRequest.KeepAlive = true;
for (int i = 0; i < e.WebRequestExecutor.WebRequest.Headers.Count; i++)
{
string key = e.WebRequestExecutor.WebRequest.Headers.GetKey(i);
string value = e.WebRequestExecutor.WebRequest.Headers.Get(i);
logger.DebugFormat("Key: {0}, Value: {1}", key, value);
}
CookieContainer container = new CookieContainer();
foreach (var cookie in token.Cookies)
{
logger.Debug("Adding cookie: " + cookie.Name);
logger.Debug("Domain: " + connection.WebUrl.Host);
logger.Debug("Expires: " + cookie.Expires.ToString());
Cookie newCookie = new Cookie(cookie.Name, cookie.Value);
newCookie.Expires = DateTime.MaxValue;
newCookie.Path = "/";
newCookie.Secure = true;
newCookie.HttpOnly = true;
newCookie.Domain = connection.WebUrl.Host;
container.Add(newCookie);
}
e.WebRequestExecutor.WebRequest.CookieContainer = container;
};
}
}
return context;
}
catch (Exception ex)
{
if (context != null)
{
context.Dispose();
}
throw;
}
}
private static CookieInfo GetFedAuthCookie(SharePointClaimsConnection connection)
{
string result = GetSamlToken(connection);
//Take this token and pass it to SharePoint STS
string stringData = String.Format(CultureInfo.InvariantCulture, "wa=wsignin1.0&wctx={0}&wresult={1}",
HttpUtility.UrlEncode(new Uri(connection.WebUrl, "/_layouts/Authenticate.aspx?Source=%2F").ToString()),
HttpUtility.UrlEncode(result));
HttpWebRequest sharepointRequest = HttpWebRequest.Create(new Uri(connection.WebUrl, "/_trust/")) as HttpWebRequest;
sharepointRequest.Method = "POST";
sharepointRequest.ContentType = "application/x-www-form-urlencoded";
sharepointRequest.CookieContainer = new CookieContainer();
sharepointRequest.AllowAutoRedirect = false; // This is important
using (Stream newStream = sharepointRequest.GetRequestStream())
{
byte[] data = Encoding.UTF8.GetBytes(stringData);
newStream.Write(data, 0, data.Length);
}
HttpWebResponse webResponse = sharepointRequest.GetResponse() as HttpWebResponse;
if (webResponse.Cookies["FedAuth"] == null)
{
return null;
}
return new CookieInfo()
{
Cookies = webResponse.Cookies.Cast<Cookie>().ToList(),
};
}
private static string GetSamlToken(SharePointClaimsConnection connection)
{
string result;
Uri STSService = new Uri(connection.SecurityTokenServiceEndPoint, WindowsTransport);
using (WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(
new WindowsWSTrustBinding(SecurityMode.Transport),
new EndpointAddress(STSService)))
{
trustChannelFactory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
trustChannelFactory.Credentials.SupportInteractive = false;
trustChannelFactory.ConfigureChannelFactory<IWSTrustChannelContract>();
//Request Security Token
RequestSecurityToken rst = new RequestSecurityToken();
rst.KeyType = KeyTypes.Bearer;
rst.RequestType = RequestTypes.Issue;
rst.AppliesTo = new EndpointAddress(connection.Realm);
var channel = trustChannelFactory.CreateChannel();
WSTrust13RequestSerializer trustSerializer = new WSTrust13RequestSerializer();
using (Message message = Message.CreateMessage(
MessageVersion.Default, WSTrust13Constants.Actions.Issue,
new RequestBodyWriter(trustSerializer, rst)))
{
Message response2 = channel.EndIssue(channel.BeginIssue(message, null, null));
XmlDictionaryReader reader = response2.GetReaderAtBodyContents();
result = reader.ReadOuterXml();
}
}
return result;
}

Asp.Net webresource.axd open redirection security flaw?

Running WebResource.axd through Burpe Suiteā€™s active scan gave indication of a possible open redirection flaw in the function WebForm_DoCallback. This function does a post based upon a generated url. The generated url is based upon the form action url or document.location.pathname
I have not figured out where my site is using this method, nor have I found a way to abuse it.
How can anyone abuse this?
This is the relevant function. The comments include the potential problem.
var xmlRequest,e;
try {
xmlRequest = new XMLHttpRequest();
}
catch(e) {
try {
xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e) {
}
}
var setRequestHeaderMethodExists = true;
try {
setRequestHeaderMethodExists = (xmlRequest && xmlRequest.setRequestHeader);
}
catch(e) {}
var callback = new Object();
callback.eventCallback = eventCallback;
callback.context = context;
callback.errorCallback = errorCallback;
callback.async = useAsync;
var callbackIndex = WebForm_FillFirstAvailableSlot(__pendingCallbacks, callback);
if (!useAsync) {
if (__synchronousCallBackIndex != -1) {
__pendingCallbacks[__synchronousCallBackIndex] = null;
}
__synchronousCallBackIndex = callbackIndex;
}
if (setRequestHeaderMethodExists) {
xmlRequest.onreadystatechange = WebForm_CallbackComplete;
callback.xmlRequest = xmlRequest;
// action is set to the url of the form or current path.
//fragmentIndex is set to the index of # in the url
var action = theForm.action || document.location.pathname, fragmentIndex = action.indexOf('#');
if (fragmentIndex !== -1) {
//action is set to index of start to the position of fragmentIndex
action = action.substr(0, fragmentIndex);
}
//From somewhere else in the script.
//var __nonMSDOMBrowser = (window.navigator.appName.toLowerCase().indexOf('explorer') == -1)
if (!__nonMSDOMBrowser) {
var queryIndex = action.indexOf('?');
if (queryIndex !== -1) {
var path = action.substr(0, queryIndex);
if (path.indexOf("%") === -1) {
action = encodeURI(path) + action.substr(queryIndex);
}
}
else if (action.indexOf("%") === -1) {
action = encodeURI(action);
}
}
//post to the generated url.
xmlRequest.open("POST", action, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
xmlRequest.send(postData);
return;
}

Categories