---
id: CAP-PFM-INT-005
title: Protect credentials throughout their useful life
feature: FEAT-PFM-INT-02
product_area: platform
module: integrations
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - CAP-INT-CRD-001
  - CAP-INT-CRD-002
  - CAP-INT-CRD-003
  - CAP-INT-CRD-004
  - CAP-INT-CRD-005
  - CAP-INT-CRD-006
  - CAP-INT-CRD-007
  - CAP-INT-CRD-008
  - CAP-INT-CRD-009
  - CAP-INT-CRD-011
  - CAP-INT-SEC-002
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-platform:docs/milestones/INTG-M1-find-install-connect.md#D5'
  - 'services-platform:docs/milestones/INTG-M1-find-install-connect.plan.md#INTG-M1-S07'
  - 'services-platform:docs/milestones/INTG-M1-find-install-connect.plan.md#INTG-M1-S08'
purpose: 'Administrators supply authentication safely while Rhythm controls its use, replacement and expiry.'
outcomes:
  - 'Stored keys remain absent from user-facing reads, records and logs.'
  - Replacement takes effect immediately while the old key remains recoverable for one week.
  - Token refresh maintains sign-in or directs the administrator to re-authorize.
concepts:
  - CON-PFM-INT-002
actors:
  - ACT-PFM-017
  - ACT-PFM-018
  - ACT-PFM-021
  - ACT-PFM-006
surfaces:
  - console
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: platform.integration-credentials.manage
    where_checked: api
    scope: tenant
  - key: platform.integrations.read
    where_checked: api
    scope: tenant
  - key: platform.integration-credentials.manage
    where_checked: console
    scope: tenant
rules:
  - id: BR-CAP-PFM-INT-005-01
    proved_by: [ATS-PFM-INT-005-01, TST-PFM-INT-486, TST-PFM-INT-491, TST-PFM-INT-526, TST-PFM-INT-529]
    statement: >-
      Credential values remain in secret storage and are absent from screens, answers, records and logs; reads
      expose method, entry and expiry dates, scopes, refresh state and a four-character hint only for keys
      longer than eight characters.
    kind: invariant
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/credential-state.integration.spec.js#answers
      no secret value, secret name or key version
    origin: 'services-platform:docs/milestones/INTG-M1-find-install-connect.md#D5'
  - id: BR-CAP-PFM-INT-005-02
    proved_by: [ATS-PFM-INT-005-02, TST-PFM-INT-486]
    statement: >-
      Entering, replacing or authorizing credentials requires the separate credentials permission, while
      integration read permits inspection of non-secret facts.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/e2e/credentials.e2e.spec.js#is refused to a caller who
      manages connections but not their credentials
    origin: none
  - id: BR-CAP-PFM-INT-005-03
    proved_by: [ATS-PFM-INT-005-03, TST-PFM-INT-485, TST-PFM-INT-525]
    statement: >-
      Entered credentials must match the connection’s method and its exact declared fields, with at most
      twenty fields, one hundred characters per field name and 8192 characters per value; rejection stores and
      logs no secret.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/e2e/credential-entry-limits.e2e.spec.js#INT-ACC-0166 step
      5: refuses a value of 8193 characters with 400 naming the field and the limit, and stores nothing
    origin: none
  - id: BR-CAP-PFM-INT-005-04
    proved_by: [ATS-PFM-INT-005-04, ATS-PFM-INT-005-13, ATS-PFM-INT-005-14, TST-PFM-INT-525, TST-PFM-INT-527]
    statement: >-
      Calls immediately use a replacement credential, while the superseded secret remains recoverable for
      seven days before destruction and has no scheduled rotation requirement.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/credential-lifecycle.integration.spec.js#INT-ACC-0163
      step 5: svc_accounting’s payments.authorize right after the replacement, before the re-verification
      runs, leases the new credential
    origin: 'services-platform:docs/milestones/INTG-M1-find-install-connect.md#D5'
  - id: BR-CAP-PFM-INT-005-05
    proved_by: [ATS-PFM-INT-004-14, ATS-PFM-INT-005-05, ATS-PFM-INT-005-16, ATS-PFM-INT-005-17, ATS-PFM-INT-013-13]
    statement: >-
      Credential entry starts verification and moves waiting, failed-verification or re-authorization-required
      connections to verifying; other states remain, and suspended connections start no check.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/integrations/services/credentials/credential-store.ts:verification_retried'
    origin: none
  - id: BR-CAP-PFM-INT-005-06
    proved_by: [ATS-PFM-INT-005-06, TST-PFM-INT-525, TST-PFM-INT-531]
    statement: >-
      The broker lends credential values in memory for a specific execution and adapter, for five minutes by
      default and at most sixty, with value-free audit and accepted access only; suspension refuses a lease
      and verifying is permitted only for verification.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/broker.integration.spec.js#is
      refused as connection_unavailable for a suspended connection
    origin: none
  - id: BR-CAP-PFM-INT-005-07
    proved_by: [ATS-PFM-INT-005-07, TST-PFM-INT-509]
    statement: >-
      Only the issuing adapter principal may renew a lease for its own connection while its execution remains
      running.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
      a lease whose execution is not running, execution_not_running
    origin: none
  - id: BR-CAP-PFM-INT-005-08
    proved_by: [ATS-PFM-INT-005-08, TST-PFM-INT-532]
    statement: >-
      Rhythm begins OAuth authorization with a proof key and a single-use state tied to the person and
      connection for ten minutes, requesting only accepted-capability scopes; entered-key methods are refused.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/oauth-flow.integration.spec.js#answers
      the provider’s URL with the client, the one redirect, the state and the accepted capabilities’ scopes
    origin: none
  - id: BR-CAP-PFM-INT-005-09
    proved_by: [ATS-PFM-INT-005-09, TST-PFM-INT-486, TST-PFM-INT-532]
    statement: >-
      The public OAuth callback saves tokens and returns to the connection page only for valid issued state;
      unknown, expired, consumed or forged state returns a page asking the person to restart.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/e2e/credentials.e2e.spec.js#answers the public callback a
      page for a state nobody issued, with no signed context
    origin: none
  - id: BR-CAP-PFM-INT-005-10
    proved_by: []
    statement: >-
      Re-authorization starts OAuth for a sign-in connection, asks a key connection for replacement
      credentials and tells a platform-account connection that no re-authorization is needed.
    kind: policy
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/e2e/credentials.e2e.spec.js#tells a key connection to enter
      a new credential, with 409
    origin: none
  - id: BR-CAP-PFM-INT-005-11
    proved_by: [ATS-PFM-INT-005-10, ATS-PFM-INT-005-15, ATS-PFM-INT-006-12, TST-PFM-INT-535]
    statement: >-
      The minute refresh pass handles tokens expiring within ten minutes, serializes refresh by connection and
      moves a refused refresh to re-authorization required with an administrator notice.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/refresh.integration.spec.js#makes
      one token request when two refreshes of a connection run at once
    origin: none
  - id: BR-CAP-PFM-INT-005-12
    proved_by: [ATS-PFM-INT-005-11, ATS-PFM-INT-005-13, TST-PFM-INT-528]
    statement: >-
      Daily maintenance records revoked or superseded credentials as destroyed after secret deletion, sends
      one notice per refresh-token expiry within fourteen days and removes sign-in attempts abandoned for over
      a day.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/credential-maintenance.integration.spec.js#sends
      nothing and audits nothing more on the next day’s pass for the same expiry
    origin: none
  - id: BR-CAP-PFM-INT-005-13
    proved_by: []
    statement: >-
      The service keeps credential records within fifty by removing the oldest unreferenced destroyed entries,
      refuses a new credential if that cannot free enough room and never revives a destroyed credential.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/integrations/services/persistence/integration-credentials.ts:makeRoomForCredential
    origin: none
  - id: BR-CAP-PFM-INT-005-14
    proved_by: [ATS-PFM-INT-005-12]
    statement: Stored credentials contain at most one hundred authorization scopes.
    kind: validation
    enforced_at:
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/integrations/db/integration-credentials.table.ts:integration_credentials_scopes_check
    origin: none
scenarios:
  - id: SC-CAP-PFM-INT-005-01
    given: A connection’s key is replaced on Monday.
    when: Accounting makes its next payment immediately afterwards.
    then: 'The replacement is leased, while the superseded secret stays recoverable for one week.'
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/credential-lifecycle.integration.spec.js#INT-ACC-0163
      step 3: the replacement is active with the hint K3mT and no rotation date, the only active credential
  - id: SC-CAP-PFM-INT-005-02
    given: An entered key is eight characters long.
    when: Staff read the credential facts.
    then: The response omits the key hint rather than exposes four of those characters.
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/credentials/credential-hint.integration.spec.js#INT-ACC-0168
      step 1: is absent from the row’s metadata for a key of eight characters
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-INT-004
related:
  - CAP-PFM-INT-009
  - CAP-PFM-INT-006
journeys:
  - JNY-PFM-INT-001
open_questions: []
evidence:
  - >-
    services-platform source inventory: modules/integrations/services/credentials/* (broker.ts,
    credential-store.ts, credential-values.ts, oauth-flow.ts, refresh.ts, credential-passes.ts),
    db/integration-credentials.table.ts, db/integration-oauth-states.table.ts,
    aws/logic/integrations-credentials.schedule.ts; rule-level automated test references;
    docs/integrations/README.md (Credentials); docs/milestones/INTG-M1-find-install-connect.md (D5),
    INTG-M4-oauth-and-credential-lifecycle.md.
  - >-
    react-console source inventory: components/integrations/credential-entry.tsx, connection-facts.tsx,
    lib/integrations/credential-expiry.ts.
  - >-
    Operation coverage: getIntegrationConnectionCredential, putIntegrationConnectionCredential,
    startIntegrationConnectionOauth, completeIntegrationOauth, reauthorizeIntegrationConnection,
    renewIntegrationLease.
  - 'Persistent record coverage: integration_credentials, integration_oauth_states.'
  - >-
    End-to-end definition inventory: credentials.e2e.spec.js, credential-entry-limits.e2e.spec.js,
    reader-access.e2e.spec.js.
controls: [TC-AVL-001, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-INT-02-01, FAS-PFM-INT-02-02, FAS-PFM-INT-02-03, FAS-PFM-INT-02-04, FAS-PFM-INT-02-05]
suites: [TST-PFM-INT-485, TST-PFM-INT-486, TST-PFM-INT-491, TST-PFM-INT-509, TST-PFM-INT-525, TST-PFM-INT-526, TST-PFM-INT-527, TST-PFM-INT-528, TST-PFM-INT-529, TST-PFM-INT-531, TST-PFM-INT-532, TST-PFM-INT-535]
---
# Protect credentials throughout their useful life

## Purpose

Administrators supply authentication safely while Rhythm controls its use, replacement and expiry.

## How It Works

An administrator supplies a key or starts the provider’s sign-in flow. Subsequent screens provide credential facts instead of the original secret. The gateway borrows the values when an operation needs authentication. Replacement separates the new usable credential from the old recoverable one. Token refresh keeps authorization current where the provider permits it. When renewal fails, staff see that another sign-in is needed. Maintenance closes the old credential lifecycle and clears abandoned sign-ins. OAuth is implemented ahead of its proposed milestone and remains separate from accepted key handling. The product should preserve that evidence distinction when planning sign-in acceptance.

## Relationships

- Requires maintain a verified provider account connection to supply the prerequisite configuration or operation.
- Connects with run provider operations safely through the gateway through shared integration records.
- Connects with recognize integration problems and trace changes through shared integration records.
