Management commands

Django OAuth Toolkit exposes some useful management commands that can be run via shell or by other means such as cron or Celery.

cleartokens

The cleartokens management command allows the user to remove refresh tokens that can no longer be used:

  • those that have been idle longer than REFRESH_TOKEN_EXPIRE_SECONDS – i.e. whose paired access token expired more than REFRESH_TOKEN_EXPIRE_SECONDS ago (see REFRESH_TOKEN_EXPIRE_SECONDS for the idle-expiry semantics);

  • those that have been revoked for longer than the REFRESH_TOKEN_GRACE_PERIOD_SECONDS setting; and

  • orphaned refresh tokens – non-revoked refresh tokens whose access token was deleted out of band, leaving nothing to refresh against. These are removed unconditionally, regardless of REFRESH_TOKEN_EXPIRE_SECONDS.

It is important that this command is run regularly (eg: via cron) to avoid cluttering the database with expired refresh tokens.

If cleartokens runs daily the maximum delay before a refresh token is removed is its retention period (REFRESH_TOKEN_EXPIRE_SECONDS for expired tokens, REFRESH_TOKEN_GRACE_PERIOD_SECONDS for revoked ones) + 1 day. This is normally not a problem since refresh tokens are long lived.

Note that REFRESH_TOKEN_EXPIRE_SECONDS is also enforced when a refresh token is presented, so a refresh token past its lifetime is rejected even if cleartokens has not yet removed it.

To prevent the CPU and RAM high peaks during deletion process use CLEAR_EXPIRED_TOKENS_BATCH_SIZE and CLEAR_EXPIRED_TOKENS_BATCH_INTERVAL settings to adjust the process speed.

The cleartokens management command will also delete expired access and ID tokens alongside expired refresh tokens.

Refresh tokens that have already been revoked (for example by refresh token rotation) are removed as soon as their REFRESH_TOKEN_GRACE_PERIOD_SECONDS grace period has passed, without waiting for REFRESH_TOKEN_EXPIRE_SECONDS. The exception is when REFRESH_TOKEN_REUSE_PROTECTION is enabled: revoked refresh tokens are then what allows reuse of a rotated token to be detected, so they are kept until they expire per REFRESH_TOKEN_EXPIRE_SECONDS.

Note: Refresh tokens need to expire before AccessTokens can be removed from the database. Using cleartokens without REFRESH_TOKEN_EXPIRE_SECONDS has limited effect. When REFRESH_TOKEN_EXPIRE_SECONDS is unset (or 0), cleartokens prints a warning to stderr to make this easy to notice: only revoked and orphaned refresh tokens are removed, and expired access and ID tokens that are still bound to a refresh token are retained as long as that refresh token lives. (Expired access tokens with no refresh token, and their ID tokens, are still cleared regardless of REFRESH_TOKEN_EXPIRE_SECONDS.) Set REFRESH_TOKEN_EXPIRE_SECONDS to enable expiry-based cleanup.

clearcimdapplications

The clearcimdapplications management command deletes CIMD-registered applications (registration_source="cimd") whose cached metadata has expired (cimd_expires_at in the past) and that hold no live access token, ID token, grant, or unrevoked refresh token. Because CIMD rows are created automatically on first sight of a client URL — and re-created the same way if the client returns — deleting them only reclaims storage. Run it regularly (eg: via cron, alongside cleartokens) when CIMD is enabled, since the application store is otherwise attacker-mintable (see the CIMD security model).

Deletion is batched (--batch-size, default 1000). Each batch’s liveness check and delete run in one transaction with the application rows locked, so a token minted concurrently cannot slip in between the check and the delete and be cascade-deleted with its application; batching keeps the number of rows locked at once bounded.

createapplication

The createapplication management command provides a shortcut to create a new application in a programmatic way.

usage: manage.py createapplication [-h] [--client-id CLIENT_ID] [--user USER]
                                   [--redirect-uris REDIRECT_URIS]
                                   [--post-logout-redirect-uris POST_LOGOUT_REDIRECT_URIS]
                                   [--client-secret CLIENT_SECRET]
                                   [--name NAME] [--skip-authorization]
                                   [--algorithm ALGORITHM] [--version]
                                   [-v {0,1,2,3}] [--settings SETTINGS]
                                   [--pythonpath PYTHONPATH] [--traceback]
                                   [--no-color] [--force-color]
                                   [--skip-checks]
                                   client_type authorization_grant_type

Shortcut to create a new application in a programmatic way

positional arguments:
  client_type           The client type, one of: confidential, public
  authorization_grant_type
                        The type of authorization grant to be used, one of:
                        authorization-code, implicit, password, client-
                        credentials, openid-hybrid

optional arguments:
  -h, --help            show this help message and exit
  --client-id CLIENT_ID
                        The ID of the new application
  --user USER           The user the application belongs to
  --redirect-uris REDIRECT_URIS
                        The redirect URIs, this must be a space separated
                        string e.g 'URI1 URI2'
  --post-logout-redirect-uris POST_LOGOUT_REDIRECT_URIS
                        The post logout redirect URIs, this must be a space
                        separated string e.g 'URI1 URI2'
  --client-secret CLIENT_SECRET
                        The secret for this application
  --name NAME           The name this application
  --skip-authorization  If set, completely bypass the authorization form, even
                        on the first use of the application
  --algorithm ALGORITHM
                        The OIDC token signing algorithm for this application,
                        one of: RS256, HS256
  --version             Show program's version number and exit.
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions.
  --no-color            Don't colorize the command output.
  --force-color         Force colorization of the command output.
  --skip-checks         Skip system checks.

If you let createapplication auto-generate the secret then it displays the value before hashing it.