ExternalLinkConfirmation
in package
Pairing an OAuth identity with an existing password account needs the account owner's confirmation by e-mail ("pre-hijacking" protection).
Why: with account.activation.auto = 1 anyone can register an account with somebody else's
e-mail (nothing verifies it). When the real owner of the e-mail later signs in with Google,
a provider-verified e-mail used to pair automatically — and the victim ended up in the
attacker's account. A password account is therefore paired only after its owner clicks the
one-time link sent to the account's e-mail.
The plain token travels only in the e-mailed link; the database keeps its sha256 hash and an expiry (same scheme as PasswordResetToken). Pure, no I/O — the Api managers own storage and delivery.
Table of Contents
Constants
- RESEND_INTERVAL_SECONDS : int = 900
- At most one confirmation e-mail per account in this window. A repeated sign-in attempt inside it keeps the pending request (and the already e-mailed link) untouched, so it never invalidates the link the owner is about to click.
- TTL_SECONDS : int = 86400
- Link validity (24 h).
Methods
- buildExpiry() : DateTime
- canResend() : bool
- May a new confirmation e-mail be sent? `$lastRequestedAt` = when the currently pending request was created (`null` = none pending).
- generate() : string
- hash() : string
- isRequired() : bool
- Does an account with this state need the e-mail confirmation before an OAuth identity found by e-mail may be paired with it?
- isValid() : bool
- Is the stored request still usable? The token itself is looked up by its hash, so this only checks the expiry.
- providerLabel() : string
- Human name of the provider for the e-mail text.
Constants
RESEND_INTERVAL_SECONDS
At most one confirmation e-mail per account in this window. A repeated sign-in attempt inside it keeps the pending request (and the already e-mailed link) untouched, so it never invalidates the link the owner is about to click.
public
int
RESEND_INTERVAL_SECONDS
= 900
TTL_SECONDS
Link validity (24 h).
public
int
TTL_SECONDS
= 86400
Methods
buildExpiry()
public
static buildExpiry(DateTimeInterface $now) : DateTime
Parameters
- $now : DateTimeInterface
Return values
DateTimecanResend()
May a new confirmation e-mail be sent? `$lastRequestedAt` = when the currently pending request was created (`null` = none pending).
public
static canResend(DateTimeInterface|null $lastRequestedAt, DateTimeInterface $now) : bool
Parameters
- $lastRequestedAt : DateTimeInterface|null
- $now : DateTimeInterface
Return values
boolgenerate()
public
static generate() : string
Return values
stringhash()
public
static hash(string $plainToken) : string
Parameters
- $plainToken : string
Return values
stringisRequired()
Does an account with this state need the e-mail confirmation before an OAuth identity found by e-mail may be paired with it?
public
static isRequired(string|null $passwordHash, string|null $externalId) : bool
- a password is set and the account carries no provider identity yet ⇒ yes (it was registered with a password, nobody has proven the e-mail belongs to its owner),
- no password (the account was created by an OAuth sign-in) ⇒ no, pairs automatically,
- a provider identity is already stored (
externalId) ⇒ no — the e-mail was proven by the provider when the account was created or by an earlier confirmed pairing.
Parameters
- $passwordHash : string|null
- $externalId : string|null
Return values
boolisValid()
Is the stored request still usable? The token itself is looked up by its hash, so this only checks the expiry.
public
static isValid(DateTimeInterface|null $expiresAt, DateTimeInterface $now) : bool
Parameters
- $expiresAt : DateTimeInterface|null
- $now : DateTimeInterface
Return values
boolproviderLabel()
Human name of the provider for the e-mail text.
public
static providerLabel(string $provider) : string
Parameters
- $provider : string