Page MenuHomePhabricator

[OAuth] Login/Logout
Closed, ResolvedPublic

Description

Although it is not a task for this week we can start discussing the options we have and how we will implement the login, logout, and how we will authorize the users to access Toolhunt

Event Timeline

Hannah_Njoroge renamed this task from OAuth to [OAuth] Login/Logout.Jan 25 2023, 6:13 AM

I posted most of these links in the Zulip chat already, but here they are again for future reference.

Mediawiki Guides and Registration
OAuth/For Developers
OAuth/Owner-only consumers
OAuth consumer registration

General Reference
OAuth 2.0

Python-specific documentation and reference:
Requests-OAuthlib
MediaWiki OAuth Library
My First Flask OAuth tool

My own attempts to get OAuth up and running on the backend have been unsuccessful so far. Unfortunately, there are a few things that I might have been doing wrong, and I lack the experience to know where, exactly, the problem might lie.

I read through *OAuth/For Developers* and *OAuth/Owner-only consumers* and it seemed to me that an Owner-only setup would be sufficient for the backend; it's described as being "meant for bots and similar tools which always authenticate with the same user account," which seemed a reasonable description of what the backend is going to be doing: making GET and POST requests to the Toolhub API from the same account.

My understanding -- please correct me if I'm wrong -- is that we'll be authenticating users on the frontend, but not doing much with that information other than acquiring and storing their usernames ( for which we may need to make a special request ) our app won't be using their credentials to make requests to the Toolhub API.

So, anyway, I registered for an owner-only OAuth 2 token, added Authorization: Bearer <access token> to my header and attempted to make a POST request to the test server, but received the message "Authentication credentials were not provided."

So I registered for an owner-only OAuth 1 token, installed requests_oauthlib, did some reading and tried again, following the guidance in the documentation, only to receive the same result.

I can see a number of possible points of error:

  1. User error, typo or something.
  2. Owner-only authentication is not sufficient.
  3. When registering for the key, I had a choice of "applicable projects" and, in the absence of anything obviously Toolhub-related, selected "metawiki" (meta.metawiki.org). Maybe I chose poorly.
  4. I requested authorization for "Basic rights." Maybe the basic rights are insufficient for what I'm trying to do.

@Slst2020 @bd808 @Damilare - any thoughts or insights? This time I'm definitely feeling stuck.

@Slst2020 @bd808 @Damilare - any thoughts or insights? This time I'm definitely feeling stuck.

This all sounds like you are trying to use MediaWiki as the OAuth Authorization server instead of using Toolhub as the OAuth Authorization server. This confusion is understandable. Toolhub itself uses MediaWiki as an OAuth Authorization server rather than dealing with passwords and account creation issues locally. A tool like Toolhunt that wants to interact with the Toolhub API as various users needs however to use Toolhub as the OAuth Authorization server instead of MediaWiki.

We have not yet written any tutorials on using Toolhub's OAuth Authorization server. I did however make an example client that can be found in the contrib/oauth-client-example/ directory including a short README on how to configure and run that demo in your local development environment. There is also a toolhub-gadget-demo tool that is a client of the https://toolhub-demo.wmcloud.org/ OAuth Authorization server. The source for that tool is available at https://gitlab.wikimedia.org/toolforge-repos/toolhub-gadget-demo. Both examples are using the Flask framework for the basic app and the Authlib Flask OAuth client library.

I would be happy to answer additional questions you may have after you have had a chance to look at those examples.

That would indeed explain it! Thank you again, @bd808.

So I managed to get the OAuth client example to run in my development environment by doing the following:

  1. Registering a new client application with the Toolhub demo server by using the /api/oauth/applications/ POST endpoint. I set the redirect_url to http://fakedomain.com:8002/authorize
  2. Setting the TOOLHUB_CLIENT_ID and TOOLHUB_CLIENT_SECRET env variables: $ export TOOLHUB_CLIENT_ID={client_id value}, similar for the client_secret
  3. Adding the line 127.0.0.1 fakedomain.com to my /etc/hosts file
  4. Substituting fakedomain.com for localhost everywhere in contrib/oauth-client-example/config.py
  5. Editing contrib/oauth-client-example/app.py:
oauth.register(
    name="toolhub",
    access_token_url="https://toolhub-demo.wmcloud.org/o/token/",
    access_token_params=None,
    authorize_url="https://toolhub-demo.wmcloud.org/o/authorize/",
    authorize_params=None,
    api_base_url="https://toolhub-demo.wmcloud.org/api/",
    client_kwargs=None,
)
  1. Running make oauth-client from Toolhub root to spin up the application
  2. Navigating to fakedomain.com:8002

This works on Firefox for me but not on Chrome:

Screenshot 2023-02-13 at 12.38.14.png (1,003×58 px, 41 KB)

@NicoleLBee mentioned having similar problems using Edge.

This works on Firefox for me but not on Chrome:

Screenshot 2023-02-13 at 12.38.14.png (1,003×58 px, 41 KB)

@NicoleLBee mentioned having similar problems using Edge.

Interesting. I can recreate this error locally in Chrome myself. I'm attempting to authorize the demo client running locally against Toolhub also running locally. Clicking on the "login" link in the demo client takes me to the /o/authorize/ form in Toolhub as expected. That page contains a form that posts back to the Toolhub instance which makes the Content-Security-Policy violation message from Chrome quite confusing:

Refused to send form data to 'http://localhost:8000/o/authorize/?response_type=code&client_id=gPY2n3Raj9OHcJfEUTjMvrOVwQTSmfAUwW6NeAkS&redirect_uri=http%3A%2F%2Flocalhost%3A8002%2Fauthorize&state=UTEB6T7AR9ewgBkSl97W4ls0KMv8zb' because it violates the following Content Security Policy directive: "form-action 'self'".

I can see the POST happen on the Toolhub side, so CSP is not actually blocking it outbound as the error message indicates. I think the issue is that Chrome is confused by the 302 redirect response that is returned from the POST?

I forked T329563: OAuth handshake with Toolhub blocked by CSP in Chrome to dig into the unexpected Chrome Content-Security-Policy issue.

I've incorporated code from the demo projects into our backend and have successfully made it past the demo server's authentication checks. I've opened a draft pull request (#23 Add backend oauth) to track my progress.

https://toolhub-demo.wmcloud.org/api/auditlogs/6878/

Have made a simple put request to the Toolhub demo server and marked the pull request as ready for review.

We continue to move forward: pull requests by myself and @Damilare will allow us to access the authentication endpoints via the front end.

Front end: Add proxy to front end
Back end: Update dev variables and oauth endpoints

NicoleLBee changed the task status from Open to In Progress.Feb 20 2023, 2:24 PM
NicoleLBee claimed this task.

Somehow this task ended up orphaned. But it's back now.

Bad news, @bd808: @Hannah_Njoroge and I are still getting the error referenced in T329563 when we try to authorize against the Toolhub test server while using a Chromium browser.

Bad news, @bd808: @Hannah_Njoroge and I are still getting the error referenced in T329563 when we try to authorize against the Toolhub test server while using a Chromium browser.

Annoying! Let's pick on implementation of the handshake and see if we can narrow down when/how it fails, or (hopefully) decide that we have one known working implementation to compare others to. The https://toolhub-gadget-demo.toolforge.org/ tool attempts OAuth authentication with https://toolhub-demo.wmcloud.org/. I just now used an incognito window in Chrome 110.0.5481.100 on MacOS to perform the full cycle of toolhub-gadget-demo → toolhub-demo → metawiki → login → approve MediaWiki OAuth client → toolhub-demo → approve Toolhub OAuth client → toolhub-gadget-demo → display Toolhub user & their permissions. What happens when you try to use it? Starting with an incognito window test might be useful to ensure that you are not reusing a session with either the tool (OAuth client) or the Toolhub instance (both OAuth authorization server & resource server) that failed earlier.

The gadget works fine for me in the incognito windows, which would seem to suggest that there's something wrong with how I've implemented things in Toolhunt.

Using Toolhunt in an incognito window I can follow the path localhost:8082/api/login → toolhub-demo → metawiki → login → approve MediaWiki OAuth client → toolhub-demo and it's there that it fails with the same error message as before:

Refused to send form data to 'https://toolhub-demo.wmcloud.org/o/authorize/?response_type=code&client_id=p7BWpSATeU4sScJFs1iqxb3VdyZ5g8X7dlVMJA9R&redirect_uri=http%3A%2F%2Flocalhost%3A8082%2Fapi%2Fauthorize&state=wAL0rbSNafp2rDFNOl2RVHqI5BopGf' because it violates the following Content Security Policy directive: "form-action 'self' localhost:8082".

On the off chance that our proxy had thrown something funny into the mix, I cut the frontend back out and returned to pure Flask, copied the contents of the demo tool's app.py over, but still no luck.

@bd808 More digging uncovered the following:

spot_the_difference.jpg (1,667×190 px, 105 KB)

When using the gadget (the top example), the header contains form-action 'self' toolhub-gadget-demo.toolforge.org

When using Toolhunt (bottom example), the header contains the plain form-action 'self'

So it's not receiving the _csp_update decoration for some reason.

@bd808 More digging uncovered the following:

spot_the_difference.jpg (1,667×190 px, 105 KB)

When using the gadget (the top example), the header contains form-action 'self' toolhub-gadget-demo.toolforge.org

When using Toolhunt (bottom example), the header contains the plain form-action 'self'

So it's not receiving the _csp_update decoration for some reason.

That result with the old CSP header doesn't match up with the error message you posted that shows "localhost:8082" as part of the policy:

Refused to send form data to 'https://toolhub-demo.wmcloud.org/o/authorize/?response_type=code&client_id=p7BWpSATeU4sScJFs1iqxb3VdyZ5g8X7dlVMJA9R&redirect_uri=http%3A%2F%2Flocalhost%3A8082%2Fapi%2Fauthorize&state=wAL0rbSNafp2rDFNOl2RVHqI5BopGf' because it violates the following Content Security Policy directive: "form-action 'self' localhost:8082".

This conflicting information is confusing.

The gadget works fine for me in the incognito windows, which would seem to suggest that there's something wrong with how I've implemented things in Toolhunt.

The main differences I can think of in your failing scenario is mixing an OAuth client running on your localhost without HTTPS with a remote OAuth authorization server using HTTPS. I will try to reproduce the failures with a similar setup and report back.

Damnit, yes, I just realized that I was looking at the GET request on the gadget side and the POST request following the failed attempt on the Toolhunt side. The initial GET request on the Toolhunt side does infact have the correct header

The gadget works fine for me in the incognito windows, which would seem to suggest that there's something wrong with how I've implemented things in Toolhunt.

The main differences I can think of in your failing scenario is mixing an OAuth client running on your localhost without HTTPS with a remote OAuth authorization server using HTTPS. I will try to reproduce the failures with a similar setup and report back.

I can reproduce the failure locally when mixing a non-HTTPS localhost client with https://toolhub-demo.wmcloud.org/ acting as the OAuth authorization server. I do see the expected "form-action 'self' localhost:8002;" CSP header clause. In theory the lack of a protocol specifier in the "localhost:8002" host source directive means that both HTTPS and HTTP are equally valid, but I'm wondering if the Chrome/Chromium developers decided to require a protocol match unless the protocol is explicit. It would be a relatively simple extension of the patch from T329563: OAuth handshake with Toolhub blocked by CSP in Chrome to make the protocol explicit. I think that is probably worth trying.

I can reproduce the failure locally when mixing a non-HTTPS localhost client with https://toolhub-demo.wmcloud.org/ acting as the OAuth authorization server. I do see the expected "form-action 'self' localhost:8002;" CSP header clause. In theory the lack of a protocol specifier in the "localhost:8002" host source directive means that both HTTPS and HTTP are equally valid, but I'm wondering if the Chrome/Chromium developers decided to require a protocol match unless the protocol is explicit. It would be a relatively simple extension of the patch from T329563: OAuth handshake with Toolhub blocked by CSP in Chrome to make the protocol explicit. I think that is probably worth trying.

The headers thing was entirely my mistake; I didn't spot that I was looking at two different requests until it was too late to do an embarrassed delete of my message.

Thank you for looking into this. I'd started looking into mkcert as a possible way to allow us to run HTTPS locally for development, but getting that to work with Docker appears nontrivial, at least for someone at my level.

Thank you for looking into this. I'd started looking into mkcert as a possible way to allow us to run HTTPS locally for development, but getting that to work with Docker appears nontrivial, at least for someone at my level.

Setting up local TLS is a huge pain in the butt no matter what your skill set. Let's see if https://gerrit.wikimedia.org/r/c/wikimedia/toolhub/+/890914/ makes anything better before resorting to more exotic fixes.

Let's see if https://gerrit.wikimedia.org/r/c/wikimedia/toolhub/+/890914/ makes anything better before resorting to more exotic fixes.

This patch has been merged and deployed to https://toolhub-demo.wmcloud.org/. With those changes in place I repeated my test from T327856#8634994 where I attempted to authorize my http://localhost:8002/ testing client against the OAuth authorization server running on https://toolhub-demo.wmcloud.org/. This time the CSP header includes "form-action 'self' http://localhost:8002;" and the redirect following the form submission is allowed.

@NicoleLBee, what happens with your client and browser now?

@bd808, it's working perfectly now, and @Hannah_Njoroge reports the same.