@supa-media/convex 1.2.1 → 1.4.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/package.json +1 -1
- package/src/auth/index.ts +6 -1
- package/src/auth/setup.ts +18 -0
- package/src/auth/testEmail.ts +87 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
package/src/auth/index.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
createSupaAuth,
|
|
3
|
+
MAGIC_LINK_PROVIDER_ID,
|
|
4
|
+
TEST_EMAIL_PROVIDER_ID,
|
|
5
|
+
} from "./setup";
|
|
2
6
|
export type {
|
|
3
7
|
SupaAuthConfig,
|
|
4
8
|
SupaAuthMagicLinkConfig,
|
|
5
9
|
SupaAuthResendConfig,
|
|
10
|
+
SupaAuthTestEmailConfig,
|
|
6
11
|
SupaAuthTwilioConfig,
|
|
7
12
|
} from "./setup";
|
|
8
13
|
export {
|
package/src/auth/setup.ts
CHANGED
|
@@ -27,6 +27,15 @@ import { Email } from "@convex-dev/auth/providers/Email";
|
|
|
27
27
|
import { Phone } from "@convex-dev/auth/providers/Phone";
|
|
28
28
|
import type { GenericId } from "convex/values";
|
|
29
29
|
|
|
30
|
+
import { createTestEmailOtps, type SupaAuthTestEmailConfig } from "./testEmail";
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
createTestEmailOtp,
|
|
34
|
+
createTestEmailOtps,
|
|
35
|
+
TEST_EMAIL_PROVIDER_ID,
|
|
36
|
+
type SupaAuthTestEmailConfig,
|
|
37
|
+
} from "./testEmail";
|
|
38
|
+
|
|
30
39
|
export interface SupaAuthResendConfig {
|
|
31
40
|
/** The "from" address for OTP emails. */
|
|
32
41
|
fromAddress: string;
|
|
@@ -100,6 +109,14 @@ export interface SupaAuthConfig {
|
|
|
100
109
|
* `MAGIC_LINK_PROVIDER_ID` for what it does and does not change.
|
|
101
110
|
*/
|
|
102
111
|
magicLink?: SupaAuthMagicLinkConfig;
|
|
112
|
+
/**
|
|
113
|
+
* Register a fixed-code provider for one exact test account, or one
|
|
114
|
+
* provider per account when given a list (each with its own `id` and code;
|
|
115
|
+
* an app store or connector-directory reviewer is the usual second one).
|
|
116
|
+
* Unlike `DEV_OTP_BYPASS`, this may be used in production because every
|
|
117
|
+
* other email address is refused by a distinct provider.
|
|
118
|
+
*/
|
|
119
|
+
testEmail?: SupaAuthTestEmailConfig | SupaAuthTestEmailConfig[];
|
|
103
120
|
/** Resend email OTP configuration. */
|
|
104
121
|
resend?: SupaAuthResendConfig;
|
|
105
122
|
/** Twilio phone OTP configuration. */
|
|
@@ -407,6 +424,7 @@ export function createSupaAuth(config: SupaAuthConfig = {}) {
|
|
|
407
424
|
...(methods.includes("email") && config.magicLink !== undefined
|
|
408
425
|
? [createMagicLink(config)]
|
|
409
426
|
: []),
|
|
427
|
+
...(methods.includes("email") ? createTestEmailOtps(config.testEmail) : []),
|
|
410
428
|
...(methods.includes("phone") ? [createPhoneOtp(config)] : []),
|
|
411
429
|
];
|
|
412
430
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-code sign-in for named test accounts (the CUJ account, a reviewer).
|
|
3
|
+
*
|
|
4
|
+
* Each account gets a provider of its own that refuses every other address,
|
|
5
|
+
* which is what makes a fixed code safe to register in production.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Email } from "@convex-dev/auth/providers/Email";
|
|
9
|
+
|
|
10
|
+
/** The code a test account uses when its config names none. */
|
|
11
|
+
const DEFAULT_TEST_CODE = "000000";
|
|
12
|
+
|
|
13
|
+
/** Provider id for an explicitly scoped production test account. */
|
|
14
|
+
export const TEST_EMAIL_PROVIDER_ID = "test-email";
|
|
15
|
+
|
|
16
|
+
export interface SupaAuthTestEmailConfig {
|
|
17
|
+
/** The only email address allowed to use the fixed test code. */
|
|
18
|
+
email: string;
|
|
19
|
+
/** Fixed verification code. Defaults to `000000`. */
|
|
20
|
+
code?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Provider id the client signs in with. Defaults to `TEST_EMAIL_PROVIDER_ID`.
|
|
23
|
+
*
|
|
24
|
+
* Needed only when an app has more than one fixed-code account, because
|
|
25
|
+
* `@convex-dev/auth` mints a provider's code without knowing the address,
|
|
26
|
+
* so each account needs a provider, and so an id, of its own. Must start
|
|
27
|
+
* with `TEST_EMAIL_PROVIDER_ID`, which keeps it clear of the customer
|
|
28
|
+
* `email` provider whose `authorize` it must never share.
|
|
29
|
+
*/
|
|
30
|
+
id?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Build the fixed-code provider separately so its security boundary is testable. */
|
|
34
|
+
export function createTestEmailOtp(config: SupaAuthTestEmailConfig) {
|
|
35
|
+
const email = config.email.trim().toLowerCase();
|
|
36
|
+
const code = config.code ?? DEFAULT_TEST_CODE;
|
|
37
|
+
const id = config.id ?? TEST_EMAIL_PROVIDER_ID;
|
|
38
|
+
if (!/^test-email(-[a-z0-9]+)*$/.test(id)) {
|
|
39
|
+
throw new Error(`testEmail.id must be "${TEST_EMAIL_PROVIDER_ID}" or start with "${TEST_EMAIL_PROVIDER_ID}-"`);
|
|
40
|
+
}
|
|
41
|
+
if (!/^\d{6}$/.test(code)) {
|
|
42
|
+
throw new Error("testEmail.code must be exactly six digits");
|
|
43
|
+
}
|
|
44
|
+
if (email.length === 0) {
|
|
45
|
+
throw new Error("testEmail.email must not be empty");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const provider = Email({
|
|
49
|
+
maxAge: 10 * 60,
|
|
50
|
+
generateVerificationToken: () => code,
|
|
51
|
+
sendVerificationRequest: async ({ identifier }) => {
|
|
52
|
+
if (identifier.trim().toLowerCase() !== email) {
|
|
53
|
+
throw new Error("This test sign-in provider is not available for that email.");
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
...provider,
|
|
59
|
+
id,
|
|
60
|
+
authorize: async (params: Record<string, unknown>, account: { providerAccountId: string }) => {
|
|
61
|
+
if (
|
|
62
|
+
typeof params.email !== "string" ||
|
|
63
|
+
params.email.trim().toLowerCase() !== email ||
|
|
64
|
+
account.providerAccountId.trim().toLowerCase() !== email
|
|
65
|
+
) {
|
|
66
|
+
throw new Error("This test sign-in provider is not available for that email.");
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** One fixed-code provider per configured account, refusing a repeated id or address. */
|
|
73
|
+
export function createTestEmailOtps(
|
|
74
|
+
config: SupaAuthTestEmailConfig | SupaAuthTestEmailConfig[] | undefined,
|
|
75
|
+
) {
|
|
76
|
+
const configs = config === undefined ? [] : Array.isArray(config) ? config : [config];
|
|
77
|
+
const providers = configs.map(createTestEmailOtp);
|
|
78
|
+
const ids = new Set(providers.map((provider) => provider.id));
|
|
79
|
+
const emails = new Set(configs.map((entry) => entry.email.trim().toLowerCase()));
|
|
80
|
+
if (ids.size !== providers.length) {
|
|
81
|
+
throw new Error("testEmail entries must each have their own id");
|
|
82
|
+
}
|
|
83
|
+
if (emails.size !== configs.length) {
|
|
84
|
+
throw new Error("testEmail entries must each have their own email");
|
|
85
|
+
}
|
|
86
|
+
return providers;
|
|
87
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
export {
|
|
10
10
|
createSupaAuth,
|
|
11
11
|
MAGIC_LINK_PROVIDER_ID,
|
|
12
|
+
TEST_EMAIL_PROVIDER_ID,
|
|
12
13
|
requireAuth,
|
|
13
14
|
requireAuthId,
|
|
14
15
|
getOptionalAuth,
|
|
@@ -18,6 +19,7 @@ export type {
|
|
|
18
19
|
SupaAuthConfig,
|
|
19
20
|
SupaAuthMagicLinkConfig,
|
|
20
21
|
SupaAuthResendConfig,
|
|
22
|
+
SupaAuthTestEmailConfig,
|
|
21
23
|
SupaAuthTwilioConfig,
|
|
22
24
|
} from "./auth";
|
|
23
25
|
|