For time based tokens there are basically two methods.
First of all, as the time In a token cannot be manipulated (either it is a isolated frob with bad clock or it is hosted on a system where you cannot influence the clock).
So what needs to be done is on the authentication server, you keep a estimated time offset (or more complex compensation model) and adjust it on each login.
Basically you compare the input from the current authentication with a few codes before and after the current code. If it matches you know a offset to apply next time.
This correction method can be as complicated as you want, since you don’t want to allow an attacker to influence your estimates or you don’t want to make the window too big as it allows more guessing. (For example keep a history of the latest 3 authentications and offsets and do a linear regression to find not only the offset but also the slope of difference)
If you decide that the time drift is too big (or you have no initial waydata for prediction) you ask for multiple entries. This will avoidreduce the risk of accepting a repeating code (in a too wide timespan). And in
For the end this will also allowinitial registration you probably want to calculateallow a wider timespan before you reject a token (however be careful if the token has a broken clock and you accept the registration you might risk that a future login fails, which is more annoying than rejecting a device specific time correction.registration)
The exactly same mechanism applies to counter based systems as well (with the additional task to account for missed timer updates and luckily never negative offsets)