In-app Personal Access Token expiry notification
What does this MR do?
Problem Statement
With !33783 (merged), system administrators now have an option to toggle Enforcement of Personal Access Token Expiry.
When the enforcement is disabled, users can continue to use their PAT after the expiry date, unless it is explicitly revoked (ref).
Currently, the only way for a user to know if their token requires rotation is by visiting their Personal Access Tokens page.
What does this MR do?
This MR shows an in-app notification for the user when:
- At least one of their PAT is about to expire (in the next 7 days)-
- At least one of their PAT has expired-
The purpose of the notification is to encourage the user to create a new token on expiry. The follow-up issue enhances this behavior.
Banner visibility
There is no exclusive capability for token rotation currently. The logic to stop showing the notification is determined as below:
Notification type | Logic |
---|---|
Expired token | Stop showing if a new token has been created after we started notifying the user about the most recent expired token (i.e. 7 days before expiry) |
Expiring token | Stop showing if a new token has been created after we started notifying the user about the most recently expiring token |
Following is an illustration to depict the notification banner behavior for a single PAT token:
When there is more than one token, the notification banner can be depicted as:
Please note that in case both Expired and Expiring soon cases are valid, the Expired notification takes precendence.
Banner dismissal
In case a user dismisses the banner, it will not show up for the next 7 days.
Mentions #214723 (closed)
Database Queries
We perform queries to find if the Rotation is required for Expired or Expiring tokens.
In worst case scenario, we may execute a total for 4 queries. Refer to the Cache section for optimization around this.
The query details are detailed below:
To check if rotation is required for Expired tokens, the following 2 queries are executed:
- Fetch the
expires_at
of the most recently expired token:
SELECT
MAX("personal_access_tokens"."expires_at")
FROM
"personal_access_tokens"
WHERE
"personal_access_tokens"."user_id" = 644
AND "personal_access_tokens"."impersonation" = FALSE
AND ("personal_access_tokens"."revoked" = FALSE
OR "personal_access_tokens"."revoked" IS NULL)
AND (expires_at <= '2020-06-24 16:22:49.021349')
Time: 3.848 ms
- planning: 0.336 ms
- execution: 3.512 ms
- I/O read: 3.414 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 0 from the buffer pool
- reads: 3 (~24.00 KiB) from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0
Execution Plan: https://explain.depesz.com/s/xPLE
- Fetch if new PATs have been created after we started notifying the user about the recent expired token
SELECT
1 AS one
FROM
"personal_access_tokens"
WHERE
"personal_access_tokens"."user_id" = 644
AND "personal_access_tokens"."impersonation" = FALSE
AND (revoked = FALSE
AND (expires_at >= NOW()
OR expires_at IS NULL))
AND (created_at >= '2020-06-13')
LIMIT 1
Time: 5.876 ms
- planning: 0.305 ms
- execution: 5.571 ms
- I/O read: 5.442 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 0 from the buffer pool
- reads: 4 (~32.00 KiB) from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0
Execution Plan: https://explain.depesz.com/s/kyMF
To check if rotation is required for Expiring tokens, the following 2 queries are executed:
- Fetch the
expires_at
of the most recently expiring token:
SELECT
MAX("personal_access_tokens"."expires_at")
FROM
"personal_access_tokens"
WHERE
"personal_access_tokens"."user_id" = 644
AND "personal_access_tokens"."impersonation" = FALSE
AND ("personal_access_tokens"."revoked" = FALSE
OR "personal_access_tokens"."revoked" IS NULL)
AND (expires_at > NOW()
AND expires_at <= '2020-07-01 16:36:12.491102')
Time: 0.731 ms
- planning: 0.650 ms
- execution: 0.081 ms
- I/O read: 0.000 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 3 (~24.00 KiB) from the buffer pool
- reads: 0 from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0
Execution plan: https://explain.depesz.com/s/xq4sl
- Fetch if new PATs have been created after we started notifying the user about the recent expiring token
SELECT
1 AS one
FROM
"personal_access_tokens"
WHERE
"personal_access_tokens"."user_id" = 644
AND "personal_access_tokens"."impersonation" = FALSE
AND (revoked = FALSE
AND (expires_at >= NOW()
OR expires_at IS NULL))
AND (created_at >= '2020-06-21')
LIMIT 1
Time: 4.862 ms
- planning: 0.223 ms
- execution: 4.639 ms
- I/O read: 4.555 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 0 from the buffer pool
- reads: 4 (~32.00 KiB) from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0
Execution Plan: https://explain.depesz.com/s/OgiT
Caching
Since the notifications are shown is every page, we cache the results of expired?
and expires_soon
for a user to avoid making database queries for every call.
- Cache key is based on
user_id
- Valid until end of day
- Cleared when the user creates a new PAT, or revokes an existing PAT
Screenshots
Token expired
Token expiring soon
Does this MR meet the acceptance criteria?
Conformity
-
Changelog entry - [-] Documentation (if required)
-
Code review guidelines -
Merge request performance guidelines -
Style guides -
Database guides -
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. - [-] Tested in all supported browsers
- [-] Informed Infrastructure department of a default or new setting change, if applicable per definition of done
Security
If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:
- [-] Label as security and @ mention
@gitlab-com/gl-security/appsec
- [-] The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
- [-] Security reports checked/validated by a reviewer from the AppSec team