Skip to content

Conversation

di
Copy link
Member

@di di commented Sep 12, 2025

Fixes #18425.

This PR maintains a record of device information across logins for each user:

  • For TOTP logins, confirmation via a link sent to the primary email is required for each new device;
  • For non-TOTP logins, no confirmation is required.

@di di requested a review from a team as a code owner September 12, 2025 17:11
Copy link
Member

@miketheman miketheman left a comment

Choose a reason for hiding this comment

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

Lots of comments inline. Let me know if they need further details.

Aside: I wonder if there's an opportunity to use these kinds of "annoying" interactions to push webauthn more, but I still want that to be a smoother experience.

def send_unrecognized_login_email(request, user, *, ip_address, user_agent, token):
return {
"username": user.username,
"ip_address": ip_address,
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: include geo data as well

Comment on lines +433 to +435
user_service.update_user(
userid, last_totp_value=form.totp_value.data
)
Copy link
Member

Choose a reason for hiding this comment

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

question: would it make sense to also update a unique_login.last_used or something like that when we see it again?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure what that would give us. Do we want to "expire" trusted devices after some period of time?

)
user: Mapped[User] = orm.relationship(back_populates="unique_logins")

ip_address: Mapped[str] = mapped_column(String, nullable=False)
Copy link
Member

Choose a reason for hiding this comment

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

question: Should this be its own string, or a relationship to warehouse.events.models.IpAddress?

Copy link
Member Author

Choose a reason for hiding this comment

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

Given that that model is primarily used for banning IPs, I didn't want to overload it, but I think if we need to unify these in the future we could.

Copy link
Member

Choose a reason for hiding this comment

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

When running a manual verification, the email sent was only text, not HTML. Any clue as to why?

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you talking about the ConsoleAndSMTPEmailSender in local dev? I don't think it displays HTML, only text:

Text: {message.body_text}"""

Copy link
Member

Choose a reason for hiding this comment

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

I am - one line above it is the note that you can visualize the HTML version in MailDev, which usually displays something richer for other emails.

@di di requested a review from miketheman September 24, 2025 11:02
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.

Email confirmation for TOTP-based logins
2 participants