seamless-cli 0.2.0 → 0.5.0
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/README.md +5 -6
- package/dist/commands/help.js +18 -1
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.js +104 -21
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/verify.js +259 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/bootstrapSecret.js +12 -4
- package/dist/core/bootstrapSecret.js.map +1 -1
- package/dist/core/exec.js +2 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/fetch.js +2 -1
- package/dist/core/fetch.js.map +1 -1
- package/dist/core/images.js +11 -0
- package/dist/core/images.js.map +1 -0
- package/dist/core/oauthProviders.js +89 -0
- package/dist/core/oauthProviders.js.map +1 -0
- package/dist/core/templates.js +185 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/generators/auth/auth.js +40 -19
- package/dist/generators/auth/auth.js.map +1 -1
- package/dist/generators/config/config.js +3 -4
- package/dist/generators/config/config.js.map +1 -1
- package/dist/generators/docker/docker.js +51 -31
- package/dist/generators/docker/docker.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/prompts/oauthSetup.js +36 -0
- package/dist/prompts/oauthSetup.js.map +1 -0
- package/dist/prompts/projectSetup.js +43 -19
- package/dist/prompts/projectSetup.js.map +1 -1
- package/package.json +6 -1
- package/verify/adapter-app/Dockerfile +14 -0
- package/verify/adapter-app/package.json +13 -0
- package/verify/adapter-app/server.mjs +62 -0
- package/verify/adapter-app/vendor/.gitignore +3 -0
- package/verify/adapter-app/vendor/.gitkeep +0 -0
- package/verify/docker-compose.verify.yml +154 -0
- package/verify/harness/adapter/emailOtpLogin.spec.ts +12 -0
- package/verify/harness/adapter/magicLink.spec.ts +14 -0
- package/verify/harness/adapter/oauth.spec.ts +11 -0
- package/verify/harness/adapter/registration.spec.ts +11 -0
- package/verify/harness/adapter/sessionLifecycle.spec.ts +20 -0
- package/verify/harness/api/adminBootstrap.spec.ts +34 -0
- package/verify/harness/api/emailOtp.spec.ts +17 -0
- package/verify/harness/api/jwks.spec.ts +17 -0
- package/verify/harness/api/magicLink.spec.ts +36 -0
- package/verify/harness/api/oauth.spec.ts +12 -0
- package/verify/harness/api/organizations.spec.ts +28 -0
- package/verify/harness/api/phoneOtp.spec.ts +19 -0
- package/verify/harness/api/sessionLifecycle.spec.ts +26 -0
- package/verify/harness/api/stepUp.spec.ts +39 -0
- package/verify/harness/api/totp.spec.ts +24 -0
- package/verify/harness/global-setup.ts +45 -0
- package/verify/harness/lib/adapterFlows.ts +57 -0
- package/verify/harness/lib/client.ts +32 -0
- package/verify/harness/lib/env.ts +45 -0
- package/verify/harness/lib/fixtures.ts +20 -0
- package/verify/harness/lib/flows.ts +204 -0
- package/verify/harness/lib/matrixReporter.ts +73 -0
- package/verify/harness/lib/reactFlows.ts +117 -0
- package/verify/harness/lib/serviceToken.ts +26 -0
- package/verify/harness/lib/totp.ts +36 -0
- package/verify/harness/mock-oidc.ts +112 -0
- package/verify/harness/package-lock.json +93 -0
- package/verify/harness/package.json +14 -0
- package/verify/harness/playwright.config.ts +30 -0
- package/verify/harness/react/emailOtpLogin.spec.ts +13 -0
- package/verify/harness/react/logout.spec.ts +17 -0
- package/verify/harness/react/magicLinkLogin.spec.ts +29 -0
- package/verify/harness/react/oauth.spec.ts +13 -0
- package/verify/harness/react/passkeyLogin.spec.ts +24 -0
- package/verify/harness/react/passkeyRegister.spec.ts +15 -0
- package/verify/harness/react/register.spec.ts +18 -0
- package/verify/react-vendor/.gitkeep +0 -0
- package/dist/commands/contribute.js +0 -121
- package/dist/commands/contribute.js.map +0 -1
- package/dist/commands/deploy.js +0 -190
- package/dist/commands/deploy.js.map +0 -1
- package/dist/commands/destroy.js +0 -122
- package/dist/commands/destroy.js.map +0 -1
- package/dist/contributor/catalog.js +0 -129
- package/dist/contributor/catalog.js.map +0 -1
- package/dist/contributor/workspace.js +0 -551
- package/dist/contributor/workspace.js.map +0 -1
- package/dist/core/acm.js +0 -24
- package/dist/core/acm.js.map +0 -1
- package/dist/core/aws.js +0 -374
- package/dist/core/aws.js.map +0 -1
- package/dist/core/buildPush.js +0 -91
- package/dist/core/buildPush.js.map +0 -1
- package/dist/core/configure.js +0 -25
- package/dist/core/configure.js.map +0 -1
- package/dist/core/deployConfig.js +0 -66
- package/dist/core/deployConfig.js.map +0 -1
- package/dist/core/deployManifest.js +0 -163
- package/dist/core/deployManifest.js.map +0 -1
- package/dist/core/deploySecrets.js +0 -31
- package/dist/core/deploySecrets.js.map +0 -1
- package/dist/core/destroy.js +0 -38
- package/dist/core/destroy.js.map +0 -1
- package/dist/core/envDiscovery.js +0 -153
- package/dist/core/envDiscovery.js.map +0 -1
- package/dist/core/envRegistry.js +0 -76
- package/dist/core/envRegistry.js.map +0 -1
- package/dist/core/health.js +0 -11
- package/dist/core/health.js.map +0 -1
- package/dist/core/help.js +0 -75
- package/dist/core/help.js.map +0 -1
- package/dist/core/preReqs.js +0 -14
- package/dist/core/preReqs.js.map +0 -1
- package/dist/core/terraform.js +0 -34
- package/dist/core/terraform.js.map +0 -1
- package/dist/core/tfVars.js +0 -69
- package/dist/core/tfVars.js.map +0 -1
- package/dist/generators/backend/express.js +0 -13
- package/dist/generators/backend/express.js.map +0 -1
- package/dist/generators/deploy/aws/dev/files.js +0 -36
- package/dist/generators/deploy/aws/dev/files.js.map +0 -1
- package/dist/generators/deploy/aws/dev/template.js +0 -1240
- package/dist/generators/deploy/aws/dev/template.js.map +0 -1
- package/dist/generators/frontend/react.js +0 -13
- package/dist/generators/frontend/react.js.map +0 -1
- package/dist/prompts/contributeSetup.js +0 -39
- package/dist/prompts/contributeSetup.js.map +0 -1
- package/dist/prompts/deploySetup.js +0 -244
- package/dist/prompts/deploySetup.js.map +0 -1
- package/dist/utils/repoUtils.js +0 -23
- package/dist/utils/repoUtils.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../../src/generators/docker/docker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../../src/generators/docker/docker.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { runCheck } from "./commands/check.js";
|
|
|
4
4
|
import { printHelp } from "./commands/help.js";
|
|
5
5
|
import pkg from "../package.json" with { type: "json" };
|
|
6
6
|
import { runBootstrapAdmin } from "./commands/bootstrapAdmin.js";
|
|
7
|
+
import { runVerify } from "./commands/verify.js";
|
|
7
8
|
export const VERSION = pkg.version;
|
|
8
9
|
const args = process.argv.slice(2);
|
|
9
10
|
const command = args[0];
|
|
@@ -21,8 +22,12 @@ async function main() {
|
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
if (command === "init") {
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const rest = args.slice(1);
|
|
26
|
+
const aliases = rest
|
|
27
|
+
.filter((a) => a.startsWith("--"))
|
|
28
|
+
.map((a) => a.replace(/^--+/, ""));
|
|
29
|
+
const projectName = rest.find((a) => !a.startsWith("--"));
|
|
30
|
+
await runCLI(projectName, aliases);
|
|
26
31
|
return;
|
|
27
32
|
}
|
|
28
33
|
if (command === "check") {
|
|
@@ -34,6 +39,10 @@ async function main() {
|
|
|
34
39
|
await runBootstrapAdmin(email);
|
|
35
40
|
return;
|
|
36
41
|
}
|
|
42
|
+
if (command === "verify") {
|
|
43
|
+
await runVerify(args.slice(1));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
37
46
|
await runCLI(command);
|
|
38
47
|
}
|
|
39
48
|
main().catch((err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;AACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC7C,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI;aACjB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,MAAM,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,QAAQ,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,iBAAiB,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;AACxB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { multiselect, password, text } from "@clack/prompts";
|
|
2
|
+
import { OAUTH_PROVIDER_CATALOG, } from "../core/oauthProviders.js";
|
|
3
|
+
// Runs when the selected web template opts into OAuth setup. Lets the user pick
|
|
4
|
+
// providers and paste each one's client id and secret, so the scaffolded auth
|
|
5
|
+
// server has OAuth working right after `docker compose up`. Blank credentials are
|
|
6
|
+
// allowed (the provider is scaffolded disabled for the user to fill in later).
|
|
7
|
+
export async function runOAuthSetupPrompts() {
|
|
8
|
+
const chosen = (await multiselect({
|
|
9
|
+
message: "Which OAuth providers do you want to enable? (space to select)",
|
|
10
|
+
options: OAUTH_PROVIDER_CATALOG.map((p) => ({ value: p.id, label: p.label })),
|
|
11
|
+
required: false,
|
|
12
|
+
}));
|
|
13
|
+
if (!Array.isArray(chosen) || chosen.length === 0) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const collected = [];
|
|
17
|
+
for (const id of chosen) {
|
|
18
|
+
const catalog = OAUTH_PROVIDER_CATALOG.find((p) => p.id === id);
|
|
19
|
+
if (!catalog)
|
|
20
|
+
continue;
|
|
21
|
+
const clientId = (await text({
|
|
22
|
+
message: `${catalog.label} client ID`,
|
|
23
|
+
placeholder: "leave blank to configure later",
|
|
24
|
+
}));
|
|
25
|
+
const clientSecret = (await password({
|
|
26
|
+
message: `${catalog.label} client secret`,
|
|
27
|
+
}));
|
|
28
|
+
collected.push({
|
|
29
|
+
catalog,
|
|
30
|
+
clientId: (clientId ?? "").trim(),
|
|
31
|
+
clientSecret: (clientSecret ?? "").trim(),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return collected;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=oauthSetup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthSetup.js","sourceRoot":"","sources":["../../src/prompts/oauthSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE7D,OAAO,EACL,sBAAsB,GAEvB,MAAM,2BAA2B,CAAC;AAEnC,gFAAgF;AAChF,8EAA8E;AAC9E,kFAAkF;AAClF,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,MAAM,GAAG,CAAC,MAAM,WAAW,CAAC;QAChC,OAAO,EAAE,gEAAgE;QACzE,OAAO,EAAE,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7E,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAa,CAAC;IAEhB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAA6B,EAAE,CAAC;IAE/C,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC;YAC3B,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,YAAY;YACrC,WAAW,EAAE,gCAAgC;SAC9C,CAAC,CAAW,CAAC;QAEd,MAAM,YAAY,GAAG,CAAC,MAAM,QAAQ,CAAC;YACnC,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,gBAAgB;SAC1C,CAAC,CAAW,CAAC;QAEd,SAAS,CAAC,IAAI,CAAC;YACb,OAAO;YACP,QAAQ,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YACjC,YAAY,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
import { confirm, select } from "@clack/prompts";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
// Builds the framework choices for one layer (web or api) from the registry, so
|
|
3
|
+
// adding a template is a registry edit, not a code change here. coming-soon
|
|
4
|
+
// templates show as disabled; beta templates are selectable but labelled.
|
|
5
|
+
function toOptions(templates, kind) {
|
|
6
|
+
const forKind = templates.filter((t) => t.kind === kind);
|
|
7
|
+
if (forKind.length === 0) {
|
|
8
|
+
throw new Error(`The template registry has no ${kind} templates.`);
|
|
9
|
+
}
|
|
10
|
+
return forKind.map((t) => ({
|
|
11
|
+
value: t.id,
|
|
12
|
+
label: t.status === "coming-soon"
|
|
13
|
+
? `${t.label} (coming soon)`
|
|
14
|
+
: t.status === "beta"
|
|
15
|
+
? `${t.label} (beta)`
|
|
16
|
+
: t.label,
|
|
17
|
+
disabled: t.status === "coming-soon",
|
|
18
18
|
}));
|
|
19
|
+
}
|
|
20
|
+
function labelFor(templates, id) {
|
|
21
|
+
return templates.find((t) => t.id === id)?.label ?? id;
|
|
22
|
+
}
|
|
23
|
+
export async function runProjectSetupPrompts(templates, preselect = {}) {
|
|
24
|
+
let webTemplateId = preselect.webTemplateId;
|
|
25
|
+
if (webTemplateId) {
|
|
26
|
+
console.log(`Web example: ${labelFor(templates, webTemplateId)}`);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
webTemplateId = (await select({
|
|
30
|
+
message: "Web example",
|
|
31
|
+
options: toOptions(templates, "web"),
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
let apiTemplateId = preselect.apiTemplateId;
|
|
35
|
+
if (apiTemplateId) {
|
|
36
|
+
console.log(`Backend: ${labelFor(templates, apiTemplateId)}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
apiTemplateId = (await select({
|
|
40
|
+
message: "Backend framework",
|
|
41
|
+
options: toOptions(templates, "api"),
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
19
44
|
const authMode = (await select({
|
|
20
45
|
message: "How would you like to run SeamlessAuth?",
|
|
21
46
|
options: [
|
|
@@ -49,7 +74,6 @@ export async function runProjectSetupPrompts() {
|
|
|
49
74
|
],
|
|
50
75
|
}));
|
|
51
76
|
}
|
|
52
|
-
let useDocker = true;
|
|
53
77
|
if (authMode === "local") {
|
|
54
78
|
const confirmDocker = await confirm({
|
|
55
79
|
message: "Auth server still requires Docker for full stack. Enable Docker?",
|
|
@@ -61,9 +85,9 @@ export async function runProjectSetupPrompts() {
|
|
|
61
85
|
}
|
|
62
86
|
return {
|
|
63
87
|
web: true,
|
|
64
|
-
|
|
88
|
+
webTemplateId,
|
|
65
89
|
api: true,
|
|
66
|
-
|
|
90
|
+
apiTemplateId,
|
|
67
91
|
authMode,
|
|
68
92
|
useDocker: true,
|
|
69
93
|
includeAdmin,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectSetup.js","sourceRoot":"","sources":["../../src/prompts/projectSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"projectSetup.js","sourceRoot":"","sources":["../../src/prompts/projectSetup.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seamless-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The Seamless Auth command-line interface",
|
|
5
5
|
"homepage": "https://github.com/fells-code/seamless-cli#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -20,12 +20,16 @@
|
|
|
20
20
|
"build": "tsc",
|
|
21
21
|
"dev": "tsx src/index.ts",
|
|
22
22
|
"clean": "rm -rf dist",
|
|
23
|
+
"changeset": "changeset",
|
|
24
|
+
"version-packages": "changeset version",
|
|
25
|
+
"release:stable": "npm run build && changeset publish",
|
|
23
26
|
"prepublishOnly": "npm run build"
|
|
24
27
|
},
|
|
25
28
|
"main": "dist/index.js",
|
|
26
29
|
"files": [
|
|
27
30
|
"dist",
|
|
28
31
|
"templates",
|
|
32
|
+
"verify",
|
|
29
33
|
"README.md",
|
|
30
34
|
"LICENSE"
|
|
31
35
|
],
|
|
@@ -35,6 +39,7 @@
|
|
|
35
39
|
"kleur": "^4.1.5"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
42
|
+
"@changesets/cli": "^2.31.0",
|
|
38
43
|
"@types/adm-zip": "^0.5.7",
|
|
39
44
|
"@types/node": "^24.10.13",
|
|
40
45
|
"tsx": "^4.21.0",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM node:20-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
RUN apk add --no-cache curl
|
|
4
|
+
COPY package.json ./
|
|
5
|
+
RUN npm install
|
|
6
|
+
# --local mode: install locally-built @seamless-auth/* tarballs over the registry
|
|
7
|
+
# versions. vendor/ is empty in --released mode, so this is a no-op there.
|
|
8
|
+
COPY vendor/ ./vendor/
|
|
9
|
+
RUN if ls ./vendor/*.tgz >/dev/null 2>&1; then \
|
|
10
|
+
npm install ./vendor/seamless-auth-core-*.tgz ./vendor/seamless-auth-express-*.tgz; \
|
|
11
|
+
fi
|
|
12
|
+
COPY server.mjs ./
|
|
13
|
+
EXPOSE 3000
|
|
14
|
+
CMD ["node", "server.mjs"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "seamless-verify-adapter",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Minimal adopter backend for the conformance harness — real @seamless-auth/express with a capture transport.",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@seamless-auth/express": "^0.6.0",
|
|
9
|
+
"cookie-parser": "^1.4.6",
|
|
10
|
+
"cors": "^2.8.5",
|
|
11
|
+
"express": "^4.19.2"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import cookieParser from "cookie-parser";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import createSeamlessAuthServer from "@seamless-auth/express";
|
|
5
|
+
|
|
6
|
+
// The adapter strips OTP/magic-link secrets before responding to the browser, so
|
|
7
|
+
// the conformance harness can't read codes from responses. These handlers receive
|
|
8
|
+
// the raw delivery payloads and stash them for the harness to read via /__captured.
|
|
9
|
+
const captured = new Map();
|
|
10
|
+
const ok = (channel) => ({ accepted: true, provider: "capture", channel });
|
|
11
|
+
|
|
12
|
+
const handlers = {
|
|
13
|
+
async sendOtpEmail({ to, token }) {
|
|
14
|
+
captured.set(to, { token: String(token) });
|
|
15
|
+
return ok("email");
|
|
16
|
+
},
|
|
17
|
+
async sendOtpSms({ to, token }) {
|
|
18
|
+
captured.set(to, { token: String(token) });
|
|
19
|
+
return ok("sms");
|
|
20
|
+
},
|
|
21
|
+
async sendMagicLinkEmail({ to, token, magicLinkUrl }) {
|
|
22
|
+
captured.set(to, { token, magicLinkUrl });
|
|
23
|
+
return ok("email");
|
|
24
|
+
},
|
|
25
|
+
async sendBootstrapInviteEmail({ to, token, inviteUrl }) {
|
|
26
|
+
captured.set(to, { token, inviteUrl });
|
|
27
|
+
return ok("email");
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const app = express();
|
|
32
|
+
// The React app (browser) calls the adapter cross-origin with credentials, so
|
|
33
|
+
// CORS must echo its origin and allow cookies. WEB_ORIGIN is the React app host.
|
|
34
|
+
app.use(
|
|
35
|
+
cors({
|
|
36
|
+
origin: process.env.WEB_ORIGIN ?? "http://localhost:5173",
|
|
37
|
+
credentials: true,
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
app.use(express.json());
|
|
41
|
+
app.use(cookieParser());
|
|
42
|
+
|
|
43
|
+
app.get("/", (_req, res) => res.json({ ok: true }));
|
|
44
|
+
app.get("/__captured/:email", (req, res) =>
|
|
45
|
+
res.json(captured.get(req.params.email) ?? null),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
app.use(
|
|
49
|
+
"/auth",
|
|
50
|
+
createSeamlessAuthServer({
|
|
51
|
+
authServerUrl: process.env.AUTH_SERVER_URL,
|
|
52
|
+
cookieSecret: process.env.COOKIE_SIGNING_KEY,
|
|
53
|
+
serviceSecret: process.env.API_SERVICE_TOKEN,
|
|
54
|
+
issuer: process.env.APP_ORIGIN,
|
|
55
|
+
audience: process.env.AUTH_SERVER_URL,
|
|
56
|
+
jwksKid: process.env.JWKS_KID,
|
|
57
|
+
messaging: { handlers, defaults: { appName: "Seamless Verify" } },
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const port = Number(process.env.PORT ?? 3000);
|
|
62
|
+
app.listen(port, () => console.log(`verify adapter listening on :${port}`));
|
|
File without changes
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Conformance stack for `seamless verify`.
|
|
2
|
+
# Brings up Postgres + the auth API + the Express adapter from local source
|
|
3
|
+
# (paths supplied by the verify command via env) so the Playwright harness can
|
|
4
|
+
# drive every auth flow. NODE_ENV=test enables the API's external-delivery seam
|
|
5
|
+
# so OTP / magic-link tokens are returned in responses (no real email/SMS).
|
|
6
|
+
#
|
|
7
|
+
# Required env (the `seamless verify` command writes these into .env.verify):
|
|
8
|
+
# SEAMLESS_API_DIR absolute path to seamless-auth-api source
|
|
9
|
+
# SEAMLESS_ADAPTER_DIR absolute path to the express adapter source
|
|
10
|
+
# API_SERVICE_TOKEN shared secret between API and adapter
|
|
11
|
+
# JWKS_KID key id the adapter expects (must match the API's active kid)
|
|
12
|
+
# SEAMLESS_BOOTSTRAP_SECRET bearer secret for POST /bootstrap/admin-invite
|
|
13
|
+
|
|
14
|
+
name: seamless-verify
|
|
15
|
+
|
|
16
|
+
services:
|
|
17
|
+
postgres:
|
|
18
|
+
image: postgres:16
|
|
19
|
+
environment:
|
|
20
|
+
POSTGRES_USER: seamless
|
|
21
|
+
POSTGRES_PASSWORD: seamless
|
|
22
|
+
POSTGRES_DB: seamless_auth
|
|
23
|
+
healthcheck:
|
|
24
|
+
test: ['CMD-SHELL', 'pg_isready -U seamless -d seamless_auth']
|
|
25
|
+
interval: 3s
|
|
26
|
+
timeout: 3s
|
|
27
|
+
retries: 20
|
|
28
|
+
|
|
29
|
+
auth-api:
|
|
30
|
+
# Built with the production Dockerfile (compiles dist/), but run with
|
|
31
|
+
# NODE_ENV=test so signing keys auto-generate and prod-only env (JWKS keys)
|
|
32
|
+
# is skipped. validateEnvs.sh gates startup on the env below.
|
|
33
|
+
build:
|
|
34
|
+
context: ${SEAMLESS_API_DIR}
|
|
35
|
+
dockerfile: Dockerfile
|
|
36
|
+
ports:
|
|
37
|
+
- '5312:5312'
|
|
38
|
+
environment:
|
|
39
|
+
# `development` (not `test`): the API's JWKS endpoint only serves the
|
|
40
|
+
# auto-generated dev public key when NODE_ENV === 'development', even though
|
|
41
|
+
# signing treats any non-production env as dev. Running as development keeps
|
|
42
|
+
# JWKS publication working so the adapter/SDKs can verify tokens.
|
|
43
|
+
NODE_ENV: development
|
|
44
|
+
PORT: '5312'
|
|
45
|
+
APP_NAME: Seamless Verify
|
|
46
|
+
APP_ID: seamless-verify
|
|
47
|
+
APP_ORIGINS: http://localhost:3000,http://localhost:5173
|
|
48
|
+
# Must equal the adapter's AUTH_SERVER_URL: the adapter validates the `iss`
|
|
49
|
+
# claim on API-signed tokens against the URL it reaches the API on.
|
|
50
|
+
ISSUER: http://auth-api:5312
|
|
51
|
+
DEFAULT_ROLES: user
|
|
52
|
+
AVAILABLE_ROLES: user,admin
|
|
53
|
+
LOGIN_METHODS: passkey,magic_link,email_otp,phone_otp,oauth
|
|
54
|
+
DB_LOGGING: 'false'
|
|
55
|
+
ACCESS_TOKEN_TTL: 15m
|
|
56
|
+
REFRESH_TOKEN_TTL: 1h
|
|
57
|
+
RATE_LIMIT: '100000'
|
|
58
|
+
DELAY_AFTER: '100000'
|
|
59
|
+
RPID: localhost
|
|
60
|
+
ORIGINS: http://localhost:5173,http://localhost:3000
|
|
61
|
+
DB_HOST: postgres
|
|
62
|
+
DB_PORT: '5432'
|
|
63
|
+
DB_USER: seamless
|
|
64
|
+
DB_PASSWORD: seamless
|
|
65
|
+
DB_NAME: seamless_auth
|
|
66
|
+
API_SERVICE_TOKEN: ${API_SERVICE_TOKEN}
|
|
67
|
+
SEAMLESS_BOOTSTRAP_ENABLED: 'true'
|
|
68
|
+
SEAMLESS_BOOTSTRAP_SECRET: ${SEAMLESS_BOOTSTRAP_SECRET}
|
|
69
|
+
# OAuth: a single "mock" provider backed by the harness's in-process OIDC.
|
|
70
|
+
# authorizationUrl is browser/harness-visible (localhost); token/userInfo are
|
|
71
|
+
# called server-side from the container, so they go via host.docker.internal.
|
|
72
|
+
MOCK_CLIENT_SECRET: mock-secret
|
|
73
|
+
OAUTH_PROVIDERS: '[{"id":"mock","name":"Mock OIDC","enabled":true,"clientId":"mock-client","clientSecretEnv":"MOCK_CLIENT_SECRET","authorizationUrl":"http://localhost:9000/authorize","tokenUrl":"http://host.docker.internal:9000/token","userInfoUrl":"http://host.docker.internal:9000/userinfo","scopes":["openid","email"],"redirectUri":"http://localhost:5173/oauth/callback","redirectUris":["http://localhost:5173/oauth/callback"],"allowSignup":true,"accountLinking":"email"}]'
|
|
74
|
+
extra_hosts:
|
|
75
|
+
- 'host.docker.internal:host-gateway'
|
|
76
|
+
depends_on:
|
|
77
|
+
postgres:
|
|
78
|
+
condition: service_healthy
|
|
79
|
+
healthcheck:
|
|
80
|
+
test: ['CMD', 'node', 'dist/healthCheck.js']
|
|
81
|
+
interval: 3s
|
|
82
|
+
timeout: 5s
|
|
83
|
+
retries: 40
|
|
84
|
+
|
|
85
|
+
# Harness-owned adopter backend: real @seamless-auth/express with a capture
|
|
86
|
+
# transport so the harness can read OTP/magic-link codes the adapter would
|
|
87
|
+
# otherwise strip. ISSUER on auth-api is set to this AUTH_SERVER_URL host.
|
|
88
|
+
adapter:
|
|
89
|
+
build:
|
|
90
|
+
context: ./adapter-app
|
|
91
|
+
ports:
|
|
92
|
+
- '3000:3000'
|
|
93
|
+
environment:
|
|
94
|
+
PORT: '3000'
|
|
95
|
+
AUTH_SERVER_URL: http://auth-api:5312
|
|
96
|
+
APP_ORIGIN: http://localhost:3000
|
|
97
|
+
# Browser origin allowed by CORS (the React app, served below).
|
|
98
|
+
WEB_ORIGIN: http://localhost:5173
|
|
99
|
+
API_SERVICE_TOKEN: ${API_SERVICE_TOKEN}
|
|
100
|
+
COOKIE_SIGNING_KEY: ${API_SERVICE_TOKEN}
|
|
101
|
+
JWKS_KID: ${JWKS_KID}
|
|
102
|
+
depends_on:
|
|
103
|
+
auth-api:
|
|
104
|
+
condition: service_healthy
|
|
105
|
+
healthcheck:
|
|
106
|
+
test: ['CMD', 'curl', '-fsS', 'http://localhost:3000/']
|
|
107
|
+
interval: 3s
|
|
108
|
+
timeout: 3s
|
|
109
|
+
retries: 20
|
|
110
|
+
|
|
111
|
+
# React starter app (built SPA on nginx), pointed at the adapter so the React
|
|
112
|
+
# SDK's `${apiHost}/auth/*` calls hit the adapter's mount. Browser-visible URL,
|
|
113
|
+
# so API_URL is the host-mapped adapter (localhost:3000), not the compose host.
|
|
114
|
+
# Gated behind the `react` profile so the api/adapter-only runs skip the build.
|
|
115
|
+
#
|
|
116
|
+
# The inline Dockerfile mirrors the starter's own Dockerfile, but additionally
|
|
117
|
+
# installs a locally-built @seamless-auth/react tarball when present (--local
|
|
118
|
+
# mode packs it into ./react-vendor; that dir is empty otherwise → published SDK).
|
|
119
|
+
react:
|
|
120
|
+
profiles: ['react']
|
|
121
|
+
build:
|
|
122
|
+
context: ${SEAMLESS_REACT_DIR}
|
|
123
|
+
additional_contexts:
|
|
124
|
+
sdk: ./react-vendor
|
|
125
|
+
dockerfile_inline: |
|
|
126
|
+
FROM node:20-slim AS builder
|
|
127
|
+
WORKDIR /app
|
|
128
|
+
COPY package.json package-lock.json ./
|
|
129
|
+
RUN npm ci
|
|
130
|
+
COPY . .
|
|
131
|
+
COPY --from=sdk . /tmp/sdk/
|
|
132
|
+
RUN if ls /tmp/sdk/seamless-auth-react-*.tgz >/dev/null 2>&1; then \
|
|
133
|
+
npm install /tmp/sdk/seamless-auth-react-*.tgz; \
|
|
134
|
+
fi
|
|
135
|
+
RUN npm run build
|
|
136
|
+
FROM nginx:alpine
|
|
137
|
+
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
|
138
|
+
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
139
|
+
COPY entrypoint.sh /entrypoint.sh
|
|
140
|
+
RUN chmod +x /entrypoint.sh
|
|
141
|
+
EXPOSE 80
|
|
142
|
+
CMD ["/entrypoint.sh"]
|
|
143
|
+
ports:
|
|
144
|
+
- '5173:80'
|
|
145
|
+
environment:
|
|
146
|
+
API_URL: http://localhost:3000
|
|
147
|
+
depends_on:
|
|
148
|
+
adapter:
|
|
149
|
+
condition: service_healthy
|
|
150
|
+
healthcheck:
|
|
151
|
+
test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost/health']
|
|
152
|
+
interval: 3s
|
|
153
|
+
timeout: 3s
|
|
154
|
+
retries: 20
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { loginViaEmailOtp, registerAndVerifyEmail } from '../lib/adapterFlows';
|
|
3
|
+
|
|
4
|
+
test.describe('email OTP login (adapter, cookies)', () => {
|
|
5
|
+
test('register -> verify -> login OTP -> session cookie -> /users/me', async ({ adapterActor }) => {
|
|
6
|
+
await registerAndVerifyEmail(adapterActor.ctx, adapterActor.email);
|
|
7
|
+
await loginViaEmailOtp(adapterActor.ctx, adapterActor.email);
|
|
8
|
+
|
|
9
|
+
const me = await adapterActor.ctx.get('/auth/users/me');
|
|
10
|
+
expect(me.status(), 'authenticated via session cookie').toBe(200);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { loginViaMagicLink, registerAndVerifyEmail } from '../lib/adapterFlows';
|
|
3
|
+
|
|
4
|
+
test.describe('magic link login (adapter, cookies)', () => {
|
|
5
|
+
test('register -> verify -> magic-link request/verify/poll -> session -> /users/me', async ({
|
|
6
|
+
adapterActor,
|
|
7
|
+
}) => {
|
|
8
|
+
await registerAndVerifyEmail(adapterActor.ctx, adapterActor.email);
|
|
9
|
+
await loginViaMagicLink(adapterActor.ctx, adapterActor.email);
|
|
10
|
+
|
|
11
|
+
const me = await adapterActor.ctx.get('/auth/users/me');
|
|
12
|
+
expect(me.status(), 'authenticated via magic-link session cookie').toBe(200);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { oauthLogin } from '../lib/flows';
|
|
3
|
+
|
|
4
|
+
test.describe('OAuth login (adapter, cookies)', () => {
|
|
5
|
+
test('oauth callback sets a session cookie -> /users/me', async ({ adapterActor }) => {
|
|
6
|
+
await oauthLogin(adapterActor.ctx, 'mock', '/auth');
|
|
7
|
+
|
|
8
|
+
const me = await adapterActor.ctx.get('/auth/users/me');
|
|
9
|
+
expect(me.status(), 'authenticated via the oauth session cookie').toBe(200);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { registerAndVerifyEmail } from '../lib/adapterFlows';
|
|
3
|
+
|
|
4
|
+
test.describe('registration (adapter, cookies)', () => {
|
|
5
|
+
test('register + verify email OTP issues a session cookie', async ({ adapterActor }) => {
|
|
6
|
+
await registerAndVerifyEmail(adapterActor.ctx, adapterActor.email);
|
|
7
|
+
|
|
8
|
+
const me = await adapterActor.ctx.get('/auth/users/me');
|
|
9
|
+
expect(me.status(), 'authenticated via the registration session cookie').toBe(200);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { loginViaMagicLink, registerAndVerifyEmail } from '../lib/adapterFlows';
|
|
3
|
+
|
|
4
|
+
test.describe('session lifecycle (adapter, cookies)', () => {
|
|
5
|
+
test('logout clears the session', async ({ adapterActor }) => {
|
|
6
|
+
// Establish the session via magic-link login: it sets the session cookie and
|
|
7
|
+
// uses the magic-link rate limiter, keeping this spec off the OTP limiter so
|
|
8
|
+
// the adapter project's aggregate OTP traffic stays under the per-IP cap.
|
|
9
|
+
await registerAndVerifyEmail(adapterActor.ctx, adapterActor.email);
|
|
10
|
+
await loginViaMagicLink(adapterActor.ctx, adapterActor.email);
|
|
11
|
+
|
|
12
|
+
expect((await adapterActor.ctx.get('/auth/users/me')).status(), 'authenticated').toBe(200);
|
|
13
|
+
|
|
14
|
+
const out = await adapterActor.ctx.delete('/auth/logout');
|
|
15
|
+
expect(out.status(), 'logout returns 204').toBe(204);
|
|
16
|
+
|
|
17
|
+
const me = await adapterActor.ctx.get('/auth/users/me');
|
|
18
|
+
expect(me.status(), 'session rejected after logout').not.toBe(200);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import {
|
|
3
|
+
createBootstrapInvite,
|
|
4
|
+
pollMagicLink,
|
|
5
|
+
registerWithBootstrapToken,
|
|
6
|
+
requestMagicLink,
|
|
7
|
+
verifyMagicLink,
|
|
8
|
+
} from '../lib/flows';
|
|
9
|
+
|
|
10
|
+
test.describe('admin bootstrap (api)', () => {
|
|
11
|
+
// The first-admin invite is single-use per DB (the API returns 410 once an
|
|
12
|
+
// admin exists), so this relies on the fresh DB `seamless verify` provisions.
|
|
13
|
+
test('invite -> register -> magic-link completion promotes the user to admin', async ({
|
|
14
|
+
actor,
|
|
15
|
+
}) => {
|
|
16
|
+
const inviteToken = await createBootstrapInvite(actor.ctx, actor.email);
|
|
17
|
+
const ephemeral = await registerWithBootstrapToken(actor.ctx, actor.email, inviteToken);
|
|
18
|
+
|
|
19
|
+
const { token } = await requestMagicLink(actor.ctx, ephemeral);
|
|
20
|
+
const verified = await verifyMagicLink(actor.ctx, token);
|
|
21
|
+
expect(verified.ok(), `verify -> ${verified.status()}`).toBeTruthy();
|
|
22
|
+
|
|
23
|
+
const completed = await pollMagicLink(actor.ctx, ephemeral);
|
|
24
|
+
expect(completed.status(), 'poll after verify issues a session').toBe(200);
|
|
25
|
+
|
|
26
|
+
const body = await completed.json();
|
|
27
|
+
expect(body.token, 'session access token').toBeTruthy();
|
|
28
|
+
const roles = (body.roles ?? []) as string[];
|
|
29
|
+
expect(
|
|
30
|
+
roles.includes('admin'),
|
|
31
|
+
`bootstrap promotion grants the admin role (got ${JSON.stringify(roles)})`,
|
|
32
|
+
).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { expect, test } from '../lib/fixtures';
|
|
2
|
+
import { registerAndVerifyEmail, registerEmail, requestEmailOtp, verifyEmailOtp } from '../lib/flows';
|
|
3
|
+
|
|
4
|
+
test.describe('email OTP (api)', () => {
|
|
5
|
+
test('register -> request OTP -> verify -> session issued', async ({ actor }) => {
|
|
6
|
+
const session = await registerAndVerifyEmail(actor.ctx, actor.email);
|
|
7
|
+
expect(session.token).toBeTruthy();
|
|
8
|
+
expect(session.refreshToken).toBeTruthy();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('a wrong OTP code is rejected with 401', async ({ actor }) => {
|
|
12
|
+
const ephemeral = await registerEmail(actor.ctx, actor.email);
|
|
13
|
+
await requestEmailOtp(actor.ctx, ephemeral); // issue a real code we deliberately won't use
|
|
14
|
+
const res = await verifyEmailOtp(actor.ctx, ephemeral, 'ZZZZZZ');
|
|
15
|
+
expect(res.status()).toBe(401);
|
|
16
|
+
});
|
|
17
|
+
});
|