Should I create a State class where the local player's state is saved at every tick in a buffer, for revert being possible ? Or should I wait for the server to respond and should the client be a simple renderer ?
In an ideal authoritative server you would do the latter. However, this might cause a problem for some games: Even the best internet connection still has a few ms of latency. The player will notice when their character reacts to their input with a certain delay. The more skill-oriented and fast your game is, the more annoying this will become.
For that reason you should implement "client-sided control with revert" for the player-character(s) and "listen to the server" for anything else.
The server should inform the client about everything which happens around it, including its own actions. When the player gives a command, then you should both process it on the client and send it to the server. The server will then respond with how it interprets the command. When the client receives an answer which agrees with its own interpretation, everything is OK. When the server disagrees with the client, the client needs to correct.
Player: I press W.
Client: Hey, Rendering Engine: move player from 10:10 to 12:10. Hey, Server: move my player from 10:10 to 12:10
Server: That client wants to do what? Someone else just moved to 12:10 and blocks it now. And besides, the current maximum move speed of that player is just one unit per tick. The furthest it can move is 11:10. Hey, all clients: Player1 is now at 11:10.
Client: Ok, my bad. Rendering Engine: move player back to 11:10.