diff --git a/.changeset/young-cats-retire.md b/.changeset/young-cats-retire.md new file mode 100644 index 00000000000..988414e7e5b --- /dev/null +++ b/.changeset/young-cats-retire.md @@ -0,0 +1,7 @@ +--- +'@clerk/clerk-js': minor +'@clerk/shared': minor +'@clerk/types': minor +--- + +[Experimental] Signals: Add support for calling `signIn.password()` without an identifier. diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 3c7623a66c5..be58ff8939b 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -503,12 +503,18 @@ class SignInFuture implements SignInFutureResource { submitPassword: this.submitResetPassword.bind(this), }; + fetchStatus: 'idle' | 'fetching' = 'idle'; + constructor(readonly resource: SignIn) {} get status() { return this.resource.status; } + get availableStrategies() { + return this.resource.supportedFirstFactors ?? []; + } + async sendResetPasswordEmailCode(): Promise<{ error: unknown }> { eventBus.emit('resource:error', { resource: this.resource, error: null }); try { @@ -593,12 +599,13 @@ class SignInFuture implements SignInFutureResource { } } - async password({ identifier, password }: { identifier: string; password: string }): Promise<{ error: unknown }> { + async password({ identifier, password }: { identifier?: string; password: string }): Promise<{ error: unknown }> { eventBus.emit('resource:error', { resource: this.resource, error: null }); + const previousIdentifier = this.resource.identifier; try { await this.resource.__internal_basePost({ path: this.resource.pathRoot, - body: { identifier, password }, + body: { identifier: identifier || previousIdentifier, password }, }); } catch (err: unknown) { eventBus.emit('resource:error', { resource: this.resource, error: err }); @@ -688,4 +695,20 @@ class SignInFuture implements SignInFutureResource { return { error: null }; } + + async finalize(): Promise<{ error: unknown }> { + eventBus.emit('resource:error', { resource: this.resource, error: null }); + try { + if (!this.resource.createdSessionId) { + throw new Error('Cannot finalize sign-in without a created session.'); + } + + await SignIn.clerk.setActive({ session: this.resource.createdSessionId }); + } catch (err: unknown) { + eventBus.emit('resource:error', { resource: this.resource, error: err }); + return { error: err }; + } + + return { error: null }; + } } diff --git a/packages/shared/src/error.ts b/packages/shared/src/error.ts index 90f8a4c4807..ee75bf0ba30 100644 --- a/packages/shared/src/error.ts +++ b/packages/shared/src/error.ts @@ -82,7 +82,7 @@ export function isKnownError(error: any): error is ClerkAPIResponseError | Clerk * @internal */ export function isClerkAPIResponseError(err: any): err is ClerkAPIResponseError { - return 'clerkError' in err; + return err && 'clerkError' in err; } /** diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 5948c880da0..7c7dc10b7de 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -126,9 +126,16 @@ export interface SignInResource extends ClerkResource { } export interface SignInFutureResource { + fetchStatus: 'idle' | 'fetching'; + availableStrategies: SignInFirstFactor[]; status: SignInStatus | null; - create: (params: { identifier: string }) => Promise<{ error: unknown }>; - password: (params: { identifier: string; password: string }) => Promise<{ error: unknown }>; + create: (params: { + identifier?: string; + strategy?: OAuthStrategy | 'saml' | 'enterprise_sso'; + redirectUrl?: string; + actionCompleteRedirectUrl?: string; + }) => Promise<{ error: unknown }>; + password: (params: { identifier?: string; password: string }) => Promise<{ error: unknown }>; emailCode: { sendCode: (params: { email: string }) => Promise<{ error: unknown }>; verifyCode: (params: { code: string }) => Promise<{ error: unknown }>; @@ -144,6 +151,7 @@ export interface SignInFutureResource { redirectUrl: string; redirectUrlComplete: string; }) => Promise<{ error: unknown }>; + finalize: () => Promise<{ error: unknown }>; } export type SignInStatus =