Skip to main content

touchstone-auth

PyPI-downloads PyPI-version PyPI-license Supported python versions

Rationale

MIT itself and MIT-adjacent organizations offer many useful web services through a Single-Sign-On (SSO) service called Touchstone, with two-factor logins provided by Duo. This is great, and allows easy access to many functionalities, but because MIT does not use a commercial SSO provider (like Okta and others), there is limited ability to access Touchstone-protected sites without a web browser.

Enter touchstone-auth, a Python package powered mostly by the requests package! This lets user authenticate themselves programmatically. Cookies are cached, meaning that re-authentication is only needed once cookies expire.

Okta updates

MIT recently moved to using Okta for Touchstone flow. This deprecates certificate and Kerberos authentication!

Install

This package is on Pip, so you can just:

pip install touchstone-auth

Alternatively, you can get built wheels from the Releases tab on Github.

N.B. if installing manually, requests_pkcs12 must be version v1.10 (pip install handles this automatically).

Quickstart

The class TouchstoneSession is simply a requests.Session that performs the Touchstone authentication flow before returning a working session to you, the authenticated user.

It is easiest to use as a context manager. Because Touchstone authentication requires a client-side certificate, remember to not hard-code your credentials! The example here loads credentials from a json file called credentials.json:

{
    "certfile": "some_client_credential.p12",
    "password": "horse-battery-staple-correct"
}

Then, in your Python file, you can do the following:

import json
from touchstone_auth import TouchstoneSession, CertificateAuth

with open('credentials.json') as cred_file:
    credentials = json.load(cred_file)

with TouchstoneSession(
    base_url='https://atlas.mit.edu',
    auth_type=CertificateAuth(credentials['certfile'], credentials['password']),
    cookiejar_filename='cookies.pickle') as s:

    response = s.get('https://atlas.mit.edu/atlas/Main.action')

When you call this the first time, your Python script will hang on the 2FA step until the second-factor (by default, Duo push) is accepted. Subsequent requests should not block until the 30-day "remember me" period is exceeded.

If this blocking behavior is undesired, you can set the argument should_block=False in the TouchstoneSession constructor. If a blocking 2FA push is required, the error WouldBlockError will instead be raised.

Finally, there is a verbose argument; setting verbose=True will output extra information about how processing is proceeding.

Alternate authentication (deprecated by Okta)

You can use other authentication methods as well, but these will be removed because they are not supported by Okta!

Certificate as a byte array

If you have your certificate as a byte string instead of a filename, just pass the bytes as your certificate:

with TouchstoneSession(
    base_url='...',
    auth_type=CertificateAuth(cert_bytes, cert_password),
    cookiejar_filename='cookies.pickle') as s:

Username/password

To use your username and password (do not hard code your credentials in your code!), pass a UsernamePassAuth instead:

with TouchstoneSession(
    base_url='...',
    auth_type=UsernamePassAuth(kerb_account, kerb_password),
    cookiejar_filename='cookies.pickle') as s:

Kerberos tickets

To authenticate using Kerberos tickets, pass KerberosAuth() as the auth_type parameter to TouchstoneSession, as in:

with TouchstoneSession(
    base_url='...',
    auth_type=KerberosAuth(),
    cookiejar_filename='cookies.pickle') as s:

Complete Examples

Get your latest paystub from ADP:

import json
from touchstone_auth import TouchstoneSession, UsernamePassAuth

with open('credentials.json') as cred_file:
    credentials = json.load(cred_file)

with TouchstoneSession(
    base_url='https://myadp.mit.edu',
    auth_type=UsernamePassAuth(credentials['username'], credentials['password']),
    cookiejar_filename='cookies.pickle') as s:

    response = s.get('https://my.adp.com/myadp_prefix/v1_0/O/A/payStatements?adjustments=yes&numberoflastpaydates=160')
    response_json = json.loads(response.text)
    latest = response_json['payStatements'][0]
    print('Latest paystub ({}): ${} net, ${} gross'.format(
        latest['payDate'],
        latest['netPayAmount']['amountValue'],
        latest['grossPayAmount']['amountValue']))

which returns Latest paystub (2021-08-13): $XXXX.XX net, $YYYY.YY gross when run.

Check your Covidpass building access status:

import json
from touchstone_auth import TouchstoneSession, UsernamePassAuth

with open('credentials.json') as cred_file:
    credentials = json.load(cred_file)

with TouchstoneSession(
    base_url=r'https://atlas-auth.mit.edu/oauth2/authorize?identity_provider=Touchstone&redirect_uri=https://covidpass.mit.edu&response_type=TOKEN&client_id=2ao42ccnajj7jpqd7h059n7eoc&scope=covid19/impersonate covid19/user digital-id/search digital-id/user openid profile',
    auth_type=UsernamePassAuth(credentials['username'], credentials['password']),
    cookiejar_filename='cookies.pickle') as s:

    response = json.loads(s.get('https://api.mit.edu/pass-v1/pass/access_status').text)
    print('Current Covidpass status: {}'.format(response['status']))

This returns Current Covidpass status: access_granted if you are in fact up to date on Covidpass.

For the various "new Atlas" OAUTH2 applications, you need to find the relevant authorization URL to put as the base URL.

How did I find the proper URL for Covidpass? By looking in your browser's Developer Tools, you can locate the last GET request prior to redirect to idp.mit.edu, then remove the extraneous state parameter.

Get the registration list for a class, using Kerberos authentication:

from touchstone_auth import TouchstoneSession, UsernamePassAuth
from bs4 import BeautifulSoup

with open('credentials.json') as cred_file:
    credentials = json.load(cred_file)

with TouchstoneSession(base_url='https://student.mit.edu/',
                       auth_type=UsernamePassAuth(config['username'], config['password']),
                       cookiejar_filename='cookies.pickle') as s:
    payload = {'termcode': '2023FA', 'SUBJECT01': '6.1600'}
    headers = {'Referer': 'https://student.mit.edu/cgi-bin/sfprwlst_sel.sh'}
    r = s.post('https://student.mit.edu/cgi-bin/sfprwlst.sh', data=payload, headers=headers)
    print(BeautifulSoup(r.text, 'html.parser').pre.text)

Selecting two-factor method

With version 0.3.0, you can also select between phone-call and Duo Push two factor authentication. touchstone-auth defaults to Duo Push if you do not select one.

To switch between the two, pass an additional twofactor_auth argument. For example, to use the phone-call two factor method in the above example, additionally import the TwofactorType enum and pass it to the session constructor:

import json
from touchstone_auth import TouchstoneSession, UsernamePassAuth, TwofactorType

with open('credentials.json') as cred_file:
    credentials = json.load(cred_file)

with TouchstoneSession(
    base_url=r'https://atlas-auth.mit.edu/oauth2/authorize?identity_provider=Touchstone&redirect_uri=https://covidpass.mit.edu&response_type=TOKEN&client_id=2ao42ccnajj7jpqd7h059n7eoc&scope=covid19/impersonate covid19/user digital-id/search digital-id/user openid profile',
    auth_type=UsernamePassAuth(credentials['username'], credentials['password']),
    cookiejar_filename='cookies.pickle',
    twofactor_type=TwofactorType.PHONE_CALL) as s:

    response = json.loads(s.get('https://api.mit.edu/pass-v1/pass/access_status').text)
    print('Current Covidpass status: {}'.format(response['status']))

Developer install

If you'd like to hack locally on touchstone-auth, after cloning this repository:

$ git clone https://github.com/meson800/touchstone-auth.git
$ cd git

you can create a local virtual environment, and install touchstone-auth in "development mode"

$ python -m venv env
$ .\env\Scripts\activate    (on Windows)
$ source env/bin/activate   (on Mac/Linux)
$ pip install -e .

After this 'local install', you can use and import touchstone-auth freely without having to re-install after each update.

Debugging

You can run this package as a module to generate debug output. By default:

python -m touchstone_auth

attempts to login to https://atlas.mit.edu using the credentials in credentials.json and prints out the title of the resulting HTML.

You can change the target and the credential file with the --url and --credentials arguments.

This is useful for a sanity check, as it returns the verbose debug information.

Known problems

  • New MIT applications may not use Shibboleth going forward. This library does not currently support non-Shibboleth applications, but this is an intended feature for 0.9.0

Changelog

See the CHANGELOG for detailed changes.

## [0.8.0] - 2024-06-23
### Updated
- Now works with the new Okta Touchstone flow, for applications that use the Shibboleth proxy.
### Added
- The library can now be run as a module (python -m touchstone_auth) for debugging purposes.

License

This is licensed by the MIT license. Use freely!

Metadata

Release files for touchstone-auth 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for touchstone-auth 0.8.0
File Size Uploaded Provenance
touchstone_auth-0.8.0.tar.gz 17.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for touchstone-auth 0.8.0
File Interpreter ABI Platform
touchstone_auth-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.5 kB

Release files / touchstone_auth-0.8.0.tar.gz

Download URL touchstone_auth-0.8.0.tar.gz
Size 17.4 kB
Tags Source
SHA-256 checksum
How to use checksums
2eb9de4a8bd84dce680106b03f321bc592300fb0e0bca77a5ec96790341e45a4
BLAKE2b-256 checksum
How to use checksums
14f097bdbd74e641e20f646c3ea8905dcd890971685b7a8102f7d656291930e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.11.6

Release files / touchstone_auth-0.8.0-py3-none-any.whl

Download URL touchstone_auth-0.8.0-py3-none-any.whl
Size 15.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d09442b888d22d4a44874138cc2ec1cb54f2d4bd263f7e9116eb34f90d177891
BLAKE2b-256 checksum
How to use checksums
8c82dbb89416292ab859fda38e9afb39e69535fbedf1d7a6c8015b77c83b17be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.11.6

Security for touchstone-auth 0.8.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page