Skip to content

Commit fef5276

Browse files
feat: add clear expired tokens task in ol-social-auth (#778)
* feat: add task * fix: issues * chore: bump version * docs: update readme * fix: issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: rename to CELERYBEAT_SCHEDULE --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent e8da800 commit fef5276

9 files changed

Lines changed: 137 additions & 1 deletion

File tree

src/ol_social_auth/README.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,15 @@ Make sure to properly configure the plugin following the links in the above "Con
3232
* Install the plugin in the lms following the installation steps above.
3333
* Verify that you are not logged in on edx-platform.
3434
* Create a new user in your MIT application and verify that a corresponding user is successfully created on the edX platform.
35+
36+
Expired Token Cleanup
37+
---------------------
38+
This plugin includes a scheduled Celery task (``ol_clear_expired_tokens``) that automatically removes expired OAuth2 access tokens, refresh tokens, and grant tokens from the database.
39+
40+
**Behavior:**
41+
42+
* Runs every **Monday at 9:00 AM** (server time) via Celery Beat by default. The schedule can be customized by overriding the ``ol_clear_expired_tokens`` entry in ``CELERYBEAT_SCHEDULE``.
43+
* Uses django-oauth-toolkit's ``clear_expired()`` to delete tokens that have exceeded the configured expiration threshold.
44+
* Sets ``REFRESH_TOKEN_EXPIRE_SECONDS`` to **30 days** (overriding the edx-platform default of 90 days). Tokens revoked or expired longer than 30 days ago will be cleaned up.
45+
46+
**Note:** If running this plugin for the first time on a database with a large backlog of expired tokens (millions of rows), consider running the ``edx_clear_expired_tokens`` management command manually first to reduce the initial volume before relying on the scheduled task.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""ol_social_auth Django application initialization."""
2+
3+
from django.apps import AppConfig
4+
from edx_django_utils.plugins import PluginSettings
5+
from openedx.core.djangoapps.plugins.constants import ProjectType, SettingsType
6+
7+
8+
class OLSocialAuthConfig(AppConfig):
9+
name = "ol_social_auth"
10+
verbose_name = "OL Social Auth"
11+
12+
plugin_app = {
13+
PluginSettings.CONFIG: {
14+
ProjectType.LMS: {
15+
SettingsType.COMMON: {
16+
PluginSettings.RELATIVE_PATH: "settings.common",
17+
},
18+
SettingsType.PRODUCTION: {
19+
PluginSettings.RELATIVE_PATH: "settings.production",
20+
},
21+
},
22+
},
23+
}

src/ol_social_auth/ol_social_auth/settings/__init__.py

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Common settings for the ol-social-auth plugin."""
2+
3+
from celery.schedules import crontab
4+
5+
6+
def plugin_settings(settings):
7+
"""Settings for the ol-social-auth plugin.""" # noqa: D401
8+
settings.OAUTH2_PROVIDER["REFRESH_TOKEN_EXPIRE_SECONDS"] = (
9+
30 * 24 * 60 * 60 # 30 days
10+
)
11+
# Add ol_clear_expired_tokens to the Celery beat schedule.
12+
if not hasattr(settings, "CELERYBEAT_SCHEDULE"):
13+
settings.CELERYBEAT_SCHEDULE = {}
14+
settings.CELERYBEAT_SCHEDULE["ol_clear_expired_tokens"] = {
15+
"task": "ol_social_auth.tasks.ol_clear_expired_tokens",
16+
"schedule": crontab(hour=9, minute=0, day_of_week="monday"),
17+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Production settings for the ol-social-auth plugin."""
2+
3+
4+
def plugin_settings(settings):
5+
"""Production overrides for ol-social-auth plugin."""
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Celery tasks for ol-social-auth plugin."""
2+
3+
import logging
4+
5+
from celery import shared_task
6+
from oauth2_provider.models import clear_expired
7+
8+
log = logging.getLogger(__name__)
9+
oauth2_logger = logging.getLogger("oauth2_provider")
10+
11+
12+
@shared_task(acks_late=True)
13+
def ol_clear_expired_tokens():
14+
"""Clear expired OAuth2 access, refresh, and ID tokens."""
15+
log.info("Starting ol_clear_expired_tokens...")
16+
# Suppress debug-level logs from oauth2_provider during cleanup.
17+
# Its batch_delete debug logs lack the 'userid' field expected by
18+
# Open edX's custom log formatter, causing noisy ValueError tracebacks.
19+
original_level = oauth2_logger.level
20+
oauth2_logger.setLevel(logging.INFO)
21+
try:
22+
clear_expired()
23+
finally:
24+
oauth2_logger.setLevel(original_level)
25+
log.info("Finished ol_clear_expired_tokens.")

src/ol_social_auth/pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "ol-social-auth"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
description = "An Open edX plugin implementing MIT social auth backend"
55
authors = [
66
{name = "MIT Office of Digital Learning"}
@@ -11,9 +11,13 @@ requires-python = ">=3.11"
1111
keywords = ["Python", "edx"]
1212
dependencies = [
1313
"Django>=4.0",
14+
"django-oauth-toolkit",
1415
"social-auth-core>=4.5.4",
1516
]
1617

18+
[project.entry-points."lms.djangoapp"]
19+
ol_social_auth = "ol_social_auth.apps:OLSocialAuthConfig"
20+
1721
[build-system]
1822
requires = ["hatchling"]
1923
build-backend = "hatchling.build"

src/ol_social_auth/setup.cfg

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[tool:pytest]
2+
pep8maxlinelength = 119
3+
DJANGO_SETTINGS_MODULE = lms.envs.test
4+
addopts = --nomigrations --reuse-db --durations=20
5+
filterwarnings =
6+
default
7+
ignore::xblock.exceptions.FieldDataDeprecationWarning
8+
ignore::pytest.PytestConfigWarning
9+
ignore:No request passed to the backend, unable to rate-limit:UserWarning
10+
ignore:Flags not at the start of the expression:DeprecationWarning
11+
ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc':DeprecationWarning
12+
ignore:invalid escape sequence:DeprecationWarning
13+
ignore:`formatargspec` is deprecated since Python 3.5:DeprecationWarning
14+
ignore:the imp module is deprecated in favour of importlib:DeprecationWarning
15+
ignore:"is" with a literal:SyntaxWarning
16+
ignore:defusedxml.lxml is no longer supported:DeprecationWarning
17+
ignore: `np.int` is a deprecated alias for the builtin `int`.:DeprecationWarning
18+
ignore: `np.float` is a deprecated alias for the builtin `float`.:DeprecationWarning
19+
ignore: `np.complex` is a deprecated alias for the builtin `complex`.:DeprecationWarning
20+
ignore: 'etree' is deprecated. Use 'xml.etree.ElementTree' instead.:DeprecationWarning
21+
ignore: defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead.:DeprecationWarning
22+
23+
junit_family = xunit2
24+
norecursedirs = .* *.egg build conf dist node_modules test_root cms/envs lms/envs
25+
python_classes =
26+
python_files = tests.py test_*.py tests_*.py *_tests.py __init__.py
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Tests for ol_social_auth tasks."""
2+
3+
from ol_social_auth import tasks
4+
5+
6+
def test_ol_clear_expired_tokens(mocker):
7+
"""Test that ol_clear_expired_tokens calls the clear_expired function."""
8+
patched_clear_expired = mocker.patch("ol_social_auth.tasks.clear_expired")
9+
10+
tasks.ol_clear_expired_tokens.delay()
11+
patched_clear_expired.assert_called_once_with()
12+
13+
14+
def test_ol_clear_expired_tokens_logging(mocker):
15+
"""Test that ol_clear_expired_tokens logs start and finish messages."""
16+
mocker.patch("ol_social_auth.tasks.clear_expired")
17+
patched_log_info = mocker.patch("ol_social_auth.tasks.log.info")
18+
19+
tasks.ol_clear_expired_tokens()
20+
21+
expected_log_call_count = 2
22+
assert patched_log_info.call_count == expected_log_call_count # noqa: S101
23+
patched_log_info.assert_any_call("Starting ol_clear_expired_tokens...")
24+
patched_log_info.assert_any_call("Finished ol_clear_expired_tokens.")

0 commit comments

Comments
 (0)