Class PasswordResetLink
The link carries one query parameter, PARAM, whose value is the user ID, a mark of the user's current password hash, the purpose and the expiry time of the link, sealed with the server's own key through ISensitiveParameterHandler.encodeParameter(String) exactly as EmailVerificationLink seals a verification link: AES-256-GCM under the installation's key, the GCM tag as the keyed MAC of the link, so nothing in the URL lets a link be forged or altered. The purpose keeps a value the server sealed for anything else from being taken as a reset link.
Single use, without a store. The mark is the first 16 bytes of SHA-256 over the stored password hash. The reset the link makes replaces that hash, so the mark no longer matches and the same link is refused afterwards; so is a link sent before any other change of the password. Nothing is written for it and nothing needs a schema. Two links requested within the validity are both live until one is used, and both go to the same registered address. The mark reveals nothing about the hash to a holder of the link: the hash itself is a salted PBKDF2 output.
- Author:
- Christopher Mindus
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordThe sealed content of a link.static final recordA verification result: the status, and the claim when the token was sealed by this server for this purpose (VALID or EXPIRED), null when INVALID.static enumThe outcome of a verification.Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe query parameter carrying the token: "prt".static final longHow long a link is valid from the moment it is built: 30 minutes.Method Summary
Modifier and TypeMethodDescriptionstatic StringbuildLink(ISensitiveParameterHandler keys, String linkBase, long userID, String hashPW, long expiresAt) Builds a link: the base with the query appended after?, or after&when the base has a query already.static StringbuildQuery(ISensitiveParameterHandler keys, long userID, String hashPW, long expiresAt) Builds the query string of a link:prt=token.static booleanTells whether a link base is on a base URL: equal to it, or the base followed by/and more - so thathttps://host.example.evilis not onhttps://host.example, and neither ishttps://host.example:8443(CC-FIX-RESET-LINK-BASE: the base passed torequestPasswordResetmust be the client session's own external base).static StringMarks a stored password hash: the first 16 bytes of its SHA-256, as 32 hexadecimal characters.static PasswordResetLink.Resultverify(ISensitiveParameterHandler keys, String token, long now) Verifies a token.
Field Details
PARAM
VALIDITY_MS
public static final long VALIDITY_MSHow long a link is valid from the moment it is built: 30 minutes.- See Also:
Method Details
mark
Marks a stored password hash: the first 16 bytes of its SHA-256, as 32 hexadecimal characters. The mark changes with the hash and reveals nothing of it.- Parameters:
hashPW- The stored password hash.- Returns:
- The mark.
- Throws:
NullPointerException- IfhashPWis null.
buildQuery
public static String buildQuery(ISensitiveParameterHandler keys, long userID, String hashPW, long expiresAt) throws GeneralSecurityException Builds the query string of a link:prt=token.- Parameters:
keys- The server's sealing keys.userID- The user ID the link resets.hashPW- The user's stored password hash now; the link is spent when it changes.expiresAt- The expiry time, milliseconds since the epoch.- Returns:
- The query string, URL-safe.
- Throws:
GeneralSecurityException- If the content cannot be sealed.NullPointerException- IfkeysorhashPWis null.
buildLink
public static String buildLink(ISensitiveParameterHandler keys, String linkBase, long userID, String hashPW, long expiresAt) throws GeneralSecurityException Builds a link: the base with the query appended after?, or after&when the base has a query already.- Parameters:
keys- The server's sealing keys.linkBase- The URL the link starts with.userID- The user ID the link resets.hashPW- The user's stored password hash now.expiresAt- The expiry time, milliseconds since the epoch.- Returns:
- The link.
- Throws:
GeneralSecurityException- If the content cannot be sealed.NullPointerException- If a parameter is null.
isOnBase
Tells whether a link base is on a base URL: equal to it, or the base followed by/and more - so thathttps://host.example.evilis not onhttps://host.example, and neither ishttps://host.example:8443(CC-FIX-RESET-LINK-BASE: the base passed torequestPasswordResetmust be the client session's own external base).- Parameters:
linkBase- The URL a link starts with, e.g.https://host.example/app/signin/.base- The base URL without a trailing slash, e.g.https://host.example, null for none.- Returns:
- true when the link base is on the base; false when it is not, or when either is null.
verify
public static PasswordResetLink.Result verify(ISensitiveParameterHandler keys, String token, long now) Verifies a token.The token is opened with the server's keys, which fails for a token sealed by another server, for one altered anywhere and for anything that is not a token at all; the content must then carry the purpose; and the expiry time must not have passed. Whether the link is spent is the caller's check: the claim's mark against
mark(String)of the user's stored hash now. Nothing is thrown for a bad token.- Parameters:
keys- The server's sealing keys.token- The token from the link, null or empty for none.now- The current time, milliseconds since the epoch.- Returns:
- The result, never null.