How To Achieve User Authentication By Web3 Wallet Using Spring Security, JWTs, Web3j & Kotlin

One of the greatest things that a web3 enabled browser gives us is saying goodbye to our primal ways of authenticating users with a username & password combination.

If you're looking to authenticate users using a metamask or any web3 wallet then continue reading this article or simply explore the minimal code here: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo

You can checkout the code and follow along, or start fresh from spring initializer and start fresh, or add whatever you learned here to your existing project!

How Does This Work?

  1. The minimal frontend code uses web3js to request the user to sign a message using their web3 enabled browser.
  2. The backend receives the message and makes sure that the person who signed it is the one who called the controller login action by comparing the public key of the user with public key that is extracted from the signed message.
  3. If the login is successful, a jwt is generated by spring security and we send it back to the frontend.
  4. Frontend uses the jwt to call authenticated actions.

Necessities

Be sure to take a look at the pom: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/pom.xml and grab whatever your project is missing.

Add and configure your application.properties similarly to this: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/src/main/resources/application.properties

Spring Security Configuration

Killing CORS: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/src/main/kotlin/com/example/demo/config/WebMvcConfig.kt

Adding authenticated paths, adding our simple "NoPasswordEncoder" inline class, and other spring security configurations: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/src/main/kotlin/com/example/demo/config/SecurityConfig.kt

Controller Actions

Two actions needed to demonstrate this: 1. Authentication action to generate the jwt and make sure you are the signer of the message. 2. A detail fetching service that requires a live jwt token to demonstrate that we're actually logged in. And we actually fetch who the calling user is using the token which is another nice convenience when building dApps.

Both of these can be found here: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/src/main/kotlin/com/example/demo/controller/UserController.kt

Spring Security Things

This part of the demo can honestly be copied and pasted but I recommend reading over it to know what's going on. It's basically giving us the @CurrentUser annotation, telling spring security that we're using jwts, and everything in between. It's all here: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/tree/master/src/main/kotlin/com/example/demo/security

Demonstrative Frontend

Our frontend is very minimal and only used to call two things: 1. POST /api/user -> To create the user entity in the database for the current public address of the user calling it. And generating a jwt for the user to use for further interactions with the dApp. The jwt gets saved in localStorage. 2. GET /api/userDetails -> To get the details if jwt is valid. To show the unathenticated resposne if the jwt isn't, or if there isn't one in the browser's localStorage.

The frontend's bread and butter is here: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo/blob/master/demo_frontend/src/views/LandingPage/LandingPage.jsx

How To Run

Special Thanks

To Callicoder for this tutorial series that taught me how to do spring security authentication using passwords. https://www.callicoder.com/spring-boot-spring-security-jwt-mysql-react-app-part-1/