RSS

python-cas Flask Example

An example project to demo how to integrate python-cas into a Flask app.

python-cas is Python CAS (Central Authentication Server) client library support CAS 1.0/2.0/3.0. It was initially split from django-cas-ng to support any Python app to easily implement a CAS client.

This post is an example project to demo how to integrate python-cas into a Flask app. This should give you idea how to integrate python-cas into any other Python app.

If you are new to CAS, to get started about CAS, please checkout CAS 101 .

Flask Example Project

I created a Flask project and integrated python-cas to support CAS login/logout, The integration is pretty straightforward, the key point is Flask need call some CAS API list below:

Design

/login

/login to handle client login request and redirect to next after login successfully.

Accept query parameters:

  • next: string. The redirect URL after login successfully. e.g. %2Fprofile. NOTE: Need URL encoded.
  • ticket: string. CAS service ticket. When CAS authenticated success, it will have this parameter in callback. e.g. ST-1580754576-qbgmySJHjm.

After login to CAS successfully, set create a session to local Flask app to save login state. The session var is username.

/logout and /logout_callback

/logout will send logout request to CAS server to logout from CAS server, after CAS logged out user, CAS will redirect to /logout_callback, it will logged user out from Flask app locally by delete session.

/profile

/profile show logged user info. e.g. username.

Sequence Diagram

CAS login / logout flow sequence diagram as below:

CAS Login / Logout Flow

Code

The code is pretty simple, less than 50 lines of code. All the hard work is done by python-cas.

$ cloc app.py
--------------------------------------------------------------
Language        files        blank      comment           code
--------------------------------------------------------------
Python              1           18            0             47
--------------------------------------------------------------

The source code as below, it is self explained:

from flask import Flask, request, session, redirect, url_for
from cas import CASClient

app = Flask(__name__)
app.secret_key = 'V7nlCN90LPHOTA9PGGyf'

cas_client = CASClient(
    version=3,
    service_url='http://localhost:5000/login?next=%2Fprofile',
    server_url='https://django-cas-ng-demo-server.herokuapp.com/cas/'
)


@app.route('/')
def index():
    return redirect(url_for('login'))


@app.route('/profile')
def profile(method=['GET']):
    if 'username' in session:
        return 'Logged in as %s. <a href="/logout">Logout</a>' % session['username']
    return 'Login required. <a href="/login">Login</a>', 403


@app.route('/login')
def login():
    if 'username' in session:
        # Already logged in
        return redirect(url_for('profile'))

    next = request.args.get('next')
    ticket = request.args.get('ticket')
    if not ticket:
        # No ticket, the request come from end user, send to CAS login
        cas_login_url = cas_client.get_login_url()
        app.logger.debug('CAS login URL: %s', cas_login_url)
        return redirect(cas_login_url)

    # There is a ticket, the request come from CAS as callback.
    # need call `verify_ticket()` to validate ticket and get user profile.
    app.logger.debug('ticket: %s', ticket)
    app.logger.debug('next: %s', next)

    user, attributes, pgtiou = cas_client.verify_ticket(ticket)

    app.logger.debug(
        'CAS verify ticket response: user: %s, attributes: %s, pgtiou: %s', user, attributes, pgtiou)

    if not user:
        return 'Failed to verify ticket. <a href="/login">Login</a>'
    else:  # Login successfully, redirect according `next` query parameter.
        session['username'] = user
        return redirect(next)


@app.route('/logout')
def logout():
    redirect_url = url_for('logout_callback', _external=True)
    cas_logout_url = cas_client.get_logout_url(redirect_url)
    app.logger.debug('CAS logout URL: %s', cas_logout_url)

    return redirect(cas_logout_url)


@app.route('/logout_callback')
def logout_callback():
    # redirect from CAS logout request after CAS logout successfully
    session.pop('username', None)
    return 'Logged out from CAS. <a href="/login">Login</a>'

You can clone it from https://github.com/python-cas/flask-example/

Run your local server:

$ git clone git@github.com:python-cas/flask-example.git python-cas-flask-example
$ cd python-cas-flask-example
$ pip install -r requirements.txt
$ sh run_debug_server.sh
 * Serving Flask app "app.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

You may curious the actual CAS flow, Here is the debug for reference, you can get more insight about how CAS redirect and ticket validation.

# Login
"GET /login HTTP/1.1" 302 -
DEBUG in app: CAS login URL: https://django-cas-ng-demo-server.herokuapp.com/cas/login?service=http%3A%2F%2Flocalhost%3A5000%2Flogin%3Fnext%3D%252Fprofile
"GET /login HTTP/1.1" 302 -
DEBUG in app: ticket: ST-1580754576-qbgmySJHjmvdeFjy4ZGwkA1IwxGcZxL4
DEBUG in app: next: /profile
DEBUG in app: user: admin, attributes: {}, pgtiou: None
"GET /login?next=%2Fprofile&ticket=ST-1580754576-qbgmySJHjmvdeFjy4ZGwkA1IwxGcZxL4 HTTP/1.1" 302 -
"GET /profile HTTP/1.1" 200 -

# Logout
DEBUG in app: CAS logout URL: https://django-cas-ng-demo-server.herokuapp.com/cas/logout?service=http%3A%2F%2Flocalhost%3A5000%2Flogout_callback
"GET /logout HTTP/1.1" 302 -
"GET /logout_callback HTTP/1.1" 200 -

Live Demo

If you want to see a live demo, you can click here. The following username/password can be used to login in demo server.

username: admin
password: django-cas-ng

Reference

A1Blocker - Hide Apps

Hide Apps from Home Screen, App Library and Lock Apps to protect your privacy even further (requires iOS 16).

DNS Firewall for iOS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad