Skip to content
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

feat: totp recipe for MFA #763

Merged
merged 93 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
26d1e48
chore: update to mfa branch of web-js
porcellus Oct 12, 2023
db3be18
feat: mfa recipe setup
porcellus Oct 13, 2023
8b339a4
feat: add support for firstFactor
porcellus Oct 13, 2023
02429f8
fix: fix chooseComponentBasedOnFirstFactors to properly get the max c…
porcellus Oct 15, 2023
f3c955e
feat: navigation based on claim validation failures
porcellus Oct 16, 2023
8932a0e
feat: implement factor chooser screen
porcellus Oct 16, 2023
ad99b5f
feat: factor chooser fixes and updates
porcellus Oct 19, 2023
083e908
feat: update pwless to support otp based mfa
porcellus Oct 19, 2023
f7c710b
feat: initial impl of totp mfa
porcellus Oct 22, 2023
9174480
feat: export totp bundle
porcellus Oct 25, 2023
f9b13c3
feat: finish TOTP + test setup WIP
porcellus Oct 26, 2023
9b8e063
feat: finish TOTP + test setup WIP
porcellus Oct 26, 2023
e61dd45
feat: add back button to factor chooser and filter the options
porcellus Oct 30, 2023
1114c60
Merge remote-tracking branch 'origin/feat/mfa/recipe-setup' into feat…
porcellus Oct 30, 2023
b589515
feat: update how back buttons are styled
porcellus Oct 30, 2023
85b1c4d
test: update how back button is selected in tests
porcellus Oct 30, 2023
4b1bd32
Merge remote-tracking branch 'origin/feat/mfa/recipe-setup' into feat…
porcellus Oct 30, 2023
3dd7c52
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Oct 30, 2023
f17de3a
feat: finishing totp initial impl
porcellus Oct 30, 2023
b8b3e3c
test: initial test for MFA
porcellus Oct 30, 2023
a9ce3b5
feat: self-review fixes & updates
porcellus Oct 31, 2023
c62fd92
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Oct 31, 2023
fa3d6d5
feat: self-review fixes&cleanup
porcellus Oct 31, 2023
068d687
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Oct 31, 2023
ca49aee
feat: self-review fixes&cleanup
porcellus Oct 31, 2023
ce5e615
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Oct 31, 2023
c11f185
test: add firstFactors tests
porcellus Oct 31, 2023
acfecc8
tests: complete initial test-set of MFA
porcellus Nov 6, 2023
3deb3cd
fix: firstFactors + test helpers
porcellus Nov 6, 2023
9653c1b
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 6, 2023
8e231ae
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 6, 2023
6b84340
fix: small test based fixes
porcellus Nov 6, 2023
8129ef0
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 6, 2023
1ec993a
feat: finishing touches
porcellus Nov 6, 2023
3f48569
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 6, 2023
7ead9c6
test: update first factor tests to match new behaviour w/ extra checks
porcellus Nov 7, 2023
7355f50
feat: clean up first factor handling
porcellus Nov 7, 2023
e66c584
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 7, 2023
61da851
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 7, 2023
0b2b35e
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 7, 2023
1da8517
feat: add retry info to verifyCode/Device
porcellus Nov 9, 2023
fb89c18
refactor: rename passwordlessFirstFactors since it's not only first f…
porcellus Nov 9, 2023
23e6a46
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 9, 2023
f159c84
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 9, 2023
792316b
feat: self-review fixes
porcellus Nov 9, 2023
f45271c
chore: add mfa ui to size limits
porcellus Nov 9, 2023
1990358
refactor: remove updates that will be added in other PRs + simplifica…
porcellus Nov 9, 2023
666d3fb
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 9, 2023
5ad586e
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 9, 2023
8b44d6b
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 9, 2023
60e0cbb
test: clean up
porcellus Nov 9, 2023
46ae327
chore: increase size limits
porcellus Nov 9, 2023
150c9ad
test: add reset method to mfa recipe
porcellus Nov 9, 2023
b1203cf
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 9, 2023
897f900
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 9, 2023
ba6621e
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 9, 2023
907930e
feat: make sure firstFactor conflicts hit user error boundaries
porcellus Nov 13, 2023
fbd63e4
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 13, 2023
ae7ee82
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 13, 2023
f84d9b8
feat: update design and fix font-weight issues
porcellus Nov 15, 2023
ea768d1
chore: update web-js dep
porcellus Nov 15, 2023
a9b010f
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 15, 2023
598be73
feat: update design
porcellus Nov 15, 2023
a774c13
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 15, 2023
be04faa
feat: update design
porcellus Nov 15, 2023
d1f615f
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 15, 2023
a4c0095
refactor: self-review fixes
porcellus Nov 16, 2023
b397c67
refactor: self-review fixes
porcellus Nov 16, 2023
69a2110
chore: update changelog
porcellus Nov 16, 2023
4ecfc27
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 16, 2023
0d9ea8e
fix: revert unnecessary changes
porcellus Nov 16, 2023
abce472
feat: use redirect info if available on mfa success
porcellus Nov 16, 2023
00ee185
refactor: use redirectToFactor instead of re-implementing
porcellus Nov 16, 2023
7d8f419
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 16, 2023
95bd42f
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 16, 2023
21dee6b
fix: add missing param into redirectToFactorChooser call
porcellus Nov 16, 2023
b520384
feat: self-review fixes
porcellus Nov 17, 2023
b483c83
chore: update changelog
porcellus Nov 17, 2023
51c7c4d
Merge branch 'feat/mfa/totp' into feat/mfa/tests
porcellus Nov 17, 2023
78c2a96
test: update tests for MFA
porcellus Nov 21, 2023
3082073
test: skip mock mfa tests until removal
porcellus Nov 21, 2023
7891b73
fix: override and routing fixes
porcellus Nov 21, 2023
da4ac03
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Nov 21, 2023
8f35657
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Nov 21, 2023
405c419
Merge branch 'feat/mfa/tests' into feat/mfa/totp
porcellus Nov 21, 2023
9d8fae1
feat: allow showing matching sub-recipe components if not on websiteb…
porcellus Dec 6, 2023
c0d686f
Merge branch 'feat/mfa/recipe-setup' into feat/mfa/otp
porcellus Dec 6, 2023
82aaef8
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Dec 6, 2023
fb74a61
Merge remote-tracking branch 'origin/feat/mfa/base' into feat/mfa/otp
porcellus Dec 10, 2023
ec913fb
fix: exposing mfa in tppwless + cleanup/refactor
porcellus Dec 10, 2023
4ce1591
Merge branch 'feat/mfa/otp' into feat/mfa/totp
porcellus Dec 10, 2023
930a5a4
Merge remote-tracking branch 'origin/feat/mfa/base' into feat/mfa/totp
porcellus Dec 10, 2023
c4e3592
refactor: cleanup&renames
porcellus Dec 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ To use this you'll need compatible versions:
- Removed an `ErrorBoundary` wrapping all our feature components to make sure all errors are properly catchable by the app
- Added a `footer` prop to `EmailOrPhoneForm`, `EmailForm` and `PhoneForm` which is used to override the default sign in/up footers in case the component is for MFA
- The passwordless and thirdpartypasswordless sign in/up screens now respect the first configuration (defined in the `MultiFactorAuth` recipe or in the tenant information) when selecting the available contact methods.
- Added TOTP recipe. For more details please check our guide [here](TODO)
- Fixed a font loading issue, that caused apps using the default (Rubik) font to appear with the incorrect font weights
- Some default styling has changed related to how fonts/font-weights are applied

Expand Down
102 changes: 101 additions & 1 deletion examples/for-tests/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyema
import ThirdPartyPasswordless from "supertokens-auth-react/recipe/thirdpartypasswordless";
import UserRoles from "supertokens-auth-react/recipe/userroles";
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth";
import TOTP from "supertokens-auth-react/recipe/totp";

import axios from "axios";
import { useSessionContext } from "supertokens-auth-react/recipe/session";
Expand Down Expand Up @@ -332,7 +333,100 @@ const customFields = [

const testContext = getTestContext();

let storedTOTPDevices = window.localStorage.getItem("totpDevices");
let totpDevices = storedTOTPDevices ? JSON.parse(storedTOTPDevices) : [];

function removeTOTPDevice(deviceName) {
const origLength = totpDevices.length;
totpDevices = totpDevices.filter((d) => d.deviceName !== deviceName);
window.localStorage.setItem("totpDevices", JSON.stringify(totpDevices));
return totpDevices.length !== origLength;
}

function addTOTPDevice(deviceName) {
totpDevices.push({
deviceName,
verified: false,
});
window.localStorage.setItem("totpDevices", JSON.stringify(totpDevices));
}

function verifyTOTPDevice(deviceName) {
totpDevices = totpDevices.filter((d) => d.deviceName !== deviceName);
totpDevices.push({
deviceName,
verified: true,
});
window.localStorage.setItem("totpDevices", JSON.stringify(totpDevices));
}
let tryCount = 0;

setInterval(() => (tryCount = tryCount > 0 ? tryCount - 1 : 0), 30000);
window.resetTOTP = () => {
totpDevices = [];
window.localStorage.setItem("totpDevices", JSON.stringify(totpDevices));
tryCount = 0;
};
let recipeList = [
TOTP.init({
override: {
functions: (oI) => ({
...oI,
listDevices: async () => ({ devices: totpDevices, status: "OK" }),
removeDevice: async ({ deviceName }) => {
return { status: "OK", didDeviceExist: removeTOTPDevice(deviceName) };
},
createDevice: async ({ deviceName }) => {
deviceName = deviceName ?? `totp-${Date.now()}`;
addTOTPDevice(deviceName);
return {
status: "OK",
deviceName: deviceName,
issuerName: "st",
qrCodeString: deviceName,
secret: `secret-${deviceName}`,
};
},
verifyCode: async ({ totp }) => {
const dev = totpDevices.find((d) => d.deviceName.endsWith(totp) && d.verified);
if (dev) {
await fetch("http://localhost:8082/completeFactor", {
method: "POST",
body: JSON.stringify({ id: "totp" }),
headers: new Headers([["Content-Type", "application/json"]]),
});
return { status: "OK" };
}

if (++tryCount > 3) {
return { status: "LIMIT_REACHED_ERROR", retryAfterMs: 30000 };
}
return { status: "INVALID_TOTP_ERROR" };
},
verifyDevice: async ({ deviceName, totp }) => {
const dev = totpDevices.find((d) => d.deviceName === deviceName);
if (!dev) {
return { status: "UNKNOWN_DEVICE_ERROR" };
}
if (deviceName.endsWith(totp)) {
const wasAlreadyVerified = dev.verified;
verifyTOTPDevice(deviceName);
await fetch("http://localhost:8082/completeFactor", {
method: "POST",
body: JSON.stringify({ id: "totp" }),
headers: new Headers([["Content-Type", "application/json"]]),
});
return { status: "OK", wasAlreadyVerified };
}

if (++tryCount > 3) {
return { status: "LIMIT_REACHED_ERROR", retryAfterMs: 30000 };
}
return { status: "INVALID_TOTP_ERROR" };
},
}),
},
}),
MultiFactorAuth.init({
firstFactors: testContext.firstFactors,
}),
Expand Down Expand Up @@ -645,7 +739,13 @@ export function DashboardHelper({ redirectOnLogout, ...props } = {}) {
</div>
<div className="session-context-userId">session context userID: {sessionContext.userId}</div>
<pre className="invalidClaims">{JSON.stringify(sessionContext.invalidClaims, undefined, 2)}</pre>
<a onClick={() => MultiFactorAuth.redirectToFactorChooser(true, props.history)}>MFA chooser</a>
<a
className="goToFactorChooser"
onClick={() => {
return MultiFactorAuth.redirectToFactorChooser(true, props.history);
}}>
MFA chooser
</a>
</div>
);
}
Expand Down
3 changes: 2 additions & 1 deletion examples/for-tests/src/AppWithReactDomRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/email
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";
import { AccessDeniedScreen } from "supertokens-auth-react/recipe/session/prebuiltui";
import { MultiFactorAuthPreBuiltUI } from "supertokens-auth-react/recipe/multifactorauth/prebuiltui";
import { TOTPPreBuiltUI } from "supertokens-auth-react/recipe/totp/prebuiltui";
import { BaseComponent, Home, Contact, Dashboard, DashboardNoAuthRequired } from "./App";
import { getEnabledRecipes } from "./testContext";

Expand All @@ -35,7 +36,7 @@ function AppWithReactDomRouter(props) {
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";
const websiteBasePath = window.localStorage.getItem("websiteBasePath") || undefined;

let recipePreBuiltUIList = [MultiFactorAuthPreBuiltUI];
let recipePreBuiltUIList = [MultiFactorAuthPreBuiltUI, TOTPPreBuiltUI];
if (enabledRecipes.includes("emailpassword")) {
recipePreBuiltUIList.push(EmailPasswordPreBuiltUI);
}
Expand Down
31 changes: 19 additions & 12 deletions examples/for-tests/src/testContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function getTestContext() {
thirdPartyRedirectURL: localStorage.getItem("thirdPartyRedirectURL"),
authRecipe: window.localStorage.getItem("authRecipe") || "emailpassword",
usesDynamicLoginMethods: localStorage.getItem("usesDynamicLoginMethods") === "true",
enableAllRecipes: localStorage.getItem("enableAllRecipes") === "true",
clientRecipeListForDynamicLogin: localStorage.getItem("clientRecipeListForDynamicLogin"),
mockLoginMethodsForDynamicLogin: localStorage.getItem("mockLoginMethodsForDynamicLogin"),
staticProviderList: localStorage.getItem("staticProviderList"),
Expand All @@ -28,18 +29,24 @@ export function getEnabledRecipes() {

let enabledRecipes = [];

if (testContext.usesDynamicLoginMethods) {
if (testContext.clientRecipeListForDynamicLogin) {
enabledRecipes = JSON.parse(testContext.clientRecipeListForDynamicLogin);
} else {
enabledRecipes = [
"emailpassword",
"thirdparty",
"thirdpartyemailpassword",
"passwordless",
"thirdpartypasswordless",
];
}
if (testContext.enableAllRecipes) {
enabledRecipes = [
"emailpassword",
"thirdparty",
"thirdpartyemailpassword",
"passwordless",
"thirdpartypasswordless",
];
} else if (testContext.clientRecipeListForDynamicLogin) {
enabledRecipes = JSON.parse(testContext.clientRecipeListForDynamicLogin);
} else if (testContext.usesDynamicLoginMethods) {
enabledRecipes = [
"emailpassword",
"thirdparty",
"thirdpartyemailpassword",
"passwordless",
"thirdpartypasswordless",
];
} else {
if (testContext.authRecipe === "both") {
enabledRecipes.push("emailpassword", "thirdparty");
Expand Down
2 changes: 2 additions & 0 deletions lib/build/components/assets/blockedIcon.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/build/components/assets/totpIcon.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions lib/build/emailpassword-shared4.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 0 additions & 100 deletions lib/build/emailpassword-shared5.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading