Skip to content

Support Content-Security-Policy (CSP) nonces in SDKs' injected styles - #59

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:flexible-vendor-config
Aug 7, 2026
Merged

Support Content-Security-Policy (CSP) nonces in SDKs' injected styles#59
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:flexible-vendor-config

Conversation

@brionmario

@brionmario brionmario commented Aug 6, 2026

Copy link
Copy Markdown
Member

Purpose

  • Updated emotion styles imports across multiple components to use a centralized emotion module.
  • Introduced configureEmotionNonce function to set CSP nonce for injected styles, ensuring compliance with strict Content-Security-Policy.
  • Modified ThunderIDProvider and ThunderIDPlugin to accept and forward cspNonce option.
  • Enhanced tests to verify correct behavior of nonce configuration and style injection.
  • Added new tests for emotion utilities to ensure proper functionality with and without nonce.

Approach

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
  • 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.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • New Features

    • Added optional CSP nonce support for generated styles in React and Vue integrations, improving compatibility with strict Content Security Policies.
    • Added configurable spinner width overrides for precise sizing.
  • Style

    • Improved consistency of component styling across consent, authentication, menus, images, and form controls.
    • Enhanced dropdown hover highlighting and loading-state layouts.
  • Tests

    • Expanded coverage for CSP nonce handling, generated styles, and responsive styling behavior.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@brionmario, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5c9a8218-5db9-4759-8934-3df5bb1ede4b

📥 Commits

Reviewing files that changed from the base of the PR and between adf1afb and 85a036f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (79)
  • packages/react/src/components/actions/SignInButton/BaseSignInButton.tsx
  • packages/react/src/components/actions/SignOutButton/BaseSignOutButton.tsx
  • packages/react/src/components/actions/SignUpButton/BaseSignUpButton.tsx
  • packages/react/src/components/adapters/Consent.tsx
  • packages/react/src/components/adapters/ConsentCheckboxList.styles.ts
  • packages/react/src/components/adapters/ConsentCheckboxList.tsx
  • packages/react/src/components/adapters/ImageComponent.tsx
  • packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.styles.ts
  • packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx
  • packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.styles.ts
  • packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.tsx
  • packages/react/src/components/presentation/UserProfile/BaseUserProfile.styles.ts
  • packages/react/src/components/presentation/UserProfile/BaseUserProfile.tsx
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.styles.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/InviteUser/BaseInviteUser.styles.ts
  • packages/react/src/components/presentation/auth/InviteUser/BaseInviteUser.tsx
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.styles.ts
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.styles.ts
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
  • packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.tsx
  • packages/react/src/components/primitives/Alert/Alert.styles.ts
  • packages/react/src/components/primitives/Alert/Alert.tsx
  • packages/react/src/components/primitives/Avatar/Avatar.styles.ts
  • packages/react/src/components/primitives/Avatar/Avatar.tsx
  • packages/react/src/components/primitives/Button/Button.styles.ts
  • packages/react/src/components/primitives/Button/Button.tsx
  • packages/react/src/components/primitives/Card/Card.styles.ts
  • packages/react/src/components/primitives/Card/Card.tsx
  • packages/react/src/components/primitives/Checkbox/Checkbox.styles.ts
  • packages/react/src/components/primitives/Checkbox/Checkbox.tsx
  • packages/react/src/components/primitives/CopyableText/CopyableText.styles.ts
  • packages/react/src/components/primitives/DatePicker/DatePicker.styles.ts
  • packages/react/src/components/primitives/DatePicker/DatePicker.tsx
  • packages/react/src/components/primitives/Dialog/Dialog.styles.ts
  • packages/react/src/components/primitives/Dialog/Dialog.tsx
  • packages/react/src/components/primitives/Divider/Divider.styles.ts
  • packages/react/src/components/primitives/Divider/Divider.tsx
  • packages/react/src/components/primitives/FormControl/FormControl.styles.ts
  • packages/react/src/components/primitives/FormControl/FormControl.tsx
  • packages/react/src/components/primitives/InputLabel/InputLabel.styles.ts
  • packages/react/src/components/primitives/InputLabel/InputLabel.tsx
  • packages/react/src/components/primitives/KeyValueInput/KeyValueInput.styles.ts
  • packages/react/src/components/primitives/KeyValueInput/KeyValueInput.tsx
  • packages/react/src/components/primitives/Logo/Logo.styles.ts
  • packages/react/src/components/primitives/Logo/Logo.tsx
  • packages/react/src/components/primitives/MultiInput/MultiInput.styles.ts
  • packages/react/src/components/primitives/MultiInput/MultiInput.tsx
  • packages/react/src/components/primitives/OtpField/OtpField.styles.ts
  • packages/react/src/components/primitives/OtpField/OtpField.tsx
  • packages/react/src/components/primitives/PasswordField/PasswordField.styles.ts
  • packages/react/src/components/primitives/PasswordField/PasswordField.tsx
  • packages/react/src/components/primitives/Select/Select.styles.ts
  • packages/react/src/components/primitives/Select/Select.tsx
  • packages/react/src/components/primitives/Spinner/Spinner.styles.ts
  • packages/react/src/components/primitives/Spinner/Spinner.tsx
  • packages/react/src/components/primitives/TextField/TextField.styles.ts
  • packages/react/src/components/primitives/TextField/TextField.tsx
  • packages/react/src/components/primitives/Toggle/Toggle.styles.ts
  • packages/react/src/components/primitives/Toggle/Toggle.tsx
  • packages/react/src/components/primitives/Tooltip/Tooltip.styles.ts
  • packages/react/src/components/primitives/Tooltip/Tooltip.tsx
  • packages/react/src/components/primitives/Typography/Typography.styles.ts
  • packages/react/src/components/primitives/Typography/Typography.tsx
  • packages/react/src/contexts/ThunderID/ThunderIDProvider.tsx
  • packages/react/src/contexts/ThunderID/__tests__/ThunderIDProvider.test.tsx
  • packages/react/src/models/config.ts
  • packages/react/src/styles/__tests__/emotion.test.ts
  • packages/react/src/styles/emotion.ts
  • packages/vue/package.json
  • packages/vue/src/__tests__/plugins/ThunderIDPlugin.test.ts
  • packages/vue/src/plugins/ThunderIDPlugin.ts
  • packages/vue/src/styles/__tests__/injectStyles.test.ts
  • packages/vue/src/styles/injectStyles.ts
📝 Walkthrough

Walkthrough

The PR adds shared Emotion utilities with CSP nonce support, wires nonce configuration into React and Vue, migrates React styling imports to the shared module, replaces selected inline styles with generated classes, and updates spinner sizing.

Changes

Shared Emotion and CSP support

Layer / File(s) Summary
React Emotion runtime and nonce configuration
packages/react/src/styles/emotion.ts, packages/react/src/contexts/ThunderID/*, packages/react/src/models/config.ts
Adds nonce-aware Emotion caching, the optional React cspNonce setting, synchronous provider configuration, and tests for cache reuse and nonce changes.
Vue style injection and plugin wiring
packages/vue/src/plugins/*, packages/vue/src/styles/*, packages/vue/src/__tests__/*
Adds optional CSP nonce forwarding and applies the nonce to injected style elements. Tests cover injection and deduplication.
React adapter and presentation styling
packages/react/src/components/actions/*, packages/react/src/components/adapters/*, packages/react/src/components/presentation/*
Replaces direct @emotion/css imports and selected inline layout styles with the shared Emotion module and generated classes.
React primitive styling and spinner API
packages/react/src/components/primitives/*
Migrates primitive styling imports. Spinner sizing now uses widthOverride and generated custom-size styles. Tests inspect generated CSS rules.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReactApp
  participant ThunderIDProvider
  participant SharedEmotion
  participant EmotionStyleTag
  ReactApp->>ThunderIDProvider: render with cspNonce
  ThunderIDProvider->>SharedEmotion: configureEmotionNonce(cspNonce)
  SharedEmotion->>EmotionStyleTag: insert generated CSS with nonce
  ReactApp->>SharedEmotion: create component classes
  SharedEmotion->>EmotionStyleTag: add component rules
Loading
sequenceDiagram
  participant VueApp
  participant ThunderIDPlugin
  participant injectStyles
  participant StyleElement
  VueApp->>ThunderIDPlugin: install with cspNonce
  ThunderIDPlugin->>injectStyles: pass vendor and nonce
  injectStyles->>StyleElement: create or reuse injected style
  injectStyles->>StyleElement: assign nonce when provided
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the purpose and changes, but the Approach section is empty and the checklist and security checks remain incomplete. Complete the Approach section and update each checklist item, including testing, documentation, breaking-change status, and security checks.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: refactoring Emotion integration to support CSP nonces.
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.
✨ 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.

@brionmario brionmario changed the title Refactor emotion integration for CSP nonce support Support Content-Security-Policy (CSP) nonces in SDKs' injected styles Aug 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react/src/components/primitives/Spinner/Spinner.tsx (1)

41-45: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Document the Spinner public API migration.

No repository consumer passes Spinner a style prop, but the prop is removed from SpinnerProps and is not forwarded. Document that callers should use widthOverride for custom dimensions, since spinnerCustomSize already sets both width and height and overrides size-based styles.

🤖 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 `@packages/react/src/components/primitives/Spinner/Spinner.tsx` around lines 41
- 45, Update the public API documentation for SpinnerProps to state that callers
should use widthOverride instead of the removed style prop for custom
dimensions. Clarify that spinnerCustomSize already sets both width and height
and takes precedence over size-based styles, anchoring the change to
SpinnerProps and the widthOverride documentation.
🤖 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 `@packages/react/src/styles/emotion.ts`:
- Line 27: Update the Emotion cache setup around EMOTION_CACHE_KEY to derive the
key with getVendorPrefix(vendor) from the configured ThunderIDReactConfig
instead of hardcoding “thunderid”. Pass the vendor into the module as needed,
while ensuring Emotion re-initialization remains triggered only by nonce
changes.

---

Nitpick comments:
In `@packages/react/src/components/primitives/Spinner/Spinner.tsx`:
- Around line 41-45: Update the public API documentation for SpinnerProps to
state that callers should use widthOverride instead of the removed style prop
for custom dimensions. Clarify that spinnerCustomSize already sets both width
and height and takes precedence over size-based styles, anchoring the change to
SpinnerProps and the widthOverride documentation.
🪄 Autofix

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: d08ee384-f24a-4718-b0d2-c4ca6d238a62

📥 Commits

Reviewing files that changed from the base of the PR and between adf1afb and ffeeac4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (79)
  • packages/react/src/components/actions/SignInButton/BaseSignInButton.tsx
  • packages/react/src/components/actions/SignOutButton/BaseSignOutButton.tsx
  • packages/react/src/components/actions/SignUpButton/BaseSignUpButton.tsx
  • packages/react/src/components/adapters/Consent.tsx
  • packages/react/src/components/adapters/ConsentCheckboxList.styles.ts
  • packages/react/src/components/adapters/ConsentCheckboxList.tsx
  • packages/react/src/components/adapters/ImageComponent.tsx
  • packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.styles.ts
  • packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx
  • packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.styles.ts
  • packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.tsx
  • packages/react/src/components/presentation/UserProfile/BaseUserProfile.styles.ts
  • packages/react/src/components/presentation/UserProfile/BaseUserProfile.tsx
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.styles.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/InviteUser/BaseInviteUser.styles.ts
  • packages/react/src/components/presentation/auth/InviteUser/BaseInviteUser.tsx
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.styles.ts
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.styles.ts
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
  • packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.tsx
  • packages/react/src/components/primitives/Alert/Alert.styles.ts
  • packages/react/src/components/primitives/Alert/Alert.tsx
  • packages/react/src/components/primitives/Avatar/Avatar.styles.ts
  • packages/react/src/components/primitives/Avatar/Avatar.tsx
  • packages/react/src/components/primitives/Button/Button.styles.ts
  • packages/react/src/components/primitives/Button/Button.tsx
  • packages/react/src/components/primitives/Card/Card.styles.ts
  • packages/react/src/components/primitives/Card/Card.tsx
  • packages/react/src/components/primitives/Checkbox/Checkbox.styles.ts
  • packages/react/src/components/primitives/Checkbox/Checkbox.tsx
  • packages/react/src/components/primitives/CopyableText/CopyableText.styles.ts
  • packages/react/src/components/primitives/DatePicker/DatePicker.styles.ts
  • packages/react/src/components/primitives/DatePicker/DatePicker.tsx
  • packages/react/src/components/primitives/Dialog/Dialog.styles.ts
  • packages/react/src/components/primitives/Dialog/Dialog.tsx
  • packages/react/src/components/primitives/Divider/Divider.styles.ts
  • packages/react/src/components/primitives/Divider/Divider.tsx
  • packages/react/src/components/primitives/FormControl/FormControl.styles.ts
  • packages/react/src/components/primitives/FormControl/FormControl.tsx
  • packages/react/src/components/primitives/InputLabel/InputLabel.styles.ts
  • packages/react/src/components/primitives/InputLabel/InputLabel.tsx
  • packages/react/src/components/primitives/KeyValueInput/KeyValueInput.styles.ts
  • packages/react/src/components/primitives/KeyValueInput/KeyValueInput.tsx
  • packages/react/src/components/primitives/Logo/Logo.styles.ts
  • packages/react/src/components/primitives/Logo/Logo.tsx
  • packages/react/src/components/primitives/MultiInput/MultiInput.styles.ts
  • packages/react/src/components/primitives/MultiInput/MultiInput.tsx
  • packages/react/src/components/primitives/OtpField/OtpField.styles.ts
  • packages/react/src/components/primitives/OtpField/OtpField.tsx
  • packages/react/src/components/primitives/PasswordField/PasswordField.styles.ts
  • packages/react/src/components/primitives/PasswordField/PasswordField.tsx
  • packages/react/src/components/primitives/Select/Select.styles.ts
  • packages/react/src/components/primitives/Select/Select.tsx
  • packages/react/src/components/primitives/Spinner/Spinner.styles.ts
  • packages/react/src/components/primitives/Spinner/Spinner.tsx
  • packages/react/src/components/primitives/TextField/TextField.styles.ts
  • packages/react/src/components/primitives/TextField/TextField.tsx
  • packages/react/src/components/primitives/Toggle/Toggle.styles.ts
  • packages/react/src/components/primitives/Toggle/Toggle.tsx
  • packages/react/src/components/primitives/Tooltip/Tooltip.styles.ts
  • packages/react/src/components/primitives/Tooltip/Tooltip.tsx
  • packages/react/src/components/primitives/Typography/Typography.styles.ts
  • packages/react/src/components/primitives/Typography/Typography.tsx
  • packages/react/src/contexts/ThunderID/ThunderIDProvider.tsx
  • packages/react/src/contexts/ThunderID/__tests__/ThunderIDProvider.test.tsx
  • packages/react/src/models/config.ts
  • packages/react/src/styles/__tests__/emotion.test.ts
  • packages/react/src/styles/emotion.ts
  • packages/vue/package.json
  • packages/vue/src/__tests__/plugins/ThunderIDPlugin.test.ts
  • packages/vue/src/plugins/ThunderIDPlugin.ts
  • packages/vue/src/styles/__tests__/injectStyles.test.ts
  • packages/vue/src/styles/injectStyles.ts

Comment thread packages/react/src/styles/emotion.ts Outdated
Comment thread packages/react/src/styles/__tests__/emotion.test.ts Outdated
rajithacharith
rajithacharith previously approved these changes Aug 6, 2026
@brionmario
brionmario force-pushed the flexible-vendor-config branch 2 times, most recently from d7e76ba to 21acbc3 Compare August 6, 2026 18:15
- Updated emotion styles imports across multiple components to use a centralized emotion module.
- Introduced `configureEmotionNonce` function to set CSP nonce for injected styles, ensuring compliance with strict Content-Security-Policy.
- Modified `ThunderIDProvider` and `ThunderIDPlugin` to accept and forward `cspNonce` option.
- Enhanced tests to verify correct behavior of nonce configuration and style injection.
- Added new tests for emotion utilities to ensure proper functionality with and without nonce.
@brionmario
brionmario force-pushed the flexible-vendor-config branch from 21acbc3 to 85a036f Compare August 6, 2026 18:26
@brionmario
brionmario merged commit a98fb78 into thunder-id:main Aug 7, 2026
1 check passed
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