How can I generate something similar to 2FA on-chain? Could the private key or public key be used somehow in secrecy to compute a value or something? I need a way to authenticate a smart contract user caller so no-one else can falsely pretend to possess his claim.
In short: probably not.
All information in the blockchain is public even if you use variable definitions such as
private or so.
Authentication in smart contracts is typically done against the sender's address. So access to certain functions is limited to only a whitelisted list of sender addresses. This way you of course need to know the addresses in advance.
In theory users can use their private key to compute some secret value (besides their public address). But using such a value would be rather pointless as the admin has no way of knowing what the secret should be unless they possess the same private keys (which is a no-no).
If the user creates a secret with his private key and sends the result to the admin out-of-blockchain the admin would have to add it to the blockchain (to a whitelist of secrets, of sorts) and it becomes public instantly and someone else can use it.
One option might be for the user to send a transaction with a secret and the smart contract would mark the transaction as pending internally. Then the admin (or some backend system) checks the secret out-of-blockchain and issues an "ok, allow this transaction" to the contract. But, then again, this way the actual verification would be off-chain so what's the point.
It really depends on what you are going after and what level of privacy, security, and decentralization you need. It also depends on what you mean by "2FA".
Regardless, if you need to double-check a user's identity or whatever, you have to actually rely on two, distinct pieces of information. In your question, it sounds like you are trying to create a second piece of information from the first piece of information (the user's key).
In the case of compromise of the user's private key, the hacker has all the information necessary to generate this second piece of information. So even though it would be two piece of information, it's really only one.
If you are creating a contract
You can't really control the wallet / account types users use to interact with your contract, unless you manually set people up with wallets & whitelist specific addresses once they have met certain requirements.
You could potentially call an off-chain source for verification via an on-chain oracle. This could associate an external piece of information with the user's on-chain address / transaction. However, I believe the user would need to first authenticate the on-chain source before the on-chain source, which feels weird but could be solved via UI/UX.
You could also whitelist addresses once they complete X steps. An address that hasn't verified their identity can't send via your smart contract.
More interestingly, though....
If you are creating a wallet: TOTP
Typically when people refer to 2FA, they are referring to TOTP (time-based one-time password).
This relies on a "shared secret". You have a secret, the server has the secret, math and time and algorithms are used and if the result of the data generated by the
secret + time + mathmatch, you are auth'd in.
This isn't really possible on the blockchain as there is no place to put that "shared secret" that isn't public. And, if everyone can get access to your secret, it's not really a secret. 😉
There is probably a centralized mechanism where a transaction needs to be signed and then approved by a second party. The user can generate the transaction via their device but needs to approve it via a centralized, traditional service in order for it to be fully signed & sent to the network. In this case, it would be
private key on users deviceand then
username + password + traditional TOTP 2faon a server somewhere. Which is actually "three factors".
If you are creating a wallet: No Shared Secrets
U2F, most commonly used by Yubikeys and hardware wallets, is another form of 2FA that doesn't rely on a shared secret. Here's how that works:
You, the user, are the only one with the secret (the private key).
The server generates a challenge and sends it to you.
Your U2F device signs the challenge and sends it back.
The server verifies it and allows you to log in.
Again, this is assuming a server, not a smart contract. Additionally, this is almost exactly what the current situation whenever you sign a transaction to send your crypto-assets around currently is.
You decide where you want to send, how much you want to send.
You format it in a universally agreed upon way.
You sign it with your private key.
You send it to all the servers (aka broadcast it to the network to all the nodes and miners).
They verify that you indeed signed it.
The transaction is sent successfully.
This isn't "two factors" though because you aren't combining it with a traditional login—this is only one factor: your private key.
If you are creating a wallet: No Shared Secrets + Multiple Pieces of Information Needed
The most secure, private, and decentralized option is going to be a multisig.
Multisigs require multiple keys to sign and approve a transaction before it can be sent.
While we often think about multisigs being for multiple different parties who are controlling or protecting a lot of money, it could just as easily be a user on their computer and the same user on their phone.
Gnosis Safe is attempting to do this for regular users.
This is "two factors", even though it's not the code on your phone like you might have been thinking about.