Authenticating using OATH2 on Azure app registration works fine on IIS express, but fails in IIS - asp.net

I have an application that I am trying to add a layer of SSO, authenticating against Azure AD app registration. The code works fine running in IIS Express, but fails with a 400 Bad Request whenever I attempt to run it from any IIS environment, including localhost.
I have a working function that requests an authorisation code, which works with no issues and the code is returned in the querystring. The issue happens in the next stage, where I use that code to retrieve the user's Sub ID from Microsoft. This is the code I have:
'Get the base azure values
Dim AzureClient As String = ClientInfo
Dim AzureSecret As String = AzureSecret
Dim AuthRedirectUri As String = "The address of the page"
Dim TenantID As String = AzureTenantID
Dim codeVerifier = Verifier string passed in earlier function
Dim httpWReq As HttpWebRequest = DirectCast(WebRequest.Create("https://login.microsoftonline.com/" & TenantID & "/oauth2/v2.0/token"), HttpWebRequest)
httpWReq.Method = "POST"
httpWReq.Host = "login.microsoftonline.com"
httpWReq.ContentType = "application/x-www-form-urlencoded"
Dim postData As String = "client_id=" & AzureClient
postData += "&scope=openid&20email&20profile"
postData += "&code=" & Code
postData += "&redirect_uri=" & AuthRedirectUri
postData += "&grant_type=authorization_code"
postData += "&code_verifier=" & codeVerifier
postData += "&client_secret=" & AzureSecret
Dim encoding As New ASCIIEncoding()
Dim byteArray As Byte() = encoding.GetBytes(postData)
' Set the ContentLength property of the WebRequest.
httpWReq.ContentLength = byteArray.Length
Using streamWriter = New StreamWriter(httpWReq.GetRequestStream())
streamWriter.Write(postData)
End Using
' Get the response.
Dim response As WebResponse = httpWReq.GetResponse() <--- This is where the 400 Bad Request is thrown in IIS, but not IIS Express
Dim responseString As String = New StreamReader(response.GetResponseStream()).ReadToEnd()
Dim o As OAuth2AccessTokenReponse = DirectCast(JsonConvert.DeserializeObject(responseString, GetType(OAuth2AccessTokenReponse)), OAuth2AccessTokenReponse)
Dim IDToken As String = o.id_token
Dim stream = IDToken
Dim handler = New JwtSecurityTokenHandler()
Dim jsonToken = handler.ReadToken(stream)
Dim tokenS = TryCast(jsonToken, JwtSecurityToken)
Dim subID = tokenS.Claims.First(Function(claim) claim.Type = "sub").Value
Return subID
I've compared the calls coming from both environments and they are identical. I have both localhost addresses (localhost IIS address and IIS Express port) so the only differences are the port numbers used in the redirect URI field.
Does anyone have any idea what could be throwing this?

Problem sorted. After much, much digging about I found that IIS was still supporting TLS1.0 - removed that and everything works fine now.

Related

SagePay RedirectURL failure

Using server integration and .net, I post the original request to SagePay and get the NextURL fine, and so go to the payment pages... step through them OK, but then I get the error:
Server error 5006: Unable to redirect to Vendor's web site. The Vendor failed to provide a RedirectionURL.
HTTP error 500: The request was unsuccessful due to an unexpected condition encountered by the server.
But I am sending a RedirectionURL (though the docs call is RedirectURL, which is somewhat confusing - anyway, I've tried using both. This si what I'm sending back from my NotificatioURL - what's wrong?
Dim sb As New StringBuilder
sb.Append("Status=OK")
sb.Append("&StatusDetail=Fine")
sb.Append("&RedirectURL=https://mydomain.co.uk/sagepay.aspx")
Dim urlTEST As String = "https://test.sagepay.com/gateway/service/vspserver-register.vsp"
Dim urlLIVE As String = "https://live.sagepay.com/gateway/service/vspserver-register.vsp"
Try
Dim data As Byte() = Encoding.UTF8.GetBytes(sb.ToString)
Dim request As WebRequest = WebRequest.Create(urlTEST)
request.Method = "POST"
request.ContentType = "application/x-www-form-urlencoded"
request.ContentLength = data.Length
ServicePointManager.ServerCertificateValidationCallback = AddressOf ValidateRemoteSSLCertificate
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
Dim stream = request.GetRequestStream()
stream.Write(data, 0, data.Length)
stream.Close()
Dim response As WebResponse = request.GetResponse()
response.Close()
Catch ex As Exception
'log error
End Try
Update: I gather this POST (unlike the initial one with the basket info) requires that the data be sent plain text key-value pairs separated by CrLf's, so I amended this to
Dim sb As New StringBuilder
sb.AppendLine("Status=OK")
sb.AppendLine("StatusDetail=Fine")
sb.Append("RedirectURL=https://mydomain.co.uk/sagepay.aspx")
but it still fails with the same errors. I also tried using, instead of the WebRequst class, the simpler:
Dim client As WebClient = New WebClient()
Dim reply As String = client.UploadString(urlTEST, sb.ToString)
But still no joy. Also tried changing the request.ContentType to "text/plain", but nope.
Come on, someone, please - this si basic to the their operations, someone must have done it....
Blimey - it never pays to be clever. This script doesn't require WebRequest or anything, just write to the simple Response object. Sigh.. as Linus said ....

Problems With Paypal Express Checkout Integration (WEBREQUEST)

So I was struggling with making head or tail out of the PayPal documentation and always felt that something was not right with my Webrequest.
So I stripped all the code back to basic and simply submitted the request via HTTP and the PLUS side is that I now get a response back from the PayPal sandbox server where ACK=Success and TOKEN=Valid-token-value-here there are some other variables returned too, such as CORRELATIONID and TIMESTAMP.
And hence so I tried some of the webrequest samples and I simply get a blank screen instead of being redirected to Paypal for the (sandbox) customer to complete payment.
So if anyone can post their WebRequest method that would be great.
Here is the code I used for my webrequest, I'm sure its wrong but cannot pinpoint where it is going wrong.
Also, when I run the code on my localhost during debugging, everything works fine and the call is completed with SUCCESS and a TOKEN is received.
When I run it live, I recieve Error Number 5 in the Error exception and also the text `Remote host failed to connect' in the STATUS DESCRIPTION.
THIS IS THE UPDATED CODE
Function MakeWebRequest(ByVal pUseSandbox As Boolean, ByVal pRequestMethod As String, ByVal pReturnUrl As String, ByVal pCancelUrl As String, ByRef pRtnStatus As String, ByRef pRtnStatusId As HttpStatusCode, ByRef pRtnResponseString As String) As Boolean
'
Dim _sxHost As String = Nothing
Dim _sxEndpoint As String = Nothing
Dim _sxNameValCol As System.Collections.Specialized.NameValueCollection = Nothing
Dim _sxResponseCol As System.Collections.Specialized.NameValueCollection = Nothing
Dim _sxCounta As Integer = Nothing
Dim _sxParamsString As String = Nothing
'
'-> Init
_sxParamsString = ""
MakeWebRequest = False
_sxNameValCol = New System.Collections.Specialized.NameValueCollection()
_sxResponseCol = New System.Collections.Specialized.NameValueCollection()
If pUseSandbox Then
_sxHost = "http://www.sandbox.paypal.com"
_sxEndpoint = "https://api-3t.sandbox.paypal.com/nvp"
Else
_sxHost = "http://www.paypal.com"
_sxEndpoint = "https://api-3t.paypal.com/nvp"
End If
'-> Create Request
Try
'-> Key/Value Collection Params
_sxNameValCol.Add("METHOD", "SetExpressCheckout")
_sxNameValCol.Add("USER", _UserName)
_sxNameValCol.Add("PWD", _Password)
_sxNameValCol.Add("SIGNATURE", _Signature)
_sxNameValCol.Add("PAYMENTREQUEST_0_AMT", Format(_Basket.BasketTotalIncDelivery / 100, "0.00"))
_sxNameValCol.Add("PAYMENTREQUEST_0_PAYMENTACTION", "Sale")
_sxNameValCol.Add("PAYMENTREQUEST_0_CURRENCYCODE", "GBP")
_sxNameValCol.Add("RETURNURL", pReturnUrl)
_sxNameValCol.Add("CANCELURL", pCancelUrl)
_sxNameValCol.Add("REQCONFIRMSHIPPING", "0")
_sxNameValCol.Add("NOSHIPPING", "2")
_sxNameValCol.Add("LOCALECODE", "EN")
_sxNameValCol.Add("BUTTONSOURCE", "PP-ECWizard")
_sxNameValCol.Add("VERSION", "93.0")
'-> UrlEncode
For _sxCounta = 0 To _sxNameValCol.Count - 1
If _sxCounta = 0 Then
_sxParamsString = _sxParamsString & _sxNameValCol.Keys(_sxCounta) & "=" & HttpUtility.UrlEncode(_sxNameValCol(_sxCounta))
Else
_sxParamsString = _sxParamsString & "&" & _sxNameValCol.Keys(_sxCounta) & "=" & HttpUtility.UrlEncode(_sxNameValCol(_sxCounta))
End If
Next
'-> Credentials (not used)
'_sxRequest.Credentials = CredentialCache.DefaultCredentials
Try
Dim _sxRequest As WebRequest = DirectCast(System.Net.WebRequest.Create(_sxEndpoint), System.Net.HttpWebRequest)
'-> Convert request to byte-array
Dim _sxByteArray As Byte() = Encoding.UTF8.GetBytes(_sxParamsString)
_sxRequest.Method = "POST" 'Our method is post, otherwise the buffer (_sxParamsString) would be useless
_sxRequest.ContentType = "application/x-www-form-urlencoded" 'We use form contentType, for the postvars
_sxRequest.ContentLength = _sxByteArray.Length 'The length of the buffer (postvars) is used as contentlength
Dim _sxPostDataStream As System.IO.Stream = _sxRequest.GetRequestStream() 'We open a stream for writing the postvars
_sxPostDataStream.Write(_sxByteArray, 0, _sxByteArray.Length) 'Now we write, and afterwards, we close
_sxPostDataStream.Close() 'Closing is always important!
'-> Create Response
Dim _sxResponse As HttpWebResponse = DirectCast(_sxRequest.GetResponse(), HttpWebResponse)
'-> Get Response Status
pRtnStatus = _sxResponse.StatusDescription
pRtnStatusId = _sxResponse.StatusCode
'-> Reponse Stream
Dim _sxResponseStream As Stream = _sxResponse.GetResponseStream() 'Open a stream to the response
'-> Response Stream Reader
Dim _sxStreamReader As New StreamReader(_sxResponseStream) 'Open as reader for the stream
pRtnResponseString = _sxStreamReader.ReadToEnd() 'Read the response string
MakeWebRequest = True
'-> Tidy up
_sxStreamReader.Close()
_sxResponseStream.Close()
_sxResponse.Close()
_sxByteArray = Nothing
_sxPostDataStream = Nothing
_sxRequest = Nothing
_sxResponse = Nothing
_sxResponseStream = Nothing
_sxStreamReader = Nothing
Catch ex As Exception
pRtnStatusId = Err.Number
pRtnStatus = "response(" & ex.Message & ")"
Decode(pRtnResponseString, _sxResponseCol)
pRtnResponseString = Stringify(_sxResponseCol)
End Try
Catch ex As Exception
pRtnStatusId = Err.Number
pRtnStatus = "request(" & ex.Message & ")"
Decode(pRtnResponseString, _sxResponseCol)
pRtnResponseString = Stringify(_sxResponseCol)
End Try
'-> Tidy Up
_sxHost = Nothing
_sxEndpoint = Nothing
_sxNameValCol = Nothing
_sxResponseCol = Nothing
_sxCounta = Nothing
_sxParamsString = Nothing
'
End Function
OK, so it's now clear that you're not getting any response from the server because your server isn't able to connect to PayPal's servers at all. Hence, you got no server-response and the message Unable to connect to the remote server. When I tested, I got a HTTP 200 response with the following body:
TIMESTAMP=2015-07-07T09:07:39Z&CORRELATIONID=7f4d2313c9696&ACK=Failure&VERSION=93.0&BUILD=17276661&L_ERRORCODE0=10002&L_SHORTMESSAGE0=Authentication/Authorization Failed&L_LONGMESSAGE0=You do not have permissions to make this API call&L_SEVERITYCODE0=Error
Obviously that's because I tested with a blank username and password.
So, something is wrong with your server setup that's preventing you from making outside connections, either at the IIS level or due to your firewall configuration.
Without physically being present at your machine, there's not a lot we can do to track down what's blocking it, but you can try opening HTTP requests to other public websites like Google.com and see if those succeed.

Use of Google Directions Service in VB.Net

I am using Google Maps api v3..
According to This Documentation I can able to get the Path between any 2 points as in the Example.
Not I want to get the Same using VB.Net.. I want to get the same result in the server side processing (Means want to get the Path[lat lon pair]).
I have tried with the code
Dim lat As Double = 17.38489
Dim lon As Double = 78.486939
Dim lat2 As Double = 20.296095
Dim lon2 As Double = 85.82459
'For counter As Integer = 0 To 1
Try
Dim myURL As String = "http://maps.googleapis.com/maps/api/directions/json?origin=" & lat.ToString("0.000000") & "," & lon.ToString("0.000000") & "&destination=" & lat2.ToString("0.000000") & "," & lon2.ToString("0.000000") & "&sensor=false"
Dim myRequest As HttpWebRequest = WebRequest.Create(myURL)
myRequest.Method = "GET"
' Add request headers
myRequest.KeepAlive = True
myRequest.Accept = "*/*"
myRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0;)"
myRequest.Headers.Add("Accept-Language: en-us")
myRequest.Referer = "http://www.google.com/mapmaker?ll=16.303005,81.05464&spn=0.021583,0.040684&z=15"
myRequest.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-sg")
'myRequest.ContentType = "application/x-www-form-urlencoded"
Dim c As Char = "c"
Dim sb As New StringBuilder()
' Send the request and get the response
Dim myResponse As HttpWebResponse = myRequest.GetResponse
Dim reader As StreamReader = New StreamReader(myResponse.GetResponseStream())
sb.Append(reader.ReadToEnd())
reader.Close()
myResponse.Close()
Dim s As String = sb.ToString()
Dim ds As String = s
Response.Write(s)
Catch ex As Exception
End Try
Can any one Help me out..
I'm fairly sure doing this server-side is against the TOS? Plus there are limits to how many requests can be made from an IP address. What happens if you go over that limit? You need to check for error messages from Google and handle them gracefully. If you're paying for the service then there will probably be less restrictions.

asp.net cross server httpwebrequest cookie login

I am building a sister site. I want my logged in user to be able to login to the sister site.
The user is entered into both databases and has a token (guid) that matches.
I am posting the token in a token-auth page via httpwebrequest to the sister site.
The sister sites locates the user from the database with the matching token.
(so far so good)
The token-auth page (via httpwebrequest) is supposed to set a cookie that my forms authentication checks. (Then the page does a redirect to the sister site and user should be logged in.)
The problem is the last part. The cookie is not being set by the token-auth page via httpwebrequest.
Thus, forms authentication fails and the user login appears.
I see the cookie from the httpwebrequest via the CookieContainer; however it's not being saved to the cookies on the computer... and then the authentication on the redirect fails.
Anyone know how to get the cookies to save via httpwebrequest? This should be possible right?
Here's some code:
The HttpWebRequest page (on load)
Dim baseURL As String = "http://localhost:5894"
Dim poststring As String = String.Format("token={0}", u.toolkit_token)
Dim url As String = baseURL & "/GetAuthToken.aspx"
Dim cookies As CookieContainer = New CookieContainer()
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
req.Accept = "*/*"
req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)"
req.Method = "POST"
req.ContentType = "application/x-www-form-urlencoded"
req.AllowAutoRedirect = False
req.CookieContainer = cookies
Dim bytedata() As Byte = Encoding.UTF8.GetBytes(poststring)
req.ContentLength = bytedata.Length
Dim rs As Stream = req.GetRequestStream()
rs.Write(bytedata, 0, bytedata.Length)
rs.Close()
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim sr As Stream = res.GetResponseStream()
Dim result As String = String.Empty
Dim reader As New StreamReader(sr)
result = reader.ReadToEnd
If result = "200" Then
Response.Redirect(baseURL)
Else
Response.Write("Error: Token Not Authorized.")
End If
The Auth-Token page
If Not Request.Form("token") Is Nothing Then
Dim u As BusinessLayer.DataContainer.oUser = Nothing
u = BusinessLayer.BusinessObject.GetUserByToken(Request.Form("token"))
If u IsNot Nothing Then
'-----Set Cookie
Dim cookie As HttpCookie = Nothing
Dim _CookieId As String = Guid.NewGuid().ToString() & "-" & Guid.NewGuid().ToString()
Call BusinessLayer.BusinessObject.UpdateUsersCookieId(_CookieId, u.id)
cookie = New HttpCookie("KeepSignedIn")
cookie.Values.Add("KeepSignedIn", "True")
cookie.Values.Remove("CookieId")
cookie.Values.Add("CookieId", _CookieId)
cookie.Expires = Now.AddYears(1)
Response.Cookies.Add(cookie)
'---------------
Response.Write("200")
End If
End If
Please advise on how to get the Auth-Token page to save it's cookies to the file system.
Is it a cross-domain issue? How else would you go about this?
I should also note that if I login from the site directly, not using the token page, the forms authentication works using the cookie. I've used this code for years. I'm certain it is not an issue with that. The cookie is just not there to authentication against when using the token-auth page.

Post file to ashx page on different server

I have a asp.net web site where the user chooses some files with a fileUpload Control. Then the files need to be posted to another server
My domain is [http://www.mydomain.com]
The address where i have to upload the files is something like: [https://www.externaldomain.com/upload.ashx?asd2t423eqwdq]
I have tried the following:
Dim uploadedFiles As HttpFileCollection = Request.Files
Dim userPostedFile As HttpPostedFile = uploadedFiles(0)
Dim filePath As String
filePath = "https://www.externaldomain.com/upload.ashx?asd2t423eqwdq" & "/" & userPostedFile.FileName
userPostedFile.SaveAs(filePath)
But i get an error:
The SaveAs method is configured to require a rooted path, and the path 'https://www.externaldomain.com/upload.ashx?asd2t423eqwdq/Core CSS 3.pdf' is not rooted
I searched the internet, but all i could find were examples on how to upload to the page's server.
EDIT:
I used HttpWebRequest to access the link and it partialy worked. I also need to send 2 POST parameters, username and password.
This is how my code looks like now:
Dim link As String = "https://www.externaldomain.com/upload.ashx?e9879cc77c764220ae80"
Dim req As HttpWebRequest = WebRequest.Create(link)
Dim boundary As String = "-----"
req.ContentType = "multipart/form-data; boundary=" + boundary
req.Method = "POST"
Dim username As String = "test"
Dim userpass As String = "123456"
Dim credentials() As Byte = Encoding.UTF8.GetBytes("username=" & username & "&password=" & userpass & "--\r\n" & boundary & "--\r\n")
Dim separators() As Byte = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n")
Dim uploadedFiles As HttpFileCollection = Request.Files //this is where i take the file that the user wants to upload
Dim userPostedFile As HttpPostedFile = uploadedFiles(0)
//i convert the file to a byte array
Dim binaryReader As IO.BinaryReader
Dim fileBytes() As Byte
binaryReader = New BinaryReader(userPostedFile.InputStream)
fileBytes = binaryReader.ReadBytes(userPostedFile.ContentLength)
//'get the request length
req.ContentLength += credentials.Length
req.ContentLength += userPostedFile.ContentLength
req.ContentLength += separators.Length
req.ContentLength += 1
Dim dataStream As Stream
dataStream = req.GetRequestStream
dataStream.Write(credentials, 0, credentials.Length)
dataStream.Write(separators, 0, separators.Length)
dataStream.Write(fileBytes, 0, fileBytes.Length)
dataStream.Close()
Dim response As HttpWebResponse = req.GetResponse
The error i get is "forbidden". The username and password are 100% correct. I think the problem is that i do not create the request correctly. If i post only the credentials i get the error saying that i have no file...
Any ideas?
Eventually I used the code provided here http://aspnetupload.com/
I compiled it into a dll and added a reference to my solution.
It works :)

Resources