Skip to content

Phase 1A: Identity & Access — carddemo-auth Spring Boot module - #210

Open
devin-ai-integration[bot] wants to merge 5 commits into
devin/1781281050-modernization-blueprintfrom
devin/1781281980-phase1a-auth-service
Open

devin-ai-integration[bot] wants to merge 5 commits into
devin/1781281050-modernization-blueprintfrom
devin/1781281980-phase1a-auth-service

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 12, 2026

Copy link
Copy Markdown

Summary

Adds services/carddemo-auth, a Spring Boot 3.2 module that replaces the COBOL sign-on and user-management programs with a stateless JWT-based REST API.

COBOL → Java mapping:

Legacy Program Endpoint
COSGN00C (sign-on against VSAM USRSEC) POST /auth/login → returns JWT
COUSR00C (list users) GET /users
COUSR01C (add user) POST /users
COUSR02C (update user) PUT /users/{id}
COUSR03C (delete user) DELETE /users/{id}

Key design decisions:

  • User entity mirrors copybook CSUSR01Y (SEC-USR-ID 8-char PK, SEC-USR-FNAME/LNAME 20-char, SEC-USR-TYPE A/U → enum ADMIN/USER)
  • Passwords stored as BCrypt hashes (replaces COBOL plaintext PIC X(08))
  • RBAC: ADMIN role can CRUD all users; USER role restricted to own profile (cannot escalate via userType field)
  • Stateless auth via JwtAuthenticationFilter extracting Bearer tokens and setting SecurityContext
  • PostgreSQL for production, H2 in-memory for test profile
  • 22 tests (3 unit for AuthService, 8 unit for UserService, 11 integration covering auth flows + RBAC enforcement)

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/7fbaff67c2b94193a54161b7559bd27f
Requested by: @DhrovS


Open in Devin Review

Add new Maven module services/carddemo-auth implementing:
- Spring Boot 3.2.x with Spring Security and JWT authentication
- JPA User entity mapped from COBOL copybook CSUSR01Y (SEC-USER-DATA)
- REST endpoints: POST /auth/login, GET/POST/PUT/DELETE /users
- Role-based access control (ADMIN vs USER roles)
- BCrypt password hashing (replacing COBOL plaintext storage)
- Unit tests for AuthService and UserService
- Integration tests for all endpoints with H2
- README documenting COBOL-to-Java mapping

Modernizes legacy COBOL programs:
- COSGN00C (sign-on) -> POST /auth/login
- COUSR00C (list) -> GET /users
- COUSR01C (add) -> POST /users
- COUSR02C (update) -> PUT /users/{id}
- COUSR03C (delete) -> DELETE /users/{id}
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

Open in Devin Review

Comment on lines +33 to +42
String userId = tokenProvider.getUserIdFromToken(token);
String role = tokenProvider.getRoleFromToken(token);

List<SimpleGrantedAuthority> authorities = List.of(
new SimpleGrantedAuthority("ROLE_" + role)
);

UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userId, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);

@devin-ai-integration devin-ai-integration Bot Jun 12, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🚩 Deleted or role-changed users retain JWT-based access for up to 24 hours

The JwtAuthenticationFilter (JwtAuthenticationFilter.java:32-42) validates the JWT signature and expiration but never checks whether the user still exists in the database or whether their role has changed. This means if an admin deletes a user via DELETE /users/{id}, that user's JWT remains valid for up to 24 hours (application.yml:23 sets expiration-ms: 86400000). A deleted admin user could continue to create users, update any user, or delete other users until token expiry. This is an inherent trade-off of stateless JWT design (which the PR explicitly chose per the README's "Stateless Auth" section), but it's worth noting for an IAM service where user revocation may be expected to be immediate. Consider adding a token blacklist, a short-lived token + refresh token pattern, or a lightweight DB existence check in the filter if immediate revocation is a requirement.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Acknowledged — this is an intentional trade-off of stateless JWT architecture. For immediate revocation, a token blacklist or short-lived tokens + refresh flow can be added in a future iteration. The 24h TTL is configurable via app.jwt.expiration-ms.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Acknowledged — as noted, this is an intentional trade-off of stateless JWT architecture documented in the README. For immediate revocation needs, a token blacklist (e.g., Redis-backed) or short-lived access tokens + refresh token pattern can be added in a future iteration. The 24h TTL is configurable via app.jwt.expiration-ms and can be lowered to reduce the revocation window.

Comment thread services/carddemo-auth/src/main/resources/application.yml Outdated
- Guard against NPE in GlobalExceptionHandler when getFieldError() is null
- Reject blank strings in UpdateUserRequest partial updates (treat as no-op)
- Change ddl-auto from 'update' to 'validate' for production profile
devin-ai-integration[bot]

This comment was marked as resolved.

- Add @transactional(readOnly=true) at class level, @transactional on
  mutating methods to prevent lost updates and TOCTOU races
- Add DataIntegrityViolationException handler returning 409 Conflict
  as defense-in-depth for concurrent create races
- Add @SiZe(max=72) on password fields to prevent BCrypt DoS via
  oversized inputs (BCrypt truncates at 72 bytes anyway)
@devin-ai-integration
devin-ai-integration Bot changed the base branch from main to devin/1781281050-modernization-blueprint June 12, 2026 16:53
devin-ai-integration[bot]

This comment was marked as resolved.

- Add dummy BCrypt comparison when user not found to ensure constant-time
  response regardless of userId existence (prevents user enumeration)
- Remove hardcoded default JWT secret from application.yml; app now
  requires JWT_SECRET env var to start (fail-fast in production)

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Open in Devin Review

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"))
)
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.POST, "/auth/login").permitAll()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🚩 No brute-force / rate-limiting protection on the login endpoint

The POST /auth/login endpoint is permitAll() (line 38 of SecurityConfig) with no rate limiting or account lockout mechanism. While this isn't strictly a code bug (it's a missing feature), it's worth flagging for a security-focused auth service: an attacker can make unlimited login attempts. The original COBOL COSGN00C program typically had lockout after N failed attempts. Consider adding Spring's rate limiting, a lockout counter, or delegating to an API gateway rate limiter.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Acknowledged — rate limiting / account lockout is a valid hardening concern. The original COBOL COSGN00C did enforce lockout after failed attempts. This can be added in a follow-up (e.g., a failed-attempt counter in the users table or an API gateway rate limiter like Bucket4j or Spring Cloud Gateway filters). Keeping this PR scoped to the core auth functionality for now.

Replace the malformed 51-char dummy hash with a proper 60-char BCrypt
hash so that BCryptPasswordEncoder.matches() actually performs the
full computation on the user-not-found path.
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.

1 participant