Skip to content
This repository was archived by the owner on Mar 19, 2025. It is now read-only.

Commit 85f2eba

Browse files
[DEV-878] Handle disabled state for no first factors (#96)
* fix(DEV-878): handle disabled state for no first factors * Update to v1.0.6-alpha.1 --------- Co-authored-by: Userfront Robot <41898282+github-actions[bot]@users.noreply.github.com>
1 parent b92235a commit 85f2eba

8 files changed

Lines changed: 102 additions & 117 deletions

File tree

package-lock.json

Lines changed: 3 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@userfront/toolkit",
3-
"version": "1.0.6-alpha.0",
3+
"version": "1.0.6-alpha.1",
44
"description": "Bindings and components for authentication with Userfront with React, Vue, other frameworks, and plain JS + HTML",
55
"type": "module",
66
"directories": {

package/src/forms/UniversalForm.jsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,11 @@ const strings = {
154154
},
155155
},
156156
general: {
157+
disabled: "Authentication is disabled",
157158
redirecting: "Redirecting...",
159+
unhandledError: "Oops, something went wrong",
158160
verified: "Verified",
159161
welcome: "Welcome",
160-
unhandledError: "Oops, something went wrong",
161162
},
162163
};
163164

@@ -206,7 +207,14 @@ const componentForStep = (state) => {
206207
Component: Placeholder,
207208
};
208209
}
209-
210+
case "disabled":
211+
return {
212+
title: strings.general.disabled,
213+
Component: GeneralErrorMessage,
214+
props: {
215+
message: "Please contact an administrator for assistance",
216+
},
217+
};
210218
case "selectFirstFactor.showForm":
211219
return {
212220
title: strings[type].title,
@@ -491,7 +499,7 @@ const componentForStep = (state) => {
491499
case "missingFlowInDevModeError":
492500
case "missingFlowInLocalModeError":
493501
case "missingFlowFromServerError":
494-
case "UnhandledError":
502+
case "unhandledError":
495503
return {
496504
title: strings.general.unhandledError,
497505
Component: GeneralErrorMessage,

package/src/models/config/guards.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ import { getUserfrontPropertySync } from "../../services/userfront";
1414

1515
// GUARDS / PREDICATES
1616

17+
const ssoStrategies = [
18+
"apple",
19+
"azure",
20+
"google",
21+
"github",
22+
"twitter",
23+
"facebook",
24+
"linkedin",
25+
"okta",
26+
];
27+
1728
// Is this factor an SSO provider?
18-
export const isSsoProvider = (factor: Factor) => {
19-
return (
20-
factor.channel === "email" &&
21-
(factor.strategy === "apple" ||
22-
factor.strategy === "azure" ||
23-
factor.strategy === "google" ||
24-
factor.strategy === "github" ||
25-
factor.strategy === "twitter" ||
26-
factor.strategy === "facebook" ||
27-
factor.strategy === "linkedin" ||
28-
factor.strategy === "okta")
29-
);
30-
};
29+
export const isSsoProvider = (factor: Factor) =>
30+
factor.channel === "email" && ssoStrategies.includes(factor.strategy);
3131

3232
// Is a second factor required to complete the signup or login?
3333
export const secondFactorRequired = (

package/src/models/forms/universal.ts

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,22 @@ import emailCodeConfig from "../views/emailCode";
3030
import emailLinkConfig from "../views/emailLink";
3131
import setNewPasswordConfig from "../views/setNewPassword";
3232
import {
33-
isSsoProvider,
33+
hasLinkQueryParams,
3434
hasNoActiveFactor,
3535
isLocalMode,
36+
isLocalModeWithoutFlow,
37+
isLoggedIn,
38+
isLoggedInOrHasLinkCredentials,
3639
isMissingFlow,
3740
isMissingFlowFromServer,
38-
isLocalModeWithoutFlow,
3941
isMissingTenantId,
42+
isPasswordReset,
43+
isSecondFactor,
44+
isSetup,
45+
isSsoProvider,
4046
passwordsMatch,
41-
hasLinkQueryParams,
4247
secondFactorRequired,
4348
secondFactorRequiredFromView,
44-
isLoggedIn,
45-
isSecondFactor,
46-
isPasswordReset,
47-
isLoggedInOrHasLinkCredentials,
48-
isSetup,
4949
} from "../config/guards";
5050
import {
5151
setActiveFactor,
@@ -92,7 +92,12 @@ export const defaultOptions = {
9292
// Predicates for first factors:
9393
// Does the flow have multiple first factors?
9494
hasMultipleFirstFactors: (context: AuthContext<any>, event: any) => {
95-
return (context.config.flow?.firstFactors?.length ?? 0) > 1;
95+
const firstFactors = context.config.flow?.firstFactors ?? [];
96+
return firstFactors.length > 1;
97+
},
98+
hasNoFirstFactors: (context: AuthContext<any>) => {
99+
const firstFactors = context.config.flow?.firstFactors ?? [];
100+
return firstFactors.length === 0;
96101
},
97102
// Is the flow only one factor? One predicate per factor type.
98103
// If there's only one allowed factor, allows us to skip the select screen
@@ -186,54 +191,54 @@ export const defaultOptions = {
186191
return isSsoProvider(event.factor);
187192
},
188193

194+
hasLinkQueryParams,
189195
hasNoActiveFactor,
190196
isLocalMode,
197+
isLocalModeWithoutFlow,
198+
isLoggedIn,
199+
isLoggedInOrHasLinkCredentials,
191200
isMissingFlow,
192201
isMissingFlowFromServer,
193-
isLocalModeWithoutFlow,
194202
isMissingTenantId,
203+
isPasswordReset,
204+
isSecondFactor,
205+
isSetup,
195206
passwordsMatch,
196-
hasLinkQueryParams,
197207
secondFactorRequired,
198208
secondFactorRequiredFromView,
199-
isLoggedIn,
200-
isSecondFactor,
201-
isPasswordReset,
202-
isLoggedInOrHasLinkCredentials,
203-
isSetup,
204209
},
205210
actions: {
206-
setActiveFactor,
211+
clearError,
212+
clearResentMessage,
213+
disableBack,
214+
enableBack,
215+
markAsSecondFactor,
216+
markQueryParamsInvalid,
217+
readQueryParams,
218+
redirectIfLoggedIn,
219+
redirectOnLoad,
207220
resumeIfNeeded,
208-
setFlowFromUserfrontApi,
221+
setActiveFactor,
222+
setAllowedSecondFactors,
223+
setAllowedSecondFactorsFromView,
224+
setCode,
209225
setEmail,
226+
setErrorForPasswordMismatch,
227+
setErrorFromApiError,
228+
setFirstFactorAction,
229+
setFlowFromUserfrontApi,
210230
setPassword,
231+
setPasswordForReset,
211232
setPhoneNumber,
233+
setQrCode,
234+
setResentMessage,
235+
setSecondFactorAction,
236+
setShowEmailOrUsernameIfFirstFactor,
212237
setTenantIdIfPresent,
213238
setTotpCode,
239+
setupView,
214240
setUseBackupCode,
215-
setQrCode,
216-
redirectIfLoggedIn,
217-
redirectOnLoad,
218-
setCode,
219-
setErrorFromApiError,
220-
clearError,
221-
setErrorForPasswordMismatch,
222-
setShowEmailOrUsernameIfFirstFactor,
223-
markAsSecondFactor,
224-
setAllowedSecondFactors,
225-
setAllowedSecondFactorsFromView,
226241
storeFactorResponse,
227-
disableBack,
228-
enableBack,
229-
setupView,
230-
readQueryParams,
231-
markQueryParamsInvalid,
232-
setResentMessage,
233-
clearResentMessage,
234-
setFirstFactorAction,
235-
setSecondFactorAction,
236-
setPasswordForReset,
237242
},
238243
};
239244

@@ -526,6 +531,11 @@ const universalMachineConfig: AuthMachineConfig = {
526531
target: "handleLoginWithLink",
527532
cond: "hasLinkQueryParams",
528533
},
534+
// Handle no first factors
535+
{
536+
target: "noFirstFactors",
537+
cond: "hasNoFirstFactors",
538+
},
529539
// If there are multiple first factors, then show the factor selection view
530540
{
531541
actions: "enableBack",
@@ -584,6 +594,22 @@ const universalMachineConfig: AuthMachineConfig = {
584594
],
585595
},
586596

597+
// Show the disabled view if there are no first factors
598+
noFirstFactors: {
599+
id: "noFirstFactors",
600+
always: [
601+
{
602+
target: "disabled",
603+
},
604+
],
605+
},
606+
607+
// Show the disabled error view
608+
disabled: {
609+
id: "disabled",
610+
type: "final",
611+
},
612+
587613
// Show the first factor selection view, non-compact
588614
// Parallel states for the Password view and the rest of the form
589615
selectFirstFactor: selectFactorConfig,

package/src/views/GeneralErrorMessage.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ import ErrorMessage from "../components/ErrorMessage";
88
* @param {object} props
99
* @param {object} error - a Userfront error to display
1010
*/
11-
const GeneralErrorMessage = ({ error }) => {
11+
const GeneralErrorMessage = ({
12+
error,
13+
message = "An unhandled error occurred. Please try again later.",
14+
}) => {
1215
return (
1316
<div>
14-
<p>An unhandled error occurred. Please try again later.</p>
15-
<ErrorMessage error={error} />
17+
{message && <p>{message}</p>}
18+
{error && <ErrorMessage error={error} />}
1619
</div>
1720
);
1821
};

site/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"dependencies": {
1212
"@xstate/inspect": "^0.7.0",
1313
"@xstate/react": "^3.0.1",
14-
"@userfront/toolkit": "^1.0.0-alpha.17",
14+
"@userfront/toolkit": "file:../package",
1515
"lodash.get": "^4.4.2",
1616
"react": "^18.2.0",
1717
"react-dom": "^18.2.0",

0 commit comments

Comments
 (0)