Implementing OAuth2 and JWT authentication in a Micronaut application is crucial for making sure your REST APIs are secure. Here’s a practical guide to get you set up in no time.
Setting Up Your Micronaut Application
First up, let’s get your Micronaut application off the ground. You can whip up a new Micronaut project using the CLI. Just run this command:
mn create-app my-app --features security-jwt
This command will spin up a new Micronaut application, complete with security and JWT features already enabled. Simple, right?
Enabling Security
Next, you’ll need to enable security in your shiny new Micronaut application. Update your build.gradle
file with the necessary dependencies.
dependencies {
annotationProcessor "io.micronaut:micronaut-security"
implementation "io.micronaut:micronaut-security"
}
Then, it’s time to configure your application.yml
file to turn on these security features.
micronaut:
security:
enabled: true
With this setup, any endpoint you try to access will kick back an HTTP Status Unauthorized (401) unless you’re authenticated.
Configuring JWT Authentication
Let’s dive into setting up JWT authentication. JSON Web Tokens (JWT) are a solid pick for their blend of simplicity and security. Micronaut’s got you covered with out-of-the-box support using the Nimbus JOSE + JWT library.
First, specify your JWT settings in the ever-important application.yml
.
micronaut:
security:
token:
jwt:
signatures:
secret:
generator:
secret: "pleaseChangeThisSecretForANewOne"
jws-algorithm: "HS256"
This setup configures a secret key for signing and verifying your JWTs. Remember to change the secret key to something more secure!
Creating a Login Endpoint
Now, let’s create a login endpoint that issues these JWT tokens. Here’s a nifty example to model your endpoint after:
import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Post
import io.micronaut.security.authentication.UsernamePasswordCredentials
import io.micronaut.security.token.render.BearerAccessRefreshToken
import static io.micronaut.http.HttpHeaders.AUTHORIZATION
@CompileStatic
@Controller("/login")
class LoginController {
@Post
BearerAccessRefreshToken login(@Body UsernamePasswordCredentials credentials) {
// Implement your authentication logic here
// For example, you can use a service to validate credentials
// and then generate a JWT token
return new BearerAccessRefreshToken("your-generated-token", "your-refresh-token")
}
}
This controller takes care of the login request and will return a JWT token if the entered credentials are good to go.
Securing Endpoints with JWT
Once your login endpoint is operational, you’re ready to lock down other endpoints by requiring a JWT token in the Authorization
header. Check out this example showing how to secure an endpoint:
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.annotation.Secured
import io.micronaut.security.annotation.Secured
import io.micronaut.security.rules.SecurityRule
@CompileStatic
@Controller("/")
@Secured(SecurityRule.IS_AUTHENTICATED)
class HomeController {
@Get
@Header(AUTHORIZATION)
String home(@Header(AUTHORIZATION) String authorization) {
// This endpoint is only accessible if JWT token is provided
return "Welcome to the home page!"
}
}
In this code, the home
endpoint is wrapped with the @Secured
annotation, meaning you need to be authenticated to get in.
Using OAuth2 for Authentication
OAuth2 is fantastic when you want to integrate with external providers like Google, Okta, or Auth0. Here’s how you can gear up your Micronaut application with OAuth2.
Configuring OAuth2
Start with adding the OAuth2 dependency to your build.gradle
file:
dependencies {
implementation "io.micronaut.configuration:micronaut-security-oauth2"
}
Then, update your application.yml
with the OAuth2 settings. Here’s an example using Google as the provider:
micronaut:
security:
oauth2:
enabled: true
clients:
google:
client-id: 'your-client-id'
client-secret: 'your-client-secret'
openid:
issuer: 'https://accounts.google.com'
Swap out the client ID and secret with those provided by your chosen OAuth2 provider.
Handling OAuth2 Callback
With OAuth2, there’s always a callback from the provider. Micronaut handles this gracefully. Configure your callback handling as follows:
micronaut:
security:
oauth2:
clients:
google:
openid:
issuer: 'https://accounts.google.com'
callback-url: '/oauth/callback/google'
Don’t forget to set up the redirect URL in your OAuth2 provider’s settings. For Google, for example, you’d include http://localhost:8080/oauth/callback/google
as an authorized redirect URI.
Testing Your Setup
Of course, you want to make sure everything works perfectly. Writing tests will help you catch any bugs early on. Here’s a test example for JWT authentication using Micronaut’s HTTP client:
import io.micronaut.http.client.annotation.Client
import io.micronaut.security.authentication.UsernamePasswordCredentials
import io.micronaut.security.token.render.BearerAccessRefreshToken
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import spock.lang.Specification
@MicronautTest
class DeclarativeHttpClientWithJwtSpec extends Specification {
@Inject
AppClient appClient
void "Verify JWT authentication works with declarative @Client"() {
when: 'Login endpoint is called with valid credentials'
def credentials = new UsernamePasswordCredentials("username", "password")
def token = appClient.login(credentials)
then: 'The token is not null'
token != null
when: 'The home endpoint is called with the JWT token'
def response = appClient.home("Bearer " + token.accessToken)
then: 'The response is successful'
response == "Welcome to the home page!"
}
}
@Client("/")
interface AppClient {
@Post("/login")
BearerAccessRefreshToken login(@Body UsernamePasswordCredentials credentials)
@Get
@Header(AUTHORIZATION)
String home(@Header(AUTHORIZATION) String authorization)
}
This test ensures your JWT token is issued and used correctly to access a secured endpoint.
Increasing Log Levels for Debugging
If you hit any snags along the way, ramping up your log levels can be a lifesaver. Configure logging in your application.yml
file like this:
logger:
levels:
io.micronaut.security.authentication: DEBUG
This gives you more detailed logs, super useful for debugging authentication issues.
Conclusion
Rolling out OAuth2 and JWT authentication in a Micronaut application isn’t rocket science. With these straightforward steps, you’ll have a secure setup, ensuring your REST APIs are safeguarded. Always remember to rigorously test to catch any issues early on. Micronaut’s built-in security features make this a breeze. Enjoy secure coding!