Skip to content

Add JWT authentication module and static analysis integration#3

Open
phongphongg wants to merge 1 commit into
mainfrom
feat/authentication
Open

Add JWT authentication module and static analysis integration#3
phongphongg wants to merge 1 commit into
mainfrom
feat/authentication

Conversation

@phongphongg
Copy link
Copy Markdown
Contributor

@phongphongg phongphongg commented Nov 23, 2025

@coderabbitai

Summary by CodeRabbit

Release Notes

  • New Features

    • Implemented authentication module with JSON-based login support
    • Added CSRF protection with token endpoints
    • Added authentication information endpoint
    • Standardized API response format with consistent error handling
  • Infrastructure

    • Enhanced code quality with static analysis tools
    • Improved code safety with null-safety checking across modules
    • Updated configuration management and deployment profiles

✏️ Tip: You can customize this high-level summary in your review settings.

@phongphongg phongphongg self-assigned this Nov 23, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 23, 2025

📝 Walkthrough

Walkthrough

This PR introduces a new authentication module with JSON-based login security infrastructure, adds an API response framework to the common module, integrates compile-time null-checking via Error Prone and NullAway, reorganizes module component scanning, and establishes consistent package-level null-safety annotations across modules.

Changes

Cohort / File(s) Summary
CI/CD & Build Infrastructure
.github/workflows/workastra_server.yaml, .vscode/launch.json
Separated JVM and native test execution with SPRING_PROFILES_ACTIVE=test environment configuration; added VSCode launch JVM args and dev profile env.
Compile-Time Null Checking
build.gradle.kts, gradle/libs.versions.toml, config/checkstyle/checkstyle.xml
Added Error Prone plugin (v4.3.0) with NullAway (v0.12.12) integration at root and subproject levels; enabled null-safety checks with JSpecify mode and error elevation; added Checkstyle rule to enforce @Bean methods as package-private.
Authentication Module: Build & Configuration
modules/authentication/build.gradle.kts, modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java, modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java, modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java
New authentication module with gradle build setup; configured in-memory user store (admin/admin with BCrypt encoding), AuthenticationManager bean, and custom JSON login flow via SecurityFilterChain with CSRF protection and public health/CSRF/login endpoints.
Authentication Module: Handlers & Filters
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/Json*.java, modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java
Implemented JsonAuthenticationEntryPoint (401 handler), JsonAccessDeniedHandler (403 handler), JsonAuthSuccessHandler, JsonAuthFailureHandler, and JsonLoginAuthenticationFilter for custom JSON request/response handling.
Authentication Module: Controller & Null Safety
modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java, modules/authentication/src/main/java/com/workastra/authentication/*/package-info.java
New AuthController exposing CSRF token and authentication endpoints; package-level @NullMarked annotations added to authentication module and subpackages.
Common Module: API Response Framework
modules/common/src/main/java/com/workastra/common/api/{ApiResponse,ErrorItem,Meta,Pagination}.java
Introduced generic ApiResponse record with success/error factories, supporting error lists and metadata; added ErrorItem, Meta (with request ID and timestamp resolution), and Pagination records; all with JsonInclude(NON\_NULL) and @NullMarked.
Common Module: Dependency Update
modules/common/build.gradle.kts
Replaced implementation of spring-boot-starter with api exposure of spring-boot-starter-webmvc.
Console Module: Integration
modules/console/build.gradle.kts, modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java, modules/console/src/main/java/com/workastra/console/package-info.java
Added authentication module dependency and annotation processor for configuration metadata; narrowed component scan from explicit packages to root "com.workastra" package; added package-level @NullMarked.
Console Module: Configuration & Metadata
modules/console/src/main/resources/application.yaml, modules/console/src/main/resources/application-dev.yaml, modules/console/src/main/resources/application-test.yaml, modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json
Removed structured logging config; enabled API versioning path segments and disabled whitelabel error page; added DEBUG logging for Spring Security in dev profile; created test profile; added JWT configuration metadata (issuer, secret, TTL).
Worker Module & Settings
modules/worker/src/main/java/com/workastra/worker/package-info.java, settings.gradle.kts
Added package-level @NullMarked to worker module; updated settings to include authentication module in build.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SecurityFilterChain
    participant JsonLoginAuthenticationFilter
    participant AuthenticationManager
    participant UserDetailsService
    participant JsonAuthSuccessHandler
    participant JsonAuthFailureHandler
    participant Response

    Client->>SecurityFilterChain: POST /api/v1/auth/login (JSON)
    SecurityFilterChain->>JsonLoginAuthenticationFilter: intercept request
    JsonLoginAuthenticationFilter->>JsonLoginAuthenticationFilter: parse JSON (username, password)
    JsonLoginAuthenticationFilter->>JsonLoginAuthenticationFilter: create UsernamePasswordAuthenticationToken
    JsonLoginAuthenticationFilter->>AuthenticationManager: authenticate(token)
    
    alt Authentication Success
        AuthenticationManager->>UserDetailsService: loadUserByUsername(username)
        UserDetailsService-->>AuthenticationManager: return UserDetails
        AuthenticationManager-->>JsonLoginAuthenticationFilter: authenticated token
        JsonLoginAuthenticationFilter->>JsonAuthSuccessHandler: onAuthenticationSuccess()
        JsonAuthSuccessHandler->>Response: HTTP 200 + JSON {success: true, username}
    else Authentication Failure
        AuthenticationManager-->>JsonLoginAuthenticationFilter: throw AuthenticationException
        JsonLoginAuthenticationFilter->>JsonAuthFailureHandler: onAuthenticationFailure(exception)
        JsonAuthFailureHandler->>Response: HTTP 401 + JSON {success: false, error}
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Security critical: Custom authentication filter, SecurityFilterChain configuration with CSRF, filter chain ordering, and handler wiring require careful verification for bypass risks and proper exception handling.
  • Password management: Hardcoded demo credentials in UserDetailsService must be flagged as intentional and temporary; BCrypt encoding validation needed.
  • API contracts: New ApiResponse generic structure, Meta.fromCurrentRequest() request ID resolution (header→MDC→UUID), and error response design have integration implications across all endpoints.
  • Module coupling: New authentication module dependency added to console; component scan scope broadened from explicit packages to "com.workastra" root—verify no unintended bean discovery.
  • Null-safety enforcement: Error Prone/NullAway applied across modules with JSpecify mode; check for annotation consistency and compatibility with existing code patterns.
  • Handler implementations: Four custom JSON handlers (success, failure, entry point, access denied) implementing Spring Security contracts; validate IOException handling and JSON serialization edge cases.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately summarizes the PR's two main contributions: adding a JWT authentication module and integrating static analysis (Error Prone with NullAway).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/authentication

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@phongphongg phongphongg added this to the v0.1.0 milestone Nov 23, 2025
@phongphongg phongphongg force-pushed the feat/authentication branch 2 times, most recently from da20aa8 to d782581 Compare November 24, 2025 14:07
@phongphongg phongphongg force-pushed the feat/authentication branch 9 times, most recently from 07ef2c5 to 235ded8 Compare November 29, 2025 09:25
@phongphongg
Copy link
Copy Markdown
Contributor Author

/gemini summary

@phongphongg phongphongg marked this pull request as ready for review November 29, 2025 09:48
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

This pull request significantly enhances the project's security posture by introducing a dedicated authentication module built on Spring Security and JSON Web Tokens. Concurrently, it elevates code quality standards and developer experience through the integration of advanced static analysis tools and refined build configurations. The changes lay a robust foundation for secure API interactions and improved code maintainability.

Highlights

  • New Authentication Module: A new authentication module has been introduced, providing JWT-based authentication capabilities using Spring Security. This includes JWT encoding/decoding, security filter chain configuration, and a dedicated login endpoint.
  • Enhanced Static Analysis: The build system has been upgraded to integrate Error Prone and NullAway, enforcing strict nullness checks and other code quality rules across the project to improve reliability and reduce common errors.
  • Checkstyle Rule for @bean Methods: A new Checkstyle rule has been added to ensure that @Bean methods in Spring configurations are declared without explicit modifiers, promoting a consistent package-private scope.
  • VS Code Launch Configuration Update: The .vscode/launch.json has been modified to include vmArgs for UseCompactObjectHeaders and to set the SPRING_PROFILES_ACTIVE environment variable to dev for local development.
  • Spring Boot Configuration Refinements: The console module's application.yaml now includes API versioning for Spring MVC and removes structured logging. New application-dev.yaml and application-test.yaml files have been added to manage JWT-specific configurations.
Changelog
  • .vscode/launch.json
    • Added vmArgs for UseCompactObjectHeaders to the launch configuration.
    • Set SPRING_PROFILES_ACTIVE to dev in the environment variables for the console application.
  • build.gradle.kts
    • Integrated the net.ltgt.gradle.errorprone plugin.
    • Configured Error Prone and NullAway dependencies and options for Java compilation tasks, enabling nullness checks and promoting them to errors.
    • Imported libsCatalog for streamlined dependency management.
  • config/checkstyle/checkstyle.xml
    • Added a new MatchXpath module to enforce that @Bean methods do not declare any modifiers (i.e., are package-private).
  • gradle/libs.versions.toml
    • Added error-prone-core and nullaway libraries.
    • Included the net-ltgt-errorprone plugin.
  • modules/authentication/build.gradle.kts
    • Created a new Gradle build file for the authentication module.
    • Applied java-library and spring.dependency.management plugins.
    • Added Spring Boot security and OAuth2 client dependencies.
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
    • Added a new Spring @Configuration class for the authentication module.
    • Configured SecretKey, JwtEncoder, and JwtDecoder as Spring beans.
    • Defined a SecurityFilterChain to secure API endpoints, allowing public access to /actuator/health, /api/v1/csrf, /api/v1/login, and requiring authentication for /api/**.
    • Configured an in-memory UserDetailsService with a default 'admin' user.
    • Provided PasswordEncoder and AuthenticationManager beans.
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java
    • Added a new record JwtConfiguration for JWT-related properties (secret, issuer, ttl).
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/package-info.java
    • Added package-info.java with the @NullMarked annotation for nullness analysis.
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
    • Added a new REST controller AuthController to handle authentication requests.
    • Implemented a /api/v1/login endpoint for user authentication and JWT token generation.
  • modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java
    • Added a new service JwtTokenService responsible for generating JWT tokens.
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
    • Added package-info.java with the @NullMarked annotation for nullness analysis.
  • modules/authentication/src/main/java/com/workastra/authentication/package-info.java
    • Added package-info.java with the @NullMarked annotation for nullness analysis.
  • modules/common/build.gradle.kts
    • Changed implementation("org.springframework.boot:spring-boot-starter") to api("org.springframework.boot:spring-boot-starter-webmvc").
  • modules/console/build.gradle.kts
    • Added the authentication module as an implementation dependency.
    • Included spring-boot-configuration-processor as an annotationProcessor dependency.
    • Configured compileOnly to extend annotationProcessor.
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java
    • Updated the scanBasePackages in @SpringBootApplication to scan the entire com.workastra package.
  • modules/console/src/main/java/com/workastra/console/package-info.java
    • Added package-info.java with the @NullMarked annotation for nullness analysis.
  • modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json
    • Added metadata for workastra.jwt.issuer, workastra.jwt.secret, and workastra.jwt.ttl properties.
  • modules/console/src/main/resources/application-dev.yaml
    • Added a new application-dev.yaml file with development-specific JWT configuration.
  • modules/console/src/main/resources/application-test.yaml
    • Added a new application-test.yaml file with test-specific JWT configuration.
  • modules/console/src/main/resources/application.yaml
    • Removed the structured logging configuration.
    • Added Spring MVC API versioning configuration.
  • modules/worker/src/main/java/com/workastra/worker/package-info.java
    • Added package-info.java with the @NullMarked annotation for nullness analysis.
  • settings.gradle.kts
    • Included the new modules:authentication project in the build.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/workastra_server.yaml
Activity
  • On 2025-11-23, coderabbitai[bot] skipped the review as the pull request was in draft status.
  • On 2025-11-24, github-advanced-security[bot] reported a 'Disabled Spring CSRF protection' vulnerability.
  • On 2025-11-29, phongphongg requested a summary using the /gemini summary command.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Add JWT authentication module and static analysis integration Nov 29, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (1)
modules/console/src/main/resources/application-test.yaml (1)

1-6: Same JWT secret management considerations as in application-dev.yaml.

This test profile repeats the same workastra.jwt block. The guidance about treating secret as a non-production placeholder and sourcing real secrets from env/secret management (see comment on application-dev.yaml) applies here as well.

🧹 Nitpick comments (10)
modules/console/src/main/java/com/workastra/console/package-info.java (1)

1-4: Package-level @NullMarked usage is fine; consider documenting package semantics

The null-safety annotation and import are correct and idiomatic for package-info. To improve clarity for future readers, consider adding a brief Javadoc on the package describing its role and the “non-null by default” contract enforced by @NullMarked.

modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java (1)

1-4: Null-safety default for controllers looks good

Using @NullMarked at the controller package is appropriate and consistent. As a minor improvement, you could add a package Javadoc clarifying that controller code is “non-null by default” and how to handle nullable inputs/outputs (e.g., explicitly annotate them).

modules/worker/src/main/java/com/workastra/worker/package-info.java (1)

1-4: Worker package null-default is correctly defined

The package-level @NullMarked and import are correct and align with the rest of the codebase. Optionally, add a short package Javadoc describing the worker module’s responsibility and the non-null-by-default convention.

modules/authentication/src/main/java/com/workastra/authentication/configuration/package-info.java (1)

1-4: Configuration package null-safety annotation is consistent and correct

@NullMarked at the configuration package level is appropriate and consistent with other modules. For extra clarity, consider adding package-level Javadoc summarizing the configuration role and stating that all declarations are non-null by default unless explicitly annotated otherwise.

modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1)

6-6: Be explicit about component scan scope instead of scanning the entire com.workastra tree.

Switching to scanBasePackages = "com.workastra" will pick up beans from every submodule on the classpath (including worker or future apps), which can lead to unintended controllers/configs being loaded into the console context. Consider narrowing this to only the modules you actually want in the console app, e.g.:

-@SpringBootApplication(scanBasePackages = "com.workastra")
+@SpringBootApplication(
+    scanBasePackages = {
+        "com.workastra.console",
+        "com.workastra.common",
+        "com.workastra.authentication"
+    }
+)

This aligns better with least surprise and avoids accidental cross-wiring between applications.

modules/console/build.gradle.kts (1)

8-12: Simplify annotation processor wiring if you don’t need processors on the compile classpath.

The new dependency on :modules:authentication and the spring-boot-configuration-processor look good. The compileOnly { extendsFrom(configurations.annotationProcessor.get()) } block is only necessary if you specifically want all annotation processors also on the compile classpath (common for Lombok, not required for Spring Boot’s configuration-processor).

If you’re only using spring-boot-configuration-processor, you can simplify to:

-configurations {
-  compileOnly {
-    extendsFrom(configurations.annotationProcessor.get())
-  }
-}
-
 dependencies {
   implementation(project(":modules:common"))
   implementation(project(":modules:authentication"))
   implementation("org.springframework.boot:spring-boot-starter-actuator")
   implementation("org.springframework.boot:spring-boot-starter-webmvc")
   annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
   ...
 }

This keeps the compile classpath narrower and avoids leaking processors into places they’re not needed.

Also applies to: 16-16, 19-19

gradle/libs.versions.toml (1)

6-7: Static analysis additions look good; centralize versions and confirm JDK compatibility.

Adding error-prone-core, nullaway, and the net.ltgt.errorprone plugin is a solid move. Two suggestions:

  1. Centralize versions for easier upgrades
    Consider moving these version numbers into the [versions] section and referencing them via version.ref for consistency with spring-boot:

    [versions]
    spring-boot = "4.0.0"
    error-prone = "2.44.0"
    nullaway = "0.12.12"
    
    [libraries]
    error-prone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "error-prone" }
    nullaway = { module = "com.uber.nullaway:nullaway", version.ref = "nullaway" }
    
    [plugins]
    net-ltgt-errorprone = { id = "net.ltgt.errorprone", version = "4.3.0" }
  2. Verify compatibility with your JDK level
    Please double-check that these specific versions support the Java version you’re building with (especially if you’re on a recent JDK).

Also applies to: 13-13

modules/console/src/main/resources/application-dev.yaml (1)

1-6: Avoid shipping a concrete default JWT secret, even in dev configs.

The secret here is clearly a placeholder, which is good, but to reduce the risk of someone promoting this config unchanged to a higher environment, consider:

  • Removing the default secret value and requiring it via env/secret manager, or
  • Keeping the placeholder but adding an explicit comment or validation that fails fast if the placeholder value is used at runtime.

This keeps JWT key management safer and makes misconfigurations more obvious.

modules/common/build.gradle.kts (1)

6-9: Reconsider leaking spring-boot-starter-webmvc via api from common

Declaring spring-boot-starter-webmvc as an api dependency in the common module means every module depending on common is now transitively tied to MVC/web. For a “common” library this is usually undesirable and increases coupling.

If only web-oriented modules need MVC, prefer:

-dependencies {
-  implementation(platform(libs.spring.boot.bom))
-  api("org.springframework.boot:spring-boot-starter-webmvc")
-}
+dependencies {
+  implementation(platform(libs.spring.boot.bom))
+  implementation("org.springframework.boot:spring-boot-starter-webmvc")
+}

or move MVC-specific code to a dedicated web module.

modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java (1)

1-2: Move JwtTokenService out of the controller package

This is a pure service component (@Service) with no web concerns, but it lives in com.workastra.authentication.controller. Mixing services into controller packages hurts layering and discoverability.

Consider moving it to something like com.workastra.authentication.service (and updating imports) to keep controllers thin and services grouped.

Also applies to: 16-17

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a55f290 and 235ded8.

📒 Files selected for processing (23)
  • .github/workflows/workastra_server.yaml (1 hunks)
  • .vscode/launch.json (1 hunks)
  • build.gradle.kts (3 hunks)
  • config/checkstyle/checkstyle.xml (1 hunks)
  • gradle/libs.versions.toml (1 hunks)
  • modules/authentication/build.gradle.kts (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/package-info.java (1 hunks)
  • modules/common/build.gradle.kts (1 hunks)
  • modules/console/build.gradle.kts (1 hunks)
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1 hunks)
  • modules/console/src/main/java/com/workastra/console/package-info.java (1 hunks)
  • modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json (1 hunks)
  • modules/console/src/main/resources/application-dev.yaml (1 hunks)
  • modules/console/src/main/resources/application-test.yaml (1 hunks)
  • modules/console/src/main/resources/application.yaml (1 hunks)
  • modules/worker/src/main/java/com/workastra/worker/package-info.java (1 hunks)
  • settings.gradle.kts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/src/main/java/**/*.java

⚙️ CodeRabbit configuration file

**/src/main/java/**/*.java: You are acting as a Senior Java Engineer and Software Architect.
Review with a direct, practical, no-nonsense tone.

You must enforce modern Java (Java 25) best practices and discourage legacy or outdated patterns.
Always prefer:

  • modern language features when appropriate (var, records, switch expressions, pattern matching, sealed types, etc.)
  • the java.time API over java.util.Date/Calendar
  • java.util.concurrent and modern concurrency utilities over legacy synchronized collections
  • NIO and modern I/O APIs where suitable
  • streams and functional APIs where they clearly improve readability and safety (but avoid over-complicating simple logic)

You must explicitly:

  • Call out usage of old/legacy or deprecated APIs, raw types, Vector, Hashtable, synchronized collections, old I/O where NIO is better, and other outdated patterns.
  • Highlight any code that is not aligned with current Java 25 idioms.
  • Enforce import hygiene: avoid star imports, unused imports, and messy import sections.

Handle NullPointerException (NPE) risks very carefully:

  • Identify every place where NPE can occur (parameters, return values, map lookups, collection elements, optional values, chained calls, etc.).
  • Recommend explicit null checks, Objects.requireNonNull, Optional, or nullability annotations where appropriate.
  • Call out dangerous chaining on potentially null values.
  • Prefer designs that minimize nullable state and unexpected nulls.

You must review naming quality:

  • Check that class, method, variable, and field names are clear, descriptive, and follow Java conventions (CamelCase for classes, lowerCamelCase for fields/variables/methods, UPPER_SNAKE_CASE for constants).
  • Flag vague, misleading, or overly generic names (e.g. data, obj, tmp, doStuff, handle, manager, util).
  • Suggest better, more meaningful names that reflect the intent and domain.
  • Prefer names that express behavior and domain concepts over implementation details.

Enforce...

Files:

  • modules/console/src/main/java/com/workastra/console/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java
  • modules/worker/src/main/java/com/workastra/worker/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java
  • modules/authentication/src/main/java/com/workastra/authentication/configuration/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/package-info.java
🧠 Learnings (1)
📚 Learning: 2025-11-23T02:13:19.027Z
Learnt from: phongphongg
Repo: workastra/server PR: 2
File: gradle/libs.versions.toml:1-10
Timestamp: 2025-11-23T02:13:19.027Z
Learning: Spring Boot 4.0.0 is compatible with org.graalvm.buildtools.native plugin version 0.11.3, which is the recommended version for Spring Boot 4 native image builds.

Applied to files:

  • modules/console/build.gradle.kts
  • gradle/libs.versions.toml
🧬 Code graph analysis (1)
modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1)
modules/worker/src/main/java/com/workastra/worker/WorkastraWorkerApplication.java (1)
  • SpringBootApplication (6-12)
🔇 Additional comments (7)
config/checkstyle/checkstyle.xml (1)

143-147: Verify strict @bean access control aligns with project architecture.

The XPath correctly flags @bean methods with any modifier beyond the annotation (public, protected, private, static, etc.). However, this enforces package-private access for ALL @bean methods, which deviates from standard Spring Boot patterns where @bean methods are typically public within @configuration classes.

Confirm:

  1. Is forbidding public @bean methods intentional?
  2. Will existing code need significant refactoring?
  3. Does this apply project-wide or only to specific authentication/security modules?
.vscode/launch.json (1)

10-11: Perfect! I now have all the information needed. The verification reveals that:

  1. Java version: The project explicitly targets Java 25 (JavaLanguageVersion.of(25)), which fully supports UseCompactObjectHeaders (available since Java 17 via JEP 400).

  2. application-dev.yaml: The file definitively exists at modules/console/src/main/resources/application-dev.yaml.

  3. Spring profile configuration: The dev profile is properly configured with no conflicts detected.

The original review comment raises concerns that are not supported by the actual codebase.


The launch configuration is correct. Java 25 fully supports UseCompactObjectHeaders (available since Java 17), and application-dev.yaml exists at modules/console/src/main/resources/application-dev.yaml.

The dev profile is properly configured and the environment variable setting is appropriate for local development.

Likely an incorrect or invalid review comment.

modules/console/src/main/resources/application.yaml (1)

9-12: Confirm expected type for spring.mvc.apiversion.use.path-segment.

If this property is boolean-like, prefer true/false over 1/0. Please double-check the library’s expected type and adjust if needed.

settings.gradle.kts (1)

4-8: Authentication module inclusion looks correct.

Including "modules:authentication" alongside common, console, and worker is consistent and should wire the new module into the build as expected.

build.gradle.kts (1)

1-62: Error Prone / NullAway integration looks consistent

The net.ltgt.errorprone plugin, errorprone configuration, and JavaCompile options are wired correctly for subprojects with sensible defaults (generated-code exclusion, NullAway OnlyNullMarked/JSpecify options). No changes needed here.

modules/authentication/build.gradle.kts (1)

1-13: Authentication module Gradle setup is reasonable

Using java-library with the Spring BOM and security-related starters (plus tests) is appropriate for this module. No changes needed based on this diff.

modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java (1)

18-21: I'll help you verify and rewrite this review comment. Let me start by examining the AuthController implementation and related security configuration.
<function_calls>

Find and read the AuthController file

fd -t f "AuthController.java" --exec wc -l {} ;


</function_calls>
<function_calls>

Read the AuthController file

fd -t f "AuthController.java" --exec cat -n {} ;


</function_calls>


- name: Run checks
run: ./gradlew --no-daemon clean check
run: ./gradlew --no-daemon clean check -x test
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Redundant clean commands defeat Gradle caching and slow down CI/CD.

Each test step runs clean, which wipes the build cache and forces a full rebuild. Since the check step (line 49) already builds the project, the subsequent test steps should reuse that build state instead of cleaning and rebuilding from scratch. This is especially costly for native compilation.

Apply this diff to remove redundant cleans:

      - name: Run checks
-        run: ./gradlew --no-daemon clean check -x test
+        run: ./gradlew --no-daemon check -x test

      - name: Run JVM tests
        env:
          SPRING_PROFILES_ACTIVE: test
-        run: ./gradlew --no-daemon clean test
+        run: ./gradlew --no-daemon test

       - name: Run native tests
         env:
           SPRING_PROFILES_ACTIVE: test
-        run: ./gradlew --no-daemon clean nativeTest
+        run: ./gradlew --no-daemon nativeTest

Also applies to: 54-54, 59-59

🤖 Prompt for AI Agents
In .github/workflows/workastra_server.yaml around lines 49, 54 and 59, the run
commands include a redundant clean that defeats Gradle caching and forces full
rebuilds; remove the literal "clean" token from those run lines so they read
e.g. "./gradlew --no-daemon check -x test" (or the equivalent test invocation
without "clean"), preserving other flags like --no-daemon and -x test as
appropriate so subsequent steps reuse the previously built state.

Comment on lines +33 to +38
@Bean
SecretKey jwtSecretKey(JwtConfiguration properties) {
Assert.notNull(properties, "JWT properties must not be null");
Assert.notNull(properties.secret(), "JWT secret must not be null");
return new SecretKeySpec(properties.secret().getBytes(StandardCharsets.UTF_8), "HmacSHA256");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the JwtConfiguration and related files
find . -type f -name "*JwtConfiguration*" -o -name "*JwtTokenService*" | head -20

Repository: workastra/server

Length of output: 176


🏁 Script executed:

#!/bin/bash
# List the authentication module structure
find modules/authentication -type f -name "*.java" | grep -E "(Config|Jwt)" | head -30

Repository: workastra/server

Length of output: 357


🏁 Script executed:

#!/bin/bash
# Read JwtConfiguration to see what properties exist
cat -n modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java

Repository: workastra/server

Length of output: 394


🏁 Script executed:

#!/bin/bash
# Read JwtTokenService to see how issuer/ttl are used
cat -n modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java

Repository: workastra/server

Length of output: 2216


🏁 Script executed:

#!/bin/bash
# Read full AuthenticationModuleConfig to see context
cat -n modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java

Repository: workastra/server

Length of output: 4442


🏁 Script executed:

#!/bin/bash
# Search for any usage of jwtConfiguration.ttl()
rg -n "\.ttl\(\)" modules/authentication/

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for "jwtConfiguration" usage across the codebase
rg -n "jwtConfiguration" modules/authentication/ -A 2 -B 2

Repository: workastra/server

Length of output: 1921


Perfect. I now have enough information to verify and rewrite the review comment. Here are my findings:

Key findings:

  1. issuer IS used: Line 38 of JwtTokenService calls this.jwtConfiguration.issuer() in the JWT claims builder. If null, this will cause an issue.
  2. ttl is NOT used: The search returned no matches. The token expiration is hardcoded to 1 hour (line 29 of JwtTokenService), not using the ttl property from the config.
  3. Validation gap confirmed: The jwtSecretKey() bean method only validates secret, not issuer, creating a risk of runtime failures if the issuer is missing from configuration.

The original review comment is partially correct but includes an incorrect suggestion about validating ttl since that property is never actually used. I need to correct this.


Validate the required JWT issuer property, not unused fields

jwtSecretKey(JwtConfiguration properties) only asserts properties and properties.secret() are non-null. However, issuer is required for correct token generation (used in JwtTokenService line 38) and will cause issues if missing. Fail fast at startup by adding:

     Assert.notNull(properties, "JWT properties must not be null");
     Assert.notNull(properties.secret(), "JWT secret must not be null");
+    Assert.hasText(properties.issuer(), "JWT issuer must not be null or blank");
     return new SecretKeySpec(properties.secret().getBytes(StandardCharsets.UTF_8), "HmacSHA256");

Note: Do not validate ttl—it is not used in token generation and would be unnecessary validation on dead config.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
around lines 33 to 38, the jwtSecretKey bean currently validates only properties
and properties.secret(), but JwtTokenService uses properties.issuer() and will
fail at runtime if issuer is missing; update the bean to also assert that
properties.issuer() is not null (e.g., Assert.notNull(properties.issuer(), "JWT
issuer must not be null")) so the application fails fast on startup when issuer
is absent; do not add validation for ttl since it is not used.

Comment on lines +50 to +71
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf.spa())
.authorizeHttpRequests((auth) ->
auth
.requestMatchers(HttpMethod.GET, "/actuator/health")
.permitAll()
.requestMatchers("/api/v1/csrf")
.permitAll()
.requestMatchers("/api/v1/login")
.permitAll()
.requestMatchers("/api/**")
.authenticated()
.anyRequest()
.denyAll()
)
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.formLogin((form) -> form.disable());

return http.build();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the authentication module configuration file
find . -type f -name "AuthenticationModuleConfig.java" | head -5

Repository: workastra/server

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Read the full AuthenticationModuleConfig file
wc -l ./modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java

Repository: workastra/server

Length of output: 162


🏁 Script executed:

#!/bin/bash
# Read the full file to understand complete JWT and session configuration
cat -n ./modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java

Repository: workastra/server

Length of output: 4442


🏁 Script executed:

#!/bin/bash
# Find AuthController to see how authentication tokens are handled
find . -type f -name "AuthController.java" | head -5

Repository: workastra/server

Length of output: 158


🏁 Script executed:

#!/bin/bash
# Read AuthController to understand token creation and session handling
cat -n ./modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java

Repository: workastra/server

Length of output: 2368


🏁 Script executed:

#!/bin/bash
# Find JwtTokenService
find . -type f -name "JwtTokenService.java" | head -5

Repository: workastra/server

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Read JwtTokenService
cat -n ./modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java

Repository: workastra/server

Length of output: 2216


🏁 Script executed:

#!/bin/bash
# Search for oauth2ResourceServer configuration anywhere in the project
rg -n "oauth2ResourceServer" --type java

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for JWT filters, authentication filters, or any filter implementations
rg -n "Filter|filter" --type java | grep -i jwt

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for other Spring Security configuration classes
find . -type f -name "*Config.java" -o -name "*Security*.java" | grep -v test | grep -v target

Repository: workastra/server

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Search for README or documentation about authentication
find . -type f \( -name "README*" -o -name "*.md" \) | xargs grep -l -i "auth\|jwt\|token" 2>/dev/null | head -5

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for test files related to authentication or JWT
find . -type f -name "*Test.java" -o -name "*Tests.java" | xargs grep -l "jwt\|token\|login" -i 2>/dev/null | head -10

Repository: workastra/server

Length of output: 42


Configure SecurityFilterChain to use JWT resource server authentication for stateless API requests

The SecurityFilterChain config issues a JwtDecoder bean (lines 45–48) but never wires it into the security chain. Instead:

  • SessionCreationPolicy.IF_REQUIRED (line 67) allows sessions to be created on-demand
  • No oauth2ResourceServer().jwt(...) call to validate JWT tokens on incoming requests
  • AuthController explicitly calls httpRequest.getSession(true) (line 39) after login, forcing session creation

Result: JWT tokens are generated and returned to clients, but /api/** requests are authenticated via session, not JWT validation. The JwtDecoder is unused.

To fix, choose one approach:

Option 1 (Recommended for REST APIs): Switch to stateless JWT authentication:

-            .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
-            .formLogin((form) -> form.disable());
+            .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+            .formLogin((form) -> form.disable())
+            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));

Add import: org.springframework.security.config.Customizer

Remove the explicit session creation from AuthController (line 39): httpRequest.getSession(true);

Option 2: If session-based auth is intentional, remove the unused JwtDecoder, JwtEncoder, and JwtTokenService beans and simplify AuthController.

The current hybrid setup is inconsistent and confusing.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf.spa())
.authorizeHttpRequests((auth) ->
auth
.requestMatchers(HttpMethod.GET, "/actuator/health")
.permitAll()
.requestMatchers("/api/v1/csrf")
.permitAll()
.requestMatchers("/api/v1/login")
.permitAll()
.requestMatchers("/api/**")
.authenticated()
.anyRequest()
.denyAll()
)
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.formLogin((form) -> form.disable());
return http.build();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf.spa())
.authorizeHttpRequests((auth) ->
auth
.requestMatchers(HttpMethod.GET, "/actuator/health")
.permitAll()
.requestMatchers("/api/v1/csrf")
.permitAll()
.requestMatchers("/api/v1/login")
.permitAll()
.requestMatchers("/api/**")
.authenticated()
.anyRequest()
.denyAll()
)
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin((form) -> form.disable())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
around lines 50 to 71, the SecurityFilterChain config issues a JwtDecoder bean
but never wires it into the chain and still allows sessions; change the chain to
use oauth2ResourceServer().jwt(...) (e.g., oauth2ResourceServer((o) ->
o.jwt(Customizer.withDefaults()))) and set sessionManagement to
SessionCreationPolicy.STATELESS to enforce JWT-based stateless auth; add the
import org.springframework.security.config.Customizer; then remove the explicit
session creation call in AuthController (httpRequest.getSession(true)) so
requests authenticate via JWT; alternatively, if you prefer session-based auth,
delete the JwtEncoder/JwtDecoder/JwtTokenService beans and adjust the controller
and security policy to consistently use sessions.

Comment on lines +6 to +7
@ConfigurationProperties(prefix = "workastra.jwt")
public record JwtConfiguration(String secret, String issuer, Duration ttl) {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

ttl property is defined but ignored by token generation

JwtConfiguration exposes a Duration ttl, but JwtTokenService.generateToken currently hard-codes Duration.ofHours(1) for expiry. This makes workastra.jwt.ttl a dead configuration knob and will confuse anyone trying to tune token lifetime.

Either:

  • use jwtConfiguration.ttl() when computing expiresAt, or
  • drop ttl from this record if you don’t plan to support configurable TTL.

I recommend wiring ttl into JwtTokenService so configuration is the single source of truth.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java
around lines 6 to 7, the ttl Duration field is declared but not used; update
JwtTokenService.generateToken to derive token expiry from jwtConfiguration.ttl()
rather than hard-coding Duration.ofHours(1) (or, alternatively, remove ttl from
the record if you intentionally want a fixed TTL). Inject or pass
JwtConfiguration into JwtTokenService and replace the hard-coded expiry
calculation with one that uses jwtConfiguration.ttl(), ensuring null/default
handling if ttl is absent.

Comment on lines +30 to +31
@PostMapping(path = "/api/v{version}/login", version = "1")
public ResponseEntity<TokenResponse> postMethodName(@RequestBody LoginRequest request) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the repository exists and find the AuthController file
find . -type f -name "AuthController.java" 2>/dev/null | head -5

Repository: workastra/server

Length of output: 158


🏁 Script executed:

#!/bin/bash
# Search for Spring Framework documentation or usage patterns
# Check what version of Spring is used in the project
find . -type f \( -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" \) 2>/dev/null | head -3

Repository: workastra/server

Length of output: 162


🏁 Script executed:

#!/bin/bash
# Read the AuthController file to see the exact lines
cat -n ./modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java | head -50

Repository: workastra/server

Length of output: 2368


🏁 Script executed:

#!/bin/bash
# Check for build.gradle.kts in authentication module and root
find ./modules/authentication -type f -name "build.gradle.kts" 2>/dev/null
ls -la ./build.gradle.kts 2>/dev/null || echo "No root build.gradle.kts"

Repository: workastra/server

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Check Spring version and dependencies in build.gradle.kts
cat ./modules/authentication/build.gradle.kts

Repository: workastra/server

Length of output: 593


🌐 Web query:

Spring Framework @PostMapping annotation supported attributes

💡 Result:

@PostMapping supports the same attributes as a composed @RequestMapping. The elements are:

  • name
  • value
  • path
  • params
  • headers
  • consumes
  • produces

Each is an alias for the corresponding RequestMapping attribute. [1]

Sources:
[1] Spring Framework @PostMapping Javadoc (elements: name, value, path, params, headers, consumes, produces).


🏁 Script executed:

#!/bin/bash
# Search for error handling (ControllerAdvice, ExceptionHandler)
find ./modules/authentication -type f -name "*.java" | xargs grep -l "ControllerAdvice\|ExceptionHandler" 2>/dev/null

Repository: workastra/server

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Check for validation annotations on LoginRequest or similar patterns
rg -n "@NotNull|@NotBlank|@Valid" ./modules/authentication/src/main/java/com/workastra/authentication/controller/ 2>/dev/null | head -20

Repository: workastra/server

Length of output: 42


Based on my verification, the original review comment is correct and identifies a critical issue. The version attribute on @PostMapping is definitively unsupported—Spring's @PostMapping only supports: name, value, path, params, headers, consumes, and produces.

I've also identified additional issues beyond the original comment:

  1. Line 39 creates an HTTP session (httpRequest.getSession(true)) while line 41 returns a JWT token. This is an architectural contradiction—JWT is stateless, so session creation should be removed.

  2. Missing error handling: There's no try-catch or ControllerAdvice to handle authentication failures. The authenticationManager.authenticate() call will throw exceptions that are unhandled.

  3. Missing validation: LoginRequest fields (username, password) have no validation annotations.


Fix @PostMapping attributes, method name, remove conflicting session creation, and add error handling

@PostMapping does not have a version attribute; the current usage won't compile. Additionally, creating an HTTP session (line 39) contradicts JWT token usage (line 41)—JWT is stateless, so remove the session creation. Add error handling for authentication failures.

-    @PostMapping(path = "/api/v{version}/login", version = "1")
-    public ResponseEntity<TokenResponse> postMethodName(@RequestBody LoginRequest request) {
+    @PostMapping("/api/v1/login")
+    public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
         Authentication authentication = authenticationManager.authenticate(
             new UsernamePasswordAuthenticationToken(request.username(), request.password())
         );

         SecurityContextHolder.getContext().setAuthentication(authentication);
-        HttpServletRequest httpRequest =
-            ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
-        httpRequest.getSession(true);

         String token = jwtTokenService.generateToken(authentication);

         return ResponseEntity.ok(new TokenResponse(token));

Remove the session creation (lines 37–39) since JWT is stateless. Also add a ControllerAdvice handler for BadCredentialsException or similar to provide meaningful error responses instead of letting exceptions propagate.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping(path = "/api/v{version}/login", version = "1")
public ResponseEntity<TokenResponse> postMethodName(@RequestBody LoginRequest request) {
@PostMapping("/api/v1/login")
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.username(), request.password())
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = jwtTokenService.generateToken(authentication);
return ResponseEntity.ok(new TokenResponse(token));
}
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
around lines 30–41, update the @PostMapping to remove the unsupported version
attribute (use path/value like "/api/v1/login" or param-based versioning),
rename the handler from postMethodName to a meaningful name such as login,
remove the httpRequest.getSession(true) session creation (JWT is stateless),
wrap authenticationManager.authenticate(...) in a try-catch to return an
appropriate ResponseEntity on authentication failure (or rethrow a custom
exception handled by ControllerAdvice), and add validation annotations to
LoginRequest fields (e.g., @NotBlank) so incoming payloads are validated.

Comment on lines +27 to +30
public String generateToken(Authentication authentication) {
Instant now = Instant.now();
Instant expiresAt = now.plus(Duration.ofHours(1));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and read JwtTokenService.java
fd -t f "JwtTokenService.java" --exec cat -n {} \;

Repository: workastra/server

Length of output: 2216


🏁 Script executed:

#!/bin/bash
# Find and read JwtConfiguration.java
fd -t f "JwtConfiguration.java" --exec cat -n {} \;

Repository: workastra/server

Length of output: 394


Replace hard-coded 1-hour token expiry with configured TTL

Line 29 currently bypasses the configured TTL:

Instant expiresAt = now.plus(Duration.ofHours(1));

The JwtConfiguration record exposes ttl() as a field from application configuration (workastra.jwt.ttl), but this is not being used. The hard-coded duration makes the configuration property ineffective.

Use the configured TTL instead:

-        Instant expiresAt = now.plus(Duration.ofHours(1));
+        Instant expiresAt = now.plus(jwtConfiguration.ttl());

This keeps token expiry fully configurable and removes the magic constant.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/controller/JwtTokenService.java
around lines 27 to 30, the token expiry is hard-coded to one hour; replace that
with the configured TTL from JwtConfiguration. Use the injected
JwtConfiguration.ttl() value (convert to a Duration if the record stores a
numeric TTL) when computing expiresAt (e.g., now.plus(jwtConfiguration.ttl()) or
now.plus(Duration.ofSeconds(jwtConfiguration.ttl())) depending on type),
ensuring the service uses the application property instead of the magic
constant.

Comment on lines +1 to +4
@NullMarked
package com.workastra.authentication;

import org.jspecify.annotations.NullMarked;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Import must come before package declaration.

The import is placed after the package declaration. In package-info.java, imports must come first, then the annotated package statement. This code won't compile because @NullMarked is used before it's imported.

Apply this diff:

-@NullMarked
-package com.workastra.authentication;
-
 import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+package com.workastra.authentication;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@NullMarked
package com.workastra.authentication;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.NullMarked;
@NullMarked
package com.workastra.authentication;
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/package-info.java
around lines 1 to 4, the @NullMarked annotation is applied before the import
statement, causing a compile error; move the import for
org.jspecify.annotations.NullMarked above the package declaration so imports
come first, then the package annotation and declaration, ensuring the file has
the import line(s) first followed by @NullMarked and package
com.workastra.authentication; no other changes required.

Comment on lines +1 to +19
{
"properties": [
{
"name": "workastra.jwt.issuer",
"type": "java.lang.String",
"description": "The issuer claim value for JWT tokens"
},
{
"name": "workastra.jwt.secret",
"type": "java.lang.String",
"description": "The secret key used to sign and verify JWT tokens"
},
{
"name": "workastra.jwt.ttl",
"type": "java.lang.Integer",
"description": "The JWT token expiration time (in seconds or duration format)"
}
]
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for JwtConfiguration class
fd -e java -e kt | xargs rg -l "class JwtConfiguration" --type java --type kotlin

Repository: workastra/server

Length of output: 44


🏁 Script executed:

# Search for the JWT properties defined in the codebase
rg -n "workastra\.jwt\." --type java --type kotlin -A 2 -B 2

Repository: workastra/server

Length of output: 42


🏁 Script executed:

# Check for Spring Boot configuration processor setup in authentication module
fd -name "pom.xml" -o -name "build.gradle" | xargs rg -l "spring-boot-configuration-processor"

Repository: workastra/server

Length of output: 340


🏁 Script executed:

# Check what's in the console module's metadata file and related structure
fd "additional-spring-configuration-metadata.json"

Repository: workastra/server

Length of output: 149


🏁 Script executed:

# List the modules directory structure
ls -la modules/

Repository: workastra/server

Length of output: 397


🏁 Script executed:

# Find all Java/Kotlin files with proper fd syntax
fd --type f -e java | head -20

Repository: workastra/server

Length of output: 1193


🏁 Script executed:

# Search for JWT-related class definitions with simpler pattern
rg "class.*Jwt" --type java --type kotlin

Repository: workastra/server

Length of output: 188


🏁 Script executed:

# Check the console build.gradle.kts to understand dependencies and processor config
cat modules/console/build.gradle.kts

Repository: workastra/server

Length of output: 1206


🏁 Script executed:

# View the JwtConfiguration class
cat modules/authentication/src/main/java/com/workastra/authentication/configuration/JwtConfiguration.java

Repository: workastra/server

Length of output: 345


🏁 Script executed:

# Check authentication module's build.gradle.kts
cat modules/authentication/build.gradle.kts

Repository: workastra/server

Length of output: 593


🏁 Script executed:

# Also check if there's any metadata file in authentication module
fd "additional-spring-configuration-metadata.json" modules/authentication/

Repository: workastra/server

Length of output: 42


Perfect! I now have all the information needed to verify and rewrite the review comment.


Fix critical type mismatch in JWT metadata and configure annotation processor in the authentication module.

The metadata contains two significant issues:

  1. Critical: workastra.jwt.ttl type is incorrect
    The metadata declares type: "java.lang.Integer", but the actual JwtConfiguration record defines ttl as java.time.Duration:

    @ConfigurationProperties(prefix = "workastra.jwt")
    public record JwtConfiguration(String secret, String issuer, Duration ttl) {}

    The metadata must be corrected to:

    {
      "name": "workastra.jwt.ttl",
      "type": "java.time.Duration",
      "description": "The JWT token expiration time"
    }
  2. Processor configuration in wrong module
    The spring-boot-configuration-processor is currently in the console module's build.gradle.kts, but JwtConfiguration lives in the authentication module. The processor should be added to the authentication module so metadata is automatically generated at build time rather than maintained manually in console. This prevents drift between the actual properties and their metadata declarations.

🤖 Prompt for AI Agents
In
modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json
lines 1-19: update the JWT metadata and move the processor - the TTL property is
incorrectly typed as java.lang.Integer but JwtConfiguration.ttl is
java.time.Duration so change the metadata entry type to java.time.Duration and
simplify its description to reflect duration semantics, and remove/manual
maintenance by enabling the spring-boot-configuration-processor in the
authentication module (where JwtConfiguration is defined) as an
annotationProcessor dependency so the configuration metadata is generated
automatically at build time rather than kept in the console module.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
modules/common/src/main/java/com/workastra/common/api/Pagination.java (1)

3-8: Clarify pagination semantics (0- vs 1-based and invariants).

The record is fine structurally, but it’s unclear whether page/totalPages are 0- or 1-based and whether negative values are allowed. Consider documenting this in Javadoc and/or validating these fields at creation time to avoid subtle off‑by‑one bugs downstream.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1)

21-39: Add explicit UTF‑8 encoding (and optionally logging) to the 403 response.

Handler logic is correct, but the response relies on the container’s default charset. For JSON APIs, it’s safer to set UTF‑8 explicitly and (optionally) log the denial for diagnostics.

-import java.io.IOException;
-
-import org.springframework.http.MediaType;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.http.MediaType;
@@
-        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
         response.getWriter().write(this.mapper.writeValueAsString(ApiResponse.error(
                 "FORBIDDEN",
                 "You do not have permission to access this resource.",
                 Meta.fromCurrentRequest())));

You can also consider logging the accessDeniedException at WARN for traceability.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1)

21-42: Add UTF‑8 charset and consider extracting shared error-response helper.

The 401 response is correct and consistent with the other handlers, but again there’s no explicit charset and the error-writing pattern is duplicated across handlers.

You can harden and de-duplicate this as follows:

-import java.io.IOException;
-
-import org.springframework.http.MediaType;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.http.MediaType;
@@
         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
@@
-        ApiResponse<Void> body = ApiResponse.error(
+        ApiResponse<Void> body = ApiResponse.error(
                 "UNAUTHORIZED",
                 "Authentication is required to access this resource.",
                 Meta.fromCurrentRequest()
         );

Longer term, consider a small utility (or base class) that takes (response, status, code, message) and writes the ApiResponse.error(...) JSON so JsonAuthenticationEntryPoint and JsonAccessDeniedHandler don’t duplicate this boilerplate.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (1)

10-41: Align success payload with global ApiResponse and verify ObjectMapper import

Two things to tighten up here:

  1. Response shape consistency
    Other endpoints and handlers use ApiResponse as a standard envelope, while this handler returns a bare {success, username} map. That forces clients to special‑case login responses. Consider something like:

    record LoginSuccess(String username) {}
    // ...
    var body = ApiResponse.ok(new LoginSuccess(authentication.getName()));
    response.getWriter().write(objectMapper.writeValueAsString(body));

    This keeps the JSON schema uniform and leverages Meta.fromCurrentRequest().

  2. Charset + ObjectMapper package

    • You only set Content-Type without an explicit charset; for JSON APIs it’s safer to set UTF‑8 explicitly (or include ;charset=UTF-8 in the content type) so the writer and Jackson agree on encoding.
    • The import is tools.jackson.databind.ObjectMapper, which is non‑standard; normally this is com.fasterxml.jackson.databind.ObjectMapper. If tools.jackson isn’t an intentionally shaded package in this project, switch to the standard Jackson package or fix the dependency so this compiles.
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/package-info.java (1)

1-9: Add @NullMarked to this package to enforce non‑null by default

Other modules are using JSpecify’s @NullMarked at the package level. This package is missing it, so all the filters here default back to “unknown nullability”.

Add:

@NullMarked
package com.workastra.authentication.infrastructure.filter;

import org.jspecify.annotations.NullMarked;

to make non‑null the default and keep null‑safety consistent across the module.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1)

19-86: This configuration is session‑based; confirm that’s intentional and verify Jackson wiring

The filter chain is internally consistent but a couple of points are worth double‑checking:

  1. Session‑based vs JWT‑based auth

    • You use HttpSessionSecurityContextRepository and SessionCreationPolicy.IF_REQUIRED, which means authenticated sessions are stored in the HTTP session and reused via JSESSIONID cookies.
    • There is no oauth2ResourceServer().jwt(...) or equivalent JWT validation configured on incoming requests.

    That’s perfectly valid for an SPA using cookie‑based sessions, but it’s not a stateless JWT setup. Given the PR title references JWT, confirm whether the intent is:

    • Stateful sessions (current code): then this is fine, but make sure docs/expectations reflect this and remove any unused JWT pieces elsewhere; or
    • Stateless JWT: then you should switch to SessionCreationPolicy.STATELESS, drop HttpSessionSecurityContextRepository, and wire oauth2ResourceServer(oauth2 -> oauth2.jwt(...)) (plus the corresponding JwtDecoder / JwtEncoder configuration).

    Don’t mix both approaches; pick one and keep it consistent.

  2. ObjectMapper import
    As in other files, the import is tools.jackson.databind.ObjectMapper. If that isn’t an intentionally shaded package in your build, you’ll want to switch this and all related imports to com.fasterxml.jackson.databind.ObjectMapper and depend on the standard Jackson artifact.

modules/common/src/main/java/com/workastra/common/api/Meta.java (1)

15-73: Tighten Meta typing and clean up commented pagination resolver

Two small improvements here:

  1. Prefer Instant over String for timestamp
    You already use Instant.now(). Keeping the record field as Instant and letting Jackson serialize to ISO‑8601 preserves type safety and avoids stringly‑typed time handling:

  • String timestamp, // ISO-8601
  • Instant timestamp, // serialized as ISO-8601 by Jackson

and in `fromCurrentRequest()`:

```diff
-        String timestamp = Instant.now().toString();
+        Instant timestamp = Instant.now();
  1. Remove or complete the commented resolvePagination
    The commented‑out resolvePagination method is dead code and adds noise. Either implement it fully and wire it into fromCurrentRequest() or delete it until you actually need pagination extraction.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 235ded8 and e9859c3.

📒 Files selected for processing (21)
  • AGENTS.md (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/package-info.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/ApiResponse.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/ErrorItem.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/Meta.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/Pagination.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/package-info.java (1 hunks)
  • modules/console/src/main/resources/application-dev.yaml (1 hunks)
  • modules/console/src/main/resources/application-test.yaml (1 hunks)
  • modules/console/src/main/resources/application.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • AGENTS.md
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/package-info.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • modules/console/src/main/resources/application-dev.yaml
  • modules/console/src/main/resources/application.yaml
🧰 Additional context used
📓 Path-based instructions (1)
**/src/main/java/**/*.java

⚙️ CodeRabbit configuration file

**/src/main/java/**/*.java: You are acting as a Senior Java Engineer and Software Architect.
Review with a direct, practical, no-nonsense tone.

You must enforce modern Java (Java 25) best practices and discourage legacy or outdated patterns.
Always prefer:

  • modern language features when appropriate (var, records, switch expressions, pattern matching, sealed types, etc.)
  • the java.time API over java.util.Date/Calendar
  • java.util.concurrent and modern concurrency utilities over legacy synchronized collections
  • NIO and modern I/O APIs where suitable
  • streams and functional APIs where they clearly improve readability and safety (but avoid over-complicating simple logic)

You must explicitly:

  • Call out usage of old/legacy or deprecated APIs, raw types, Vector, Hashtable, synchronized collections, old I/O where NIO is better, and other outdated patterns.
  • Highlight any code that is not aligned with current Java 25 idioms.
  • Enforce import hygiene: avoid star imports, unused imports, and messy import sections.

Handle NullPointerException (NPE) risks very carefully:

  • Identify every place where NPE can occur (parameters, return values, map lookups, collection elements, optional values, chained calls, etc.).
  • Recommend explicit null checks, Objects.requireNonNull, Optional, or nullability annotations where appropriate.
  • Call out dangerous chaining on potentially null values.
  • Prefer designs that minimize nullable state and unexpected nulls.

You must review naming quality:

  • Check that class, method, variable, and field names are clear, descriptive, and follow Java conventions (CamelCase for classes, lowerCamelCase for fields/variables/methods, UPPER_SNAKE_CASE for constants).
  • Flag vague, misleading, or overly generic names (e.g. data, obj, tmp, doStuff, handle, manager, util).
  • Suggest better, more meaningful names that reflect the intent and domain.
  • Prefer names that express behavior and domain concepts over implementation details.

Enforce...

Files:

  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java
  • modules/common/src/main/java/com/workastra/common/api/Pagination.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/ApiResponse.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java
  • modules/common/src/main/java/com/workastra/common/api/Meta.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
  • modules/common/src/main/java/com/workastra/common/api/ErrorItem.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java
🧬 Code graph analysis (3)
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (4)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)
  • Configuration (21-60)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1)
  • Configuration (26-87)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1)
  • JsonAccessDeniedHandler (21-40)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1)
  • JsonAuthenticationEntryPoint (21-44)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (5)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)
  • Configuration (21-60)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java (1)
  • JsonLoginAuthenticationFilter (34-64)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1)
  • JsonAccessDeniedHandler (21-40)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1)
  • JsonAuthFailureHandler (19-43)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (1)
  • JsonAuthSuccessHandler (19-42)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (2)
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1)
  • Configuration (23-43)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1)
  • Configuration (26-87)
🪛 GitHub Actions: Linter & Formatter
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/common/src/main/java/com/workastra/common/api/Pagination.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/common/src/main/java/com/workastra/common/api/ApiResponse.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/common/src/main/java/com/workastra/common/api/Meta.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/common/src/main/java/com/workastra/common/api/ErrorItem.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java

[error] 1-1: Code is not formatted properly. Please run Prettier locally and commit the changes.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Check
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (6)
modules/console/src/main/resources/application-test.yaml (1)

1-1: Configuration file is empty — clarify intent.

The test profile configuration file contains only the YAML document start marker. Given that CI activates the test profile for both JVM and native tests, and the PR introduces JWT authentication, determine whether this should remain empty (tests inherit production-like config) or provide test-specific overrides (e.g., authentication bypass, test database config, logging levels).

If intentional, add a comment explaining why. If config is needed, populate it.

modules/common/src/main/java/com/workastra/common/api/ErrorItem.java (1)

3-7: ErrorItem record is fine; fix CI formatting.

Record shape (field, code, message) is straightforward and idiomatic; no correctness issues. CI is failing with a Prettier “code is not formatted properly” error for this file, so run the project’s formatter (Prettier/Gradle task) before merging.

modules/common/src/main/java/com/workastra/common/api/package-info.java (1)

1-4: Package-level @NullMarked usage looks good.

Package annotation + import ordering is correct and gives a clear non-null-by-default contract for this API package.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1)

19-42: I'll verify the concerns raised in this review comment. Let me start by examining the codebase for the patterns mentioned.
<function_calls>


#!/bin/bash

Find other authentication/security handlers to verify ApiResponse pattern

fd -t f "Handler.java$" modules/authentication/src --exec grep -l "onAuthenticationFailure|onAuthenticationSuccess" {} ;


</function_calls>

<function_calls>


#!/bin/bash

Search for ApiResponse usage in authentication module

rg -n "ApiResponse." modules/authentication/src --type java -C 2


</function_calls>

<function_calls>


#!/bin/bash

Find ApiResponse and Meta classes

fd -t f "ApiResponse.java" . && fd -t f "Meta.java" .


</function_calls>

<function_calls>


#!/bin/bash

Check for other handlers in security-related packages

find . -path "*/handler/*Handler.java" -type f | grep -i "auth|security" | head -20


</function_calls>

<function_calls>


#!/bin/bash

Search for other implementations of AuthenticationFailureHandler or similar

rg -n "implements.*Handler|extends.*Handler" modules/authentication/src --type java -A 1 | head -40


</function_calls>

modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1)

23-42: Handler bean wiring looks fine

Bean definitions for JsonAuthenticationEntryPoint and JsonAccessDeniedHandler are straightforward and correctly match the constructor injection in SecurityConfig. No issues from my side here.

modules/common/src/main/java/com/workastra/common/api/ApiResponse.java (1)

33-81: ApiResponse design is solid

Generic record shape, nullability annotations, and factory methods are all reasonable and consistent with the rest of the module. I don’t see issues that need changing right now.

Comment on lines +13 to +22
public class AuthController {
@GetMapping(path = "/api/v{version}/csrf", version = "1")
public ApiResponse<CsrfToken> getCsrf(CsrfToken csrfToken) {
return ApiResponse.ok(csrfToken);
}

@GetMapping(path = "/api/v{version}/auth/me", version = "1")
public ApiResponse<Authentication> me(Authentication authentication) {
return ApiResponse.ok(authentication);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all Java files to understand the repository structure
git ls-files "*.java" | head -50

Repository: workastra/server

Length of output: 2381


🏁 Script executed:

#!/bin/bash
# Read the AuthController file
cat -n modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java

Repository: workastra/server

Length of output: 971


🏁 Script executed:

#!/bin/bash
# Check ApiResponse implementation
cat -n modules/common/src/main/java/com/workastra/common/api/ApiResponse.java

Repository: workastra/server

Length of output: 3366


🏁 Script executed:

#!/bin/bash
# Check SecurityConfig to understand path patterns
cat -n modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java

Repository: workastra/server

Length of output: 4920


🌐 Web query:

Spring @GetMapping valid attributes parameters

💡 Result:

@GetMapping is a composed variant of @RequestMapping for HTTP GET. It supports the same mapping attributes as @RequestMapping (except you don't need to set method). Main attributes:

  • value / path — URI(s) to map (aliases). [1]
  • params — request parameter conditions (presence, absence, exact value, e.g. "id", "!id", "type=full"). [2]
  • headers — request header conditions (e.g. "X-My-Header=abc"). [2]
  • consumes — media types the handler can consume (matches Content-Type). [1]
  • produces — media types the handler can produce (matches Accept). [1]
  • name — logical name for the mapping (used for URI building / naming). [1]

Notes:

  • You can supply arrays for value/path, params, headers, consumes, produces. [1]
  • params/headers support negation and value checks (e.g. "flag", "!flag", "q=1"). [2]

Sources: Spring Framework RequestMapping javadoc and reference documentation. [1][2]

References
[1] Spring RequestMapping javadoc. (docs.spring.io)
[2] Spring reference — Mapping requests (params/headers examples). (docs.spring.io)

Citations:


🏁 Script executed:

#!/bin/bash
# Check for test files or build output
find . -type f \( -name "*Test.java" -o -name "*Tests.java" \) | grep -i auth | head -10

Repository: workastra/server

Length of output: 42


🌐 Web query:

Spring Security Authentication CsrfToken Jackson serialization JSON

💡 Result:

Short answer

  • Use Spring Security's Jackson modules (SecurityJackson2Modules.getModules(...)) and register them with your ObjectMapper so CsrfToken and Authentication-related types serialize/deserialize correctly. [1][2]

Example (Java)

  • Register modules:
    ObjectMapper mapper = new ObjectMapper();
    ClassLoader loader = getClass().getClassLoader();
    mapper.registerModules(SecurityJackson2Modules.getModules(loader));
    // then serialize e.g. mapper.writeValueAsString(csrfTokenOrSecurityContext);

Notes / gotchas

  • DefaultCsrfToken implements Serializable and is supported by the web Jackson modules, so fields headerName/parameterName/token will be handled. [2]
  • Spring Security historically enabled Jackson typing for some modules; be careful when enabling default typing or deserializing untrusted JSON — use the modules approach and Jackson allowlist/mixins rather than blanket default typing to avoid remote code execution risks. [1][3]
  • When storing sessions as JSON (e.g., Redis via Spring Session) you may need to handle Jackson allowlist/mixins for certain value types (some issues reported around allowlist entries like java.lang.Long inside security-related objects). If you hit deserialization allowlist errors, add explicit mixins or mappings rather than re-enabling unsafe default typing. [4]

References

  • Spring Security Jackson integration (register SecurityJackson2Modules). [1]
  • API / DefaultCsrfToken and web Jackson modules. [2]
  • Spring Security blog on serialization/typing security considerations. [3]
  • GitHub issue: allowlist/deserialization edge case when persisting sessions as JSON. [4]

Fix invalid @GetMapping attributes and avoid exposing Authentication directly

There are critical issues here:

  1. Invalid version attribute on @GetMapping (compile‑time error)
    @GetMapping doesn't have a version attribute; supported attributes are path/value, params, headers, consumes, produces, and name. This will not compile. The {version} path variable is also unused. SecurityConfig explicitly configures /api/v1/... paths (lines 68–70), not parameterized version paths. Fix by hard-coding v1:

  • @GetMapping(path = "/api/v{version}/csrf", version = "1")
  • @GetMapping(path = "/api/v1/csrf")
    public ApiResponse getCsrf(CsrfToken csrfToken) {
    return ApiResponse.ok(csrfToken);
    }
  • @GetMapping(path = "/api/v{version}/auth/me", version = "1")
  • @GetMapping(path = "/api/v1/auth/me")
    public ApiResponse me(Authentication authentication) {
    return ApiResponse.ok(authentication);
    }

2. **Leaking `Authentication` and `CsrfToken` over the wire**  
Returning raw Spring Security types couples your API schema to framework internals and creates serialization fragility. `CsrfToken` and `Authentication` require special Jackson modules (SecurityJackson2Modules) to serialize correctly; without them, serialization will fail. Even if configured, exposing these types is poor API design—clients shouldn't depend on Spring Security internals.

Use focused DTOs instead:

```java
public record AuthMeResponse(String username, List<String> roles) {}
public record CsrfTokenResponse(String token, String headerName, String parameterName) {}

@GetMapping(path = "/api/v1/auth/me")
public ApiResponse<AuthMeResponse> me(Authentication authentication) {
    var principal = authentication.getPrincipal();
    // Map to username + roles
}

@GetMapping(path = "/api/v1/csrf")
public ApiResponse<CsrfTokenResponse> getCsrf(CsrfToken csrfToken) {
    return ApiResponse.ok(new CsrfTokenResponse(csrfToken.getToken(), csrfToken.getHeaderName(), csrfToken.getParameterName()));
}
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
around lines 13-22, remove the invalid version attribute from the @GetMapping
annotations and hard-code the v1 paths (e.g. "/api/v1/csrf" and
"/api/v1/auth/me"); stop returning Spring Security types directly — create DTOs
(e.g. CsrfTokenResponse with token, headerName, parameterName and AuthMeResponse
with username and roles) and map CsrfToken and Authentication to those DTOs (use
csrfToken.getToken(), getHeaderName(), getParameterName(); for authentication
derive username from authentication.getName() or principal and collect roles
from authentication.getAuthorities()), update method signatures to return
ApiResponse<...> of the DTOs, and add necessary imports for the new DTOs and any
collection utilities.

Comment on lines +21 to +59
@Configuration
public class PasswordEncoderConfig {

/**
* Provides the BCryptPasswordEncoder bean for password encryption.
* BCrypt automatically handles salt generation and secure hashing.
*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

/**
* Provides the AuthenticationManager bean from the AuthenticationConfiguration.
* This manager delegates to the configured authentication providers.
*/
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}

/**
* Provides an in-memory UserDetailsService with a default admin user.
* In production, this should be replaced with a persistent user repository
* connected to a database.
*
* Default user:
* - username: "admin"
* - password: "admin" (bcrypt encoded)
* - roles: "USER"
*/
@Bean
UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
return new InMemoryUserDetailsManager(
User.withUsername("admin")
.password(passwordEncoder.encode("admin"))
.roles("USER")
.build());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current file and surrounding context for @Profile annotations
cat -n modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java

Repository: workastra/server

Length of output: 2818


🏁 Script executed:

#!/bin/bash
# Search for all @Profile annotations in the authentication module
rg -n '@Profile' modules/authentication/

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for other UserDetailsService bean definitions across the codebase
rg -n 'UserDetailsService' --type java -A 3 -B 1

Repository: workastra/server

Length of output: 3401


🏁 Script executed:

#!/bin/bash
# Check for Spring profile configurations in properties/yml files
fd -e 'yml' -o -e 'yaml' -o -e 'properties' | xargs grep -l 'spring.profiles' 2>/dev/null || echo "No profile configs found"

Repository: workastra/server

Length of output: 218


🏁 Script executed:

#!/bin/bash
# Check the project structure to understand the context (pom.xml or build.gradle for hints)
find . -maxdepth 2 -name 'pom.xml' -o -name 'build.gradle' | head -5

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for application configuration files
fd -type f \( -name 'application*.yml' -o -name 'application*.yaml' -o -name 'application*.properties' \)

Repository: workastra/server

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Search for any @Profile annotations in the entire codebase
rg -n '@Profile' --type java

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there are test configurations in src/test
find . -path '*/src/test/java/*' -name '*Config*.java' | head -20

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look for conditional bean definitions or alternative UserDetailsService implementations
rg -n 'InMemoryUserDetailsManager' --type java -B 5 -A 10

Repository: workastra/server

Length of output: 4545


Gate the in-memory admin/admin user to non-production environments.

The hard-coded admin user with password admin is not currently gated to any Spring profile and will be active in all environments (including production) if this configuration is deployed. While the code comment says "for demo purposes," there is no @Profile annotation enforcing this restriction.

Recommended fixes:

  1. Add @Profile("dev") to the userDetailsService() bean method (lines 52–59), or
  2. Add @Profile("!prod") to gate it out of production, or
  3. Move the bean to a separate dev-only configuration class and gate that class with @Profile("dev").

The PasswordEncoder and AuthenticationManager beans are fine as-is; only the in-memory user needs gating.

Comment on lines +19 to +63
/**
* JsonLoginAuthenticationFilter is a custom authentication filter that processes
* login requests containing username and password in JSON format. It extends
* the UsernamePasswordAuthenticationFilter to handle authentication via a JSON
* payload instead of traditional form data.
*
* This filter expects a POST request with a JSON body that includes the following
* fields:
* - username: The username of the user attempting to authenticate.
* - password: The password of the user attempting to authenticate.
*
* If the request method is not POST, an AuthenticationServiceException is thrown.
* Upon successful authentication, it returns an Authentication object; otherwise,
* it returns null in case of an IOException during the reading of the request input.
*/
public class JsonLoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

record LoginRequest(String username, String password) {
}

private final ObjectMapper objectMapper;

public JsonLoginAuthenticationFilter(AuthenticationManager authenticationManager, ObjectMapper objectMapper) {
super(authenticationManager);
this.objectMapper = objectMapper;
}

@Override
public Authentication attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException {
if (!request.getMethod().equals(HttpMethod.POST.name())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}

try {
var loginRequest = this.objectMapper.readValue(request.getInputStream(), LoginRequest.class);
var authentication = UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
setDetails(request, authentication);
return getAuthenticationManager().authenticate(authentication);
} catch (IOException e) {
throw new BadCredentialsException("Invalid login request", e);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the file and examine its actual content
fd -t f "JsonLoginAuthenticationFilter.java" --exec cat -n {} \;

Repository: workastra/server

Length of output: 3387


🏁 Script executed:

#!/bin/bash
# Check if the file exists and get its size
fd -t f "JsonLoginAuthenticationFilter.java"

Repository: workastra/server

Length of output: 182


🏁 Script executed:

#!/bin/bash
# Search for related authentication filter patterns and validation logic
rg -n "UsernamePasswordAuthenticationFilter" -A 3 -B 3 --type java

Repository: workastra/server

Length of output: 6305


🏁 Script executed:

#!/bin/bash
# Check for existing null/blank credential validation patterns
rg -n "username.*blank\|password.*blank\|isEmpty\|isBlank" --type java -C 2 | head -100

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check what happens when ObjectMapper deserializes missing fields into records
rg -n "objectMapper.*readValue" --type java -C 3

Repository: workastra/server

Length of output: 1333


🏁 Script executed:

#!/bin/bash
# Check if UsernamePasswordAuthenticationToken constructor validates null values
rg -n "unauthenticated.*null\|requireNonNull" --type java -C 3 | head -50

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for test files to understand expected behavior with null/empty credentials
fd -t f "JsonLoginAuthenticationFilter" | grep -i test

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check for any tests or validation related to credentials
rg -n "LoginRequest\|JsonLoginAuthenticationFilter" --type java -A 5 -B 2 | head -150

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check Spring Security's UsernamePasswordAuthenticationToken to see if it validates
# by looking at related authentication code in the repo
rg -n "new UsernamePasswordAuthenticationToken\|\.unauthenticated" --type java -C 2 | head -100

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Verify Jackson's default behavior with missing fields in records
# by checking if there are any Jackson configurations that might handle nulls differently
rg -n "ObjectMapper\|DeserializationFeature\|JsonProperty" --type java -C 2 | head -80

Repository: workastra/server

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check AuthenticationManager usage and downstream handling
rg -n "authenticate\(" --type java -C 2 | grep -A 2 -B 2 "null" | head -50

Repository: workastra/server

Length of output: 42


Fix Javadoc mismatch and validate null/blank credentials before authentication.

Two issues confirmed:

  1. Javadoc inaccuracy (lines 31–32):
    The comment claims the method "returns null in case of an IOException," but line 61 throws BadCredentialsException instead. Update the Javadoc to reflect the actual behavior.

  2. Missing null/blank credential validation:
    When the JSON body omits username or password, Jackson deserializes them as null. The code passes these directly to UsernamePasswordAuthenticationToken.unauthenticated(...) without validation, allowing invalid credentials to propagate downstream. Validate and reject empty/blank credentials upfront with a BadCredentialsException.

Suggested fix:

- * If the request method is not POST, an AuthenticationServiceException is thrown.
- * Upon successful authentication, it returns an Authentication object; otherwise,
- * it returns null in case of an IOException during the reading of the request input.
+ * If the request method is not POST, an AuthenticationServiceException is thrown.
+ * Upon successful authentication, it returns an Authentication object; otherwise,
+ * it throws a BadCredentialsException if the request cannot be read or parsed.
@@
         try {
             var loginRequest = this.objectMapper.readValue(request.getInputStream(), LoginRequest.class);
+            if (loginRequest.username() == null || loginRequest.username().isBlank()
+                    || loginRequest.password() == null || loginRequest.password().isBlank()) {
+                throw new BadCredentialsException("Username or password is empty");
+            }
             var authentication = UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
             setDetails(request, authentication);
             return getAuthenticationManager().authenticate(authentication);
         } catch (IOException e) {
             throw new BadCredentialsException("Invalid login request", e);
         }
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java
around lines 19 to 63, update the Javadoc to remove the incorrect claim that an
IOException causes a null return and state that a BadCredentialsException is
thrown on malformed/invalid JSON; after deserializing LoginRequest, validate
that username and password are non-null and non-blank (trim both) and if either
is missing/blank throw a BadCredentialsException with a clear message before
creating the UsernamePasswordAuthenticationToken so empty credentials are
rejected early.

@phongphongg phongphongg marked this pull request as draft November 30, 2025 03:21
@phongphongg phongphongg force-pushed the feat/authentication branch 3 times, most recently from 52e713f to 20634ab Compare November 30, 2025 04:49
…handlers

- Introduced a new `authentication` module for handling user authentication and authorization.
- Implemented JWT-based authentication with configurable issuer, secret, and expiration time.
- Added custom security handlers for JSON responses on authentication success, failure, and access denial.
- Updated the console module to include the new authentication module as a dependency.
- Enhanced API response structure with standardized error handling and metadata.
- Configured Spring Security to use custom filters for JSON login requests.
- Added necessary configuration files and logging settings for development and testing environments.
@phongphongg phongphongg marked this pull request as ready for review November 30, 2025 06:55
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

♻️ Duplicate comments (2)
modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json (1)

14-16: Correct the type mismatch: workastra.jwt.ttl should be java.time.Duration, not java.lang.Integer.

The metadata declares java.lang.Integer, but JwtConfiguration defines ttl as java.time.Duration. This type mismatch will cause Spring configuration binding to fail at runtime.

Apply this fix:

     {
       "name": "workastra.jwt.ttl",
-      "type": "java.lang.Integer",
-      "description": "The JWT token expiration time (in seconds or duration format)"
+      "type": "java.time.Duration",
+      "description": "The JWT token expiration time"
     }

Additionally, move spring-boot-configuration-processor to the authentication module's build configuration where JwtConfiguration is defined. This allows metadata to be generated automatically at build time rather than manually maintained in the console module, preventing metadata drift.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)

52-57: Gate in-memory admin user to non-production profile (duplicate).

Hardcoded admin/admin will be active in all environments including production. Add @Profile("dev") or @Profile("!prod") to this bean method.

🧹 Nitpick comments (9)
modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1)

6-6: Use explicit package list for component scanning clarity.

Console's gradle explicitly depends on modules:authentication, so scanning authentication beans is intentional and correct. However, scanning the entire "com.workastra" hierarchy is imprecise and creates maintenance risk:

  • Any future module added under com.workastra will be automatically scanned without explicit review
  • It differs from Worker's explicit approach ("com.workastra.worker", "com.workastra.common")
  • Explicit lists make dependencies and intentions clear

Replace with:

-@SpringBootApplication(scanBasePackages = "com.workastra")
+@SpringBootApplication(scanBasePackages = { "com.workastra.console", "com.workastra.common", "com.workastra.authentication" })
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1)

33-33: Consider a dedicated response record for consistency and type safety.

Using Map.of() works, but a record DTO aligns better with modern Java practices and ensures a consistent error response structure across handlers.

public record AuthErrorResponse(boolean success, String error) {}

Then:

-var body = Map.of("success", false, "error", ex.getMessage());
+var body = new AuthErrorResponse(false, "Authentication failed");
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (2)

30-30: Redundant status code.

HttpServletResponse.SC_OK (200) is already the default status. This line is unnecessary.

Apply this diff:

-response.setStatus(HttpServletResponse.SC_OK);
 response.setContentType(MediaType.APPLICATION_JSON_VALUE);

33-35: Prefer a record for the response body.

Using Map.of with hardcoded keys is not reusable or type-safe. Define a response record for clarity and maintainability, consistent with modern Java 25 practices and the project's ApiResponse pattern.

Define a record at the top of the file or in a separate DTO package:

public record AuthSuccessResponse(boolean success, String username) {}

Then update the method:

-var body = Map.of("success", true, "username", authentication.getName());
-
-response.getWriter().write(this.objectMapper.writeValueAsString(body));
+var username = authentication.getName();
+var body = new AuthSuccessResponse(true, username != null ? username : "unknown");
+response.getWriter().write(this.objectMapper.writeValueAsString(body));
build.gradle.kts (1)

1-5: libsCatalog access is functional but unnecessarily verbose.

Consider using the direct libs reference pattern instead of manually accessing VersionCatalogsExtension. Most Gradle 8.0+ projects simply use libs.something directly in the build script without this boilerplate.

-val libsCatalog = extensions
-  .getByType(VersionCatalogsExtension::class.java)
-  .named("libs")
+// Remove boilerplate; use direct libs reference in lines 59-60 and 60 below

Then update lines 59-60:

-add("errorprone", libsCatalog.findLibrary("error-prone-core").get())
-add("errorprone", libsCatalog.findLibrary("nullaway").get())
+add("errorprone", libs.errorpronecore)
+add("errorprone", libs.nullaway)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)

21-22: Class name doesn't reflect its scope.

PasswordEncoderConfig defines PasswordEncoder, AuthenticationManager, and UserDetailsService. Consider renaming to AuthenticationBeansConfig or SecurityBeansConfig to accurately describe its responsibility.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1)

48-87: Inject handlers as beans and ensure security config is covered by tests.

Two points here:

  1. Handler construction in config
    You’re constructing JsonAuthSuccessHandler and JsonAuthFailureHandler directly in securityFilterChain. That couples the filter to concrete implementations and makes replacement/testing harder. Prefer injecting them as beans:
-    SecurityFilterChain securityFilterChain(
-        HttpSecurity http,
-        AuthenticationManager authenticationManager,
-        ObjectMapper objectMapper
-    ) throws Exception {
-        var jsonLoginAuthenticationFilter = new JsonLoginAuthenticationFilter(authenticationManager, objectMapper);
-        jsonLoginAuthenticationFilter.setFilterProcessesUrl("/api/v1/auth/login");
-        jsonLoginAuthenticationFilter.setAuthenticationSuccessHandler(new JsonAuthSuccessHandler(objectMapper));
-        jsonLoginAuthenticationFilter.setAuthenticationFailureHandler(new JsonAuthFailureHandler(objectMapper));
+    SecurityFilterChain securityFilterChain(
+        HttpSecurity http,
+        AuthenticationManager authenticationManager,
+        ObjectMapper objectMapper,
+        JsonAuthSuccessHandler successHandler,
+        JsonAuthFailureHandler failureHandler
+    ) throws Exception {
+        var jsonLoginAuthenticationFilter = new JsonLoginAuthenticationFilter(authenticationManager, objectMapper);
+        jsonLoginAuthenticationFilter.setFilterProcessesUrl("/api/v1/auth/login");
+        jsonLoginAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
+        jsonLoginAuthenticationFilter.setAuthenticationFailureHandler(failureHandler);
  1. Tests for the filter chain
    This configuration is security‑critical (public vs. protected endpoints, JSON 401/403 behavior). If you don’t already have them, add tests using Spring Security’s test support (e.g., MockMvc/WebTestClient) to assert:

    • /actuator/health, /api/v1/csrf, /api/v1/auth/login are permitAll.
    • /api/** requires authentication and returns JSON 401/403 via your handlers.
    • Non‑API URLs are denyAll.
modules/common/src/main/java/com/workastra/common/api/Meta.java (1)

17-71: Harden currentRequest() and clean up the commented pagination block.

Two things to tighten up here:

  1. Avoid ClassCastException in non-servlet contexts
    currentRequest() blindly casts RequestContextHolder.getRequestAttributes() to ServletRequestAttributes. In any non‑servlet context this will blow up with a ClassCastException instead of your clear IllegalStateException.

    Prefer an explicit type check:

-    private static HttpServletRequest currentRequest() {
-        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-
-        if (attr == null) {
-            throw new IllegalStateException(
-                "No current HTTP request — Meta.fromCurrentRequest() called outside web context"
-            );
-        }
-
-        return attr.getRequest();
-    }
+    private static HttpServletRequest currentRequest() {
+        var attributes = RequestContextHolder.getRequestAttributes();
+        if (!(attributes instanceof ServletRequestAttributes attr)) {
+            throw new IllegalStateException(
+                "No current HTTP request — Meta.fromCurrentRequest() called outside web context"
+            );
+        }
+        return attr.getRequest();
+    }
  1. Commented-out resolvePagination
    The large commented method block at the bottom will age badly. Either implement pagination support now or replace it with a short // TODO pointing to a ticket, and remove the commented code.
modules/common/src/main/java/com/workastra/common/api/ApiResponse.java (1)

31-55: Make error factories symmetric with ok() and add tests for response shape.

Right now ok()/ok(data) compute Meta via Meta.fromCurrentRequest(), but error(...) requires callers to supply Meta. That’s an odd asymmetry and leaks Meta construction details into every error path.

Consider adding convenience overloads that mirror ok() and centralize Meta creation:

     public static <T> ApiResponse<T> ok() {
         return new ApiResponse<>(true, "OK", null, null, null, Meta.fromCurrentRequest());
     }

     public static <T> ApiResponse<T> ok(T data) {
         return new ApiResponse<>(true, "OK", null, data, null, Meta.fromCurrentRequest());
     }

     public static <T> ApiResponse<T> error(String code, String message, @Nullable List<ErrorItem> errors, Meta meta) {
         return new ApiResponse<>(false, code, message, null, errors, meta);
     }
 
-    public static <T> ApiResponse<T> error(String code, String message, Meta meta) {
-        return error(code, message, null, meta);
-    }
+    public static <T> ApiResponse<T> error(String code, String message, Meta meta) {
+        return error(code, message, null, meta);
+    }
+
+    public static <T> ApiResponse<T> error(String code, String message) {
+        return error(code, message, null, Meta.fromCurrentRequest());
+    }

Also, per the project guidelines, make sure you have unit tests that:

  • Verify JSON serialization omits null fields (@JsonInclude.NON_NULL).
  • Cover ok(), ok(data), and error(...) overloads, including meta population from Meta.fromCurrentRequest().
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9859c3 and 47a2699.

📒 Files selected for processing (35)
  • .github/workflows/workastra_server.yaml (1 hunks)
  • .vscode/launch.json (1 hunks)
  • build.gradle.kts (3 hunks)
  • config/checkstyle/checkstyle.xml (1 hunks)
  • gradle/libs.versions.toml (1 hunks)
  • modules/authentication/build.gradle.kts (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/package-info.java (1 hunks)
  • modules/authentication/src/main/java/com/workastra/authentication/package-info.java (1 hunks)
  • modules/common/build.gradle.kts (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/ApiResponse.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/ErrorItem.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/Meta.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/Pagination.java (1 hunks)
  • modules/common/src/main/java/com/workastra/common/api/package-info.java (1 hunks)
  • modules/console/build.gradle.kts (1 hunks)
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1 hunks)
  • modules/console/src/main/java/com/workastra/console/package-info.java (1 hunks)
  • modules/console/src/main/resources/META-INF/additional-spring-configuration-metadata.json (1 hunks)
  • modules/console/src/main/resources/application-dev.yaml (1 hunks)
  • modules/console/src/main/resources/application-test.yaml (1 hunks)
  • modules/console/src/main/resources/application.yaml (1 hunks)
  • modules/worker/src/main/java/com/workastra/worker/package-info.java (1 hunks)
  • settings.gradle.kts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/package-info.java
🚧 Files skipped from review as they are similar to previous changes (16)
  • config/checkstyle/checkstyle.xml
  • modules/console/src/main/resources/application-test.yaml
  • modules/authentication/src/main/java/com/workastra/authentication/controller/AuthController.java
  • .github/workflows/workastra_server.yaml
  • modules/console/src/main/resources/application.yaml
  • modules/console/src/main/java/com/workastra/console/package-info.java
  • modules/common/build.gradle.kts
  • modules/worker/src/main/java/com/workastra/worker/package-info.java
  • modules/console/build.gradle.kts
  • modules/common/src/main/java/com/workastra/common/api/Pagination.java
  • modules/common/src/main/java/com/workastra/common/api/ErrorItem.java
  • modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java
  • modules/authentication/build.gradle.kts
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java
  • modules/authentication/src/main/java/com/workastra/authentication/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/package-info.java
🧰 Additional context used
📓 Path-based instructions (3)
**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

**/*.java: Prioritize using modern Java 25 language features and APIs; consult official Java 25 release documentation
All generated code must be fully compliant with the project's checkstyle.xml; continuously review and propose refactoring for modularity and scalability
Create comprehensive Unit Tests for any new or modified code; flag insufficient test coverage and generate necessary tests
Use explicit error handling with specific exception types and Optional over broad catch(Exception e); all log statements must be structured using SLF4J/Log4j 2 with contextual information
Write code compatible with Native Image constraints (GraalVM); minimize runtime reflection and proxies
Proactively identify and replace all deprecated or outdated APIs, libraries, and code constructs with modern Java 25 alternatives
Operate under the Non-Nullable by Default principle; all type usages (parameters, returns, fields) are non-nullable unless explicitly annotated
Use the @nullable annotation exclusively to mark any type permitted to hold a null value; never use @nonnull annotation
Nullability must specify both the container and the content (e.g., List<@nullable String> for nullable content, @nullable List for nullable container)
Audit all existing legacy code and mark any variables, method parameters, or return types that accept or return null with @nullable annotation

Files:

  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
  • modules/common/src/main/java/com/workastra/common/api/Meta.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java
  • modules/common/src/main/java/com/workastra/common/api/ApiResponse.java
**/src/main/java/**/*.java

⚙️ CodeRabbit configuration file

**/src/main/java/**/*.java: You are acting as a Senior Java Engineer and Software Architect.
Review with a direct, practical, no-nonsense tone.

You must enforce modern Java (Java 25) best practices and discourage legacy or outdated patterns.
Always prefer:

  • modern language features when appropriate (var, records, switch expressions, pattern matching, sealed types, etc.)
  • the java.time API over java.util.Date/Calendar
  • java.util.concurrent and modern concurrency utilities over legacy synchronized collections
  • NIO and modern I/O APIs where suitable
  • streams and functional APIs where they clearly improve readability and safety (but avoid over-complicating simple logic)

You must explicitly:

  • Call out usage of old/legacy or deprecated APIs, raw types, Vector, Hashtable, synchronized collections, old I/O where NIO is better, and other outdated patterns.
  • Highlight any code that is not aligned with current Java 25 idioms.
  • Enforce import hygiene: avoid star imports, unused imports, and messy import sections.

Handle NullPointerException (NPE) risks very carefully:

  • Identify every place where NPE can occur (parameters, return values, map lookups, collection elements, optional values, chained calls, etc.).
  • Recommend explicit null checks, Objects.requireNonNull, Optional, or nullability annotations where appropriate.
  • Call out dangerous chaining on potentially null values.
  • Prefer designs that minimize nullable state and unexpected nulls.

You must review naming quality:

  • Check that class, method, variable, and field names are clear, descriptive, and follow Java conventions (CamelCase for classes, lowerCamelCase for fields/variables/methods, UPPER_SNAKE_CASE for constants).
  • Flag vague, misleading, or overly generic names (e.g. data, obj, tmp, doStuff, handle, manager, util).
  • Suggest better, more meaningful names that reflect the intent and domain.
  • Prefer names that express behavior and domain concepts over implementation details.

Enforce...

Files:

  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
  • modules/common/src/main/java/com/workastra/common/api/Meta.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java
  • modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java
  • modules/common/src/main/java/com/workastra/common/api/ApiResponse.java
**/package-info.java

📄 CodeRabbit inference engine (AGENTS.md)

For every package, create a package-info.java file applying the @NullMarked annotation at package scope

Files:

  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
🧠 Learnings (9)
📚 Learning: 2025-11-23T02:13:19.046Z
Learnt from: phongphongg
Repo: workastra/server PR: 2
File: gradle/libs.versions.toml:1-10
Timestamp: 2025-11-23T02:13:19.046Z
Learning: Spring Boot 4.0.0 is compatible with org.graalvm.buildtools.native plugin version 0.11.3, which is the recommended version for Spring Boot 4 native image builds.

Applied to files:

  • gradle/libs.versions.toml
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/package-info.java : For every package, create a package-info.java file applying the NullMarked annotation at package scope

Applied to files:

  • modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Audit all existing legacy code and mark any variables, method parameters, or return types that accept or return null with Nullable annotation

Applied to files:

  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Use the Nullable annotation exclusively to mark any type permitted to hold a null value; never use NonNull annotation

Applied to files:

  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Operate under the Non-Nullable by Default principle; all type usages (parameters, returns, fields) are non-nullable unless explicitly annotated

Applied to files:

  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • build.gradle.kts
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Nullability must specify both the container and the content (e.g., List<Nullable String> for nullable content, Nullable List<String> for nullable container)

Applied to files:

  • modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
  • modules/common/src/main/java/com/workastra/common/api/package-info.java
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : All generated code must be fully compliant with the project's checkstyle.xml; continuously review and propose refactoring for modularity and scalability

Applied to files:

  • build.gradle.kts
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Use explicit error handling with specific exception types and Optional over broad catch(Exception e); all log statements must be structured using SLF4J/Log4j 2 with contextual information

Applied to files:

  • build.gradle.kts
📚 Learning: 2025-11-30T03:19:40.814Z
Learnt from: CR
Repo: workastra/server PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T03:19:40.814Z
Learning: Applies to **/*.java : Proactively identify and replace all deprecated or outdated APIs, libraries, and code constructs with modern Java 25 alternatives

Applied to files:

  • build.gradle.kts
🧬 Code graph analysis (3)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (2)
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1)
  • Configuration (21-41)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (1)
  • Configuration (24-88)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java (7)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/filter/JsonLoginAuthenticationFilter.java (1)
  • JsonLoginAuthenticationFilter (31-61)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (1)
  • JsonAccessDeniedHandler (18-46)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java (1)
  • JsonAuthFailureHandler (16-37)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java (1)
  • JsonAuthSuccessHandler (16-37)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1)
  • JsonAuthenticationEntryPoint (18-43)
modules/authentication/src/main/java/com/workastra/authentication/AuthenticationModuleConfig.java (1)
  • Configuration (21-41)
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)
  • Configuration (21-58)
modules/console/src/main/java/com/workastra/console/WorkastraConsoleApplication.java (1)
modules/worker/src/main/java/com/workastra/worker/WorkastraWorkerApplication.java (1)
  • SpringBootApplication (6-12)
🔇 Additional comments (11)
modules/console/src/main/resources/application-dev.yaml (1)

1-6: Configuration change is appropriate.

Setting Spring Security logging to DEBUG in the dev profile makes sense for troubleshooting JWT authentication logic. YAML syntax is valid, and there's no production impact.

.vscode/launch.json (2)

10-10: Verify Java version compatibility for -XX:+UseCompactObjectHeaders.

This JVM argument requires Java 15+. Ensure all developers on the team use a compatible version, or document the requirement. Otherwise, developers on older JDKs will encounter errors at launch.


11-11: Verify the dev Spring profile is defined.

Confirm that a dev profile exists in your Spring configuration (e.g., application-dev.yml or application-dev.properties). If missing, Spring will warn at startup. If authentication modules (referenced in the PR) require profile-specific configuration, ensure the dev profile includes all necessary beans and properties.

settings.gradle.kts (1)

3-8: LGTM!

The module inclusion is syntactically correct and the ordering (common first, then authentication) is logical given the dependency structure described in the PR summary.

build.gradle.kts (3)

14-14: LGTM!

Plugin application pattern is correct: applied at root with apply true and then to each subproject. This ensures Error Prone checks run across all modules consistently.

Also applies to: 34-34


6-7: Compatibility verified: Versions are appropriate for Java 25.

Error Prone must be run on JDK 21 or newer, and NullAway JSpecify mode requires a recent javac version, with Java 25 recommended. Error Prone 2.44.0 and NullAway 0.12.12 are both recent and compatible with Java 25. The net-ltgt-errorprone plugin version 4.3.0 is standard for modern Gradle builds. NullAway requires Error Prone version 2.14.0 or higher, so the 2.44.0 selection is well above the minimum.

Note on Lombok compatibility: Lombok modifies the in-memory AST causing numerous incompatibilities with Error Prone and NullAway, and is not particularly recommended for use together. If the project uses Lombok, verify compatibility.

If the project uses Lombok, confirm that NullAway is configured with the required add(lombok.Generated)=true Lombok option, or consider suppressing specific warnings with @SuppressWarnings("NullAway") on Lombok-generated code.

Also applies to: 14-14


58-75: Error Prone and NullAway configuration aligns well with project's nullability standards.

The setup is sound and appropriately matches the project's adoption of JSpecify annotations. The OnlyNullMarked + JSpecifyMode combination correctly enables gradual nullness checking on already-annotated code, and the error-level enforcement is suitable given the codebase already uses @Nullable and @NullMarked annotations across modules. The excludedPaths regex .*/build/generated/.* follows standard conventions and is appropriate for future code generation scenarios. No additional verification needed for Error Prone checks, as the current NullAway-focused configuration aligns with the project's Non-Nullable by Default principle.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java (2)

26-45: Implementation looks correct.

Status 403, JSON content type, and structured error response via ApiResponse.error with request metadata. Properly delegates serialization to the injected mapper.


12-12: Jackson 3.x namespace is correct for this project—no action needed.

Spring Boot 4.0.0 (configured in gradle/libs.versions.toml) requires Jackson 3.x, which uses the tools.jackson.databind namespace. The import is intentional and compatible with the project's Java 25 setup and Spring configuration. No verification or correction is necessary.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/PasswordEncoderConfig.java (1)

28-40: Password encoder and authentication manager beans look good.

BCrypt with default settings is appropriate. Standard pattern for exposing AuthenticationManager.

modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthenticationEntryPoint.java (1)

26-42: Implementation is correct.

Proper 401 response with JSON content type and structured ApiResponse.error body. Clean variable usage for the response body.

Comment thread gradle/libs.versions.toml
Comment on lines +6 to +7
error-prone-core = { module = "com.google.errorprone:error_prone_core", version = "2.44.0" }
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.12.12" }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Error Prone 2.44.0 Java 25 compatibility

💡 Result:

Short answer: Error Prone 2.44.0 has no explicit “Java 25” support note — its release notes do not mention JDK 25 — and Error Prone's recent releases raised the minimum runtime to JDK 21. In practice you can often run Error Prone on newer JDKs (like JDK 25) but you may need the same extra jdk.compiler exports and JVM flags used for recent JDKs (e.g. --add-exports for javac internals); if you hit problems consider upgrading to a later Error Prone (2.45+), which calls out improved compatibility with newer JDK EA builds. [1][2][3]

Sources

  • Error Prone v2.44.0 release notes. [1]
  • Error Prone note that minimum runtime is JDK 21 (v2.43.0 release). [1]
  • Notes about required jdk.compiler exports for running Error Prone on newer JDKs. [2]

If you want, I can: (a) show the exact --add-exports flags to try for JDK 25, or (b) check whether a specific Error Prone submodule/plug-in you use is reported working on JDK 25.


🌐 Web query:

NullAway 0.12.12 Java 25 compatibility

💡 Result:

Short answer: Yes — NullAway 0.12.12 is built/tested for JDK 25 and is compatible with Java 25. The project added JDK‑25 support in the 0.12.11/0.12.12 cycle and 0.12.12 fixes a performance regression from 0.12.11, so you should upgrade if you saw that issue. [1][2][3]

Notes:

  • If you enable JSpecify mode, NullAway requires a javac that supports type‑use annotations in bytecode (javac ≥ 22) or you must pass -XDaddTypeAnnotationsToSymbol=true on compatible JDK 21 builds — see the JSpecify support docs. [2]

Sources:

  • NullAway 0.12.12 release (notes). [1]
  • NullAway JSpecify support / supported JDK versions. [2]
  • NullAway 0.12.11 release (JDK 25 test/compile entries). [3]

🌐 Web query:

net-ltgt-errorprone plugin latest version 2025

💡 Result:

The latest net.ltgt.errorprone Gradle plugin release is version 4.3.0 (published June 26, 2025). Use it like:

plugins {
id("net.ltgt.errorprone") version "4.3.0"
}

Sources: Gradle Plugin Portal [1], GitHub repo [2].


Error Prone 2.44.0 requires Java 25 compatibility checks or upgrade.

NullAway 0.12.12 is confirmed compatible with Java 25 (added in the 0.12.11/0.12.12 cycle), and net-ltgt-errorprone 4.3.0 is the current latest version as of June 2025. However, error-prone-core 2.44.0 has no explicit Java 25 support documented in its release notes. While Error Prone can run on newer JDKs, Java 25 targets may require additional JVM flags (e.g., --add-exports for javac internals). Consider either:

  • Adding necessary JVM configuration flags for Java 25 compatibility, or
  • Upgrading to error-prone-core 2.45+ for improved Java 25 early-access build compatibility.
🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 6-7, error-prone-core is pinned to
2.44.0 which may lack Java 25 compatibility; update the error-prone-core entry
to 2.45.0 (or the latest 2.45+ release) to pick up Java 25 compatibility fixes,
and if upgrading is not possible add the required JVM/javac flags for Java 25
compatibility (e.g., appropriate --add-exports/--add-opens flags) in the
build.gradle/gradle.properties so Error Prone can run on JDK 25.

Comment on lines +1 to +4
@NullMarked
package com.workastra.authentication.controller;

import org.jspecify.annotations.NullMarked;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Invalid Java syntax: import after package declaration.

Same issue as common/api/package-info.java. Imports must precede the annotation and package declaration.

Apply this diff:

-@NullMarked
-package com.workastra.authentication.controller;
-
 import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+package com.workastra.authentication.controller;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@NullMarked
package com.workastra.authentication.controller;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.NullMarked;
@NullMarked
package com.workastra.authentication.controller;
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/controller/package-info.java
lines 1-4, the import is placed after the package declaration which is invalid;
move the import statement before the annotation and package declaration so the
file reads: first the import of org.jspecify.annotations.NullMarked, then the
@NullMarked annotation, then the package com.workastra.authentication.controller
declaration.

Comment on lines +1 to +10
/**
* Infrastructure layer configuration for authentication and security.
*
* This package contains Spring configuration classes that set up security beans,
* filters, and the HTTP security filter chain.
*
* - SecurityConfig: Configures the HTTP security filter chain
* - PasswordEncoderConfig: Defines password encoding and authentication manager beans
*/
package com.workastra.authentication.infrastructure.config;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add @NullMarked to enforce non-null-by-default for this package.

This package-info documents the package but does not apply @NullMarked, so you lose the intended non-null-by-default semantics and NullAway/jspecify checks for these configs.

Add the annotation and import:

 /**
  * Infrastructure layer configuration for authentication and security.
  *
  * This package contains Spring configuration classes that set up security beans,
  * filters, and the HTTP security filter chain.
  *
  * - SecurityConfig: Configures the HTTP security filter chain
  * - PasswordEncoderConfig: Defines password encoding and authentication manager beans
  */
-package com.workastra.authentication.infrastructure.config;
+@org.jspecify.annotations.NullMarked
+package com.workastra.authentication.infrastructure.config;
+
+import org.jspecify.annotations.NullMarked;

Based on learnings, this package must be annotated with @NullMarked.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/package-info.java
lines 1-10, the package lacks the @NullMarked annotation so types are not
non-null-by-default; add the jspecify NullMarked import and annotate the package
declaration with @NullMarked (i.e., add the appropriate import line for
org.jspecify.annotations.NullMarked and place @NullMarked immediately above the
package statement) so NullAway/jspecify and non-null-by-default semantics are
enforced.

Comment on lines +3 to +18
import com.workastra.authentication.infrastructure.filter.JsonLoginAuthenticationFilter;
import com.workastra.authentication.infrastructure.handler.JsonAccessDeniedHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthFailureHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthSuccessHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import tools.jackson.databind.ObjectMapper;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find the SecurityConfig.java file and read it completely
find . -name "SecurityConfig.java" -path "*/authentication/*" | head -5

Repository: workastra/server

Length of output: 169


🏁 Script executed:

# Check for ObjectMapper usage in the codebase
rg "ObjectMapper" --type java -B 2 -A 2 | head -50

Repository: workastra/server

Length of output: 6342


🏁 Script executed:

# Check build configuration for Jackson dependency
fd "pom.xml|build.gradle" | xargs cat 2>/dev/null | grep -i jackson | head -20

Repository: workastra/server

Length of output: 42


🏁 Script executed:

cat -n ./modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java

Repository: workastra/server

Length of output: 4881


🏁 Script executed:

# Look for pom.xml in authentication module
find ./modules/authentication -name "pom.xml" -exec cat {} \;

Repository: workastra/server

Length of output: 42


🏁 Script executed:

# Check if tools.jackson is used consistently throughout codebase
rg "import tools\.jackson" --type java | wc -l

Repository: workastra/server

Length of output: 61


🏁 Script executed:

# Also check if com.fasterxml.jackson is used anywhere
rg "import com\.fasterxml\.jackson" --type java | head -10

Repository: workastra/server

Length of output: 300


🏁 Script executed:

# Find pom.xml files in the project
find . -name "pom.xml" -type f

Repository: workastra/server

Length of output: 42


🏁 Script executed:

# Check root pom.xml for Jackson dependency
cat ./pom.xml | grep -A 5 -B 5 "jackson" 2>/dev/null || echo "Not found in root pom.xml"

Repository: workastra/server

Length of output: 144


🏁 Script executed:

# Check if tools.jackson package exists in any dependency or if it's just a typo
rg "package tools\.jackson" --type java

Repository: workastra/server

Length of output: 42


🏁 Script executed:

# Double-check: search for any build files and Jackson version
find . -type f \( -name "*.gradle" -o -name "*.properties" \) | head -20

Repository: workastra/server

Length of output: 122


🏁 Script executed:

# Verify there's no tools.jackson in Maven Central or any vendor
# Also check if Jackson is actually available as a dependency anywhere
rg "jackson" modules/ --type toml --type properties --type xml 2>/dev/null | head -20

Repository: workastra/server

Length of output: 42


Fix incorrect ObjectMapper import.

Line 17 imports tools.jackson.databind.ObjectMapper, which is not a valid Jackson package. The correct import is com.fasterxml.jackson.databind.ObjectMapper. This will cause a compilation or runtime error.

Update to:

-import tools.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import com.workastra.authentication.infrastructure.filter.JsonLoginAuthenticationFilter;
import com.workastra.authentication.infrastructure.handler.JsonAccessDeniedHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthFailureHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthSuccessHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import tools.jackson.databind.ObjectMapper;
import com.workastra.authentication.infrastructure.filter.JsonLoginAuthenticationFilter;
import com.workastra.authentication.infrastructure.handler.JsonAccessDeniedHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthFailureHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthSuccessHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/config/SecurityConfig.java
lines 3-18, the ObjectMapper import is incorrect
(tools.jackson.databind.ObjectMapper); replace it with the correct Jackson
package import com.fasterxml.jackson.databind.ObjectMapper and remove the
invalid import so the class compiles; update any IDE auto-imports or organize
imports to ensure the correct package is used.

Comment on lines +22 to +24
public JsonAccessDeniedHandler(ObjectMapper mapper) {
this.mapper = mapper;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add null validation for constructor parameter.

Per coding guidelines, enforce non-nullable by default with explicit validation. If mapper is null, fail fast.

+import java.util.Objects;
+
 public JsonAccessDeniedHandler(ObjectMapper mapper) {
-    this.mapper = mapper;
+    this.mapper = Objects.requireNonNull(mapper, "mapper must not be null");
 }
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAccessDeniedHandler.java
around lines 22 to 24, the constructor accepts an ObjectMapper without
validation; add a null check to fail fast if mapper is null (e.g., call
Objects.requireNonNull(mapper, "mapper must not be null") or throw new
IllegalArgumentException("mapper must not be null")) so the class enforces
non-nullable constructor parameters and avoids NPEs later.

Comment on lines +33 to +35
var body = Map.of("success", false, "error", ex.getMessage());

response.getWriter().write(this.objectMapper.writeValueAsString(body));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ex.getMessage() can return null; also exposing raw exception messages is a security risk.

Two issues here:

  1. NPE risk: AuthenticationException.getMessage() can return null, which will be serialized as "error": null. Handle this explicitly.
  2. Security: Raw exception messages may leak internal details (stack traces, class names, DB info). Use a generic user-facing message.
-var body = Map.of("success", false, "error", ex.getMessage());
+var errorMessage = ex.getMessage() != null ? ex.getMessage() : "Authentication failed";
+var body = Map.of("success", false, "error", errorMessage);

For better security, consider always returning a generic message like "Authentication failed" and logging the actual exception server-side for debugging.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthFailureHandler.java
around lines 33 to 35, the code puts ex.getMessage() into the response body
which can be null and exposes internal details; replace the response "error"
value with a fixed, generic message like "Authentication failed" (never use
ex.getMessage() in client-facing output) and instead log the actual exception
server-side (e.g. logger.warn/error("Authentication failure", ex)) so debugging
info is retained without leaking internals.

import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import tools.jackson.databind.ObjectMapper;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Wrong package for ObjectMapper.

Line 10 imports tools.jackson.databind.ObjectMapper, but the standard Jackson library uses com.fasterxml.jackson.databind.ObjectMapper. This will either fail to compile or reference the wrong class.

Apply this diff:

-import tools.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import tools.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
around line 10, the import uses tools.jackson.databind.ObjectMapper which is
incorrect; replace it with com.fasterxml.jackson.databind.ObjectMapper (i.e.,
change the import statement to the standard Jackson package) so the class
resolves to the correct Jackson ObjectMapper and the code compiles.

Comment on lines +20 to +22
public JsonAuthSuccessHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing null check on constructor parameter.

The objectMapper parameter must be non-null (per Non-Nullable by Default principle), but there's no defensive check. Add Objects.requireNonNull to fail fast if null is passed.

Apply this diff:

+import java.util.Objects;
+
 public JsonAuthSuccessHandler(ObjectMapper objectMapper) {
-    this.objectMapper = objectMapper;
+    this.objectMapper = Objects.requireNonNull(objectMapper, "objectMapper must not be null");
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public JsonAuthSuccessHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
import java.util.Objects;
public JsonAuthSuccessHandler(ObjectMapper objectMapper) {
this.objectMapper = Objects.requireNonNull(objectMapper, "objectMapper must not be null");
}
🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
around lines 20 to 22, the constructor does not validate the objectMapper
parameter; add a defensive null check using Objects.requireNonNull(objectMapper,
"objectMapper must not be null") before assigning to this.objectMapper, and
ensure java.util.Objects is imported if not already present so the class fails
fast when a null is passed.

response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);

var body = Map.of("success", true, "username", authentication.getName());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical NPE risk: authentication.getName() can return null.

Map.of throws NPE if any value is null, but Authentication.getName() can return null. This will crash the success handler.

Apply this diff to provide a fallback:

-var body = Map.of("success", true, "username", authentication.getName());
+var username = authentication.getName();
+var body = Map.of(
+    "success", true,
+    "username", username != null ? username : "unknown"
+);

Or use Map.ofEntries with explicit null handling, or mark the upstream flow to ensure getName() is never null.

🤖 Prompt for AI Agents
In
modules/authentication/src/main/java/com/workastra/authentication/infrastructure/handler/JsonAuthSuccessHandler.java
around line 33, Map.of("success", true, "username", authentication.getName())
can throw an NPE when authentication.getName() is null; fix it by computing a
safe username first (e.g., use
Optional.ofNullable(authentication.getName()).orElse(""), or a sensible default
like "unknown") and then pass that non-null username into Map.of (or switch to
Map.ofEntries with Map.entry(...) using the safe value) so no null values are
handed to Map.of.

Comment on lines +1 to +4
@NullMarked
package com.workastra.common.api;

import org.jspecify.annotations.NullMarked;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Invalid Java syntax: import after package declaration.

In package-info.java, imports must precede the annotation and package declaration. This file will not compile.

Apply this diff:

-@NullMarked
-package com.workastra.common.api;
-
 import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+package com.workastra.common.api;
🤖 Prompt for AI Agents
In modules/common/src/main/java/com/workastra/common/api/package-info.java lines
1-4, the import is placed after the package/annotation which is invalid Java;
move the import statement to the top of the file (before any annotations or the
package declaration), then place the @NullMarked annotation immediately above
the package declaration so the file reads: import
org.jspecify.annotations.NullMarked; followed by @NullMarked and then the
package com.workastra.common.api; ensuring correct ordering for valid Java
syntax.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants