Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Token login with JWT

With Basic login the password travels in every request. Usually it works differently: the client sends the login and password once to a login address and gets a token, a string that proves who the client is. After that the client sends only the token in the Authorization: Bearer ... header. The most common token format is JWT (JSON Web Token): three parts separated by dots, user data in the middle and the server's signature at the end. Without the secret key a token cannot be forged.

Settings

The micronaut-security-jwt module was already added in the previous lesson. Add two lines to the settings:

# src/main/resources/application.properties
micronaut.security.authentication=bearer
micronaut.security.token.jwt.signatures.secret.generator.secret=${JWT_SECRET:changemechangemechangemechangeme}

The first line turns on token mode: a POST /login address appears, and the server accepts the Bearer header. The second sets the signing secret. ${JWT_SECRET:...} means "take it from the JWT_SECRET environment variable, or use the value after the colon if there is none". On a server the secret is set only through the variable.

BlogAuthenticationProvider from the previous lesson stays as it is: /login checks the login and password through it.

Getting a token

curl -X POST http://localhost:8080/login \
  -H "Content-Type: application/json" \
  -d '{"username":"oleg","password":"changeme"}'
{"username":"oleg","roles":["AUTHOR"],"expires_in":3600,"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJibG9nIiwic3ViIjoib2xlZyIsIm5iZiI6MTc5MTEwNDk5OCwiZXhwIjoxNzkxMTA4NTk4LCJpYXQiOjE3OTExMDQ5OTgsInJvbGVzIjpbIkFVVEhPUiJdfQ.wmoVIAAmS-k5aPIKf9RS7Q9FUJEgsG5FVcWJMjojLFc","token_type":"Bearer"}

The token is valid for 3600 seconds, one hour. The middle part is plain Base64 and can be read:

{"iss":"blog","sub":"oleg","nbf":1791104998,"exp":1791108598,"iat":1791104998,"roles":["AUTHOR"]}

sub is the user name, roles are the roles, exp is the expiry time. There is no password in the token. The server does not store issued tokens: it checks the signature and the expiry.

Requests with a token

Save the token in a variable and pass it in the header:

TOKEN=eyJhbGciOiJIUzI1NiJ9...
curl -X POST http://localhost:8080/articles \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"With JWT","body":"Text"}'
{"id":1,"title":"With JWT","body":"Text"}

The @Secured rules from the previous lesson work the same way, with roles taken from the token. A /me controller with an Authentication parameter shows {"name":"oleg","roles":["AUTHOR"]}.

Error responses

SituationResponse
Wrong password at /login401 Credentials Do Not Match
No token, but the method requires login401 Unauthorized
The token is broken or expired401 Unauthorized
The token is valid, but the role is missing403 Forbidden

Common mistake

The HS256 signing algorithm needs a secret of at least 256 bits, which is 32 characters. With a short secret such as JWT_SECRET=changeme, the application starts, but /login answers 500 with an empty body, and the log says:

WARN  i.m.s.t.j.g.JwtTokenGenerator - JOSEException while generating token The secret length must be at least 256 bits

Use a long random string and keep it outside the repository.

Thanks for reading!

Donate to the site — we work hard for you.