Does anyone have an example to retrieve a token from a webpage

I’ve been going in circles with the AI for the last three nights and not making any progress.

My app calls a URL that confirms that I want to retrieve the token, so it’s looking good. And after I authorize the website, control is passed back to my app. But I don’t know how to actually get the token it’s sending to me. And the website has be building plist entries and redirects and it’s trying to tell me that I have to use a method in app named HandleURL that’s hardcoded to run when control is passed back to the app … but it never runs even though control comes back to the app. And this is a MacOS app and the documentation says HandleURL is only for iOS, so I think I’m on the wrong path.

Does anyone have an example to share that matches what I’m describing?

What do you do in the ContentReceived event of the socket?

Assuming we’re talking about OAuth here, I can push you in the right direction… with a whole lot of asterisks.

OAuth must be handled in a browser, so I’m going to assume you’re using an HTMLViewer to get the user to log into the service. Look into DesktopHTMLViewer — Xojo documentation as a means to interrupt the request from the provider server to your callback URL. You can parse the values you need from the URL and make the redemption request with URLConnection. You’ll get back an easy-to-parse JSON payload with the access token, refresh token, and some expiration info.

HOWEVER this is the wrong way to handle this. Most providers only allow you to register as a confidential client, which means you get a secret which is used for final redemption. Your Xojo app, unless it’s a web app, is NOT a confidential client. That secret must not be embedded in your app in any way. If your provider allows secretless non-confidential clients, then using this interception method is safe. (Safeish, but that’s a separate topic.)

Assuming you have to use a confidential client, your server must be the one to perform the redemption. How you get the usable token from your server to your app is a broad topic. Allowing users to “connect” accounts to other accounts is the most common way to solve the problem.

All of this glosses over the callback url, which is always an https:// url.

If I’m wrong and this auth scheme is not OAuth, then we’ll need API reference docs to know what the provider expects you to do.

I am using OAuth but not the DesktopHTMLViewer. Maybe that’s what’s missing.

• My routine starts by building the string authorizeURL that has the https string to the site (and specifies the callbacks)
• I execute that call:

Var sh As New Shell
sh.Execute(“open “”” + authorizeURL + “”“”)

• Then it starts a loop until oauthCode has a value.

Do
App.DoEvents
Loop Until oauthCode <> “”
messagebox("oauthCode has a value … " + oauthCode)

• And when it runs, Safari takes me to the authorization page, and I fill it out. Next, it’s trying to send a token back to me.
• And here’s where I’m lost. Control does come back to my app, but without running any code; it just makes the main window active again.
• What I mean: the MessageBox command after the loop waiting for oauthcode never runs, so the rest my GenerateToken routine looks like it’s abandoned (or maybe it’s continuing to run in the background … but that would mean I have multiple processes running now so I don’t think that’s happening).
• And I have a MessageBox command at the start of the HandleURL routine. The AI insists this routine name is hardcoded and gets kicked off when control comes back to the app. But I disbelieve because the documentation says HandleURL is iOS-only. And the MessageBox not firing shows me it never gets control.
• And I have a MessageBox in the MouseDown event after the call to the routine that is retrieving the token. That MessageBox never runs.
• I also have a MessageBox in a routine called TokenExchangeContentReceived. But I understand how it can ever run. I have this code that I don’t understand

oauthRequest = New URLConnection
AddHandler oauthRequest.ContentReceived, AddressOf App.TokenExchangeContentReceived
oauthRequest.RequestHeader(“Content-Type”) = “application/x-www-form-urlencoded”

but it never runs because it’s in the routine after the loop waiting for oauthcode to get a value.

I feel that I have the first half of the equation working — it calls the site and I can authorize the token to be released. But I’m lost trying to get the token back and using it.

I don’t have a ContentReceived anywhere, and I’ve heard the term socket before but I don’t know what it is. I just looked at it in the documentation, and I see lots of entries for sockets but I don’t know how to use them.

Ok… there’s a lot to unpack here.

Let’s start with the easiest. Use System.GoToURL instead of the shell. Running the authentication in the user’s browser is good because they’ll have access to their password managers and can clearly see the URL bar. However, it makes your life more difficult.

I’m curious what you’re passing for the callback URL. You don’t have to share the URL here, but is to a web address that you control? If so, is the code at that URL handling the redemption process? Understanding what happens with the callback is really going to drive the rest of the conversation.

Don’t ever do this. First of all, Xojo is an event driven language. Use a timer instead. Have it run every 100ms checking for whatever you need to check for and stop itself when successful. Second of all, DoEvents is the most sinister method in Xojo’s Desktop framework and never should have existed. There’s been lengthy discussions on this forum about it. The TLDR is it allows you to run other events inside your event, which causes a ton of problems with debugging, app state, and general code predictability.

1 Like

Don’t ever do that for OAuth which is supposed to run in a browser.

Your socket is running out of scope before the data arrives back.

OAuth usually has a callback url where the token is available. Both the MBS and the Chilkat plugin have examples for OAuth workflows. Here is a list to the examples of the Chilkat plugin: OAuth2 Examples for Xojo Plugin

Yeah, I had a feeling I was being led down a bad path when I saw that loop, also when the AddHandler is in a place in the code that never executes and it supposedly relied on HandleURL.

I never understood how the callback was supposed to work. The Shell call was executing:
Var authorizeURL As String = _
“``https://lichess.org/oauth/authorize``” + _
“?response_type=code” + _
“&client_id=” + App.LichessClientID + _
“&redirect_uri=com.xxxxxxxx.yyyyyyyy://callback” + _
“&code_challenge_method=S256” + _
“&code_challenge=” + challenge + _
“&scope=”

and the redirect_uri matches a plist entry. And I didn’t understand how the authorization page sends control back to the app. The explanation given to me is that it doesn’t have to give control to a website and that he redirect_uri parameter tells Lichess that after the user authorizes the request to send them back to this address (not necessarily a web page) and it includes the token in the query string so my app can retrieve it. And the address is not a traditional HTTP website but a custom URI scheme that your app has registered with macOS (via Info.plist).

But if this callback is supposed to start the process that processes the token and moves forward, then I didn’t even understand why I have a loop for oauthcode.

Does this make any sense at all or is this just an AI dream?

The high-level explanation I got is that the GenerateToken call sends control to the authorization page and the loop keeps the process active.
Then when the authorization finishes, the redirect kicks off the HandleURL process (which I don’t fully believe because this is MacOS not iOS and I don’t know how my app knows what process to run when control finally returns to it from the Authorization page). The HandleURL is supposed to set oauthcode which then causes the GenerateToken routine to finish processing.

There’s some logic in it. The OAuth spec allows redirecting to a custom URL scheme. If you do it that way, you’ll need to handle it in the App.AppleEventReceived event. It’ll look something like this:

Function AppleEventReceived(theEvent As AppleEvent, eventClass As String, eventID As String) Handles AppleEventReceived as Boolean
  If eventClass = "GURL" And eventID = "GURL" Then
    Var URL As String = theEvent.StringParam("----")
    Return Self.HandleURL(URL)
  Else
    Return False
  End If
End Function

In this case my App object has a HandleURL function.

BUT you cannot do it this way if redeeming the code for a token requires a secret, because that requires embedding your secret into your app, which is essentially leaking it. This is known as confidential client, and is the most common implementation. Unfortunately, most OAuth providers do not allow creating a public client.

I’m so far over my head.

• Are you saying that adding this function will … what? When the app receives control again, does this event run? What is the URL representing, what is it returning, and what is an eventClass?

(Is this the best direction to take or am I going down a bad path just making everything even more complicated)

And help me understand the security problem here. If the token is being passed back to me and I have it in a variable, what do you mean it’s being embedded in the app?

An OAuth confidential client has a secret. Usually you’ll be given and Client ID and Client Secret. The Client ID is public. You could post it here without harm, for example. The Client Secret must be kept secret, which means you cannot include it in an app that gets distributed to users. Even obfuscated, that secret still becomes vulnerable if you do. So if the API you’re using uses a secret as part of the redemption process, you MUST use a true server to finish the process.

That event I posted is the AppleEventReceived event on the App class. It’ll work as-is, aside from the Self.HandleURL part. You’ll need to decide what to do with the URL yourself.

I think I’m okay with the secret part. The whole purpose of this call is that anyone who uses the app can call and retrieve their own private token - they’ll never share it with anyone.

I feel I’m deep in weeds and probably making bad assumptions and not asking the right questions. Let me back out and ask a very basic question. What tools should I be using if I want to call up the authorization page and then have it return a token to me? Then that leads to questions like:
• I like the idea of using System.GoToURL to pull up the page in Safari.
• Is the callback the right way to return the code or is there a simpler process?
• I looked up AppleEventReceived in the doc and as usual I get the feeling that unless I already know the answer, it doesn’t tell me anything. You say it works as-is but I don’t even know what it does or what self.handleURL does. Is it transferring control to an app method named handleURL?
• I’m guessing AppleEventReceived won’t work in iOS because iOS doesn’t support Apple Events?

You don’t really have a choice. That’s how OAuth works. If the API you’re trying to call uses OAuth, you have to abide to its spec. The most common implementation has the user’s browser redirect back to a server you control, which trades a code for a token. After that, getting the token where you need it is entirely up to you. This is often storing it in a database (encrypted) in the user’s account where it can be retrieved later. OAuth is best at linking two accounts together.

Self.HandleURL would receive the callback URL, which you would need to parse and implement the redemption process. You’d then have a token you can store wherever you need.

Correct.

Looking at their API docs and based on your experience level with this stuff, I strongly recommend switching to using personal access tokens instead. Yes, it’s not as pleasant to ask the user to go generate a token and paste it into your app, but there are lots of ways to do OAuth wrong.

If you choose to stay with OAuth, the good news is I think you’re correct, Lichess does appear to support public clients. So it could be implemented without the use of a server.

That’s such a tempting idea.
But I’m not sure I like the idea of trying to explain to elementary school age kids that step.
If found this video … will it cover what I need to learn?

Yeah I think so. I haven’t watched through the full thing, but I jumped around a bit and it seems solid.

I watched the video and didn’t learn anything. He jumps around and demonstrates a bunch of stuff that he did but doesn’t explain it in a helpful order … at least not helpful to someone like me. I think he has the same problem that I find with the lots of the Xojo documentation. It’s good for giving you some facts if you already know what you’re doing.

So I don’t know what I’m going to do next.
The video showed the redirect uri as http://localhost:8888/callback Is that what I have to use in the call to the authorization page instead of the plist entry? If so, what runs when control is passed back to the app?

It feels like I’m close because I can make the call to open the authorization page and press the authorization button. I’m just missing the part where Xojo captures the token that’s sent back. It’s a useful app that’s helping me improve my chess and I hate throwing out nine months of work because I’m too old to figure out this one item.

Add the plist entry to create a URL handler. Then add the AppleEventHandler event to App exactly as you see it here:

If eventClass = "GURL" And eventID = "GURL" Then
  Var URL As String = theEvent.StringParam("----")
  Break
Else
  Return False
End If

Next setup the callback url to be something starting with your url handler. If you defined the handler as thisisachessapp then your callback url would be something like thisisachessapp://oauth. The important part is before the ://. Everything after will be largely irrelevant, at least for now. Down the road if you want to do more with your custom url scheme, the rest will matter.

Now run and complete an authorization. If all goes well, your breakpoint should fire and you can see the url in the debugger.

You will need to parse that url to extract the values you need, then use a URLConnection to exchange the values for an authorization token.

1 Like

I apologize, I’m going to need some translation here.
• I think I’ve got the plist entry (the AI had me do that over the weekend).
• You say to add the AppleEventHandler event to app. I’m not sure I know what that means. I know how to add an Event Handler to App. But I don’t see an event called AppleEventHandler. The only options I have are: Activated, AppearanceChanged, AppleEventReceived, CancelClosing, Closing, Deactivated, DocumentCreated, DocumentOpened, MenuBarSelected, UnhandledException
• What do you mean to set up the callback url? Is this a method in App? Are you saying that I will call it thisisachessapp (or whatever name I passed as the redirect_api parameter value and that matches my plist entry)?
• Also, this might be a piece of oauth that I misunderstood. Are you saying that what I’m going to receive is not the token but a URL that I send back to a lichess API again and then I have to somehow get that output back and that’s the token? So it’s not one item I have to figure out how to receive but two?
(It’s 1AM here and I’m feeling fuzzy — I’ll try again tomorrow. Thanks for your patience & I apologize for being such a bad learner.)

Sorry, yes you’re looking for AppleEventReceived as I mentioned originally. I typo’d in this most recent reply.

Once the user has granted authorization, they are redirected back to your server. That’s the redirect / callback url and is absolutely critical to the process. In your case, you’re not using a server but a custom URL handler. Be aware though that the user’s browser will prompt them with a “do you want to allow app X to visit this url” message after they complete authorization. But yes, this will match the plist entry.

Correct, this is not the final step. You ever go to an event where you pay for parking, get a ticket, and hand it to a person a head just 10 feet away? This is kind of like that. The redirect does not give you a token, but everything you need to get a token. You need to make another request (not in the user’s browser) to exchange that info for a token. You will use URLConnection to do this, and the response will come back to you in the ContentReceived event. That will be a JSON payload that is easily parsed with JSONItem, and you can get the access and refresh tokens from that.

When the access token expires, you use the refresh token with URLConnection to get a new access token. It’s common for access tokens to expire after only an hour. I’ve seen 3 and 30 days as well, but it’s entirely up to the provider. So you’ll need to store the access token, refresh token, and expiration somewhere secure. If the refresh token expires, the only option then is to start a new OAuth flow.

Thanks for the code.
I’ve been frustrated for the last 3 hours, and I’m still doing lots of things wrong, it seems. I added the AppleEventReceived event. I put a MessageBox at the very start so I can confirm this procedure is being run, but I never get the messagebox.

I’m using the System.GoToURL(authorizeURL) now where
Var authorizeURL As String = _
https://lichess.org/oauth/authorize” + _
“?response_type=code” + _
“&client_id=” + App.LichessClientID + _
“&redirect_uri=com.yada-yada-yada://callback” + _
“&code_challenge_method=S256” + _
“&code_challenge=” + challenge + _
“&scope=”

I’m still confused where control is supposed to be resumed in the app.
• Is the AppleEventReceived method supposed to run? (it doesn’t appear to run)
• or is the redirect referring to a method name (like thisisachessapp) that I don’t have defined?
• or is the Addhandler involved? The AI put that into my code but I don’t know what it’s for — but it never runs either because I have a messagebox before that step that never appears.