Why google login redirect back to site with hashtag - can it be avoided - asp.net

I didn't succeeded to find any relevant answer to this so I must ask :)
I implemented google plus login to my site. With a few workarounds it work fine but...
When I am redirected back to my site from google I am returned to the following URL:
http://localhost/mysite/west/Default.aspx#state=/profile&access_token=ya29.qQDrtcVtgOEbS86Bg10puFG3dksJz74BlrEGulHldlJW2o5qQ6g7ilF17zQsm8iMLG0C82PQyp2Z-g&token_type=Bearer&expires_in=3600
Because of this parameter here #state=/profile& I first have to read URL in javascript on load, remove this part to fix URL and then do this:
var url = "Default.aspx?" + queryString;
window.location = url;
And then I can continue to read query string normally in code.
I don't like this because when I do this I make two postback on page and I want to avoid this if possible.
Is this redirect url must have this or this can be avoided?
Redirect to google:
string url = "https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email&state=%2Fprofile&redirect_uri="+this.Return_url+"&response_type=token&client_id=" + this.Client_ID;
Response.Redirect(url);
When I get back to my site:
if (this.Request.QueryString["access_token"] != null)
{
String URI = "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + Request.QueryString["access_token"].ToString();
WebClient webClient = new WebClient();
Stream stream = webClient.OpenRead(URI);

Change response_type=token to response_type=code. The callback URL will then have a code query parameter which is accessible server side instead of a fragment. You will then have to implement step 4 to exchange the code for an access_token.

Related

returnUrl drops second querystring parameter

I am using MVC 5. The problem is that after SSO redirects back to the app after authentication the login method returnUrl drops the applicaitonId querystring parameter. Please help!
Here is the flow.
The app redirects unauthorized users to a login method, preserving the original request in the returnUrl.
The original request is
http://localhost:25451/shared/download?documentGroup=133&applicationId=3153
the returnUrl is
/shared/download?documentGroup=133&applicationId=3153
The app redirects to a SSO CAS server, sending along the HttpUtility.Encode returnUrl as a parameter along with login Url both part of the service parameters.
https://{redacted}/cas/login?service=http://localhost:25451/account/login%3freturnUrl%3d%2fshared%2fdownload%3fdocumentGroup%3d133%26applicationId%3d3153
After authentication, the CAS server appends the authorized ticket and redirects back to the service URL. This is what fiddler shows.
http://localhost:25451/account/login?returnUrl=/shared/download?documentGroup=133&applicationId=3153&ticket={redacted}
Here is the issue. The returnuRL in the login method is simply
/shared/download?documentGroup=133.
The returnUrl no longer has the applicationId.
Interestingly enough, the line works just fine.
var ticket = Request.QueryString.Get("ticket");
I have tried to encode the whole serviceUrl and tried to encode just the returnUrl(see below) but I get the same missing ApplicationId issue.
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
var ticket = Request.QueryString.Get("ticket");
if (!string.IsNullOrEmpty(ticket))
{
//verify the ticket...
return RedirectToLocal(returnUrl);
}
var serviceUrl = Request.Url.Scheme + System.Uri.SchemeDelimiter + Request.Url.Host + (Request.Url.IsDefaultPort ? "" : ":" + Request.Url.Port) + "/account/login" + "?returnUrl=" + HttpUtility.UrlEncode(returnUrl);
var authenCasUrl = string.Format("{0}login?service={1}", "https://{redacted}/", serviceUrl);
return Redirect(authenCasUrl);
}
Since this site will be actually called by your URL, I don't think they just throw away parts of it.
Lets try something here since I have encountered a similar problem with parameter in url strings in combination with asp.NET.
First, lets get the unedited URL from your Request:
string UneditedUrl = Request.RawUrl;
Since we are not needing anything before the ? mark, we shorten it a little bit:
string QueryString = (UneditedUrl.IndexOf('?') < UneditedUrl.Length - 1) ? UneditedUrl.Substring(UneditedUrl.IndexOf('?') + 1) : String.Empty;
This line also includes the possibility on neither having a ? mark or parameters and will return an empty string if so. Just for good measure, we don't want any exceptions here. Here you can check QueryString if it has both or more of your parameters you entered.
If there are not complete here, its not your code at fault. Something will already work on your URL before you do, probably your host then. Maybe check the settings of your IIS.
If your parameters are correctly in the edited QueryString, you can continue getting them by following this:
I learned that there is a way to let your framework do the job of parsing parameters into name/value collections. So lets give it a go:
NameValueCollection ParaCollection = HttpUtility.ParseQueryString(QueryString);
You can now check you params and their values by either using an index like ParaCollection[0] or ParaCollection["documentGroup"].
EDIT:
I've found the question which brought me to the conclusion of using Request.RawUrl. Since this may not be the answer, it will maybe help a little bit more to understand that Request.RawUrl is the actual URL the user called and not the one the server executes: RawURL vs URL
I have no experience with asp or SSO, but you may need to also HttpUtility.UrlEncode the value of the serviceUrl variable?
var authenCasUrl = string.Format("{0}login?service={1}", "https://{redacted}/", HttpUtility.UrlEncode(serviceUrl));
Since the service parameter is decoded by the CAS once, and then the value of returnUrl gets decoded by your server.
var returnUrl = "/shared/download?documentGroup=133&applicationId=3153";
var serviceUrl = "http://localhost:25451/account/login?returnUrl=" + HttpUtility.UrlEncode(returnUrl);
var casUrl = "https://{redacted}/cas/login?service=" + HttpUtility.UrlEncode(serviceUrl);
Which gives:
serviceUrl = http://localhost:25451/account/login?returnUrl=%2Fshared%2Fdownload%3FdocumentGroup%3D133%26applicationId%3D3153
casUrl = https://{redacted}/cas/login?service=http%3A%2F%2Flocalhost%3A25451%2Faccount%2Flogin%3FreturnUrl%3D%252Fshared%252Fdownload%253FdocumentGroup%253D133%2526applicationId%253D3153
Explanation attempt:
You make a HTTP request to the CAS server. It's implementation splits the query parameters and decodes each value (and possibly key). One of which is the service parameter and is now (after decoding) a valid URL.
The CAS server makes a HTTP request with the URL from the service parameter (to your server) with the ticket appended.
You split the query parameters and decode each value (and possibly key).
If you only encoded the returnUrl once, your serviceUrl will look like what you showed in your third point:
http://localhost:25451/account/login?returnUrl=/shared/download?documentGroup=133&applicationId=3153&ticket={redacted}
How does the algorithm splitting the query string differentiate between a ? or & in the serviceUrl and the ones in the returnUrl?
How should it know that ticket does not belong to the returnUrl?
As you can see in my code above, you are not encoding the returnUrl twice.
You are putting one URL in the parameters of another URL and then you put that URL in the parameters of a third URL.
You need to call UrlEncode for each value (and possibly key) when you put together a query. It does not matter whether that value is a URL, JSON, or arbitrary user input.

how to forward a link with URI to another link with the same URI

My program directs users to a webpage with their username and password. E.g.
http://example.html?username=username&password=password.
Now I created another page in asp.net and I want some code on example.html to redirect the link to http://example.aspx?username=username&password=password.
So what i want is to get the URI from the first url and direct it to the new url by appending the URI.
Any suggestions?
You can grab the querystring in its entirety via
window.location.search
See this with more about that. Using this, you can extract the parameters, append them to a new URL, and render the link, or set
window.location = "example.aspx" + window.location.search
I believe search comes with "?", but I could be wrong. I assume this is an exmaple; note it's not a good practice to pass the password through a querystring as clear text, especially if you are not using HTTPS. It's generally advisable to do a POST operation, not a GET operation with querystring, when it comes to sensitive information.
Use window.location.search to get everything after "?". Then you can just direct the new aspx page using this java script line.
window.location = "example.aspx" + window.location.search

UserManager.GenerateEmailConfirmationToken returning tokens unsuitable for URL transmission

I have a ASP.NET MVC 5 website, and I'm implementing an email confirmation process based on the template from Microsoft.
While I'm composing the email body text, first I construct the URL a user will use to "click to verify your address".
To generate the security token I call:
UserManager.GenerateEmailConfirmationTokenAsync(user.Id)
This produces a code such as:
pporPNj6KzdZ3BYG8vQsKJu3dPJMwGgh+ZEGhCNnf9X6F0AS0f6qCowOQwQNfpYkl14bgEsmyPTKya5H6N4n2na2n5PgO+wpoihXxQTA7G8pK/lUYskX3jy2iA/ZM8m4Vm0prTyUuhMgfDlV+wkbR336FBRIAbKJDwOWvHHbJBDQ21gW93hyzca0li66aI1H
Obviously, this wouldn't be valid in a URL, but even URL encoding won't solve IIS's hate of such a URL.
HTTP Error 404.11 - Not Found
The request filtering module is configured to deny a request that contains a double escape sequence.
In my UserManager implementation, I'm using the tutorial boilerplate code for a TokenProvider.
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<SiteUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
How can I make these generated tokens a bit more URL friendly? What did potentially change that would prevent the ASP.NET's tutorial code not work?
It turns out that this token will get mangled by the built in class "UrlHelper" in a MVC controller, or Url in a WebAPI controller IF the target route lists these variables as part of the path, rather than the GET vars of the URL.
Eg: this call, creates a relative URL for the site route called "ConfirmEmail" and fills in the blanks
Url.Route("ConfirmEmail", new { userId = user.Id, code = code });
Before my route was:
[Route("register-email/{code}/{userId}", Name = "ConfirmEmail")]
Changing this to:
[Route("register-email", Name = "ConfirmEmail")]
Generates valid URLS that IIS can chew through. When these are not specified, they get appended after a ? mark as normal GET vars. No idea why IIS is picky like that, but there's the solution.

Get the full QueryString from URL in ASP.NET

I have an URL with the following format:
http://www.mysite.com/login.aspx?ref=~/Module/MyPage.aspx?par=1&par2=hello&par3=7
I use the content of the QueryString it to Redirect the user back to the page he was before logging in. In order to keep also the status of the page I need the parameters in the QueryString. The number of parameters changes depending on the Page calling the Login and its status.
Let's say I want to store everything in the URL after ref in the redirectURL variable. I tried:
redirectURL = Request.QueryString("ref") // "~/Module/MyPage.aspx?par=1"
it gets everything after ref but ignores everything after the &(included). If I use:
redirectURL =Request.Url.Query // "ref=~/Module/MyPage.aspx?par=1&par2=hello&par3=7"
it gets everything, ref included. In order to achieve my goal I need just to remove the first 4 characters from the redirectURL. But I think this solution is a bit "forced" and I am sure there should be some ASP.NET function that accomplish this task.
The &s in your URL are creating additional querystring arguments.
You need to escape the value of the ref parameter before putting it in the querystring.
This will replace the &s with %26.
To do this, call Uri.EscapeDataString().
When you fetch the property from Request.QueryString, it will automatically decode it.
Consider Encoding "~/Module/MyPage.aspx?par=1&par2=hello&par3=7" before passing it to the url.
Eg.:
String MyURL = "http://www.mysite.com/login.aspx?ref=" +
Server.UrlEncode("~/Module/MyPage.aspx?par=1&par2=hello&par3=7");
And then, you can get the redirectURL using:
String redirectURL = Request.QueryString("ref");

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.

Resources