Isolating http query values after redirect

I need to process the query parameters returned after a redirect.

I followed the redirect recipe by having a javascript fnuction in the client to redirect to the openid provider. When the open id provider is finished, it redirects back to the app. How do I isolate the query parameters used by the id provider.

They are in the form of a fragment marker at the end of the url as set out below:

https://awkward-hasty-layer.anvil.app/#access_token=eyJhbGciOiJSUzI1NiIsImtpZCI6InFid3J4ZXBwV0wwIn0.eyJpc3MiOiJodHRwczovL2lucnVwdC5uZXQiLCJzdWIiOiJodHRwczovL2FudmlsMS5pbnJ1cHQubmV0L3Byb2ZpbGUvY2FyZCNtZSIsImF1ZCI6WyJjMjAxNTA0YWFlNTcwNDA3MzdhZjA1YWM5NjY5Zjc1NyJdLCJleHAiOjE1NjQ5MzE4NzAsImlhdCI6MTU2MzcyMjI3MCwianRpIjoiYzYyNzIwMzMzOGNhMzUyOCIsInNjb3BlIjoib3BlbmlkIn0.VE5NZc6dS0KRn2HiOosnfTxJVRU_c7WPtNGXtUeWoGohKMwZotRtQCWuNxE3RySC8jUBwgvSrn1jHZ2hRsBBMSII7T_bYpowul2X2Mdtg7MNA187Oa5BkZNKUSICkIJG_WaIw2uxreo7lST7hzaZ14VnW7sqPfwwRVro8VoYsMthsGxmVmVPFW1i2owDvt9bvs4KA4IVBBzgxFdZSHx4YN8UfA3HIlTjV3DkvOX6olErs4e0n68pGY6yc2S3feqCaBz5CExD9Zym6TnNtVAfOkKzmkmggz0iEi09ca0eJCagE4PrmNyfIgDgH3sNlgEK-vRCr6zdhNQxK8Ff0T7aPw&token_type=Bearer&expires_in=1209600&id_token=eyJhbGciOiJSUzI1N...

My question is, does this redirect pass through the server? If so, how can I trap the fragment, process it and then return to a useful place in the client part of the app?

I think you want to use get_url_hash()

From the docs :

get_url_hash() gets the decoded hash (the part after the ‘#’ character) of the URL used to open this app.

If the first character of the hash is a question mark (eg https://myapp.anvil.app/#?a=foo&b=bar ), it will be interpreted as query-string-type parameters and returned as a dictionary (eg {'a': 'foo', 'b': 'bar'} ).

get_url_hash() is available in Form code only.

1 Like

Thanks. That doesn’t fully answer the question though. Is get_url_hash() server code or client code?

If its server code how do I get the app back to a useful place in the client after processing the api route?

Ok. I have used it on the main form’s __init__ function. And it works fine. I am a bit concerned that it includes an id_token in the clear in the url. Is there some way I can clear it?

===============

It’s client code.

Thanks. Working fine.

I’m not quite sure what you mean by that. It’s sent as part of the URL and therefore available to the client side code. You can pass it to a server side function so nothing client side is aware of how that id is decoded, used or processed.

My app is using an openid provider and the ‘flow’ is as follows:

  1. client collects a webid
  2. passes the webid to the server
  3. server does a whole bunch of crypto stuff and prepares a re-direct url for authentication
  4. client redirects to the authentication url
  5. user goes through authentication
  6. openid provider redirects back to app with tokens in the url. For example https://myapp.anvil.app/#id_token=aldjfhsakf
  7. these tokens are visible in the browser address bar
  8. the client uses `get_url_hash to get the tokens.
    9 but they remain visible in the address bar
  9. Hence, how do we clear them out. Perhaps another redirect?

hmm…thinking off the top of my head here …

maybe you could redirect to an http endpoint first?
There you could process the data as GET parameters (without the #) and then redirect using a 302 to the main app’s urls.

edit
so you would do this :

@anvil.server.http_endpoint("/myendpoint")
def process_endpoint():
  # process the parameter data.
  # create a new UID so the main app can know what to do
  ...
  r = anvil.http.Response()
  r.status = 302
  r.Location("https://myapp.anvil.app/#?newid=xxxx")
  return r

In that function you could create a uid and store it in a data table. Then use that as a # parameter to the URL which is meaningless to the onlooker, but can be used in a server side lookup to verify its authenticity.

This way, the original (and potentially the most sensitive) token is never visible.

thanks. I will try it out tomorrow.

ok. I’m a work-a-holic :frowning:

The auth server has no ?, just /#. Therefore no parameters are being sent into the endpoint. Is there a function that can inspect the original request object in the call-back .../end_point?

@anvil.server.http_endpoint("/auth_endpoint")
def process_endpoint(*args, **kwargs):
  # process the parameter data.
  # create a new UID so the main app can know what to do
  
  print("secrets", args, kwargs)
  r = anvil.http.Response()
  r.status = 302
  r.Location("https://myapp.anvil.app/#?newid=xxxx")
  return r

Hmm, I can’t get it to respond to just a /#
Anvil just seems to strip it (and anything that follows it) at the end point.

I don’t seem to have access to the original unprocessed URL. Might need Anvil Central on this one.

Me neither. I have an idea it could be a bug in the auth program. I think it should read /#? instead of just /#. I am not sure if they will agree with me though.

I have successfully routed it through the client. I think if I use a bit of javascript to push a clean url into history I should be ok.

Yes. Using javascript helper worked fine.

  function cleanupUrl(url) {
  	history.pushState({}, null, url)
  }
3 Likes

Hello, Anvil Central here!

The fragment is stripped off by the client before reaching the server; it’s meant for the use of the browser (or other client) to locate secondary resources within the resource retrieved.

This is in RFC 3986:

… the fragment identifier is separated from the rest of the URI prior to a dereference, and thus the identifying information within the fragment itself is dereferenced solely by the user agent, regardless of the URI scheme. Although this separate handling is often perceived to be a loss of information, particularly for accurate redirection of references as resources move over time, it also serves to prevent information providers from denying reference authors the right to refer to information within a resource selectively.

Interestingly though, the fragment contains an id_token and was showing in the browser address bar. In the end, I used the function above to replace it with a cleaned up version.

Just stopped by to say thanks, Simon.

While I was filing a new forum post on exactly the same need (clear the token from the address bar) I found your solution.
Works like a charm to me.

1 Like

I’m having a hard time getting this to work.

Form code is:
class Form1(Form1Template):

def init(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)

gurl = get_url_hash()
self.label_1.text = gurl["a"]

I published app as geturl and tried to call it as:
https://geturl.anvil.app/#?a=foo

But nothing comes up!

Hello,

I’m away from a computer right now, but what is the value of gurl when you print it out?

Yeah, nothing prints out!