Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/workastra_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,16 @@ jobs:
cache: gradle

- 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.


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

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

build:
Expand Down
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"cwd": "${workspaceFolder}",
"mainClass": "com.workastra.console.WorkastraConsoleApplication",
"projectName": "console",
"env": {}
"vmArgs": "-XX:+UseCompactObjectHeaders",
"env": { "SPRING_PROFILES_ACTIVE": "dev" }
}
]
}
27 changes: 27 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import net.ltgt.gradle.errorprone.errorprone

val libsCatalog = extensions
.getByType(VersionCatalogsExtension::class.java)
.named("libs")

plugins {
java
checkstyle
alias(libs.plugins.spring.boot) apply false
alias(libs.plugins.spring.dependency.management) apply false
alias(libs.plugins.graalvm.native) apply false
alias(libs.plugins.spotless)
alias(libs.plugins.net.ltgt.errorprone) apply true
}

group = "com.workastra"
Expand All @@ -24,6 +31,7 @@ subprojects {
apply(plugin = "java")
apply(plugin = "checkstyle")
apply(plugin = "com.diffplug.spotless")
apply(plugin = "net.ltgt.errorprone")

checkstyle {
toolVersion = "12.1.2"
Expand All @@ -46,4 +54,23 @@ subprojects {
)).configFile(file("${rootDir}/java.prettierrc.yaml"))
}
}

dependencies {
add("errorprone", libsCatalog.findLibrary("error-prone-core").get())
add("errorprone", libsCatalog.findLibrary("nullaway").get())
}

tasks.withType<JavaCompile>().configureEach {
options.errorprone {
disableWarningsInGeneratedCode.set(true)
excludedPaths.set(".*/build/generated/.*")

// Enable nullness checks only in null-marked code
option("NullAway:OnlyNullMarked", "true")
// Bump checks from warnings (default) to errors
error("NullAway")
// https://github.com/uber/NullAway/wiki/JSpecify-Support
option("NullAway:JSpecifyMode", "true")
}
}
}
6 changes: 6 additions & 0 deletions config/checkstyle/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,11 @@
<property name="checks" value="HideUtilityClassConstructor" />
<property name="query" value="//CLASS_DEF[.//ANNOTATION/IDENT[@text='SpringBootApplication']]" />
</module>

<!-- @Bean methods must not have any modifier (only package-private allowed). -->
<module name="MatchXpath">
<property name="query" value="//METHOD_DEF[./MODIFIERS/ANNOTATION/IDENT[@text='Bean'] and count(./MODIFIERS/*) &gt; 1]"/>
<message key="matchxpath.match" value="@Bean methods must not declare any modifier (use package-private only)."/>
</module>
</module>
</module>
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ spring-boot = "4.0.0"

[libraries]
spring-boot-bom = { module = "org.springframework.boot:spring-boot-dependencies", version.ref = "spring-boot" }
error-prone-core = { module = "com.google.errorprone:error_prone_core", version = "2.44.0" }
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.12.12" }
Comment on lines +6 to +7
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.


[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }
graalvm-native = { id = "org.graalvm.buildtools.native", version = "0.11.3" }
spotless = { id = "com.diffplug.spotless", version = "8.1.0" }
net-ltgt-errorprone = { id = "net.ltgt.errorprone", version = "4.3.0" }
13 changes: 13 additions & 0 deletions modules/authentication/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
`java-library`
alias(libs.plugins.spring.dependency.management)
}

dependencies {
implementation(platform(libs.spring.boot.bom))
implementation(project(":modules:common"))
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-security-oauth2-client")
testImplementation("org.springframework.boot:spring-boot-starter-security-oauth2-client-test")
testImplementation("org.springframework.boot:spring-boot-starter-security-test")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.workastra.authentication;

import com.workastra.authentication.infrastructure.handler.JsonAccessDeniedHandler;
import com.workastra.authentication.infrastructure.handler.JsonAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tools.jackson.databind.ObjectMapper;

/**
* AuthenticationModuleConfig is the module-level bootstrap configuration for the authentication module.
*
* This configuration is responsible for:
* - Registering exception handlers as Spring components
* - Providing infrastructure beans needed across the module
*
* Actual security configuration is delegated to the infrastructure layer:
* - SecurityConfig: HTTP security filter chain and authorization rules
* - PasswordEncoderConfig: Password encoding and authentication beans
* - Various handlers: JSON-based exception and authentication event handling
*/
@Configuration
public class AuthenticationModuleConfig {

/**
* Registers the JsonAuthenticationEntryPoint as a Spring component.
* This handler processes 401 Unauthorized responses.
*/
@Bean
JsonAuthenticationEntryPoint jsonAuthenticationEntryPoint(ObjectMapper mapper) {
return new JsonAuthenticationEntryPoint(mapper);
}

/**
* Registers the JsonAccessDeniedHandler as a Spring component.
* This handler processes 403 Forbidden responses.
*/
@Bean
JsonAccessDeniedHandler jsonAccessDeniedHandler(ObjectMapper mapper) {
return new JsonAccessDeniedHandler(mapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.workastra.authentication.controller;

import com.workastra.common.api.ApiResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
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);
}
Comment on lines +10 to +20
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.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package com.workastra.authentication.controller;

import org.jspecify.annotations.NullMarked;
Comment on lines +1 to +4
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.workastra.authentication.infrastructure.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
* PasswordEncoderConfig defines password encoding and authentication-related beans.
*
* This configuration class sets up:
* - BCryptPasswordEncoder for secure password hashing
* - AuthenticationManager for delegating authentication
* - UserDetailsService with in-memory user store (for demo purposes)
*/
@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()
);
}
Comment on lines +21 to +57
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.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.workastra.authentication.infrastructure.config;

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;

Comment on lines +3 to +18
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.

/**
* SecurityConfig configures Spring Security for the authentication module.
* It sets up the HTTP security chain, CSRF protection, authorization rules,
* and integrates custom JSON-based authentication handlers.
*/
@Configuration
public class SecurityConfig {

private final JsonAccessDeniedHandler accessDeniedHandler;
private final JsonAuthenticationEntryPoint authenticationEntryPoint;

public SecurityConfig(
JsonAuthenticationEntryPoint authenticationEntryPoint,
JsonAccessDeniedHandler accessDeniedHandler
) {
this.authenticationEntryPoint = authenticationEntryPoint;
this.accessDeniedHandler = accessDeniedHandler;
}

/**
* Configures the security filter chain for HTTP requests.
*
* - Enables CSRF protection for Single Page Applications (SPA)
* - Permits public endpoints: /actuator/health, /api/v1/csrf, /api/v1/auth/login
* - Requires authentication for all other /api/** endpoints
* - Denies all other requests
* - Uses custom JSON authentication filter instead of form login
* - Registers custom exception handlers for unauthorized and forbidden access
*/
@Bean
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));

var sessionRepo = new HttpSessionSecurityContextRepository();
jsonLoginAuthenticationFilter.setSecurityContextRepository(sessionRepo);

http
.csrf((csrf) -> csrf.spa())
.authorizeHttpRequests((auth) ->
auth
.requestMatchers(HttpMethod.GET, "/actuator/health")
.permitAll()
.requestMatchers("/api/v1/csrf")
.permitAll()
.requestMatchers("/api/v1/auth/login")
.permitAll()
.requestMatchers("/api/**")
.authenticated()
.anyRequest()
.denyAll()
)
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.formLogin((c) -> c.disable())
.httpBasic((c) -> c.disable())
.addFilterAt(jsonLoginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling((ex) -> {
ex.authenticationEntryPoint(this.authenticationEntryPoint);
ex.accessDeniedHandler(this.accessDeniedHandler);
});

return http.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,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;
Comment on lines +1 to +10
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.

Loading
Loading