Skip to content

Implement change password functionality in JavaScript, React and Vue - #96

Draft
janithjay wants to merge 1 commit into
thunder-id:mainfrom
janithjay:pwd-change
Draft

Implement change password functionality in JavaScript, React and Vue#96
janithjay wants to merge 1 commit into
thunder-id:mainfrom
janithjay:pwd-change

Conversation

@janithjay

Copy link
Copy Markdown
Contributor

Purpose

There is no SDK component for letting a user change their own password, so every integrator builds the form themselves against POST /users/me/update-credentials and re-implements validation, error handling, theming and i18n.

This adds a ChangePassword component to the React and Vue SDKs, with the shared logic in @thunderid/javascript. It also matches the contract change in thunder-id/thunderid#5290, where the endpoint began requiring the current password.

flowchart TD
    A[Profile page loads] --> B{Does the user type<br/>declare a password?}
    B -->|no| C[Form rendered inert<br/>behind an explanation<br/>nothing is sent]
    B -->|yes| D[Form renders and submits]
    D --> E{Does the account have<br/>a password stored?}
    E -->|no| F["204<br/>first-time set"]
    E -->|yes| G{Is the current<br/>password correct?}
    G -->|yes| H["204"]
    G -->|no| I["403 USR-1029<br/>shown on the field"]

    style C fill:#fbeeec,stroke:#a8453a,color:#a8453a
    style F fill:#e7f3ec,stroke:#2c6a4c,color:#2c6a4c
    style H fill:#e7f3ec,stroke:#2c6a4c,color:#2c6a4c
Loading

Approach

Shared logic sits in @thunderid/javascript so React and Vue cannot drift apart on it, and is re-exported through @thunderid/browser so consumers change no imports.

flowchart TD
    react["@thunderid/react<br/>ChangePassword"] -->|depends on| browser["@thunderid/browser"]
    vue["@thunderid/vue<br/>ChangePassword"] -->|depends on| browser
    browser -->|depends on| core["@thunderid/javascript<br/>policy · form rules · error mapping"]

    style core fill:#e7f3ec,stroke:#2c6a4c,color:#2c6a4c
Loading

Core

  • packages/javascript/src/api/updateMeCredentials.ts - the write. Sends currentPassword only when present, so a first-time set on an account with no password still works.
  • packages/javascript/src/utils/supportsPasswordCredential.ts - reads the user type schema and reports whether password is declared. The server rejects the write otherwise, so this stops the form submitting into a guaranteed failure. An unresolved schema counts as available, so nothing is hidden on missing information.
  • packages/javascript/src/utils/resolveChangePasswordPolicy.ts - takes the rules from the password attribute's regex in the schema. The organization's policy is the only source of truth, the SDK adds no length or character-class rules of its own, which could otherwise reject a password the organization accepts.
  • packages/javascript/src/utils/evaluatePasswordPolicy.ts and evaluateChangePasswordForm.ts - the live requirement checklist and every predicate the form needs, so both components stay pure rendering. isValid deliberately excludes currentPassword, see the note below.
  • packages/javascript/src/utils/mapCredentialUpdateError.ts - decides which field a failure belongs on. 403 is the current password, 400 is the new password failing a server-side rule, anything else is form level.
  • packages/javascript/src/constants/CredentialConstants.ts - the password credential key, previously a string literal in each component.
  • packages/javascript/src/models/config.ts and utils/resolveResourceEndpoint.ts - add a usersMeCredentials endpoint override, following the existing usersMe and usersMeMeta pattern.
  • packages/javascript/src/i18n/ - new keys in models/i18n.ts and all nine locale files.

React and Vue

  • .../ChangePassword/BaseChangePassword.tsx and .../change-password/BaseChangePassword.ts - presentational only. No context, no network calls: render the fields, show the checklist, emit validated values.
  • .../ChangePassword/ChangePassword.tsx and .../change-password/ChangePassword.ts - the context-wired variants that read config from the provider and perform the write.
  • .../ChangePassword/BaseChangePassword.styles.ts and .../change-password/ChangePassword.css.ts - Emotion for React, a CSS string for Vue, matching how presentation components are already built in each package.
  • packages/vue/src/styles/injectStyles.ts - registers the new stylesheet. Missing this leaves the component silently unstyled.
  • .../primitives/PasswordField in both packages - gains an autoComplete prop defaulting to current-password, so the new and confirm fields can set new-password and password managers offer to generate and store a credential rather than filling the existing one.

Two decisions that are not obvious

  • The current password field does not block submission. The server requires it only when the account already has a password, and allows a first-time set otherwise. The client cannot tell those cases apart, so gating on it would lock federated and OTP-only users out of ever setting one. The 403 lands on the field instead, using a translated string rather than the server's message, so the response never reveals whether the account has a password.
  • A type with no password shows an inert form, not nothing. Rendered blurred and disabled behind an explanation, so an integrator who placed the component sees why it is unusable instead of finding an empty space.
image

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.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

Signed-off-by: janithjay <janithjayashan018@gmail.com>
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.

1 participant