-
Notifications
You must be signed in to change notification settings - Fork 453
feat(clerk-js, types): Trigger UserVerification within UserProfile #4127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cc45755
243e846
2b186f6
0bef2f6
ec63553
4d4eeca
1a6446a
374146a
678a2c6
85c5cd4
9ae4a62
f025a26
274ef1d
0b02ad4
68d078a
319fab0
f5099c8
fec40a1
3d7a9c5
4d5a871
063509a
8280af9
df6f3a1
8f6a7d2
8ea72f8
942ae04
3593cf2
7d6f7c5
be1abf5
acd9f53
d52fb92
f7dad10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| --- | ||
| "@clerk/clerk-js": minor | ||
| "@clerk/types": minor | ||
| --- | ||
|
|
||
| *Experimental Feature*: `<UserProfile/>` allows users to update their information. Mostly of this information is considered sensitive data. | ||
| We want to ensure that only the users themselves can alter any sensitive data. | ||
|
|
||
| To increase security we are now, require users to re-verify their credentials when they are about to perform these actions: | ||
|
|
||
|
|
||
| | Operation | Reverification | Strategy | Timeframe | | ||
| | --- |----------------| --- | --- | | ||
| | Update account (first/last name) | ❌ | | | | ||
| | Update username | ✅ | Strongest available | 10m | | ||
| | Delete account | ✅ | Strongest available | 10m | | ||
| | Create/Remove profile image | ❌ | | | | ||
| | Update password | ✅ | Strongest available | 10m | | ||
| | Remove password | ❌ | | | | ||
| | Revoke session | ✅ | Strongest available | 10m | | ||
| | Create identification | ✅ | Strongest available | 10m | | ||
| | Remove identification | ✅ | Strongest available | 10m | | ||
| | Change primary identification | ✅ | Strongest available | 10m | | ||
| | Update Passkey name | ❌ | | | | ||
| | Enable MFA (TOTP, Phone number) | ✅ | Strongest available | 10m | | ||
| | Disable MFA (TOΤP, Phone number) | ✅ | Strongest available | 10m | | ||
| | Create/Regenerate Backup Codes | ✅ | Strongest available | 10m | | ||
| | Connect External Account | ✅ | Strongest available | 10m | | ||
| | Re-authorize External Account | ❌ | | | | ||
| | Remove External Account | ✅ | Strongest available | 10m | | ||
| | Leave organization | ❌ | | | | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,9 @@ export type ComponentControls = { | |
| | 'organizationProfile' | ||
| | 'createOrganization' | ||
| | 'userVerification', | ||
| options?: { | ||
| notify?: boolean; | ||
| }, | ||
|
Comment on lines
+87
to
+89
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only internal |
||
| ) => void; | ||
| // Special case, as the impersonation fab mounts automatically | ||
| mountImpersonationFab: () => void; | ||
|
|
@@ -245,13 +248,53 @@ const Components = (props: ComponentsProps) => { | |
| setState(s => ({ ...s, ...restProps, options: { ...s.options, ...restProps.options } })); | ||
| }; | ||
|
|
||
| componentsControls.closeModal = name => { | ||
| componentsControls.closeModal = (name, options = {}) => { | ||
| const { notify = true } = options; | ||
| clearUrlStateParam(); | ||
| setState(s => ({ ...s, [name + 'Modal']: null })); | ||
| setState(s => { | ||
| function handleCloseModalForExperimentalUserVerification() { | ||
| const modal = s[`${name}Modal`] || {}; | ||
| if ('afterVerificationCancelled' in modal && notify) { | ||
| modal.afterVerificationCancelled?.(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * We need this in order for `Clerk.__experimental_closeUserVerification()` | ||
| * to properly trigger the previously defined `afterVerificationCancelled` callback | ||
| */ | ||
| handleCloseModalForExperimentalUserVerification(); | ||
|
|
||
| return { ...s, [`${name}Modal`]: null }; | ||
| }); | ||
| }; | ||
|
|
||
| componentsControls.openModal = (name, props) => { | ||
| setState(s => ({ ...s, [name + 'Modal']: props })); | ||
| function handleCloseModalForExperimentalUserVerification() { | ||
| if (!('afterVerificationCancelled' in props)) { | ||
| return; | ||
| } | ||
|
|
||
| setState(s => ({ | ||
| ...s, | ||
| [`${name}Modal`]: { | ||
| ...props, | ||
| /** | ||
| * When a UserVerification flow is completed, we need to close the modal without trigger a cancellation callback | ||
| */ | ||
| afterVerification() { | ||
| props.afterVerification?.(); | ||
| componentsControls.closeModal(name, { notify: false }); | ||
| }, | ||
| }, | ||
| })); | ||
| } | ||
|
|
||
| if ('afterVerificationCancelled' in props) { | ||
| handleCloseModalForExperimentalUserVerification(); | ||
| } else { | ||
| setState(s => ({ ...s, [`${name}Modal`]: props })); | ||
| } | ||
| }; | ||
|
|
||
| componentsControls.mountImpersonationFab = () => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,8 @@ export const AccountPage = withCardStateProvider(() => { | |
| {showEmail && <EmailsSection shouldAllowCreation={shouldAllowIdentificationCreation} />} | ||
| {showPhone && <PhoneSection shouldAllowCreation={shouldAllowIdentificationCreation} />} | ||
| {showConnectedAccounts && <ConnectedAccountsSection shouldAllowCreation={shouldAllowIdentificationCreation} />} | ||
|
|
||
| {/*TODO-STEP-UP: Verify that these work as expected*/} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these verified they work? 😄
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, although thorough testing will still need to be done. E2E tests will also be introduced before this goes to public beta |
||
| {showSamlAccounts && <EnterpriseAccountsSection />} | ||
| {showWeb3 && <Web3Section shouldAllowCreation={shouldAllowIdentificationCreation} />} | ||
| </Col> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import type { SessionWithActivitiesResource } from '@clerk/types'; | |
| import { Badge, Col, descriptors, Flex, Icon, localizationKeys, Text, useLocalizations } from '../../customizables'; | ||
| import { FullHeightLoader, ProfileSection, ThreeDotsMenu } from '../../elements'; | ||
| import { useFetch, useLoadingStatus } from '../../hooks'; | ||
| import { useAssurance } from '../../hooks/useAssurance'; | ||
| import { DeviceLaptop, DeviceMobile } from '../../icons'; | ||
| import { mqu, type PropsOfComponent } from '../../styledSystem'; | ||
| import { getRelativeToNowDateKey } from '../../utils'; | ||
|
|
@@ -48,12 +49,19 @@ export const ActiveDevicesSection = () => { | |
| const DeviceItem = ({ session }: { session: SessionWithActivitiesResource }) => { | ||
| const isCurrent = useSession().session?.id === session.id; | ||
| const status = useLoadingStatus(); | ||
| const { handleAssurance } = useAssurance(); | ||
|
|
||
| const revoke = async () => { | ||
| if (isCurrent || !session) { | ||
| return; | ||
| } | ||
| status.setLoading(); | ||
| return session.revoke().finally(() => status.setIdle()); | ||
| return ( | ||
| handleAssurance(() => session.revoke()) | ||
| // TODO-STEPUP: Properly handler the response with a setCardError | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @panteliselef is this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually this is not step-up specific, just something that i discovered (for later) |
||
| .catch(() => {}) | ||
| .finally(() => status.setIdle()) | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this table also going into our docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually, yes we currently have it in our DX gudie