Skip to content

Implement <ChangeCredential /> component across JavaScript React and Vue SDKs - #100

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
janithjay:update-cred-new-password-only
Sep 14, 2026
Merged

brionmario merged 1 commit into
thunder-id:mainfrom
janithjay:update-cred-new-password-only

Conversation

@janithjay

@janithjay janithjay commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Purpose

There was no way for a signed-in user to update their own credentials from any ThunderID SDK. BaseUserProfile deliberately filters credential attributes out of the profile form, so password was unreachable from any SDK component, even though the backend already supports POST /users/me/update-credentials.

This PR adds a ChangeCredential component to @thunderid/javascript, @thunderid/react, and @thunderid/vue, and wires it into a real "My Account" page (Personal info + Security) in all three quickstart samples - React, Vue, and the vanilla browser sample.

Approach

Core

  • updateMeCredentials - POST /users/me/update-credentials, payload wrapped under attributes. Returns void (204 No Content).
  • evaluatePasswordPolicy / resolveChangeCredentialPolicy / evaluateChangePasswordForm - client-side validation driven entirely by the schema's own regex, no hardcoded rules. One function (evaluateChangePasswordForm) feeds both the checklist and the submit gate so they can't disagree.
  • supportsCredential - true unless the schema is known and the attribute isn't marked credential: true.
  • mapCredentialUpdateError, CredentialConstants.PASSWORD - error-to-field mapping and the default credential key.
  • New user.change_password.* / validation.password.pattern strings across all 9 locales.

React / Vue

  • ChangeCredential (container) / BaseChangeCredential (presentational) - same split as UserProfile/BaseUserProfile. Two fields (new + confirm), no current-password field (server doesn't verify one yet).
  • Display name comes from the attribute's own schema displayName, falling back to title-case. attribute prop selects which credential (defaults to password); render once per credential to manage more than one.
  • Unavailable credential (not in schema) → form renders blurred/disabled behind a "contact your administrator" overlay, not hidden.
  • UserDropdown can now redirect its profile action to a custom route (onManageProfile + relabelable menu item) instead of only opening the built-in popup; Vue gained showTriggerLabel, matching React.
  • updateMeCredentials's React/Vue wrappers now share one adapter (createHttpClientFetcher, moved into @thunderid/browser) instead of duplicating it.

Samples

  • All three quickstarts get an Account page (Home / Personal info / Security) replacing the old "Manage Profile" popup. Security shows one row per credential that expands into the real ChangeCredential form.
  • Browser sample has no prebuilt components, so it's hand-built against @thunderid/browser's core functions directly (including evaluateChangePasswordForm). Unavailable state uses inert (not just CSS) so it's actually unreachable by keyboard; form fields have proper label/id associations.
  • React/Vue samples intentionally show password only - attribute="pin" works and is documented but isn't demoed in the quickstart itself.

New interface

image image image image

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided.
  • 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.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: a55630bf-1325-4a6a-b471-7907993ed961


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.

@janithjay
janithjay marked this pull request as ready for review September 14, 2026 05:24
Copilot AI lite review requested due to automatic review settings September 14, 2026 05:24
@janithjay
janithjay force-pushed the update-cred-new-password-only branch from d6c589c to ee6c563 Compare September 14, 2026 05:25

Copilot AI 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.

🟡 Changes recommended

Unresolved issues remain in credential handling, Vue i18n overrides, and sample behavior and accessibility.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds self-service credential updates across the JavaScript, React, and Vue SDKs, with account-page integrations in all quickstart samples.

Changes:

  • Adds credential APIs, validation, error mapping, localization, and tests.
  • Introduces React/Vue ChangeCredential components and dropdown enhancements.
  • Replaces sample profile popups with account pages and security forms.
File summaries
File Description
samples/vue/quickstart/src/style.css Vue account-page styling
samples/vue/quickstart/src/pages/AccountPage.vue Vue account page
samples/vue/quickstart/src/components/Nav.vue Vue account navigation
samples/vue/quickstart/src/App.vue Vue route integration
samples/react/quickstart/src/pages/AccountPage.jsx React account page
samples/react/quickstart/src/components/Nav.jsx React account navigation
samples/react/quickstart/src/App.jsx React route integration
samples/react/quickstart/src/App.css React account styling
samples/browser/quickstart/src/style.css Browser account styling
samples/browser/quickstart/src/pages/account.js Browser account page
samples/browser/quickstart/src/main.js Browser page mounting
samples/browser/quickstart/src/components/profileFields.js Profile field helpers
samples/browser/quickstart/src/components/nav.js Browser account navigation
packages/vue/src/styles/injectStyles.ts Vue style registration
packages/vue/src/index.ts Vue exports
packages/vue/src/components/primitives/PasswordField/PasswordField.ts Vue password autocomplete
packages/vue/src/components/presentation/user-dropdown/UserDropdown.ts Vue dropdown API wiring
packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts Vue dropdown styling
packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts Vue dropdown rendering
packages/vue/src/components/presentation/change-credential/ChangeCredential.ts Vue credential container
packages/vue/src/components/presentation/change-credential/ChangeCredential.css.ts Vue credential styles
packages/vue/src/components/presentation/change-credential/BaseChangeCredential.ts Vue credential form
packages/vue/src/api/updateMeCredentials.ts Vue credential API wrapper
packages/vue/src/__tests__/components/change-credential.test.ts Vue component tests
packages/vue/src/__tests__/api/update-me-credentials.test.ts Vue API tests
packages/react/src/index.ts React exports
packages/react/src/components/primitives/PasswordField/PasswordField.tsx React password autocomplete
packages/react/src/components/presentation/UserDropdown/UserDropdown.tsx React dropdown API wiring
packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.tsx React dropdown rendering
packages/react/src/components/presentation/ChangeCredential/ChangeCredential.tsx React credential container
packages/react/src/components/presentation/ChangeCredential/BaseChangeCredential.tsx React credential form
packages/react/src/components/presentation/ChangeCredential/BaseChangeCredential.styles.ts React credential styles
packages/react/src/components/presentation/ChangeCredential/__tests__/ChangeCredential.test.tsx React component tests
packages/react/src/api/updateMeCredentials.ts React credential API wrapper
packages/javascript/src/utils/supportsCredential.ts Credential availability detection
packages/javascript/src/utils/resolveResourceEndpoint.ts Credential endpoint resolution
packages/javascript/src/utils/resolveChangeCredentialPolicy.ts Policy resolution
packages/javascript/src/utils/mapCredentialUpdateError.ts Credential error mapping
packages/javascript/src/utils/evaluatePasswordPolicy.ts Password policy evaluation
packages/javascript/src/utils/evaluateChangePasswordForm.ts Form evaluation
packages/javascript/src/utils/__tests__/supportsCredential.test.ts Availability tests
packages/javascript/src/utils/__tests__/resolveResourceEndpoint.test.ts Endpoint tests
packages/javascript/src/utils/__tests__/resolveChangeCredentialPolicy.test.ts Policy tests
packages/javascript/src/utils/__tests__/mapCredentialUpdateError.test.ts Error-mapping tests
packages/javascript/src/utils/__tests__/evaluatePasswordPolicy.test.ts Password policy tests
packages/javascript/src/utils/__tests__/evaluateChangePasswordForm.test.ts Form evaluation tests
packages/javascript/src/models/config.ts Endpoint configuration
packages/javascript/src/index.ts Core exports
packages/javascript/src/i18n/translations/te-IN.ts Telugu translations
packages/javascript/src/i18n/translations/ta-IN.ts Tamil translations
packages/javascript/src/i18n/translations/si-LK.ts Sinhala translations
packages/javascript/src/i18n/translations/pt-PT.ts Portuguese translations
packages/javascript/src/i18n/translations/pt-BR.ts Brazilian Portuguese translations
packages/javascript/src/i18n/translations/ja-JP.ts Japanese translations
packages/javascript/src/i18n/translations/hi-IN.ts Hindi translations
packages/javascript/src/i18n/translations/fr-FR.ts French translations
packages/javascript/src/i18n/translations/en-US.ts English translations
packages/javascript/src/i18n/models/i18n.ts Translation type declarations
packages/javascript/src/constants/CredentialConstants.ts Credential constants
packages/javascript/src/api/updateMeCredentials.ts Core credential API
packages/javascript/src/api/__tests__/updateMeCredentials.test.ts Core API tests
Review details

Suppressed comments (11)

packages/javascript/src/constants/CredentialConstants.ts:14

  • This example has the same payload-shape mismatch as the public API: credential values must be plain strings, not {newValue: ...} objects, because updateMeCredentials performs the attributes wrapping. Consumers copying this snippet would send the wrong shape.
 *   payload: {[CredentialConstants.PASSWORD]: {newValue: newPassword}},

packages/javascript/src/utils/mapCredentialUpdateError.ts:64

  • When a 400/other API response has an empty body, ThunderIDAPIError.message is an empty string. Returning that value makes the React/Vue callers' message ?? t(messageKey, ...) choose the empty string and render no error, even though messageKey is intended as the generic fallback. Normalize empty messages to undefined in both ThunderIDError branches so the fallback translation is reachable.
    return {field: 'newPassword', message: error.message, messageKey: GENERIC_MESSAGE_KEY};

packages/react/src/api/updateMeCredentials.ts:19

  • The default transport is FetchHttpClient, which uses the native Fetch API, not an axios http.request wrapper. This public configuration description is misleading for consumers deciding whether to provide a custom fetcher.
   * which is a wrapper around axios http.request

packages/vue/src/api/updateMeCredentials.ts:27

  • This new FetchHttpClient-to-Response adapter duplicates the React implementation line-for-line. Since both framework SDKs depend on @thunderid/browser, the conversion and error handling should live in that shared layer rather than being maintained independently in Vue and React.
    packages/vue/src/components/presentation/change-credential/BaseChangeCredential.ts:115
  • BaseChangeCredential accepts preferences, but its fallback translator is always the unconfigured useI18n() result and never reads props.preferences. A standalone base component using preferences.i18n therefore silently ignores the override. Implement preference-aware translation for this headless variant or remove the prop and its promise.
    packages/vue/src/components/presentation/change-credential/ChangeCredential.ts:85
  • The merged component preferences do not affect Vue translations: t is created from useI18n() without resolvedPreferences and is then passed to the base form. Consequently preferences.i18n cannot override any label or error message, unlike the React component. Resolve a preference-aware translator here, or remove the advertised per-component i18n override.
    samples/browser/quickstart/src/pages/account.js:92
  • Both visibility buttons are labelled Show password even for the PIN credential, and the label is never changed to Hide ... after toggling. The PIN form therefore exposes an incorrect accessible name and the current visibility state is not announced; derive the label from the credential and update it when the input type changes.
    samples/browser/quickstart/src/pages/account.js:109
  • Unlike the React/Vue forms, these fields are not inside a <form> and the submit control is type="button". Pressing Enter from either credential input therefore does nothing, which prevents keyboard users from submitting the account form. Use a form submit handler (or explicitly handle Enter) rather than relying only on the click listener.
    samples/react/quickstart/src/App.css:722
  • This selector hardcodes the default thunderid- vendor prefix, but UserProfile builds its root class through withVendorCSSClassPrefix. If a consuming app overrides vendor, the selector stops matching and the Account page regains the extra profile styling. Target an app-owned class passed through UserProfile (or otherwise avoid hardcoding the SDK vendor prefix).
    samples/react/quickstart/src/pages/AccountPage.jsx:92
  • The tab state is initialized from requestedTab only on the first render. Browser back/forward navigation or another query-string change re-renders this component but leaves the local tab unchanged, so the URL and visible tab can disagree. Use the current search parameter as the source of truth or synchronize the state when it changes.
    samples/vue/quickstart/src/style.css:957
  • This selector has the same vendor-prefix problem as the React sample: it only matches the default thunderid-user-profile class, while the component's class prefix follows the configured vendor. With a vendor override the card neutralization silently stops working; use an app-owned class instead of a hardcoded SDK prefix.
  • Files reviewed: 61/61 changed files
  • Comments generated: 11
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/javascript/src/utils/resolveResourceEndpoint.ts
Comment thread packages/javascript/src/utils/supportsCredential.ts Outdated
Comment thread samples/browser/quickstart/src/pages/account.js
Comment thread samples/browser/quickstart/src/pages/account.js Outdated
Comment thread samples/react/quickstart/src/pages/AccountPage.jsx Outdated
Comment thread packages/javascript/src/constants/CredentialConstants.ts Outdated
Comment thread packages/react/src/api/updateMeCredentials.ts Outdated
Comment thread packages/react/src/api/updateMeCredentials.ts Outdated
Comment thread samples/browser/quickstart/src/pages/account.js Outdated
Comment thread samples/browser/quickstart/src/pages/account.js
@janithjay
janithjay force-pushed the update-cred-new-password-only branch 5 times, most recently from 1124da3 to 013b3f2 Compare September 14, 2026 08:41
@brionmario

brionmario commented Sep 14, 2026

Copy link
Copy Markdown
Member

Does this PR adds new E2E's to make sure that the credential reset feature is working as expected?
Also lets make sure that we add the API docs for this feature and link the PR here.

@janithjay
janithjay force-pushed the update-cred-new-password-only branch from 013b3f2 to 7d63abb Compare September 14, 2026 09:10
…SDKs

Signed-off-by: janithjay <janithjayashan018@gmail.com>
@janithjay

Copy link
Copy Markdown
Contributor Author

Does this PR adds new E2E's to make sure that the credential reset feature is working as expected? Also lets make sure that we add the API docs for this feature and link the PR here.

Update: E2E tests and API docs were added and linked to this PR.

@brionmario
brionmario merged commit d71441f into thunder-id:main Sep 14, 2026
7 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.

3 participants