Setting cookie by calling webpage by Microsoft.XMLHTTP - asp.net

The situation:
I have 2 webpages with 2 domains (backoffice.myurl.com & www.myurl.com).
The backoffice is written in classic asp, the frontend in asp.net 3.5 (vb.net)
When I hit a button in the backoffice, I want to set a cookie on the frontend.
I do this by calling a page on the frontend via Microsoft.XMLHTTP
Dim GetConnection
Set GetConnection = CreateObject("Microsoft.XMLHTTP")
GetConnection.Open "POST", webserviceLocation, False
GetConnection.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
GetConnection.Send("data=" &value)
In the aspx code I read the posted value and put it in a cookie:
If Not Request.Cookies("mytest3") Is Nothing Then
Response.Cookies("mytest3").Expires = Now.AddYears(-23)
End If
Response.Cookies.Set(New HttpCookie("mytest3", Request.Form.Item("data")))
Response.Cookies("mytest3").Expires = DateTime.Now.AddYears(30)
On another page on the frontend I want to read that cookie:
Request.Cookies("mytest3").Value
but the Request.Cookies("mytest3") is 'nothing' there.
Apparently the cookie is not set. What am I doing wrong or how can I solve this?
The pages are called (my debugger hits the breakpoints)
Is this even possible at all?

When creating the cookie you need to explicitly set the domain:
' I do not remember if the value should be set to myurl.com or .myurl.com
' Please test
Response.Cookies("mytest3").Domain = "myurl.com"
This way the browser will send the cookie along each request to *.myurl.com

Darin has answered your question but you have another problem with this line:-
Response.Cookies("mytest3").Expires = Now.AddYears(-23)
The response Cookie collection is a differentc collection to that of the Request collection. The response cookies is always empty until code specifically adds a cookie to it. Hence the above line will fail.

Related

Using Datazen in an iframe with external authentication

I was able to successfully use external authentication with datazen via HTTPWEBREQUEST from code-behind with VB.NET, but I am unclear how to use this with an iframe or even a div. I'm thinking maybe the authorization cookies/token isn't following the iframe around? The datazen starts to load correctly, but then it redirects back to the login page as if it's not being authenticated. Not sure how to do that part, this stuff is pretty new to me and any help would be greatly appreciated!!
Web page errors include:
-OPTIONS url send # jquery.min.js:19b.extend.ajax # jquery.min.js:19Viewer.Controls.List.ajax # Scripts?page=list:35Viewer.Controls.List.load # Scripts?page=list:35h.callback # Scripts?page=list:35
VM11664 about:srcdoc:1
XMLHttpRequest cannot load http://datazenserver.com/viewer/jsondata. Response for preflight has invalid HTTP status code 405Scripts?page=list:35
load(): Failed to load JSON data. V…r.C…s.List {version: "2.0", description: "KPI & dashboard list loader & controller", url: "/viewer/jsondata", index: "/viewer/", json: null…}(anonymous function) # Scripts?page=list:35c # jquery.min.js:4p.fireWith # jquery.min.js:4k # jquery.min.js:19r # jquery.min.js:19
Scripts?page=list:35
GET http://datazenserver.com/viewer/login 403 (Forbidden)(anonymous function) # Scripts?page=list:35c # jquery.min.js:4p.fireWith # jquery.min.js:4k # jquery.min.js:19r # jquery.min.js:19
' ''//////////////////////////////////
Dim myHttpWebRequest As HttpWebRequest = CType(WebRequest.Create("http://datazenserver.com/"), HttpWebRequest)
myHttpWebRequest.CookieContainer = New System.Net.CookieContainer()
Dim authInfo As String = Session("Email")
myHttpWebRequest.AllowAutoRedirect = False
myHttpWebRequest.Headers.Add("headerkey", authInfo)
myHttpWebRequest.Headers.Add("Access-Control-Allow-Origin", "*")
myHttpWebRequest.Headers.Add("Access-Control-Allow-Headers", "Accept, Content-Type, Origin")
myHttpWebRequest.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
Dim myHttpWebResponse As HttpWebResponse = CType(myHttpWebRequest.GetResponse(), HttpWebResponse)
Response.AppendHeader("Access-Control-Allow-Origin", "*")
' Create a new 'HttpWebRequest' Object to the mentioned URL.
' Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable.
Dim streamResponse As Stream = myHttpWebResponse.GetResponseStream()
Dim streamRead As New StreamReader(streamResponse)
frame1.Page.Response.AppendHeader("Access-Control-Allow-Origin", "*")
frame1.Page.Response.AppendHeader("headerkey", authInfo)
frame1.Attributes("srcdoc") = "<head><base href='http://datazenserver.com/viewer/' target='_blank'/></head>" & streamRead.ReadToEnd()
You might have to do more of this client-side, and I don't know whether you'll be able to because of security concerns.
External authentication in Datazen looks something like this:
User-Agent | Proxy | Server
-------------------|----------------------|------------------------------------
1. /viewer/home --> 2. Append header --> 3. Check cookie (not present)
<-- 5. Forward <-- 4. Redirect to /viewer/login
6. /viewer/login --> 7. Append header --> 8. Append cookie
<-- 10. Forward <-- 9. Redirect to /viewer/home
11. /viewer/home --> 12. Append header --> 13. Check cookie (valid)
<-- 15. Forward <-- 14. Give content
16. .................. Whatever the user wanted ..........................
So even though you're working off a proxy with a header, you're still getting a cookie back that it uses.
Now, that's just context.
My guess, from your description of the symptoms, is that myHttpWebResponse should have a cookie set (DATAZEN_AUTH_TOKEN, I believe), but it's essentially getting thrown out--you aren't using it anywhere.
You would need to tell your browser client to append that cookie to any subsequent (iframe-based) requests to the domain of your Datazen server, but I don't believe that's possible due to security restrictions. I don't know a whole lot about CORS, though, so there might be a way to permit it.
I don't know whether there's any good way to do what you're looking to do here. At best, I can maybe think of a start to a hack that would work, but I can't even find a good way to make that work, and you really wouldn't want to go there.
Essentially, if you're looking to embed Datazen in an iframe, I would shy away from external authentication. I'd shy away from it regardless, but especially there.
But, if you're absolutely sure you need it over something like ADFS, you'll need some way to get that cookie into your iframe requests.
The only way I can think to make this work would be to put everything on the same domain:
www.example.com
datazen.example.com (which is probably your proxy)
You could then set a cookie from your response that stores some encrypted (and likely expiring) form of Session("Email"), and passes it back down in your html.
That makes your iframe relatively simple, because you can just tell it to load the viewer home. Something to the effect of:
<iframe src="//datazen.example.com/viewer/home"></iframe>
In your proxy, you'll detect the cookie set by your web server, decrypt the email token, ensure it isn't expired, then set a header on the subsequent request onto the Datazen server.
This could be simplified at a couple places, but this should hold as true as possible to your original implementation, as long as you can mess with DNS settings.
I suppose another version of this could involve passing a parameter to your proxy, and sharing some common encryption key. That would get you past having to be on the same domain.
So if you had something like:
var emailEncrypted = encrypt(Session("Email") + ":somesalt:" + DateTime.UtcNow.ToString("O"));
Then used whatever templating language you want to set your iframe up with:
<iframe src="//{{ customDomain }}/viewer/home?emailkey={{ emailEncrypted }}"></iframe>
Then your proxy detected that emailkey parameter, decrypted it, and checked for expiration, that could work.
Now you'd have a choice to make on how to handle this, because Datazen will give you a 302 to /viewer/login to get a cookie, and you need to make sure to pass the correct emailkey on through that.
What I would do, you could accept that emailkey parameter in your proxy, set a completely new cookie yourself, then watch for that cookie on subsequent requests.
Although at that point, it would probably be reasonable to switch your external authentication mode to just use cookies. That's probably a better version of this anyway, assuming this is the only place you use Datazen, and you'd be safe to change something so fundamental. That would substantially reduce your business logic.
But, you wouldn't have to. If you didn't want to change that, you could just check for the cookie, and turn it into a header.
You should do (1), but just for good measure, one thing I'm not sure on, is whether you can pass users directly to /viewer/login to get a cookie from Datazen. Normally you wouldn't, but it seems like you should be able to.
Assuming it works as expected, you could just swap that URL out for that. As far as I know (although I'd have to double-check this), the header is actually only necessary once, to set up the cookie. So if you did that, you should get the cookie, then not need the URL parameter anymore, so the forced navigation would be no concern.
You'll, of course, want to make sure you've got a good form of encryption there, and the expiration pattern is important. But you should be able to secure that if you do it right.
I ended up just grabbing the username and password fields and entering them in with javascript. But this piece helped me a ton. You have to make sure you set the
document.domain ='basedomain.com';
in javascript on both sites in order to access the iframe contents else you'll run into the cross-domain issues.

Cookies are not persistent

I am using ASP.net to store cookies. I save a cookie on a popup in the code behind (C#). If I request the cookie, before the popup closes, then the cookie is there, however closing the popup and going back into it and looking at the cookie in the Page_Load event shows no cookie. How do I get it to persist?
Following code in popup OK button
// Set the cookie.
this.Response.Cookies["UserData"].Secure = true;
this.Response.Cookies["UserData"]["UserId"] = iId.ToString();
this.Response.Cookies["UserData"]["UserEmail"] = strEmail;
this.Response.Cookies["UserData"].Expires = DateTime.Now.AddDays(1);
Following code placed in Page_Load event
// Get the cookie.
if (null != this.Request.Cookies["UserData"])
{
// Transmit the cookie information using SSL. Note, the cookie data is still in plain text on the user's computer.
this.Request.Cookies["UserData"].Secure = true;
// Extract: Email
String strEmail = this.Server.HtmlEncode(oPage.Request.Cookies["UserData"]["UserEmail"]);
}
Naturally, the very first time will show nothingness, however subsequent loads should show the cookie.
I had slightly better luck with using .Values["Subitem"] = "whatever", but that yielded the base to persist, but all subitems to disappear.
One possible reason: your page is HTTP, but you are setting cookies to be HTTPS-only. So browser simply does not send them back with HTTP request to your site.
Use Fiddler (or other HTTP debugger) to see if cookie is correctly send in the response (and next request).

Custom cache not caching querystring

I'm using custom cache on a page, leaving a timestamp to follow cached versions.
I have
<%# OutputCache Duration="1200" VaryByParam="None" VaryByCustom="myCache" Location="ServerAndClient" %>
on the page, and the code-behind of Global.asax has (simplified)
Public Overrides Function GetVaryByCustomString(context As HttpContext, arg As String) As String
If (arg = "myCache") Then
If context.Request.QueryString("Type").ToString() = "1" Then Return "cache-1"
If context.Request.QueryString("Type").ToString() = "2" Then Return "cache-2"
If context.Request.Cookies("Type").Value = "1" Then Return "cache-1"
Return "cache-2"
End If
cache-2 is default state for when no querystring request has been made and cookie doesn't say otherwise. The page saves the cookie with value of Type.
When I call the page with either ?Type=1 or ?Type=2 the page isn't saved to cache - each refresh the timestamp changes.
I found that if I call the page without the Type parameter cache is saved and then exists when I call the page with the parameter as well.
Is there an explanation? Moreover - in Global.asax I couldn't access Response object or file system to log what's happening. Is there a way?
UPDATE
e.g. calling <url>?Type=1, then <url> gives me cache for next calls of <url>?Type=1, but gives same cache for <url>?Type=2.
UPDATE
I now have Return "cache" & context.Request.QueryString("Type"). While sending a request without Type in the querystring gives a cached version of the page, sending Type does not cache, though the result (cache1 or cache2) is the same and page should get cached.
follow up
I've found sending Type=3, or anything besides 1 and 2 does cache the page as desired. Page_Load acts upon values 1 & 2 - could there be any connection?
Answers to this question seem to explain a lot. Workaround is a separate question.

Why is the Request.Form.AllKeys collection empty after a POST and Redirect?

I have an aspx page where I want to post values to a new page and then redirect to that new page. I don't get any errors and the redirection occurs but the AllKeys collection is always empty.
Here's an example of my code:
Try
With strPost
.Append("User=" & strUserName)
.Append("&Session=" + strValue)
End With
Dim objRequest As Net.HttpWebRequest = _
Net.WebRequest.Create("http://localhost:57918/testproject/test.aspx")
With objRequest
.Method = "POST"
.ContentType = "application/x-www-form-urlencoded"
.ContentLength = strPost.ToString().Length
End With
Dim objStream As IO.StreamWriter = _
New IO.StreamWriter(objRequest.GetRequestStream())
objStream.Write(strPost.ToString)
objStream.Close()
Catch ex As Exception
Debug.Print(ex.Message)
Exit Sub
End Try
Response.Redirect("http://localhost:57918/testproject/test.aspx")
I have seen a few articles similar to this problem but none of them have helped. What am I doing wrong?
Why don't you just have your main page post directly to this other page?
If the process is:
Page A rendered to client
Client posts back to Page A
Page A code behind generates a request to Page B
Page A code behind redirects user to Page B
Page B rendered to client
Then between steps 4 and 5 you will lose all the post params. That's just how it works.
However, you could do the following:
Page A rendered to client, with the form post action set to Page B
Clients enters information and clicks submit
Post values go to page B for handling.
Another path would be to have Page A perform a redirect and pass the values on the query string. For example, Response.Redirect("/PageB.aspx?param1=value&param2=value")
If I'm correct in understanding this, you are expecting the POST values to be available in /testproject/test.aspx after the redirect.
Unfortunately it won't work like that. When you perform the WebRequest it's a one-shot post. A new request is created your page executes and then the request ends and all data associated with that page will be discarded.
When you redirect at the end of the example given that is a completely new GET request to a new instance of test.aspx. Your previous request's POST data will never be available.
You can either:
Redirect to the page and pass the User and Session values in the querystring
Store User and Session in the Session collection then redirect
If strUserName and strValue originate from another postback your could use Server.Transfer to transfer control to test.aspx and keep the current request's Form and QueryString collections intact.
The code above will result in two requests being made to http://localhost:57918/testproject/test.aspx
The webserver itself POSTs the values to the url. When the page runs this time the AllKeys collection will contain the values you posted.
The client's web-browser will perform a GET request against the page. Nothing will be posted. This time the keys will be blank.
In order to pass the parameters to the other page you could encode the values in the redirect URL:
Dim url as String = "http://localhost:57918/testproject/test.aspx"
url = url + "?User=" + strUserName
url = url + "&Session=" + strValue
Response.Redirect(url)
The values would then be available using the request object (e.g. Request["User"]).
update
If you don't want to show the data to the user; then you've really only got two other options:
Move the processing that was being carried out by test.aspx to the page that was generating the original query.
Save the User and Session values the the session state.

How to perform an HTTP POST request in ASP?

How would I go about creating a HTTP request with POST data in classic asp (not .net) ?
You can try something like this:
Set ServerXmlHttp = Server.CreateObject("MSXML2.ServerXMLHTTP.6.0")
ServerXmlHttp.open "POST", "http://www.example.com/page.asp"
ServerXmlHttp.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
ServerXmlHttp.setRequestHeader "Content-Length", Len(PostData)
ServerXmlHttp.send PostData
If ServerXmlHttp.status = 200 Then
TextResponse = ServerXmlHttp.responseText
XMLResponse = ServerXmlHttp.responseXML
StreamResponse = ServerXmlHttp.responseStream
Else
' Handle missing response or other errors here
End If
Set ServerXmlHttp = Nothing
where PostData is the data you want to post (eg name-value pairs, XML document or whatever).
You'll need to set the correct version of MSXML2.ServerXMLHTTP to match what you have installed.
The open method takes five arguments, of which only the first two are required:
ServerXmlHttp.open Method, URL, Async, User, Password
Method: "GET" or "POST"
URL: the URL you want to post to
Async: the default is False (the call doesn't return immediately) - set to True for an asynchronous call
User: the user name required for authentication
Password: the password required for authentication
When the call returns, the status property holds the HTTP status. A value of 200 means OK - 404 means not found, 500 means server error etc. (See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes for other values.)
You can get the response as text (responseText property), XML (responseXML property) or a stream (responseStream property).
You must use one of the existing xmlhttp server objects directly or you could use a library which makes life a bit easier by abstracting the low level stuff away.
Check ajaxed implementation of fetching an URL
Disadvantage: You need to configure the library in order to make it work. Not sure if this is necessary for your project.

Resources