Skip to content

Add isolated OAuth 2.0 + PKCE support for GPT Actions#38

Merged
vitorhugo-java merged 4 commits into
mainfrom
copilot/add-oauth2-integration-gpt-actions
May 29, 2026
Merged

Add isolated OAuth 2.0 + PKCE support for GPT Actions#38
vitorhugo-java merged 4 commits into
mainfrom
copilot/add-oauth2-integration-gpt-actions

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 29, 2026

This PR adds a dedicated OAuth 2.0 Authorization Code flow with PKCE for GPT Actions, without changing the existing JWT-based user auth or the separate Google Drive OAuth integration. It introduces scoped GPT access, explicit user binding, and GPT-oriented API surfaces that reuse the current per-user authorization model.

  • OAuth flow for GPT Actions

    • adds /oauth2/authorize and /oauth2/token for a dedicated GPT client
    • validates configured client ID/secret, allowed redirect URIs, requested scopes, and PKCE S256
    • persists short-lived authorization codes server-side and exchanges them for signed bearer tokens
  • Isolated security model

    • adds a separate security chain for /oauth2/** and /api/v1/gpt/**
    • keeps the existing JWT web flow unchanged for /api/v1/**
    • issues GPT tokens with explicit scope, roles, user_id, aud, iss, and token_use claims
    • maps GPT bearer tokens back into the existing Spring Security context so SecurityUtils and repository-level user isolation continue to work
  • GPT-scoped API surface

    • adds /api/v1/gpt/** endpoints for profile, applications, resumes, Google Drive status, and metrics
    • enforces least-privilege scope checks such as read:profile, read:applications, and write:applications
    • preserves existing role constraints where they already matter, e.g. Google Drive and resume endpoints still require BETA
  • Config and docs

    • adds env-driven GPT OAuth configuration:
      • OPENAI_GPT_CLIENT_ID
      • OPENAI_GPT_CLIENT_SECRET
      • OPENAI_GPT_REDIRECT_URIS
      • OPENAI_GPT_SCOPES
    • updates OpenAPI with an OAuth2 authorization-code security scheme and a GPT Actions group
    • documents GPT Action setup and the new auth flow in README.md
    • updates application.yml, application-test.yml, and .env.example
  • Persistence and regression coverage

    • adds a migration for GPT authorization-code storage
    • adds tests for config loading, code exchange/token issuance, GPT-protected endpoint access, OpenAPI exposure, JWT regression, and Google Drive regression

Example of the new GPT-side surface:

GET /oauth2/authorize?response_type=code&client_id=...&redirect_uri=...&scope=read:profile%20read:applications&code_challenge=...&code_challenge_method=S256
POST /oauth2/token
Authorization: Basic <client credentials>

GET /api/v1/gpt/profile
Authorization: ******

And the endpoint-level authorization remains explicit:

@PreAuthorize("hasAuthority('SCOPE_write:applications')")
@PostMapping("/applications")
public ResponseEntity<ApplicationResponse> createApplication(@Valid @RequestBody ApplicationRequest request) {
    return ResponseEntity.status(HttpStatus.CREATED).body(applicationService.create(request));
}

Copilot AI changed the title Add GPT Actions OAuth 2.0 support Add isolated OAuth 2.0 + PKCE support for GPT Actions May 29, 2026
Copilot AI requested a review from vitorhugo-java May 29, 2026 02:34
@vitorhugo-java
Copy link
Copy Markdown
Owner

@copilot Review and refactor it so GPT access reuses the existing API surface instead of duplicating controllers.

Context:

  • The repository already exposes the needed resources in OpenAPI:
    • /api/v1/auth/me
    • /api/v1/applications
    • /api/v1/google-drive/base-resumes
    • /api/v1/google-drive/base-resumes/{resumeId}/content
    • /api/v1/google-drive/applications/{applicationId}/generated-resumes/content
    • /api/v1/google-drive/status
  • The current PR adds a separate /api/v1/gpt/** controller layer, which duplicates existing business endpoints and will create long-term maintenance drift.
  • The current PR also adds a dedicated GPT security chain for /oauth2/** and /api/v1/gpt/**, while the existing app security chain already handles the standard API.

Goal:
Keep the GPT OAuth 2.0 / PKCE auth server support, but remove the duplicated GPT-specific resource controllers. GPT should authenticate with its own OAuth token and call the existing endpoints directly, with authorization enforced by scopes + existing roles.

Required changes:

  1. Remove the GPT-specific controller surface:

    • delete GptActionController
    • remove /api/v1/gpt/** endpoints from the design
    • do not create duplicate endpoints for profile, applications, resumes, or google-drive status
  2. Keep only the OAuth/auth pieces needed for GPT:

    • /oauth2/authorize
    • /oauth2/token
    • JWK/JWT validation support if the implementation needs it
    • GPT OAuth client registration/configuration
    • PKCE, scopes, refresh token support
  3. Reuse the existing API endpoints for both JWT users and GPT tokens:

    • /api/v1/auth/me
    • /api/v1/applications
    • /api/v1/applications/{id}
    • /api/v1/applications/{id}/status
    • /api/v1/google-drive/base-resumes
    • /api/v1/google-drive/base-resumes/{resumeId}/content
    • /api/v1/google-drive/applications/{applicationId}/generated-resumes/content
    • /api/v1/google-drive/status
  4. Update authorization on the existing controllers/services so both identities work:

    • human users continue using the current JWT auth
    • GPT tokens use OAuth scopes
    • use expressions like:
      • hasRole('USER') or hasAuthority('SCOPE_read:applications')
      • hasRole('USER') or hasAuthority('SCOPE_write:applications')
    • keep BETA checks where they already matter for Google Drive / resume features
  5. Preserve user isolation:

    • GPT auth must still resolve to the same existing User context
    • do not bypass SecurityUtils
    • do not introduce a second copy of the domain logic
  6. Update OpenAPI so it documents the existing endpoints with the OAuth2 security scheme instead of adding a parallel GPT controller section.

    • add OAuth2 security scheme
    • apply it to the existing endpoints
    • do not create new GPT-only paths unless they are truly new functionality
  7. Keep the existing JWT security chain intact.

    • If a separate OAuth security chain is needed, it should protect the OAuth endpoints and resource-server token validation, not duplicate application routes.
    • Avoid overlapping /api/v1/gpt/** route matching.
  8. Update tests:

    • verify the existing endpoints accept GPT OAuth tokens via scope-based authorization
    • verify the existing JWT flow still works
    • verify Google Drive and resume endpoints still work
    • remove tests for duplicated /api/v1/gpt/** endpoints if they were added only because of the duplicated controller layer

Implementation guidance:

  • Prefer changing annotations and security rules on the existing controllers over adding new controller classes.
  • If a helper facade is needed, keep it thin and reuse the current services.
  • The final design should be:
    • one resource API surface
    • two authentication modes
    • authorization decided by roles/scopes
    • no duplicate business endpoints
    • document endpoints
    • all existing and news tests must pass

Deliver a cleanup PR that removes the duplicated GPT controller layer and keeps only the OAuth integration + scope-based access on the existing API.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 29, 2026

@vitorhugo-java I've opened a new pull request, #39, to work on those changes. Once the pull request is ready, I'll request review from you.

…#39)

* Initial plan

* Remove GptActionController; reuse existing API for GPT OAuth tokens

- Delete GptActionController (duplicated /api/v1/gpt/** layer)
- Narrow GptOAuthSecurityConfig chain to /oauth2/** only; remove
  oauth2ResourceServer and BearerTokenAuthentication* handlers
- Extend JwtAuthenticationFilter to try GPT OAuth tokens as fallback
  when user JWT validation fails (avoids BearerTokenAuthenticationFilter
  conflict in the main chain)
- Change GptOAuthTokenService to emit ROLE_GPT_CLIENT instead of
  ROLE_USER; preserve non-USER roles (e.g. ROLE_BETA)
- Add URL-level hasAnyRole(USER, GPT_CLIENT) rules in SecurityConfig for
  each GPT-accessible path; keep hasRole(USER) catch-all for everything else
- Add @PreAuthorize(hasRole(USER) or hasAuthority(SCOPE_...)) to
  AuthController#me, ApplicationController#{create,getAll,getById,updateStatus}
- Update GoogleDriveController status/listBaseResumes/getBaseResumeContent/
  getGeneratedResumeContent to require ROLE_BETA and (USER or scope)
- Update OpenApiConfig gptOpenApi group to point to existing endpoint paths
- Update GptOAuthFlowIT to call /api/v1/applications and /api/v1/auth/me
- Update OpenApiDocumentationIT assertions to check existing paths

* Make invalid-token return path explicit in tryUserJwt

Add explicit return false with debug log when isTokenValid fails,
so the method always returns at a clear decision point rather than
falling through to the final return.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation backend tests database configuration labels May 29, 2026
@vitorhugo-java vitorhugo-java marked this pull request as ready for review May 29, 2026 11:50
@vitorhugo-java vitorhugo-java merged commit 162fcf9 into main May 29, 2026
3 checks passed
@vitorhugo-java vitorhugo-java deleted the copilot/add-oauth2-integration-gpt-actions branch May 29, 2026 11:50
@github-actions
Copy link
Copy Markdown

Qodana for JVM

6 new problems were found

Inspection name Severity Problems
Nullability and data flow problems 🔶 Warning 2
Mismatched query and update of 'StringBuilder' 🔶 Warning 1
Unused assignment 🔶 Warning 1
Non-distinguishable logging calls ◽️ Notice 2
View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/qodana-action@v2025.3.2
        with:
          upload-result: true
Contact Qodana team

Contact us at qodana-support@jetbrains.com

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

Labels

backend configuration database documentation Improvements or additions to documentation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants