Skip to content

Introduce vendor namespace support - #7

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

Introduce vendor namespace support#7
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:flexible-vendor-config

Conversation

@brionmario

@brionmario brionmario commented Jul 10, 2026

Copy link
Copy Markdown
Member

Purpose

ThunderID is hardcoded in some SDK components, making it hard for adopters to white-label the SDK. Mirrors the approach landed in thunder-id/javascript-sdks#24.

Approach

  • Added a vendor option to ThunderIDConfig (defaults to VendorConstants.vendorPrefix, "thunderid").
  • ThunderIDClient now derives the default Keychain service name from config.vendor instead of a hardcoded string.
  • thunderIDProvider(config:i18n:) now resolves the i18n locale storage key from config.vendor when no explicit ThunderIDI18n is supplied.
  • Added vendor-naming guidance to AGENTS.md so future changes keep runtime keys/names vendor-aware.

Related Issues

Checklist

  • swift build passes.
  • swiftlint lint --strict passes with 0 violations.

Summary by CodeRabbit

  • New Features
    • Added vendor configuration to customize SDK branding and namespace behavior.
    • Default storage identifiers now incorporate the configured vendor.
    • SwiftUI provider setup now automatically derives a vendor-specific localization storage key.
    • Added a public default vendor constant for simpler configuration.
  • Documentation
    • Added guidance for using vendor names consistently in runtime and storage identifiers.
  • Chores
    • Improved automated review configuration and excluded build artifacts from reviews.

Adds a `vendor` option to ThunderIDConfig so adopters can white-label
the SDK's runtime identifiers (Keychain service name, i18n locale
storage key) instead of being pinned to the `thunderid` brand prefix.

Related: thunder-id/thunderid#3896
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

📝 Walkthrough

Walkthrough

Changes

Vendor-scoped identifiers

Layer / File(s) Summary
Vendor configuration contract
Sources/ThunderID/VendorConstants.swift, Sources/ThunderID/ThunderIDConfig.swift
Adds VendorConstants.vendorPrefix and the public ThunderIDConfig.vendor property with a default value.
Vendor-derived identifier wiring
Sources/ThunderID/ThunderIDClient.swift, Sources/ThunderIDSwiftUI/environment/ThunderIDViewModifier.swift
Derives default keychain service and localization storage identifiers from the configured vendor.
Vendor naming guardrails
AGENTS.md, .coderabbit.yaml
Documents vendor naming rules and configures review checks for hardcoded vendor literals in Swift runtime keys and names.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ThunderIDConfig
  participant ThunderIDClient
  participant KeychainStorageAdapter
  participant ThunderIDViewModifier
  ThunderIDConfig->>ThunderIDClient: configured vendor
  ThunderIDClient->>KeychainStorageAdapter: service "dev.<vendor>.sdk"
  ThunderIDConfig->>ThunderIDViewModifier: configured vendor
  ThunderIDViewModifier->>ThunderIDViewModifier: resolve "<vendor>_locale"
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding vendor namespace support.
Description check ✅ Passed The description covers Purpose, Approach, Issues, and Checklist well enough, with only minor template sections omitted.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Sources/ThunderID/ThunderIDClient.swift (2)

65-65: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

reInitialize silently resets vendor when baseUrl is provided.

Line 65 creates a new ThunderIDConfig with only baseUrl and clientId, causing vendor (and all other fields) to fall back to defaults. After re-initialization, the SDK reads tokens from "dev.thunderid.sdk" instead of the custom vendor's keychain namespace, silently losing access to existing tokens for white-label customers.

This is a pre-existing bug that loses all config fields, but the addition of vendor makes it a data-integrity hazard for the white-labeling use case this PR introduces.

🛡️ Proposed fix: preserve vendor in reInitialize
- if let baseUrl { current = ThunderIDConfig(baseUrl: baseUrl, clientId: current.clientId) }
+ if let baseUrl {
+     current = ThunderIDConfig(
+         baseUrl: baseUrl,
+         clientId: current.clientId,
+         vendor: current.vendor
+     )
+ }

Note: ideally all relevant config fields should be preserved, not just vendor.

🤖 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 `@Sources/ThunderID/ThunderIDClient.swift` at line 65, Update reInitialize to
preserve the existing ThunderIDConfig fields when replacing baseUrl, especially
vendor, rather than constructing a config with only baseUrl and clientId. Use
the configuration update/copy mechanism in reInitialize so all relevant values,
including the custom keychain namespace, remain unchanged.

331-338: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add validation for empty vendor in validateConfig.

An empty vendor produces malformed identifiers: keychain service "dev..sdk" and locale storage key "_locale". A simple guard prevents this edge case.

🛡️ Proposed fix
 private func validateConfig(_ config: ThunderIDConfig) throws {
     guard !config.baseUrl.isEmpty else {
         throw ThunderIDError(code: .invalidConfiguration, message: "baseUrl is required")
     }
     guard config.baseUrl.hasPrefix("https://") else {
         throw ThunderIDError(code: .invalidConfiguration, message: "baseUrl must use HTTPS")
     }
+    guard !config.vendor.isEmpty else {
+        throw ThunderIDError(code: .invalidConfiguration, message: "vendor is required")
+    }
 }
🤖 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 `@Sources/ThunderID/ThunderIDClient.swift` around lines 331 - 338, Add a guard
in validateConfig(_:) to reject an empty config.vendor value, throwing
ThunderIDError with code .invalidConfiguration and a clear message such as
"vendor is required", alongside the existing baseUrl validation.
🧹 Nitpick comments (1)
Sources/ThunderID/VendorConstants.swift (1)

22-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting vendor-derived identifier helpers.

Vendor-derived identifier construction is now repeated across ThunderIDClient.swift ("dev.\(config.vendor).sdk") and ThunderIDViewModifier.swift ("\(config.vendor)_locale"). Adding static helpers here would centralize the format strings, prevent divergence, and allow KeychainStorageAdapter's default to resolve via VendorConstants instead of hardcoding "dev.thunderid.sdk".

As per coding guidelines: "Extract shared helpers when vendor default-resolution logic is repeated."

♻️ Proposed helpers
 public enum VendorConstants {
     /// The prefix used for vendor-specific storage identifiers (e.g. Keychain service name), or other
     /// runtime keys/names.
     public static let vendorPrefix: String = "thunderid"
+
+    /// Default Keychain service identifier for a given vendor.
+    public static func keychainService(for vendor: String) -> String {
+        "dev.\(vendor).sdk"
+    }
+
+    /// Default locale storage key for a given vendor.
+    public static func localeStorageKey(for vendor: String) -> String {
+        "\(vendor)_locale"
+    }
 }
🤖 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 `@Sources/ThunderID/VendorConstants.swift` around lines 22 - 26, Centralize
vendor-derived identifier formatting in VendorConstants by adding static helpers
for the SDK keychain service identifier and locale key, then replace the
duplicated "dev.\(config.vendor).sdk" and "\(config.vendor)_locale"
constructions in ThunderIDClient and ThunderIDViewModifier with those helpers.
Update KeychainStorageAdapter’s default service identifier to use the helper
instead of hardcoding "dev.thunderid.sdk".

Source: Coding guidelines

🤖 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 `@Sources/ThunderIDSwiftUI/environment/ThunderIDViewModifier.swift`:
- Around line 48-50: Preserve existing locale preferences when the default
storage key changes. Update ThunderIDI18n.init to use the new key first, fall
back to the legacy "thunder_locale" value when no new-key value exists, and
migrate that value to the new key; ensure thunderIDProvider continues passing
the vendor-based key through ThunderIDProviderModifier.

---

Outside diff comments:
In `@Sources/ThunderID/ThunderIDClient.swift`:
- Line 65: Update reInitialize to preserve the existing ThunderIDConfig fields
when replacing baseUrl, especially vendor, rather than constructing a config
with only baseUrl and clientId. Use the configuration update/copy mechanism in
reInitialize so all relevant values, including the custom keychain namespace,
remain unchanged.
- Around line 331-338: Add a guard in validateConfig(_:) to reject an empty
config.vendor value, throwing ThunderIDError with code .invalidConfiguration and
a clear message such as "vendor is required", alongside the existing baseUrl
validation.

---

Nitpick comments:
In `@Sources/ThunderID/VendorConstants.swift`:
- Around line 22-26: Centralize vendor-derived identifier formatting in
VendorConstants by adding static helpers for the SDK keychain service identifier
and locale key, then replace the duplicated "dev.\(config.vendor).sdk" and
"\(config.vendor)_locale" constructions in ThunderIDClient and
ThunderIDViewModifier with those helpers. Update KeychainStorageAdapter’s
default service identifier to use the helper instead of hardcoding
"dev.thunderid.sdk".
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eb06a02e-b3ef-4c71-b9a5-5c61c9356812

📥 Commits

Reviewing files that changed from the base of the PR and between d00a341 and 2c5e0c6.

📒 Files selected for processing (6)
  • .coderabbit.yaml
  • AGENTS.md
  • Sources/ThunderID/ThunderIDClient.swift
  • Sources/ThunderID/ThunderIDConfig.swift
  • Sources/ThunderID/VendorConstants.swift
  • Sources/ThunderIDSwiftUI/environment/ThunderIDViewModifier.swift

Comment on lines +48 to +50
func thunderIDProvider(config: ThunderIDConfig, i18n: ThunderIDI18n? = nil) -> some View {
let resolvedI18n = i18n ?? ThunderIDI18n(storageKey: "\(config.vendor)_locale")
return modifier(ThunderIDProviderModifier(config: config, i18n: resolvedI18n))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Default i18n storage key changes from "thunder_locale" to "thunderid_locale", silently losing existing locale preferences.

The previous default ThunderIDI18n() used storageKey: "thunder_locale". The new default ThunderIDI18n(storageKey: "\(config.vendor)_locale") produces "thunderid_locale" for the default vendor. Existing users upgrading will have their stored locale preference orphaned under the old key, silently reverting to the fallback locale.

Consider a migration fallback in ThunderIDI18n.init — if the new key has no stored value, read from the old key and migrate.

🛡️ Proposed migration fallback in ThunderIDI18n.init
 public init(
     bundles: [String: [String: String]] = [:],
     language: String? = nil,
     fallbackLanguage: String = "en-US",
     storageKey: String = "thunder_locale"
 ) {
     self.bundles = bundles
     self.fallbackLocale = fallbackLanguage
     self.storageKey = storageKey
-    let stored = UserDefaults.standard.string(forKey: storageKey)
+    let defaults = UserDefaults.standard
+    var stored = defaults.string(forKey: storageKey)
+    if stored == nil, storageKey != "thunder_locale" {
+        stored = defaults.string(forKey: "thunder_locale")
+        if let stored { defaults.set(stored, forKey: storageKey) }
+    }
     self.activeLocale = language ?? stored ?? fallbackLanguage
 }
🤖 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 `@Sources/ThunderIDSwiftUI/environment/ThunderIDViewModifier.swift` around
lines 48 - 50, Preserve existing locale preferences when the default storage key
changes. Update ThunderIDI18n.init to use the new key first, fall back to the
legacy "thunder_locale" value when no new-key value exists, and migrate that
value to the new key; ensure thunderIDProvider continues passing the
vendor-based key through ThunderIDProviderModifier.

@brionmario
brionmario merged commit 70417ee into thunder-id:main Jul 11, 2026
5 checks 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