Dear Keycloak developers,
For a project, I need to support marking a device/browser as "trusted" to streamline authentication, for example, skip 2FA.
The issue [0] contains a few notes about possible implementations and requirements.
I gave this a spin and built a prototype for this. The .gif [1] shows how this might work.
# What is a "trusted device", and how is it marked and detected?
In this case, a "trusted device" is a user agent, e.g. a browser, or an application, that can store and send cookies. Perhaps the term "Trusted Browser" might be more appropriate here.
A device can be marked as "trusted" by storing a signed "KEYCLOAK_DEVICE" cookie which the device needs to send during authentication. This cookie stores a random deviceId and some metadata as a signed JWT (HS256) created during device registration. To make Keycloak aware of trusted devices for a given user, I also store the deviceId in a new table like "trusted_devices" with information about realm_id, user_id, device_id, device_name, and createdAt timestamp. (I choose a new table since I didn't want to abuse user attributes for this. Another option could have been using the credentials table for this.)
An additional browser-fingerprinting mechanism could be applied to improve the trusted device detection. Still, I didn't consider that option as I'm not sure how robust this would be concerning browser updates.
Other services like DuoSecurity [3] also support trusted devices and implement this in a similar way (signed cookie with metadata). Instead of skipping 2FA entirely on trusted devices, they use a risk-based approach for authentication where they (might) ask less often for a 2FA frequently on trusted devices. Perhaps Keycloak will support a mechanism like that in the future too :)
# How to register a trusted device?
I use a user-initiated RequiredAction in my prototype to register a device as trusted, but this could also happen during 2FA/OTP registration. There could be a checkbox "Trust this device/device", which would avoid prompting for OTP on the OTP registration page. Another option could be to add a "Trusted Devices" section on the Security page of the Account Console to trigger this user-initiated action.
As shown in the [1] .gif, the user can register this browser as trusted or not. With a checkbox, the user can tell whether all other trusted devices (for this user) should be removed (from the Keycloak database).
Submitting this form will send a KEYCLOAK_DEVICE cookie in the response.
The KEYCLOAK_DEVICE cookie is httpOnly, secure, and only sent to the realm issuer URL (could be narrowed to auth-related URLs.). Additionally, the maxAge of the cookie can be configured to, e.g., 120 days.
# How to handle trusted devices in Authentication flows?
I use a new conditional Authenticator (Trusted Device Condition) to checks if the KEYCLOAK_DEVICE cookie is valid and matches a previously registered trusted device for the user. If we detect a trusted device, then the condition matches. The authenticator logic can be inverted, which lets the condition match if the current device is not a trusted device.
The "negated" trusted device condition can then be used in a browser
authentication flow as a conditional sub-flow to the OPT-Forms to skip OTP validation on trusted devices. An example of such an auth-flow is shown in [2].
The code is here [4].
I think something along those lines might be interesting to have in Keycloak core. What do you think?
Cheers,
Thomas