Skip to content

fix: preserve next redirect across OAuth session cleanup - #470

Open
djoseph-apphelix wants to merge 2 commits into
openedx:masterfrom
djoseph-apphelix:fix/preserve-next-across-session-cleanup
Open

djoseph-apphelix wants to merge 2 commits into
openedx:masterfrom
djoseph-apphelix:fix/preserve-next-across-session-cleanup

Conversation

@djoseph-apphelix

@djoseph-apphelix djoseph-apphelix commented Oct 28, 2026 •

Copy link
Copy Markdown

Problem

Since 4.6.2, EdXOAuth2.start() calls logout(request) when a user is already authenticated. logout() flushes the session, and social-auth had just stored the sanitized next redirect in that session. The value is lost, and do_complete() falls back to LOGIN_REDIRECT_URL.

On Credentials, LOGIN_REDIRECT_URL is the staff-only /api/v2/credentials/, so learners with an existing Credentials session who follow /login/?next=/credentials/<uuid>/ get a 403 instead of their certificate. Learners with no session are unaffected, which made it hard to reproduce.

Fix

Read next and every field named in FIELDS_STORED_IN_SESSION from the strategy session before logout(), and set them again after. The cleanup itself is unchanged. Nothing else social-auth writes before start() is touched.

The FIELDS_STORED_IN_SESSION part covers #471. do_auth() stores those fields at the same point it stores next, so the flush lost them too. No current EdXOAuth2 consumer sets that list (the LMS does, but it does not log in through this backend), so that half is hardening rather than a known production bug. next is handled explicitly because social-auth stores it through a separate, sanitized path, and listing it in FIELDS_STORED_IN_SESSION as well is harmless.

Why the existing test passed

OAuth2Test uses social-core's TestStrategy, whose session is a private dict, not request.session, so logout() cannot reach it. The new test points the strategy's session at request.session, mirroring DjangoStrategy. Without the fix it fails with None != '/credentials/abc/', and without the second commit it fails with None != 'login' on the auth_entry field.

Verification

  • pytest src/auth_backends/tests: 22 passed
  • pycodestyle and pylint clean
  • Reproduced and verified end to end on a Credentials devstack running 4.6.2 with social-auth-core 4.8.3, as a non-staff user.

Closes #471

AI Disclosure

Claude (Claude Code) assisted with the root-cause investigation, the code and test changes, and drafting this description. I reproduced the bug and verified the fix on a Credentials local development environment (devstack) and reviewed every change.

@djoseph-apphelix
djoseph-apphelix force-pushed the fix/preserve-next-across-session-cleanup branch from b5b9498 to f069208 Compare October 28, 2026 06:16
@djoseph-apphelix
djoseph-apphelix requested review from feanil and a balanced review from Copilot October 28, 2026 06:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues were identified for the change preserving the post-login redirect across logout.

No open issues 🎉

What changed in this PR

Preserves the intended post-login destination when EdXOAuth2.start() logs out an already-authenticated user, preventing session cleanup from losing social-auth’s stored next redirect.

Changes:

  • Saves next before logout and restores it afterward without preserving other session data.
  • Adds a regression test that shares the strategy session with request.session.
  • Documents the redirect fix in the changelog.
File Description
src/​auth_backends/​tests/​test_backends.py Tests that session cleanup preserves next while logging out the user.
src/​auth_backends/​backends.py Restores the stored redirect after logout flushes the session.
CHANGELOG.rst Records the fix under Unreleased.

Review effort: Balanced

@robrap robrap left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if there were other query parameters that we might also need to retain. I worked with Claude on that for a bit, and here is an AI comment based on my question. Note that openedx-platform uses this setting.

[UPDATED] I moved my comment to #471 so this could potentially land faster. I am going to add an approval to the PR as-is.

@robrap robrap left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@feanil: See my comment #470 (review). You'll need to decide if you want to approve and land this as-is, or wait until someone is able to do the larger fix. Thank you.
[UPDATE]: @djoseph-apphelix already fixed the new issue as part of this PR, so there is no longer a decision to be made around 1 vs 2 PRs.

@robrap

robrap commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

FYI: @openedx/committers-auth-backends
[UPDATE] I can't merge.

@djoseph-apphelix

Copy link
Copy Markdown
Author

Pushed 764465b, which also addresses #471: start() now carries every field in FIELDS_STORED_IN_SESSION across the forced logout as well as next. The existing test is extended to cover an auth_entry field. PR body updated; CI is green on the new commit.

@robrap

robrap commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

@openedx/committers-auth-backends: I presume (but am uncertain) that @djoseph-apphelix had support from AI. I passed on the AI policy and asked that that be made transparent (if needed), but hope that the bug fix doesn't get held up for that. This is a fix that probably should be backported, but not sure if that requires updating all the services?
[UPDATE]: The PR description has been updated accordingly.

EdXOAuth2.start() logs out an already-authenticated user before starting
a new OAuth flow. Django's logout() flushes the session, which also drops
the sanitized `next` value social-auth stored one step earlier, so the
user lands on LOGIN_REDIRECT_URL instead of the page they requested.

Read `next` before the logout and write it back afterwards. The cleanup
itself is unchanged.

The existing test did not catch this because social-core's TestStrategy
keeps its session in a private dict rather than request.session, so the
flush never reached it. The new test points the strategy at
request.session, matching DjangoStrategy.

Coding assistant: Claude Code
social-auth's do_auth() stores every field named in FIELDS_STORED_IN_SESSION
right before start(), at the same point it stores the sanitized next. The
forced logout() flushed those too. Carry them across alongside next.

No current EdXOAuth2 consumer sets FIELDS_STORED_IN_SESSION, so this is
hardening for deployments that add fields later.

Closes openedx#471

Coding assistant: Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are reported for the changes preserving OAuth redirects and configured session fields across logout.

No open issues 🎉

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: preserve FIELDS_STORED_IN_SESSION across OAuth session cleanup

3 participants