Page MenuHomePhabricator

[IP Masking] Temporary Account Expiration
Closed, ResolvedPublic

Description

User Story

As an anonymous editor,
I want to be notified that my temporary account will be expiring soon
So that I choose to create an account or else to continue editing without one, knowing a new temporary account will be assigned after that expiry period.

Acceptance Criteria

  • A user's temporary account name expires after one year (365 days) and the username is unassigned from the account.
    • A returning user will no longer see the temp account name in the personal bar.
  • Reminder popup (10 days) before we know the session is about to expire (T344694)

image.png (2×2 px, 1 MB)
*See Figma for the latest update mockup.

  • Once the temporary user dismisses the reminder popup, then it should not display again.
  • Decide on call to action allowing the person to "Extend expiry by $X period". @KStoller-WMF to confirm - after usability testing regarding expiry (T328368) - It was determined this is not necessary
  • Upon temporary account expiration, the anonymous editor should find themselves in the same flow defined in T300263: [IP Masking] Create temporary account on first edit

Questions

  1. How long after temporary account creation should we notify users accounts will expire?
    • Proposal is to let users know upon creation of the temp account as part of the temp account creation message, and each subsequent temp account notice. This task only covers the final popup reminder 10 days before we know the session is about to expire.
  2. Likewise when should their account actually expire? @Niharika
    • Accounts expire after 1 year.
  3. What is the time limit we can extend a cookie for?
    • Accounts expire after 1 year, we will not include the option to extend.
  4. Does the expiry period refresh if a new edit is made? For example, if a temp account edit is made Jan 1st 2022, if another edit is made on Jun 30th 2022, will it still expire in Jan 1st in 2023, or will it be June 30th 2023?
    • No. A new edit doesn't refresh the time period. Accounts expire after 1 year from the first edit made by the temporary account.

Related Objects

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

Aside: I would prepare for the eventuality that the expiry will happen much sooner in some (maybe most) cases because of the limited compatibility of CentralAuth with modern browser privacy features. (See T226797: CentralAuth login session and auto-login no longer work across wikis in Safari and Firefox for problems with transferring the session to another; there are also other limitations, like Safari's Intelligent Tracking Prevention capping cookie lifetime on domains which aren't manually interacted with, ie. login.wikimedia.org.)

@kostajh is the work Growth doing in this ticket also handle expiring the temporary username and assigning the returning user a new one?

I'm wondering if that is necessary... what would be the benefit to the user of seeing a new temporary username in place of the old one? In addition, AFAIK, the temporary username is assigned after an edit, so we would need to add some new code to make it possible to assign one that is not in the context of editing.

Reminder popup (x days) before we know the session is about to expire.

Could we consider flipping the rules for this, to ease the implementation? Given that most newcomers leave within the first ~24 hours (citation needed!) of an edit (cc @KStoller-WMF) perhaps we could have a rule like:

  • first day: show a reminder popup to the user if at least X hours have elapsed from temporary username assignment, and less than 24 hours. so e.g. if it's been at least, say, 4 hours, then show the popup.
  • second day: if it's been at least 24 hours, show the same popup or one with slightly different wording

As an anonymous editor,
I want to be notified temporary account will be created on my behalf
So that I do not have to reveal my personal information to edit a Wiki

Looking at it again, this user story in the task description seems disconnected from what we're talking about here. The user story talks about informing the user before making an edit. While in most of the task comments and elsewhere in the task description, we're talking about letting the user know after they've already received a temporary username, that they should make a real account / or that their temporary username will expire. Is this task about both things?

KStoller-WMF added a subscriber: RHo.

Moving to Needs Design. Let's have @RHo review both of these IP Masking tasks before we start work on them.
@Niharika / @kostajh Do you think it would be helpful if the Growth team received a general demo of IP masking plans before we start this work? Or is there an overview of plans / UX flows that we should review?

@kostajh is the work Growth doing in this ticket also handle expiring the temporary username and assigning the returning user a new one?

I'm wondering if that is necessary... what would be the benefit to the user of seeing a new temporary username in place of the old one? In addition, AFAIK, the temporary username is assigned after an edit, so we would need to add some new code to make it possible to assign one that is not in the context of editing.

Oh, right. Sorry, my bad. The username should just be unassigned after a year and when a new edit is made the user obtains a new temp username.

As an anonymous editor,
I want to be notified temporary account will be created on my behalf
So that I do not have to reveal my personal information to edit a Wiki

Looking at it again, this user story in the task description seems disconnected from what we're talking about here. The user story talks about informing the user before making an edit. While in most of the task comments and elsewhere in the task description, we're talking about letting the user know after they've already received a temporary username, that they should make a real account / or that their temporary username will expire. Is this task about both things?

Hmm, I think it should be split in two tasks but I'll let y'all decide what makes sense.
Edit: I misspoke that this is not a Legal requirement. It absolutely is and the text decided by Legal for this popup lives in this spreadsheet.

KStoller-WMF lowered the priority of this task from High to Medium.Dec 22 2022, 11:30 PM

I've lowered the priority to "Medium" until we have the Growth: AHaT Synch - IP Masking meeting in mid-January. But @Niharika please let us know if you want us to start this work before then.

@kostajh is the work Growth doing in this ticket also handle expiring the temporary username and assigning the returning user a new one?

I'm wondering if that is necessary... what would be the benefit to the user of seeing a new temporary username in place of the old one? In addition, AFAIK, the temporary username is assigned after an edit, so we would need to add some new code to make it possible to assign one that is not in the context of editing.

Oh, right. Sorry, my bad. The username should just be unassigned after a year and when a new edit is made the user obtains a new temp username.

As an anonymous editor,
I want to be notified temporary account will be created on my behalf
So that I do not have to reveal my personal information to edit a Wiki

Looking at it again, this user story in the task description seems disconnected from what we're talking about here. The user story talks about informing the user before making an edit. While in most of the task comments and elsewhere in the task description, we're talking about letting the user know after they've already received a temporary username, that they should make a real account / or that their temporary username will expire. Is this task about both things?

Hmm, I think it should be split in two tasks but I'll let y'all decide what makes sense.
Edit: I misspoke that this is not a Legal requirement. It absolutely is and the text decided by Legal for this popup lives in this spreadsheet.

I've updated the user story in the task description to reflect that this task is about informing the user that their temporary account will expire soon and give them the option to either create an account or get a new temp account. The task about informing the user before making an edit is T300263.

Aside: I would prepare for the eventuality that the expiry will happen much sooner in some (maybe most) cases because of the limited compatibility of CentralAuth with modern browser privacy features. (See T226797: CentralAuth login session and auto-login no longer work across wikis in Safari and Firefox for problems with transferring the session to another; there are also other limitations, like Safari's Intelligent Tracking Prevention capping cookie lifetime on domains which aren't manually interacted with, ie. login.wikimedia.org.)

FWIW we are looking into the possibility of improvements for this: T326281: Attempt top-level central autologin when visiting the login page (to allow autologin when the browser blocks third-party cookies). It might not be possible - browser trends have not been kind to CentralAuth's approach to authentication.

Does the expiry period refresh if a new edit is made? For example, if a temp account edit is made Jan 1st 2022, if another edit is made on Jun 30th 2022, will it still expire in Jan 1st in 2023, or will it be June 30th 2023?

@RHo just made me realize this question was waiting on an answer. We will not refresh the expiry after subsequent edits. Temporary accounts should not exist for longer than a year. So the expiry period will be one year from the creation (first edit/action) of the temporary account.
Note - we are currently discussing if this time period needs to be shorter than a year. I will provide an update on this task if that time period changes.

The popup should probably be reworded to avoid giving the impression that edits made by a temporary user can be retained when registering. Right now it can be read as saying that, and it's a natural thing to expect so we should try to be clear about it.

The popup should probably be reworded to avoid giving the impression that edits made by a temporary user can be retained when registering. Right now it can be read as saying that, and it's a natural thing to expect so we should try to be clear about it.

Good catch @Tgr - the task description's mock is outdated. Have provided the update proposed expiry message text in the MVP flow:
https://www.figma.com/file/xECM2hD7vEemP91M9YAQBC/IP-Masking?type=design&node-id=1020-26187&mode=design&t=rvwzZeSjVmxUgjdk-4

I updated the task to make acceptance criteria clearer, and specify a time period for when this reminder should appear:

Reminder popup (10 days) before we know the session is about to expire.

I believe the task should be actionable now, so I'm moving to Ready for Development.

This consists of three steps:

  1. detect that the account will expire soon and notify the user
  2. track notification seen state (the task doesn't specify but presumably if the user dismisses the popup, we don't want to show it again at the next page load)
  3. detect when the account expired and prevent further use

#3 is easy: we run a daily maintenance script on the central database that searches for expired users and call AuthManager::revokeAccessForUser(). We'll need to double-check whether that covers things like bot passwords and OAuth access tokens but IIRC it does (we are already using it for taking over system users). The "central database" depends on what authentication system one is using, so probably we'll want an extensible script similar to DeleteLocalPasswords but that still shouldn't be too much effort.

For #2 I can think of two ways: either use global preferences (that means working around T337772: Prevent GlobalPreferences from setting preferences for temporary accounts; when GlobalPreferences is not installed, we'd need to fall back to normal preferences, which I assume requires some kind of hook) or change the design specs and use an Echo notification instead of the custom popup.

For #1, if we end up using global preferences, just go with that - run a script on the central database like for #3, and set a global preference indicating that a popup needs to be shown. If we don't use global preferences, we could maybe rely on the session mechanism itself - set the expiration cutoff as session metadata (which does get replicated from the central session to the local wiki as part of the session creation process). Then the session provider would fetch the metadata and use some hook to make the popup happen.

In T300271#9075401, @Tgr wrote:
track notification seen state (the task doesn't specify but presumably if the user dismisses the popup, we don't want to show it again at the next page load)

Correct, I update the acceptance criteria to clarify that requirement. Thanks!

I've reviewed this task as part of Growth-Team IP Masking backlog. My understanding is that there are two distinct goal to fulfil in this task:

  1. Enforce temporary account expiration at N=365 days since their initial edit.
  2. Inform temp accounts M=10 days prior to account expiry, that the expiry is about to happen.

I'd like to highlight that IMO, the session expiry needs to be detached from account expiry. I think so, because session expiry is automatically prolonged (for regular accounts) and tampering with that mechanism seems like a recipe for more errors than desired.

This consists of three steps:

  1. detect that the account will expire soon and notify the user

For #1, if we end up using global preferences, just go with that - run a script on the central database like for #3, and set a global preference indicating that a popup needs to be shown. If we don't use global preferences, we could maybe rely on the session mechanism itself - set the expiration cutoff as session metadata (which does get replicated from the central session to the local wiki as part of the session creation process). Then the session provider would fetch the metadata and use some hook to make the popup happen.

Since temp accounts have a record in the user table, we can benefit from columns stored there, including user_registration (the same applies to the CentralAuth-provided global equivalents, ie. globaluser and gu_registration as well). We should be able to simply display notification if "registration date + N=365 days - M=10 days" is in the future.

I don't think we need to use global preferences or the session mechanism, as we should have a registration date (more or less timestamp of the user's first edit) readily available.

  1. track notification seen state (the task doesn't specify but presumably if the user dismisses the popup, we don't want to show it again at the next page load)

For #2 I can think of two ways: either use global preferences (that means working around T337772: Prevent GlobalPreferences from setting preferences for temporary accounts; when GlobalPreferences is not installed, we'd need to fall back to normal preferences, which I assume requires some kind of hook) or change the design specs and use an Echo notification instead of the custom popup.

Do we need to track this in any special way? Unlike regular accounts, temp. accounts are by definition only active on a single browser/device. I'm thinking about using LocalStorage or similar local storage patterns to track the notification seen state.

I see only one disadvantage of that approach, which is that notification will display once per wiki and user, instead of only once per user. I don't think that's an issue -- I don't think temp accounts are likely to visit a ton of projects (the practical maximum will likely be 3: Wikipedia + Commons + Wikidata).

  1. detect when the account expired and prevent further use

#3 is easy: we run a daily maintenance script on the central database that searches for expired users and call AuthManager::revokeAccessForUser(). We'll need to double-check whether that covers things like bot passwords and OAuth access tokens but IIRC it does (we are already using it for taking over system users). The "central database" depends on what authentication system one is using, so probably we'll want an extensible script similar to DeleteLocalPasswords but that still shouldn't be too much effort.

Similar to #1, I think this should be easily doable using a maintenance script, AuthManager::revokeAccessForUser() and the registration timestamp.

I see only one disadvantage of that approach, which is that notification will display once per wiki and user, instead of only once per user. I don't think that's an issue -- I don't think temp accounts are likely to visit a ton of projects (the practical maximum will likely be 3: Wikipedia + Commons + Wikidata).

Maybe we can use Cookies with same Domain for wikipedia projects so we only display once per user-device. Or even setting a cookie for each project domain.

The rest of the implementation plan sounds good to me. Should we should split the work between (1) and (2) and create a subtask for (2), the popup/notification feedback part?

I see only one disadvantage of that approach, which is that notification will display once per wiki and user, instead of only once per user. I don't think that's an issue -- I don't think temp accounts are likely to visit a ton of projects (the practical maximum will likely be 3: Wikipedia + Commons + Wikidata).

Maybe we can use Cookies with same Domain for wikipedia projects so we only display once per user-device. Or even setting a cookie for each project domain.

In that case, we'd need to store the notification seen state server-side as well, to be able to replicate the cookie across project domains (that's because a cookie for eg. *.wiktionary.org cannot be set from eg. en.wikipedia.org; you'd need to be on a Wiktionary to do that). Once the timestamp's stored server-side, we wouldn't need the cookie to begin with. For regular users, we could use a (global) user property entry for this, but that's not meant to be normally available to temporary users, so I'm not sure if that's a good idea to do. We could also make use of cache entries, but that's not meant as a persistent storage. I feel like the gain from cross-project notification state isn't significant enough to make storing it server-side worth it (especially since there'd be tradeoffs too). What do you think?

The rest of the implementation plan sounds good to me. Should we should split the work between (1) and (2) and create a subtask for (2), the popup/notification feedback part?

Sure thing, filled as T344695: [IP Masking] Expire temporary accounts in 1 year and T344694: [IP Masking] Alert temporary accounts 10 days before expiration respectively.

I'd like to highlight that IMO, the session expiry needs to be detached from account expiry. I think so, because session expiry is automatically prolonged (for regular accounts) and tampering with that mechanism seems like a recipe for more errors than desired.

Session expiry is something like one hour. The expiry of the long-lived "sessions" (on a technical level they aren't really single sessions) that one gets with the "remember me" login option is based on the expiry date of the user token cookie. That does not get prolonged, but cookie expiry is under the control of the browser and can be tampered with, so we probably don't want to rely on that. (It's also not very flexible if e.g. we want to change expiry over time.)

Since temp accounts have a record in the user table, we can benefit from columns stored there, including user_registration (the same applies to the CentralAuth-provided global equivalents, ie. globaluser and gu_registration as well). We should be able to simply display notification if "registration date + N=365 days - M=10 days" is in the future.

user_registration is the time the user first visited the wiki (more or less), which can happen much later than the creation of the global temp user account.

There is an equivalent CentralAuth field, so the logic will be very similar for a standalone wiki and a CentralAuth farm, but not quite the same; and for other SSO extensions, it might be even more different.

I don't think we need to use global preferences or the session mechanism, as we should have a registration date (more or less timestamp of the user's first edit) readily available.

We don't need to; we can just look up the registration date on every page load. But that might be expensive. For CentralAuth, it isn't, central user lookups are cached for a day. So you are right, probably not worth worrying about; other SSO extensions can implement their own cache.

Do we need to track this in any special way? Unlike regular accounts, temp. accounts are by definition only active on a single browser/device. I'm thinking about using LocalStorage or similar local storage patterns to track the notification seen state.

I see only one disadvantage of that approach, which is that notification will display once per wiki and user, instead of only once per user. I don't think that's an issue -- I don't think temp accounts are likely to visit a ton of projects (the practical maximum will likely be 3: Wikipedia + Commons + Wikidata).

Yeah that sounds like a reasonable trade-off.

Change 951967 had a related patch set uploaded (by Urbanecm; author: Urbanecm):

[mediawiki/core@master] [WIP] IP Masking: Expire temporary accounts in 1 year

https://gerrit.wikimedia.org/r/951967

I'd like to highlight that IMO, the session expiry needs to be detached from account expiry. I think so, because session expiry is automatically prolonged (for regular accounts) and tampering with that mechanism seems like a recipe for more errors than desired.

Session expiry is something like one hour. The expiry of the long-lived "sessions" (on a technical level they aren't really single sessions) that one gets with the "remember me" login option is based on the expiry date of the user token cookie. That does not get prolonged, but cookie expiry is under the control of the browser and can be tampered with, so we probably don't want to rely on that. (It's also not very flexible if e.g. we want to change expiry over time.)

Thanks for the clarification. I'm not sure the long-lived "remember me" cookies are not prolonged though. Most of my CentralAuth cookies (centralauth_Token, centralauth_ss0-User and others) are set to expire on 2024-08-24T10:17:14.217Z (as of now), even though I didn't log in during yesterday. I'm not sure if it happens intentionally, but prolongation seems to be happening?

In any case, relying on cookie expiration is not a good idea for the reasons you mention.

Since temp accounts have a record in the user table, we can benefit from columns stored there, including user_registration (the same applies to the CentralAuth-provided global equivalents, ie. globaluser and gu_registration as well). We should be able to simply display notification if "registration date + N=365 days - M=10 days" is in the future.

user_registration is the time the user first visited the wiki (more or less), which can happen much later than the creation of the global temp user account.

In WMF's setup, accounts are guaranteed to exist on metawiki and loginwiki. That seems to be the case for temp accounts as well, and I don't see a reason to exempt temp accounts from the CentralAuthAutoCreateWikis logic (this might impact temp accounts rollout, esp. if metawiki is not selected as a pilot, but discussion around that is beyond the scope of this task IMO).

Since temp accounts should be automatically created on metawiki , the user_registration field on that wiki should be equal to the CentralAuth provided registration timestamp. That being said, for the T344694: [IP Masking] Alert temporary accounts 10 days before expiration chunk to be doable, we'd need the global registration time anyway (as we don't have metawiki's user_registration from ie. cswiki), so adding the hook described below seems to be a good idea regardless. Once it exists, there's no point in not using it in the script, as it'd make it less dependent on which wiki it runs on.

There is an equivalent CentralAuth field, so the logic will be very similar for a standalone wiki and a CentralAuth farm, but not quite the same; and for other SSO extensions, it might be even more different.

We can use CentralAuth's registration timestamp instead. For that, we need something like a UserGetGlobalRegistrationHook. That way, extensions like CentralAuth could calculate a global registration timestamp, which can be returned from core in User::getGlobalRegistration (allowing extensions to modify User::getRegistration doesn't seem to be a good idea).

The account expiration script could then use User::getGlobalRegistration ?? User::getRegistration, which should work for single wikis, CentralAuth-maintained wikifarms and non-CA wikifarms (assuming whatever powers those wikifarms implements UserGetGlobalRegistrationHook).

An alternative would be providing the expiration logic in both core and CentralAuth, while letting extensions to disable the core provided mechanisms when they implement their own. Making that possible seems to require a new hook, which would be tied to this specific chunk of work. It seems better for the future to add UserGetGlobalRegistrationHook as described above, as it could be used for other purposes as well.

I don't think we need to use global preferences or the session mechanism, as we should have a registration date (more or less timestamp of the user's first edit) readily available.

We don't need to; we can just look up the registration date on every page load. But that might be expensive. For CentralAuth, it isn't, central user lookups are cached for a day. So you are right, probably not worth worrying about; other SSO extensions can implement their own cache.

With the UserGetGlobalRegistrationHook approach described above, we could add the global registration timestamp to the standard User object cache. Using that should be fairly cheap, especially since CentralAuth caches this info on its own. We could probably cache global registration in a more aggressive way, as it is something that virtually never changes.

Most of my CentralAuth cookies (centralauth_Token, centralauth_ss0-User and others) are set to expire on 2024-08-24T10:17:14.217Z (as of now), even though I didn't log in during yesterday. I'm not sure if it happens intentionally, but prolongation seems to be happening?

You are right, I forgot about that. It's sort of a bug (T264794: SessionManager should not emit Set-Cookies on session renewal) but not trivial to fix.

In WMF's setup, accounts are guaranteed to exist on metawiki and loginwiki.

"Guaranteed" is a strong word. I'd go with "very likely". CentralAuthCreateLocalAccountJob can fail to run, for general job queue reasons. Central login might be blocked by the browser.

We can use CentralAuth's registration timestamp instead. For that, we need something like a UserGetGlobalRegistrationHook. That way, extensions like CentralAuth could calculate a global registration timestamp, which can be returned from core in User::getGlobalRegistration (allowing extensions to modify User::getRegistration doesn't seem to be a good idea).

The account expiration script could then use User::getGlobalRegistration ?? User::getRegistration, which should work for single wikis, CentralAuth-maintained wikifarms and non-CA wikifarms (assuming whatever powers those wikifarms implements UserGetGlobalRegistrationHook).

An alternative would be providing the expiration logic in both core and CentralAuth, while letting extensions to disable the core provided mechanisms when they implement their own. Making that possible seems to require a new hook, which would be tied to this specific chunk of work. It seems better for the future to add UserGetGlobalRegistrationHook as described above, as it could be used for other purposes as well.

It wouldn't require a hook, you'd create a core maintenance script with some method (that e.g. adds a filter condition to a SelectQueryBuilder) marked as overridable in child classes. See the DeleteLocalPasswords and CentralAuthDeleteLocalPasswords maintenance scripts for a similar example.

I think having a concept of global registration date in core makes sense (although if we end up having lots of such concepts, some sort of "central user" class might be a better approach) but for this specific task, you'll want to filter by registration date, not check it one-by-one. And you probably want to iterate through the central user database in the first place (although iterating through loginwiki users would almost always work for Wikimedia wikis, but it's possible to have a wikifarm where no single wiki has all the user accounts).

Most of my CentralAuth cookies (centralauth_Token, centralauth_ss0-User and others) are set to expire on 2024-08-24T10:17:14.217Z (as of now), even though I didn't log in during yesterday. I'm not sure if it happens intentionally, but prolongation seems to be happening?

You are right, I forgot about that. It's sort of a bug (T264794: SessionManager should not emit Set-Cookies on session renewal) but not trivial to fix.

Thanks for the link!

In WMF's setup, accounts are guaranteed to exist on metawiki and loginwiki.

"Guaranteed" is a strong word. I'd go with "very likely". CentralAuthCreateLocalAccountJob can fail to run, for general job queue reasons. Central login might be blocked by the browser.

Fair point.

It wouldn't require a hook, you'd create a core maintenance script with some method (that e.g. adds a filter condition to a SelectQueryBuilder) marked as overridable in child classes. See the DeleteLocalPasswords and CentralAuthDeleteLocalPasswords maintenance scripts for a similar example.

Good idea! Thanks for pointing this out. Uploaded patches to that effect.

I think having a concept of global registration date in core makes sense (although if we end up having lots of such concepts, some sort of "central user" class might be a better approach) but for this specific task, you'll want to filter by registration date, not check it one-by-one. And you probably want to iterate through the central user database in the first place (although iterating through loginwiki users would almost always work for Wikimedia wikis, but it's possible to have a wikifarm where no single wiki has all the user accounts).

Fair point. We'd need check it one-by-one capability for T344694 anyway, so leaving the patch for that online. FWIW, Taavi has concerns regarding the hook approach on the patch, so it is possible the end result would be different at the end.

Urbanecm_WMF updated Other Assignee, added: Sgs.
Urbanecm_WMF changed the task status from Open to In Progress.Oct 16 2023, 4:04 PM

Blocked on wmf.5 deployment (scheduled to happen on Nov 16).

Change 990248 had a related patch set uploaded (by Urbanecm; author: Urbanecm):

[operations/mediawiki-config@master] [beta] Temporary accounts: Set expiry to 1 year

https://gerrit.wikimedia.org/r/990248

Change 990248 merged by jenkins-bot:

[operations/mediawiki-config@master] [beta] Temporary accounts: Set expiry to 1 year

https://gerrit.wikimedia.org/r/990248

Set expiry to 1 year (received a 👍 from Elena and Kirsten via Slack) and resolving.

Reminder popup (10 days) before we know the session is about to expire.

Did this part get implemented? (Not sure it needs to be done, just wondering.)

Reminder popup (10 days) before we know the session is about to expire.

Did this part get implemented? (Not sure it needs to be done, just wondering.)

I believe so: T344694: [IP Masking] Alert temporary accounts 10 days before expiration

Somewhat belatedly, I thought of what seems like a better method (although maybe somewhat complementary): T359064: Represent temporary account expiry with system blocks