Building a Flask project with OAuth enabled on Tool Labs

Posted on 15 May 2015 in wikimedia

This is a guide to building a simple Flask project on Tool Labs. Tool Labs is the Wikimedia shared hosting project, for projects ('tools') related to Wikipedia or one of the other Wikimedia projects.

This guide assumes you have a Tool Labs account and some knowledge of getting around. The Help pages are a good place to get started.

The goal of this guide is to:

  • Create a new tool, which
  • Runs a Python web site using Flask, which
  • Allows the user to log in using their Wikimedia SUL account.

Step 1: Creating a new tool

  • Create a new tool. In this tutorial, I've used <TOOL NAME> everywhere the tool name should be used.
  • Log in to If you are already logged in, log out and log in again.
  • Run become <TOOL NAME> to change to the tool user.

Step 2: Install a basic Flask web app

Start by creating a virtual environment for the app:

# working directory is /data/project/<TOOL NAME>/
mkdir -p www/python
cd www/python  # wd is now ~/www/python
virtualenv venv
source venv/bin/activate

Then create a source directory, define dependencies and install those:

mkdir src
cd src  # wd is now ~/www/python/src
cat > requirements.txt << EOF
pip install -r requirements.txt

Finally, download the example app bundled with flask-mwoauth.

wget -O

Step 3: Set up OAuth

We will store secret tokens in, so start by denying read access to world and group:

chmod 600

Then, register a new consumer, and enter the secret token and key you get in

  • As callback URL, use:<TOOL NAME>/oauth-callback
  • As contact e-mail address, use the e-mail address linked to your account.
  • Keep the default grant settings ('Request authorization for specific permissions.' with just 'Basic rights' selected)
  • Keep all other settings as they are

  • Don't worry about approval for now; you can use your own account before the consumer has been approved.

After submitting the form, you get two tokens.

  • Fill in the consumer token as consumer_key
  • Fill in the secret token as consumer_secret

Finally, generate a secret key using

python -c "import os; print repr(os.urandom(24))"

and use that for the app.secret_key in

Step 4: Configure the web server

cd ..  # wd is now ~/www/python
cat > uwsgi.ini << EOF
webservice2 uwsgi-python start

and follow the status using

tail -f ~/uwsgi.log

where you should see something like:

thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to TCP address :46438 fd 3
Python version: 2.7.6 (default, Mar 22 2014, 23:03:41)  [GCC 4.8.2]
Set PythonHome to /data/project/<TOOL NAME>/www/python/venv
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0xe56820
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 363960 bytes (355 KB) for 4 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 4 seconds on interpreter 0xe56820 pid: 10851 (default app)
mounting /data/project/<TOOL NAME>/www/python/src/ on /<TOOL NAME>

Step 5: Test!

Visit<TOOL NAME>/ with your browser. You should see something like this:

logged in as: None
login / logout

Click login. This should redirect you to, with the following message:


In order to complete your request, <TOOL NAME> needs permission to access information on all projects of this site on your behalf. No changes will be made with your account.

Click Allow. You are now redirected back to the main page, which should now show:

logged in as: u'<USERNAME>'
login / logout

Click logout. This shows:

Logged out!

Click back and refresh. The page shows

logged in as: None


Congratulations! Your basic flask-mwoauth is ready!


Add app.debug = True to and check uwsgi.log for more information. Note that this needs a webservice2 restart.

Incorrect keys

This can be caused e.g. by a copy-paste error of the consumer key and secret. You will get an HTTP/500 on /login, and the stack trace in uwsgi.log looks like this:

Traceback (most recent call last):
  File "/data/project/valhallasw-testing-tool/www/python/venv/local/lib/python2.7/site-packages/", line 331, in authorize
    token = self.generate_request_token(callback)[0]
  File "/data/project/valhallasw-testing-tool/www/python/venv/local/lib/python2.7/site-packages/", line 304, in generate_request_token
    tup = (data['oauth_token'], data['oauth_token_secret'])
KeyError: 'oauth_token'

Check your key and secret, or try re-registering.