Class DeviceService
Each account and browser-token hash identify one device record, which also holds its latest new-device notice. Recognition depends on the last successful login, independently of whether the owner has confirmed the device. Confirmation resolves the notice; blocking requires an email-code challenge before that browser can complete another login. Expiration of the notice alone does not remove the device or its block.
AdminDeviceService handles authentication, cookies, proof generation, email delivery
and session termination. This service receives account IDs and already hashed tokens or proofs,
applies the persistence rules and audits state changes. Repository failures are exposed as
IllegalStateException so callers can distinguish unavailable storage from a rejected proof.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidcleanup(long knownSinceCutoff) Removes unblocked devices whose last successful login is at or before the retention cutoff.voidcompleteLocation(int userId, long deviceId, long loginTime, String location) Adds a location obtained after login without replacing a location already stored for that login.Confirms an unblocked device using a proof delivered to the account owner's email.confirmAfterSecondFactor(int userId, long id, long now) Confirms a device based on a second factor already verified by the login flow.findActive(int userId, long now) Finds recent device notices that still need the account owner's attention on the dashboard.Loads device states in one query so session listings can show current confirmation information.findByTokenHash(int userId, String tokenHash) Resolves a browser cookie to its account-owned device record without changing device state.org.springframework.data.domain.Page<DeviceEntity>findDevices(int userId, org.springframework.data.domain.Pageable pageable) Lists retained device records for the account's device-management view.findEvent(int userId, long id) Loads an owned device for a notice detail or security action, regardless of its notice age.booleanissueConfirmation(int userId, long id, String hash, long now, boolean code) Stores a confirmation proof before the caller sends its secret to the account owner's email.booleanissueUnblockCode(int userId, long id, String hash, long now) Stores an email code for the pending login challenge of a blocked browser.recordLogin(int userId, String tokenHash, long now, long knownSinceCutoff, String browserName, String browserVersion, String operatingSystem, String ipAddress) Records a successful login when no location is available.recordLogin(int userId, String tokenHash, long now, long knownSinceCutoff, String browserName, String browserVersion, String operatingSystem, String ipAddress, String location) Records the latest successful login and creates a notice when browser recognition has expired.report(int userId, long id, long now) Marks an owned browser as blocked after the account owner reports an unrecognized login.Removes a browser's block after its pending login email code is accepted.
-
Constructor Details
-
DeviceService
-
-
Method Details
-
recordLogin
public DeviceEntity recordLogin(int userId, String tokenHash, long now, long knownSinceCutoff, String browserName, String browserVersion, String operatingSystem, String ipAddress) Records a successful login when no location is available.Uses the same recognition rules as the location-aware overload and stores
nullas the latest location, including when a previous login had a known location.- Parameters:
userId- account that completed authenticationtokenHash- hash of the persistent browser-recognition tokennow- successful login time in epoch millisecondsknownSinceCutoff- last-login cutoff in epoch milliseconds; older or equal values require re-detectionbrowserName- browser name to retain when the device is detectedbrowserVersion- browser version to retain when the device is detectedoperatingSystem- operating system to retain when the device is detectedipAddress- IP address of this login- Returns:
- the newly detected or re-detected device, or
nullfor an already recognized device - Throws:
IllegalStateException- if the device is blocked or the operation fails
-
recordLogin
public DeviceEntity recordLogin(int userId, String tokenHash, long now, long knownSinceCutoff, String browserName, String browserVersion, String operatingSystem, String ipAddress, String location) Records the latest successful login and creates a notice when browser recognition has expired.A device is recognized when its stored
lastSeenis strictly newer than the cutoff; confirmation is not required for recognition. Every accepted login refreshes the time, IP and location. A recognized device keeps its original browser/OS snapshot and confirmation state.A missing device is inserted; an expired device reuses its existing record. Both cases start a new notice, capture the browser/OS snapshot and clear confirmation and outstanding email proofs. Returning that record tells the caller to send a notification. Blocked devices must first pass the separate unblock flow and cannot be refreshed here.
- Parameters:
userId- account that completed authenticationtokenHash- hash of the persistent browser-recognition tokennow- successful login time in epoch millisecondsknownSinceCutoff- last-login cutoff in epoch milliseconds; older or equal values require re-detectionbrowserName- browser name to retain when the device is detectedbrowserVersion- browser version to retain when the device is detectedoperatingSystem- operating system to retain when the device is detectedipAddress- IP address of this loginlocation- advisory location of this login, ornullwhen unavailable- Returns:
- the newly detected or re-detected device, or
nullfor an already recognized device - Throws:
IllegalStateException- if the device is blocked or the operation fails
-
completeLocation
Adds a location obtained after login without replacing a location already stored for that login.The lookup must still show the same login timestamp and a missing location; otherwise the result is ignored. This check avoids applying a delayed result to a newer login. Adding the location does not refresh recognition, create another notice or send another notification.
- Parameters:
userId- account that owns the devicedeviceId- device associated with the completed loginloginTime- original successful login time in epoch millisecondslocation- advisory location resolved for that login
-
findActive
Finds recent device notices that still need the account owner's attention on the dashboard.Only unconfirmed, unblocked devices detected less than seven days ago are included. Repeated recognized logins update
lastSeen, but do not extend this notice period.- Parameters:
userId- account whose notices are requestednow- current time in epoch milliseconds, used to calculate the notice cutoff- Returns:
- unresolved devices ordered by detection time descending and ID ascending
-
findEvent
Loads an owned device for a notice detail or security action, regardless of its notice age.Unlike
findActive(int, long), this also returns confirmed, blocked and notice-expired devices. Callers must check whether their intended action is allowed for the returned state.- Parameters:
userId- account that must own the deviceid- device ID referenced by the notice or action- Returns:
- the current device record, or
nullwhen missing or owned by another account
-
findByTokenHash
Resolves a browser cookie to its account-owned device record without changing device state.Used both to check blocking before login and to associate a recognized device with the completed session. A matching record alone does not establish that recognition is still valid.
- Parameters:
userId- account to which the browser token belongstokenHash- hash of the browser-recognition token, not the raw cookie value- Returns:
- the matching device, or
nullwhen the account has no record for this token
-
findByIds
Loads device states in one query so session listings can show current confirmation information.- Parameters:
userId- account whose devices may be returnedids- device IDs attached to the account's sessions; an empty set avoids a database query- Returns:
- matching owned devices, omitting missing and foreign IDs without guaranteeing order
-
findDevices
public org.springframework.data.domain.Page<DeviceEntity> findDevices(int userId, org.springframework.data.domain.Pageable pageable) Lists retained device records for the account's device-management view.Includes confirmed, blocked and notice-expired devices, even without an active session. Pagination and ordering come from the caller rather than the active-notice rules.
- Parameters:
userId- account whose devices are listedpageable- requested page, size and sort order- Returns:
- a page of owned device records with the total matching count
-
issueConfirmation
Stores a confirmation proof before the caller sends its secret to the account owner's email.Only unconfirmed, unblocked devices qualify. Links last 24 hours; codes last ten minutes and use the resend and attempt limits described by
issueProof(int, long, String, long, boolean, boolean). Issuing one proof type preserves the other, allowing an existing email link to remain usable.- Parameters:
userId- account that must own the deviceid- device to confirmhash- hash of the new email token or code, prepared by the callernow- issue time in epoch millisecondscode-truefor a numeric email code,falsefor an email-link token- Returns:
truewhen stored, orfalsewhen the device state or code limits reject issuance
-
issueUnblockCode
Stores an email code for the pending login challenge of a blocked browser.The caller binds the code hash to the pending login and sends the email only after issuance succeeds. This reuses the device's code fields and limits; it does not remove the block.
- Parameters:
userId- account that must own the blocked deviceid- device awaiting unblock verificationhash- hash of the session-bound unblock code, prepared by the callernow- issue time in epoch milliseconds- Returns:
truewhen stored, orfalsewhen the device state or code limits reject issuance
-
confirm
Confirms an unblocked device using a proof delivered to the account owner's email.Delegates proof validation and code-attempt accounting to the shared verification flow. Successful confirmation clears both proof types and resolves the notice without refreshing the last-login timestamp. This action cannot remove a device block.
- Parameters:
userId- account that must own the deviceid- device to confirmhash- hash of the submitted email proof, prepared by the callernow- verification time in epoch millisecondscode-truefor a numeric email code,falsefor an email-link token- Returns:
- the confirmed device, or
nullwhen the device or proof cannot be accepted
-
unblock
Removes a browser's block after its pending login email code is accepted.Also confirms the device and refreshes
lastSeen, preventing immediate re-detection after a long block. The caller remains responsible for completing the pending authentication.- Parameters:
userId- account that must own the blocked deviceid- device awaiting unblock verificationhash- hash of the submitted session-bound unblock codenow- verification time in epoch milliseconds- Returns:
- the unblocked, confirmed device, or
nullwhen the device or code cannot be accepted
-
confirmAfterSecondFactor
Confirms a device based on a second factor already verified by the login flow.The caller must establish that the factor succeeded; this method does not validate it. A newly confirmed device has both email proofs revoked and the transition audited. An already confirmed device is returned unchanged. Blocked devices still require their email-code challenge.
- Parameters:
userId- account whose second factor was verifiedid- device used for the completed loginnow- confirmation time in epoch milliseconds- Returns:
- the confirmed device, or
nullwhen missing, foreign or blocked
-
report
Marks an owned browser as blocked after the account owner reports an unrecognized login.Clears confirmation and both email proofs, removing the device from active notices while retaining its record for future login checks. Repeated reports preserve the original block time and produce no additional audit entry. Session termination is handled by
AdminDeviceService.- Parameters:
userId- account reporting its deviceid- device to blocknow- report time in epoch milliseconds- Returns:
- the blocked device, including an already blocked record, or
nullwhen missing or foreign
-
cleanup
public void cleanup(long knownSinceCutoff) Removes unblocked devices whose last successful login is at or before the retention cutoff.Called by
DeviceCleanup; deleting a device also removes its notice and outstanding proofs. Blocked records are retained regardless of age so cleanup cannot silently lift a block.- Parameters:
knownSinceCutoff- latest expired last-login time in epoch milliseconds, inclusive
-