Skip to content

fix: GraphQL error on sync views for roles without credentials access - #977

Merged
nimish-ks merged 2 commits into
mainfrom
fix/provider-credentials-nullable
Aug 17, 2026
Merged

fix: GraphQL error on sync views for roles without credentials access#977
nimish-ks merged 2 commits into
mainfrom
fix/provider-credentials-nullable

Conversation

@rohan-chaturvedi

Copy link
Copy Markdown
Member

🔍 Overview

Users without the Integration Credentials: read permissions recieved the error:

[GraphQL error]: Message: Cannot return null for non-nullable field ProviderCredentialsType.credentials., Path: syncs,0,authentication,credentials

ProviderCredentialsType.resolve_credentials deliberately withholds credential values (returns None) from users whose role lacks IntegrationCredentials: read. However, the credentials field's schema type was auto-derived from the non-null model column as JSONString! — so the withholding became a hard GraphQL error that nulled the entire syncs payload, breaking sync views for any user who can see syncs but not credentials.

This has been latent since the permission check was introduced (1ad43c3): all five default roles hold IntegrationCredentials: read, so only custom roles with sync visibility but no credentials access can trigger it. The Log Streams release surfaced it rather than caused it.

An aggravating factor: both sync queries (GetAppSyncStatus, GetOrganisationSyncs) requested authentication { credentials } even though no frontend code consumes it — needlessly decrypting third-party credentials and shipping them to the browser on every sync list view, and detonating the error for under-privileged users.

💡 Proposed Changes

Three layers, so no single regression can reintroduce the breakage:

  1. Schema: credentials is now explicitly declared graphene.JSONString() (nullable) on ProviderCredentialsType — permission-withheld values are a legal null instead of a payload-killing error. Schema and frontend codegen regenerated.
  2. Queries: removed the unused credentials field from the authentication block of getAppSyncStatus.gql and GetOrgSyncs.gql. Sync views no longer request (or decrypt) credential values at all. savedCredentials on the credentials page still requests it — the update dialog needs it, and that resolver already returns [] for unauthorized users.
  3. Frontend guard: UpdateProviderCredentials tolerates a null credentials value in both JSON.parse sites.

A new schema-shape test (backend/tests/test_graphene_schema.py) pins the field's nullability so a future regeneration can't silently restore the !.

🖼️ Screenshots or Demo

No visual changes — the fix removes an error toast and restores the syncs list for affected users.

📝 Release Notes

  • Fixed: users with custom roles that grant sync visibility but not IntegrationCredentials: read no longer get a GraphQL error (and an empty sync view) on the app Syncing tab and the Integrations → Syncs page.
  • Hardening: sync list views no longer fetch decrypted third-party credential values (they were requested but never used).
  • No migrations, no breaking changes. The credentials field of ProviderCredentialsType is now nullable in the GraphQL schema; it returns null when the requesting role lacks IntegrationCredentials: read.

❓ Open Questions

  • Longer-term, should sync/credential authz be restructured so resolve_credentials never returns decrypted values wholesale (e.g. field-level masking or a dedicated reveal mutation)? Deferred — tracked as part of the holistic credential-change authz follow-up.

🧪 Testing

  • New: test_provider_credentials_field_is_nullable (schema-shape guard).
  • Full backend suite: 1,240 passed (plus the known root-user baseline failure in tests/utils/test_secret.py).
  • Frontend: tsc --noEmit clean (pre-existing baseline errors only), eslint clean on changed files, codegen regenerated.

🎯 Reviewer Focus

  • backend/backend/graphene/types.py — the explicit nullable field declaration on ProviderCredentialsType.
  • frontend/graphql/queries/syncing/getAppSyncStatus.gql / GetOrgSyncs.gql — confirm nothing consumed authentication.credentials (repo-wide grep found zero usages).
  • frontend/components/syncing/UpdateProviderCredentials.tsx — null-guards.

➕ Additional Context

✨ How to Test the Changes Locally

  1. Create a custom role with app/environment access and Integrations read, but without IntegrationCredentials: read.
  2. Assign it to a test member with access to an app that has at least one active sync.
  3. As that member, open the app's Syncing tab (and Integrations → Syncs).
  4. Before this fix: a GraphQL error toast (Cannot return null for non-nullable field ProviderCredentialsType.credentials) and a broken/empty sync list. After: the sync list renders; credential values are simply absent.
  5. As an Owner/Admin, verify Integrations → Third-party credentials → Manage still pre-fills and updates credentials correctly.

💚 Did You...

  • Ensure linting passes (code style checks)?
  • Update dependencies and lockfiles (if required)
  • Update migrations (if required)
  • Regenerate graphql schema and types (if required)
  • Verify the app builds locally?
  • Manually test the changes on different browsers/devices?

…top overfetching it in sync queries — permission-withheld credentials nulled the whole syncs payload for custom-role users
@rohan-chaturvedi
rohan-chaturvedi force-pushed the fix/provider-credentials-nullable branch from acc036c to e31e0f4 Compare August 17, 2026 16:42
@nimish-ks nimish-ks self-assigned this Aug 17, 2026
…y — the syncs page poll and dashboard card decrypted every stored credential without using them
@nimish-ks
nimish-ks merged commit aff3734 into main Aug 17, 2026
15 checks passed
@nimish-ks
nimish-ks deleted the fix/provider-credentials-nullable branch August 17, 2026 18:03
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.

2 participants