Using mwoauth2 in a Flask tool

The basic setup of a Flask tool using mwoauth2 goes as follows:

Create the Flask app:

app = flask.Flask(__name__)

Load the configuration from some secure source. For instance, a Toolforge tool may use Toolforge envvars:

app.config.from_prefixed_env('TOOL', loads=yaml.safe_load)

This would then load OAuth configuration from the following environment variables:

$ toolforge envvars create TOOL_OAUTH__CLIENT_ID 'abcde'

$ toolforge envvars create TOOL_OAUTH__CLIENT_SECRET
Enter the value of your envvar (Hit Ctrl+C to cancel): 12345

Alternatively (or in addition), you may use a configuration file, optionally using the toolforge.load_private_yaml() helper:

app.config.from_file('config.yaml', load=toolforge.load_private_yaml, silent=True)

Whichever configuration sources you use, app.config['OAUTH']['CLIENT_ID'] and app.config['OAUTH']['CLIENT_SECRET'] should be set to the client ID and client secret of an OAuth 2 client you registered.

Set up a user agent string to use (in accordance with the policy), perhaps using toolforge.set_user_agent() in the case of a Toolforge tool:

user_agent = toolforge.set_user_agent('example-tool', email='tool@example.com')
# or
user_agent = 'example-tool (tool@example.com) other-details...'

Create the OAuth management instance. If you’re going to use mwapi, use MWOAuth2FlaskMWApi:

oauth = MWOAuth2FlaskMWApi(
    host='https://meta.wikimedia.org',  # the wiki where OAuth authorization will happen
    user_agent=user_agent,
    app=app,
)

Otherwise, use MWOAuth2Flask:

oauth = MWOAuth2Flask(
    host='https://meta.wikimedia.org',
    user_agent=user_agent,
    app=app,
)

When targeting non-Wikimedia wikis, you may have to specify a custom api_path= parameter if it is not /w/api.php. Also, if you are migrating a tool from OAuth 1.0a to OAuth 2, the check_access_token_before_request=True parameter may be useful (see the API documentation for details).

Now the oauth instance is ready to use. To check if the user is logged in, use has_access_token():

@app.route(...)
def something():
    if oauth.has_access_token():
        # user is logged in
        ...
    else:
        # logged out, show a login link
        ...

To log in, redirect the user to the authorization_url():

@app.route('/login')
def login():
    return flask.redirect(oauth.authorization_url())

In the OAuth callback (the URL of which you configured in our OAuth client registration), call fetch_token() to finish logging in:

@app.route('/oauth/callback')
def oauth_callback():
    oauth.fetch_token()
    flask.session.permanent = True
    return flask.redirect(flask.url_for('index'))

Now the user is logged in and OAuth is ready to use. If you use mwapi, use mwapi_session():

@app.route('/userinfo')
def userinfo():
    session = oauth.mwapi_session()
    if session is None:
        return 'not logged in'
    response = session.get(action='query', meta='userinfo')
    return flask.jsonify(response['query']['userinfo'])

Otherwise, use oauth2_session():

@app.route('/userinfo')
def userinfo():
    session = oauth.oauth2_session()
    if session is None:
        return 'not logged in'
    response = session.get('https://meta.wikimedia.org/w/api.php',
                           params={'action': 'query', 'meta': 'userinfo', 'format': 'json'})
    return flask.jsonify(response.json()['query']['userinfo'])

To log the user out, call pop_access_token():

@app.route('/logout')
def logout():
    try:
        oauth.pop_access_token()
    except KeyError:
        pass
    flask.session.permanent = False
    return flask.redirect(flask.url_for('index'))