Skip to content

Add connections API over identity providers#3536

Draft
JayaShakthi97 wants to merge 1 commit into
thunder-id:mainfrom
JayaShakthi97:feature/integrations-page
Draft

Add connections API over identity providers#3536
JayaShakthi97 wants to merge 1 commit into
thunder-id:mainfrom
JayaShakthi97:feature/integrations-page

Conversation

@JayaShakthi97

@JayaShakthi97 JayaShakthi97 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Purpose

Adds a new /connections backend API — the foundation of the console Connections area (issue #1157). It presents the external login providers ThunderID connects to as typed, per-vendor connection resources (Google, GitHub, Custom OIDC) and is a thin layer over the existing identity-provider service: it owns no storage, so a configured connection is a real identity provider and federated login keeps working.

This is the first backend slice (IdP-backed types). Message-sender connection types (Twilio/Vonage/Custom), the frontend, and docs follow in subsequent PRs.

Endpoints

  • GET /connections — available connection types with configured status and instance count (structural data only; presentation/branding such as logo, display name and categories lives in the frontend).
  • Per type google / github / oidc:
    • GET /connections/{type} — list configured instances (typed; secrets masked)
    • POST /connections/{type} — create (strict per-vendor body)
    • GET /connections/{type}/{id} — instance detail (edit view)
    • PUT /connections/{type}/{id} — update (a secret sent as ****** keeps the stored value)
    • DELETE /connections/{type}/{id} — delete

Example:

{
  "connections": [
    { "type": "google", "configured": true,  "instanceCount": 1 },
    { "type": "github", "configured": false, "instanceCount": 0 },
    { "type": "oidc",   "configured": false, "instanceCount": 0 }
  ]
}

Approach

  • New backend/internal/connection/ package. Each vendor file (google.go, github.go, oidc.go) defines its own strict typed request/response structs and toDTO/fromDTO mappers; thin handler methods delegate the request plumbing to shared generic helpers (createConnection/getConnection/updateConnection). Type-only operations (list, delete) are shared.
  • The connection layer translates each request to/from the existing {name,value,isSecret} identity-provider property model and delegates to idp.IDPServiceInterface — no new store, no executor wiring. Wired in servicemanager.go via connection.Initialize(mux, idpService).
  • Secrets: masked as ****** on read; on update, a field echoed back as ****** is replaced with the stored value before delegating (the connection layer does this — the underlying IdP API does not).
  • Per-type integrity: GET/PUT/DELETE /connections/{type}/{id} 404 if the stored provider's type doesn't match the path, so a vendor endpoint can only act on its own instances.
  • Multiple instances per type are supported by the API (name remains unique; duplicate name → 409). Whether the UI exposes "add another" is a frontend concern.
  • A single generic OIDC type ("Custom OIDC" in the UI) covers custom providers; a generic OAuth type can be added later if a non-OIDC provider needs it.
  • OpenAPI added at api/connections.yaml with strict per-vendor schemas.

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a Connections API for managing Google, GitHub, and OIDC connections.
    • Users can now list connection types, view configured instances, create new connections, update existing ones, and delete connections.
  • Bug Fixes

    • Secret values are now masked in responses and preserved correctly during updates.
    • Connection listings now show accurate configured status and instance counts.
    • Added clearer handling for invalid requests, missing connections, and conflicts.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e6abc4ca-fbda-469c-b3a9-6a71de209c25

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new OAuth2-secured Connections API specification, backend route registration, shared connection service and mapping helpers, vendor-specific request/response handling for Google, GitHub, and OIDC, and tests covering the new flows.

Changes

Connections API

Layer / File(s) Summary
OpenAPI contract
api/connections.yaml
Defines OAuth2-secured /connections list and vendor CRUD routes for google, github, and oidc, plus reusable request, response, and error components.
Shared service and mapping
backend/internal/connection/models.go, backend/internal/connection/mapping.go, backend/internal/connection/service.go
Adds vendor summary models, secret masking and scope helpers, HTTP error writers, and type-scoped IdP service methods.
Connection listing
backend/internal/connection/listing.go, backend/internal/connection/connection_test.go
Counts configured IdP instances by type and returns the /connections summary response, with tests covering the list counts and configured flags.
Vendor handlers and tests
backend/internal/connection/handler.go, backend/internal/connection/google.go, backend/internal/connection/github.go, backend/internal/connection/oidc.go, backend/internal/connection/connection_test.go
Implements generic create/get/update/list/delete handlers, vendor payload mappings for Google, GitHub, and OIDC, and tests for secret masking, conflict handling, type mismatches, update secret preservation, delete, and OIDC field mapping.
Route registration
backend/internal/connection/init.go, backend/cmd/server/servicemanager.go
Creates the connection service and handler, mounts /connections collection and item routes with OPTIONS handlers, and initializes the connection API during server startup.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant http.ServeMux
  participant connection.handler
  participant connection.service
  participant idpService

  Client->>http.ServeMux: POST /connections/google
  http.ServeMux->>connection.handler: handleGoogleCreate
  connection.handler->>connection.service: createConnection(googleToIDPDTO)
  connection.service->>idpService: CreateIdentityProvider
  idpService-->>connection.service: created IDPDTO
  connection.service-->>connection.handler: created response DTO
  connection.handler-->>Client: 201 Created

  Client->>http.ServeMux: PUT /connections/google/{id}
  http.ServeMux->>connection.handler: handleGoogleUpdate
  connection.handler->>connection.service: updateConnection(googleToIDPDTO)
  connection.service->>idpService: GetIdentityProvider
  connection.service->>connection.service: mergeStoredSecrets(masked secret)
  connection.service->>idpService: UpdateIdentityProvider
  idpService-->>connection.service: updated IDPDTO
  connection.service-->>connection.handler: updated response DTO
  connection.handler-->>Client: 200 OK
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • thunder-id/thunderid issue 3497 — The PR adds the /connections backend facade and per-vendor CRUD/listing flow described in the issue.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a connections API over identity providers.
Description check ✅ Passed The description follows the template and includes purpose, approach, issues, PRs, checklist, and security checks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

Affected Assets, Files, and Routes:

view changes for bundle: console-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/dist-*.js -136.41kB 148 bytes -99.89%
assets/dist-*.js 5 bytes 145 bytes 3.57%
assets/dist-*.js -5 bytes 140 bytes -3.45%
assets/dist-*.js -78 bytes 70 bytes -52.7%
assets/dist-*.js 136.49kB 136.56kB 194990.0% ⚠️

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
api/connections.yaml (1)

306-319: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent type casing across the contract.

ConnectionTypeSummary.type enumerates lowercase values (google, github, oidc), while the per-vendor resource responses example type as uppercase (GOOGLE, GITHUB, OIDC). Consumers/SDKs reading both surfaces will see two different casings for the same concept. Consider normalizing to one representation (or documenting that one is the connection-type identifier and the other is the underlying IdP type).

Also applies to: 344-344

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/connections.yaml` around lines 306 - 319, Normalize the `type` casing
across the API contract by making `ConnectionTypeSummary.type` and the
per-vendor resource response examples/schemas use the same representation.
Update the relevant OpenAPI definitions in `api/connections.yaml` around
`ConnectionTypeSummary` and the vendor response schema(s) so `type` consistently
uses either lowercase or uppercase, and if both forms must remain, clearly
distinguish them in the schema names/descriptions to avoid ambiguity for SDK
consumers.
backend/internal/connection/mapping.go (1)

104-110: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Optional: trim whitespace when splitting scopes.

splitScopes splits on , without trimming, so a stored value like openid, email yields a scope with a leading space. Low impact since joinScopes writes without spaces, but defensive trimming guards against externally-seeded properties.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/connection/mapping.go` around lines 104 - 110, The
splitScopes helper currently returns raw comma-separated entries, so values like
“openid, email” keep leading spaces. Update splitScopes to trim whitespace from
each scope after strings.Split while keeping the empty-input behavior, and use
the existing splitScopes symbol to make the parsing defensive against externally
seeded values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/connections.yaml`:
- Around line 1-426: The `/connections` API change is missing the required docs
updates, including the new REST routes, vendor-specific CRUD behavior, secret
masking with “******”, and 409 duplicate-name handling. Update the relevant API
reference in docs/content/apis.mdx to cover the connection endpoints and the
ConnectionListResponse, GoogleConnectionRequest/Response,
GitHubConnectionRequest/Response, OIDCConnectionRequest/Response, Error, and
I18nMessage shapes. Also add or update a guide under docs/content/ or
docs/content/guides/ explaining the new connection resource model and how it
maps to underlying identity providers, and update any SDK docs under
docs/content/sdks/ if the system scope or client-facing schemas are exposed
there.

In `@backend/internal/connection/listing.go`:
- Around line 43-45: Add the missing documentation for the new Connections API
surfaced by handleListConnections and the related /connections/{vendor} routes:
update docs/content/apis.mdx to describe the GET /connections endpoint, its
summary fields (configured and instanceCount), the per-vendor CRUD routes, and
the required authentication. Also add a new guide under docs/content/guides/
explaining the Connections feature and how to use it.

---

Nitpick comments:
In `@api/connections.yaml`:
- Around line 306-319: Normalize the `type` casing across the API contract by
making `ConnectionTypeSummary.type` and the per-vendor resource response
examples/schemas use the same representation. Update the relevant OpenAPI
definitions in `api/connections.yaml` around `ConnectionTypeSummary` and the
vendor response schema(s) so `type` consistently uses either lowercase or
uppercase, and if both forms must remain, clearly distinguish them in the schema
names/descriptions to avoid ambiguity for SDK consumers.

In `@backend/internal/connection/mapping.go`:
- Around line 104-110: The splitScopes helper currently returns raw
comma-separated entries, so values like “openid, email” keep leading spaces.
Update splitScopes to trim whitespace from each scope after strings.Split while
keeping the empty-input behavior, and use the existing splitScopes symbol to
make the parsing defensive against externally seeded values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 29731724-19fe-425e-a765-e898551c313c

📥 Commits

Reviewing files that changed from the base of the PR and between d48454b and 0fe363c.

📒 Files selected for processing (12)
  • api/connections.yaml
  • backend/cmd/server/servicemanager.go
  • backend/internal/connection/connection_test.go
  • backend/internal/connection/github.go
  • backend/internal/connection/google.go
  • backend/internal/connection/handler.go
  • backend/internal/connection/init.go
  • backend/internal/connection/listing.go
  • backend/internal/connection/mapping.go
  • backend/internal/connection/models.go
  • backend/internal/connection/oidc.go
  • backend/internal/connection/service.go

Comment thread api/connections.yaml
Comment on lines +1 to +426
openapi: 3.0.3

info:
title: Connections API
description: >-
Configure the external services ThunderID connects to. This is a thin layer over the
identity-provider service: each connection type has a strict, vendor-specific schema and
is delegated to the underlying provider, so a configured connection is a real identity
provider. Multiple instances per type are supported.
version: "1.0"
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html

servers:
- url: https://{host}:{port}
variables:
host:
default: "localhost"
port:
default: "8090"

tags:
- name: Connections
description: Discover available connection types and configure their instances.

security:
- OAuth2: [system]

paths:
/connections:
get:
tags: [Connections]
summary: List available connection types
description: Returns every connection type with its configured status and instance count.
responses:
"200":
description: The available connection types
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionListResponse'
example:
connections:
- type: "google"
configured: true
instanceCount: 1
- type: "github"
configured: false
instanceCount: 0
"500": { $ref: '#/components/responses/InternalServerError' }

/connections/google:
get:
tags: [Connections]
summary: List configured Google connections
responses:
"200": { $ref: '#/components/responses/InstanceList' }
"500": { $ref: '#/components/responses/InternalServerError' }
post:
tags: [Connections]
summary: Create a Google connection
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/GoogleConnectionRequest' }
responses:
"201":
description: Connection created
content:
application/json:
schema: { $ref: '#/components/schemas/GoogleConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
/connections/google/{id}:
parameters:
- { $ref: '#/components/parameters/ConnectionID' }
get:
tags: [Connections]
summary: Get a Google connection
responses:
"200":
description: Connection details (secrets masked)
content:
application/json:
schema: { $ref: '#/components/schemas/GoogleConnectionResponse' }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }
put:
tags: [Connections]
summary: Update a Google connection
description: A secret sent as "******" keeps the stored value.
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/GoogleConnectionRequest' }
responses:
"200":
description: Connection updated
content:
application/json:
schema: { $ref: '#/components/schemas/GoogleConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"404": { $ref: '#/components/responses/NotFound' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
delete:
tags: [Connections]
summary: Delete a Google connection
responses:
"204": { description: Connection deleted }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }

/connections/github:
get:
tags: [Connections]
summary: List configured GitHub connections
responses:
"200": { $ref: '#/components/responses/InstanceList' }
"500": { $ref: '#/components/responses/InternalServerError' }
post:
tags: [Connections]
summary: Create a GitHub connection
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/GitHubConnectionRequest' }
responses:
"201":
description: Connection created
content:
application/json:
schema: { $ref: '#/components/schemas/GitHubConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
/connections/github/{id}:
parameters:
- { $ref: '#/components/parameters/ConnectionID' }
get:
tags: [Connections]
summary: Get a GitHub connection
responses:
"200":
description: Connection details (secrets masked)
content:
application/json:
schema: { $ref: '#/components/schemas/GitHubConnectionResponse' }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }
put:
tags: [Connections]
summary: Update a GitHub connection
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/GitHubConnectionRequest' }
responses:
"200":
description: Connection updated
content:
application/json:
schema: { $ref: '#/components/schemas/GitHubConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"404": { $ref: '#/components/responses/NotFound' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
delete:
tags: [Connections]
summary: Delete a GitHub connection
responses:
"204": { description: Connection deleted }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }

/connections/oidc:
get:
tags: [Connections]
summary: List configured OIDC connections
responses:
"200": { $ref: '#/components/responses/InstanceList' }
"500": { $ref: '#/components/responses/InternalServerError' }
post:
tags: [Connections]
summary: Create an OIDC connection
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/OIDCConnectionRequest' }
responses:
"201":
description: Connection created
content:
application/json:
schema: { $ref: '#/components/schemas/OIDCConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
/connections/oidc/{id}:
parameters:
- { $ref: '#/components/parameters/ConnectionID' }
get:
tags: [Connections]
summary: Get an OIDC connection
responses:
"200":
description: Connection details (secrets masked)
content:
application/json:
schema: { $ref: '#/components/schemas/OIDCConnectionResponse' }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }
put:
tags: [Connections]
summary: Update an OIDC connection
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/OIDCConnectionRequest' }
responses:
"200":
description: Connection updated
content:
application/json:
schema: { $ref: '#/components/schemas/OIDCConnectionResponse' }
"400": { $ref: '#/components/responses/BadRequest' }
"404": { $ref: '#/components/responses/NotFound' }
"409": { $ref: '#/components/responses/Conflict' }
"500": { $ref: '#/components/responses/InternalServerError' }
delete:
tags: [Connections]
summary: Delete an OIDC connection
responses:
"204": { description: Connection deleted }
"404": { $ref: '#/components/responses/NotFound' }
"500": { $ref: '#/components/responses/InternalServerError' }

components:
securitySchemes:
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://localhost:8090/oauth2/authorize
tokenUrl: https://localhost:8090/oauth2/token
scopes:
system: Access to system management APIs
clientCredentials:
tokenUrl: https://localhost:8090/oauth2/token
scopes:
system: Access to system management APIs

parameters:
ConnectionID:
in: path
name: id
required: true
schema:
type: string
format: uuid

responses:
InstanceList:
description: Configured connection instances
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ConnectionInstanceSummary'
BadRequest:
description: Bad request
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }
NotFound:
description: Connection not found
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }
Conflict:
description: A connection with the same name already exists
content:
application/json:
schema: { $ref: '#/components/schemas/Error' }
InternalServerError:
description: Internal server error

schemas:
ConnectionListResponse:
type: object
required: [connections]
properties:
connections:
type: array
items: { $ref: '#/components/schemas/ConnectionTypeSummary' }

ConnectionTypeSummary:
type: object
required: [type, configured, instanceCount]
properties:
type:
type: string
enum: [google, github, oidc]
example: "google"
configured:
type: boolean
example: true
instanceCount:
type: integer
example: 1

ConnectionInstanceSummary:
type: object
required: [id, name]
properties:
id: { type: string, format: uuid }
name: { type: string, example: "My Google" }
description: { type: string }

GoogleConnectionRequest:
type: object
required: [name, clientId, clientSecret, redirectUri]
properties:
name: { type: string, example: "My Google" }
clientId: { type: string }
clientSecret: { type: string, description: "Write-only. Send \"******\" to keep the stored value on update." }
redirectUri: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }
GoogleConnectionResponse:
type: object
properties:
id: { type: string, format: uuid }
name: { type: string }
type: { type: string, example: "GOOGLE" }
clientId: { type: string }
clientSecret: { type: string, description: "Masked as \"******\".", example: "******" }
redirectUri: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }

GitHubConnectionRequest:
type: object
required: [name, clientId, clientSecret, redirectUri]
properties:
name: { type: string, example: "My GitHub" }
clientId: { type: string }
clientSecret: { type: string, description: "Write-only. Send \"******\" to keep the stored value on update." }
redirectUri: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }
GitHubConnectionResponse:
type: object
properties:
id: { type: string, format: uuid }
name: { type: string }
type: { type: string, example: "GITHUB" }
clientId: { type: string }
clientSecret: { type: string, example: "******" }
redirectUri: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }

OIDCConnectionRequest:
type: object
required: [name, clientId, clientSecret, redirectUri, authorizationEndpoint, tokenEndpoint]
properties:
name: { type: string }
clientId: { type: string }
clientSecret: { type: string, description: "Write-only. Send \"******\" to keep the stored value on update." }
redirectUri: { type: string }
authorizationEndpoint: { type: string }
tokenEndpoint: { type: string }
userInfoEndpoint: { type: string }
jwksEndpoint: { type: string }
logoutEndpoint: { type: string }
issuer: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }
tokenExchangeEnabled: { type: boolean }
OIDCConnectionResponse:
type: object
properties:
id: { type: string, format: uuid }
name: { type: string }
type: { type: string, example: "OIDC" }
clientId: { type: string }
clientSecret: { type: string, example: "******" }
redirectUri: { type: string }
authorizationEndpoint: { type: string }
tokenEndpoint: { type: string }
userInfoEndpoint: { type: string }
jwksEndpoint: { type: string }
logoutEndpoint: { type: string }
issuer: { type: string }
scopes: { type: array, items: { type: string } }
prompt: { type: string }
tokenExchangeEnabled: { type: boolean }

Error:
type: object
required: [code, message]
properties:
code:
type: string
example: "IDP-1001"
message:
$ref: '#/components/schemas/I18nMessage'
description:
$ref: '#/components/schemas/I18nMessage'

I18nMessage:
type: object
required: [key, defaultValue]
properties:
key: { type: string }
defaultValue: { type: string }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🔴 Documentation Required
This PR introduces user-facing changes that are not covered by documentation updates under docs/.
Please update the relevant documentation before merging.

Missing documentation:

  • New /connections REST API (list types + per-vendor google/github/oidc CRUD): document the routes, request/response schemas, secret-masking (******) semantics, and 409 duplicate-name behavior in the API reference (docs/content/apis.mdx).
  • New connection resource concept and per-type configuration model: add or update a content/guide page describing connection types vs. underlying identity providers (docs/content/ or docs/content/guides/).
  • SDK-impacting client-facing schemas/scopes (system scope, vendor request/response shapes): update SDK docs if applicable (docs/content/sdks/).

As per path instructions: "If ANY of the above are detected and the PR does NOT include corresponding updates under docs/ ... post a single consolidated PR-level comment."

🧰 Tools
🪛 Checkov (3.3.1)

[medium] 276-280: Ensure that arrays have a maximum number of items

(CKV_OPENAPI_21)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/connections.yaml` around lines 1 - 426, The `/connections` API change is
missing the required docs updates, including the new REST routes,
vendor-specific CRUD behavior, secret masking with “******”, and 409
duplicate-name handling. Update the relevant API reference in
docs/content/apis.mdx to cover the connection endpoints and the
ConnectionListResponse, GoogleConnectionRequest/Response,
GitHubConnectionRequest/Response, OIDCConnectionRequest/Response, Error, and
I18nMessage shapes. Also add or update a guide under docs/content/ or
docs/content/guides/ explaining the new connection resource model and how it
maps to underlying identity providers, and update any SDK docs under
docs/content/sdks/ if the system scope or client-facing schemas are exposed
there.

Source: Path instructions

Comment on lines +43 to +45
// handleListConnections handles GET /connections, returning the available connection types
// with their configured status and instance count.
func (h *handler) handleListConnections(w http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether docs reference the new /connections API.
fd -t f . docs 2>/dev/null | head -50
echo "--- references to /connections in docs ---"
rg -n "connections" docs/ 2>/dev/null | rg -i "connection" | head -50

Repository: thunder-id/thunderid

Length of output: 6351


🔴 Documentation Required
This PR introduces user-facing changes that are not covered by documentation updates under docs/.
Please update the relevant documentation before merging.

Missing documentation:

  • New GET /connections API endpoint (and the per-vendor /connections/{vendor} CRUD routes wired alongside it): document the route, its summary response (configured, instanceCount per connection type), and authentication requirements in the API reference at docs/content/apis.mdx, plus a guide page under docs/content/guides/ describing the Connections feature.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/connection/listing.go` around lines 43 - 45, Add the missing
documentation for the new Connections API surfaced by handleListConnections and
the related /connections/{vendor} routes: update docs/content/apis.mdx to
describe the GET /connections endpoint, its summary fields (configured and
instanceCount), the per-vendor CRUD routes, and the required authentication.
Also add a new guide under docs/content/guides/ explaining the Connections
feature and how to use it.

Source: Path instructions

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

@JayaShakthi97 JayaShakthi97 marked this pull request as draft June 25, 2026 09:07
@JayaShakthi97 JayaShakthi97 force-pushed the feature/integrations-page branch 2 times, most recently from cb26d93 to 983280c Compare June 25, 2026 09:49
Add a /connections HTTP layer that presents external login providers as typed,
per-vendor connection resources (Google, GitHub, Custom OIDC) and delegates to the
identity-provider service. It owns no storage, so a configured connection is a real
identity provider and federated login keeps working.

Includes GET /connections (available types with configured status and instance count),
per-type CRUD with strict per-vendor request/response schemas, secret masking on read
with masked-secret preservation on update, per-type integrity checks, multi-instance
support, and the OpenAPI spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JayaShakthi97 JayaShakthi97 force-pushed the feature/integrations-page branch from 983280c to 0b5cd84 Compare June 25, 2026 11:18
@JayaShakthi97

Copy link
Copy Markdown
Contributor Author

Thanks @coderabbitai. Addressed:

  • type casing: per-vendor response type now returns the lowercase connection id (google/github/oidc), consistent with the path and the /connections listing.
  • splitScopes: now trims whitespace and drops empty entries.

Deferred / skipped:

  • Docs: tracked separately as the documentation sub-task of Introduce Integrations Onboarding UI support #1157 (this is the backend-only slice).
  • Checkov maxItems: skipping for consistency — existing API specs (e.g. api/idp.yaml) don't set maxItems on arrays.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@JayaShakthi97 Thanks for the update — the lowercase type alignment and the splitScopes cleanup both make sense.

Deferring the docs work to the documentation sub-task of #1157 also sounds reasonable for this backend-only slice, and I'm okay with skipping Checkov maxItems here for consistency with the existing API specs.

🐇👍


✏️ Learnings added
Learnt from: JayaShakthi97
URL: https://github.com/thunder-id/thunderid/pull/3536

Timestamp: 2026-06-25T11:23:24.493Z
Learning: In this repository's OpenAPI specs, arrays generally do not set `maxItems` for consistency with existing specs such as `api/idp.yaml`; avoid flagging missing `maxItems` in similar API spec reviews unless the PR introduces a different established convention.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant