Skip to content

Fix TypeError at /o/token/#1597

Open
aibaq wants to merge 2 commits intodjango-oauth:masterfrom
aibaq:master
Open

Fix TypeError at /o/token/#1597
aibaq wants to merge 2 commits intodjango-oauth:masterfrom
aibaq:master

Conversation

@aibaq
Copy link

@aibaq aibaq commented Sep 16, 2025

Fixes #

Description of the Change

Django User's last_login field is nullable, so /o/token/ endpoint raises TypeError when last_login is null when using oidc

Снимок экрана 2025-09-16 в 13 05 42

This fix sets timezone.now() as a default value for auth_time

Checklist

  • PR only contains one change (considered splitting up PR)
  • unit-test added
  • [o] documentation updated (not needed)
  • CHANGELOG.md updated (only for user relevant changes)
  • author name in AUTHORS

@aibaq aibaq changed the title Fix TypeError at /s/auth/o/token/ Fix TypeError at /o/token/ Sep 16, 2025
@codecov
Copy link

codecov bot commented Oct 3, 2025

Codecov Report

❌ Patch coverage is 66.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
oauth2_provider/oauth2_validators.py 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@dopry
Copy link
Member

dopry commented Oct 3, 2025

@aibaq this is a great start. to help me understand the issue a bit more, in what scenario did you encounter this issue? I would expect that a user would have logged in at least once by the time get_id_token_dictionary was called. I'd like to understand how this was occurring to both correct my expectations and ensure there isn't a bug somewhere else that is allow a token to be generated for a user that isn't authenticated. It also looks like there is a code path that is uncovered that will need to be tested.

@dopry dopry requested a review from Copilot October 3, 2025 16:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request fixes a TypeError that occurs at the /o/token/ endpoint when using OIDC and the Django User's last_login field is null. The fix adds a null check for last_login and provides a fallback value using the current timestamp.

  • Added null safety check for request.user.last_login before accessing it
  • Set timezone.now().timestamp() as fallback when last_login is null
  • Added test case to verify the fix works with null last_login values

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
oauth2_provider/oauth2_validators.py Added null check for last_login and fallback to current timestamp
tests/test_oauth2_validators.py Added test setup to simulate user with null last_login
CHANGELOG.md Added entry for the TypeError fix
AUTHORS Added contributor name

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@dopry
Copy link
Member

dopry commented Oct 31, 2025

@aibaq We won't be able to close this without the test coverage and details so we can reproduce the bug.

@amatissart
Copy link

For context, we recently hit the same error after upgrading oauthlib from 3.2 to 3.3. The "id_token" is now included in refresh token responses when the the openid scope is provided (oauthlib/oauthlib#838). A workaround for us was to change the default scopes so client apps that don't explicitly require openid don't get the ID token claims.

However, it's unclear for me how this should behave for users who interact only with OAuth applications. For these users, their User.last_login might remain None (unless this indicates a misconfiguration on our side?). I couldn't find proper guidance on how to update last_login when using django-oauth-toolkit, although some approaches were suggested in #348. So it is not clear whether it's safe to assume that last_login is always non-None in this code path.

@dopry
Copy link
Member

dopry commented Jan 9, 2026

@amatissart thanks for the additional context. I'm definitely open to the PR.

As you noted the guidance with regard to last_login is unclear. I see there is a test for the case, but it explicitly sets user.last_login to None. I'd like a clear understanding of how this happens in the wild and for that to be clearly documented in the code at the conditional or in a test.

The test explicitly sets the user.last_login to none, which is a bit synthetic.

I feel like the field being nullabe is probably sufficient for having some sort of fallback... but that raises the question of how should we handle this when user.last_login is not set? Is now appropriate? Should auth_time be omitted in these cases, it is optional after all? Should we look to the token chain and look for a a created time on the earliest token there? Or are we missing a mechanism to track session time that allows us to provide a reliable auth_time that can be used for session expiration? If user.last_login is not set, should we set it and then use that for auth_time?

I want to be sure we're doing the right thing, but I have limited time to research these things as a maintainer. We really depend on the support of the wider community and contributors who are focused on these issues to help us ensure we're doing what's right for the wider user community...

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.

4 participants