@robosystems/core 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth-components/SignInForm.d.ts +14 -0
- package/auth-components/SignInForm.js +32 -0
- package/auth-core/client.d.ts +10 -4
- package/auth-core/client.js +11 -5
- package/package.json +1 -1
|
@@ -17,6 +17,20 @@ export interface SignInFormProps {
|
|
|
17
17
|
* auth body) stays "Invalid email or password"; 5xx gets its own message.
|
|
18
18
|
*/
|
|
19
19
|
export declare function loginErrorMessage(error: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* Message for a login that verified the password but did not mint a session.
|
|
22
|
+
*
|
|
23
|
+
* The backend interposes a passkey step between password check and session
|
|
24
|
+
* (`mfa_required` for an enrolled user, `mfa_enrollment_required` for a
|
|
25
|
+
* privileged one under enforcement). This form implements no such step, so
|
|
26
|
+
* the flow ends here — and it must end *visibly*. Navigating on a sessionless
|
|
27
|
+
* response drops the user on a protected route that bounces them straight
|
|
28
|
+
* back to the login page, an unexplained loop.
|
|
29
|
+
*
|
|
30
|
+
* Deployments that enable passkeys need a login surface that carries the
|
|
31
|
+
* ceremony through; see the platform app's `MfaChallenge`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function mfaUnsupportedMessage(status?: string): string;
|
|
20
34
|
/**
|
|
21
35
|
* Messages for the `?reason=` a redirect into the login page can carry. The
|
|
22
36
|
* producer is `AuthProvider`'s `logout(reason)`, which sends the user to
|
|
@@ -26,6 +26,25 @@ export function loginErrorMessage(error) {
|
|
|
26
26
|
}
|
|
27
27
|
return 'Invalid email or password';
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Message for a login that verified the password but did not mint a session.
|
|
31
|
+
*
|
|
32
|
+
* The backend interposes a passkey step between password check and session
|
|
33
|
+
* (`mfa_required` for an enrolled user, `mfa_enrollment_required` for a
|
|
34
|
+
* privileged one under enforcement). This form implements no such step, so
|
|
35
|
+
* the flow ends here — and it must end *visibly*. Navigating on a sessionless
|
|
36
|
+
* response drops the user on a protected route that bounces them straight
|
|
37
|
+
* back to the login page, an unexplained loop.
|
|
38
|
+
*
|
|
39
|
+
* Deployments that enable passkeys need a login surface that carries the
|
|
40
|
+
* ceremony through; see the platform app's `MfaChallenge`.
|
|
41
|
+
*/
|
|
42
|
+
export function mfaUnsupportedMessage(status) {
|
|
43
|
+
if (status === 'mfa_enrollment_required') {
|
|
44
|
+
return 'This account must enroll a passkey before signing in, which this sign-in page cannot complete.';
|
|
45
|
+
}
|
|
46
|
+
return 'This account requires passkey verification to sign in, which this sign-in page cannot complete.';
|
|
47
|
+
}
|
|
29
48
|
/**
|
|
30
49
|
* Messages for the `?reason=` a redirect into the login page can carry. The
|
|
31
50
|
* producer is `AuthProvider`'s `logout(reason)`, which sends the user to
|
|
@@ -125,6 +144,19 @@ export function SignInForm({ onSuccess, onRedirect, redirectTo = '/home', classN
|
|
|
125
144
|
setError('');
|
|
126
145
|
try {
|
|
127
146
|
const result = await authClient.login(formData.email, formData.password);
|
|
147
|
+
// No session was minted — the backend is waiting on a passkey step this
|
|
148
|
+
// form cannot run. Stop here rather than redirecting into a route that
|
|
149
|
+
// will bounce straight back. `success` is false only for the MFA
|
|
150
|
+
// statuses; pre-MFA backends send no `status` and read as authenticated.
|
|
151
|
+
if (!result.success) {
|
|
152
|
+
if (process.env.NODE_ENV === 'development') {
|
|
153
|
+
console.error(`[SignInForm] login returned "${result.status}" — this component does not implement the passkey step. ` +
|
|
154
|
+
'Use a sign-in surface that completes the MFA ceremony.');
|
|
155
|
+
}
|
|
156
|
+
setError(mfaUnsupportedMessage(result.status));
|
|
157
|
+
setLoading(false);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
128
160
|
// Call onSuccess callback if provided
|
|
129
161
|
if (onSuccess) {
|
|
130
162
|
onSuccess(result.user);
|
package/auth-core/client.d.ts
CHANGED
|
@@ -87,11 +87,17 @@ export declare class RoboSystemsAuthClient {
|
|
|
87
87
|
/** Passwordless login: assertion → session. */
|
|
88
88
|
completePasskeyLogin(assertion: Record<string, unknown>): Promise<AuthResponse>;
|
|
89
89
|
/**
|
|
90
|
-
* Begin enrollment.
|
|
91
|
-
*
|
|
92
|
-
*
|
|
90
|
+
* Begin enrollment. The settings lane must carry a fresh re-auth proof —
|
|
91
|
+
* `password`, or a reauth-ceremony `assertion` when adding a passkey
|
|
92
|
+
* beside an existing one; a session alone is refused by the backend. The
|
|
93
|
+
* forced-enrollment lane passes only the `mfaToken` from an
|
|
94
|
+
* `mfa_enrollment_required` login, which is its own freshness proof.
|
|
93
95
|
*/
|
|
94
|
-
getPasskeyRegistrationOptions(
|
|
96
|
+
getPasskeyRegistrationOptions(proof?: {
|
|
97
|
+
mfaToken?: string;
|
|
98
|
+
password?: string;
|
|
99
|
+
assertion?: Record<string, unknown>;
|
|
100
|
+
}): Promise<Record<string, unknown>>;
|
|
95
101
|
/**
|
|
96
102
|
* Finish enrollment. First passkey returns recovery codes (once); in the
|
|
97
103
|
* forced-enrollment lane the result carries the completed login and the
|
package/auth-core/client.js
CHANGED
|
@@ -570,14 +570,20 @@ export class RoboSystemsAuthClient {
|
|
|
570
570
|
return this.finalizeAuthResponse(response.data);
|
|
571
571
|
}
|
|
572
572
|
/**
|
|
573
|
-
* Begin enrollment.
|
|
574
|
-
*
|
|
575
|
-
*
|
|
573
|
+
* Begin enrollment. The settings lane must carry a fresh re-auth proof —
|
|
574
|
+
* `password`, or a reauth-ceremony `assertion` when adding a passkey
|
|
575
|
+
* beside an existing one; a session alone is refused by the backend. The
|
|
576
|
+
* forced-enrollment lane passes only the `mfaToken` from an
|
|
577
|
+
* `mfa_enrollment_required` login, which is its own freshness proof.
|
|
576
578
|
*/
|
|
577
|
-
async getPasskeyRegistrationOptions(
|
|
579
|
+
async getPasskeyRegistrationOptions(proof) {
|
|
578
580
|
const response = await getPasskeyRegistrationOptions({
|
|
579
581
|
client: this.client,
|
|
580
|
-
body: {
|
|
582
|
+
body: {
|
|
583
|
+
mfa_token: proof === null || proof === void 0 ? void 0 : proof.mfaToken,
|
|
584
|
+
password: proof === null || proof === void 0 ? void 0 : proof.password,
|
|
585
|
+
assertion: proof === null || proof === void 0 ? void 0 : proof.assertion,
|
|
586
|
+
},
|
|
581
587
|
});
|
|
582
588
|
return response.data.options;
|
|
583
589
|
}
|
package/package.json
CHANGED