@robelest/convex-auth 0.0.4-preview.30 → 0.0.4-preview.32
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/dist/bin.js +125 -36
- package/dist/browser/index.d.ts +3 -13
- package/dist/browser/index.js +47 -12
- package/dist/browser/navigation.js +1 -1
- package/dist/browser/passkey.js +7 -7
- package/dist/browser/runtime.js +13 -15
- package/dist/client/core/types.d.ts +179 -63
- package/dist/client/core/types.js +6 -0
- package/dist/client/factors/totp.js +1 -1
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +115 -56
- package/dist/client/runtime/mutex.js +3 -2
- package/dist/component/_generated/component.d.ts +40 -0
- package/dist/component/http.js +9 -0
- package/dist/component/index.d.ts +1 -1
- package/dist/component/model.d.ts +27 -27
- package/dist/component/model.js +2 -1
- package/dist/component/modules.js +1 -0
- package/dist/component/public/factors/passkeys.js +31 -1
- package/dist/component/public/identity/codes.js +1 -1
- package/dist/component/public/identity/tokens.js +2 -1
- package/dist/component/public/identity/verifiers.js +15 -5
- package/dist/component/public.js +2 -2
- package/dist/component/schema.d.ts +287 -285
- package/dist/component/schema.js +2 -1
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.js +7 -2
- package/dist/expo/index.d.ts +21 -0
- package/dist/expo/index.js +148 -0
- package/dist/expo/passkey.js +174 -0
- package/dist/providers/apple.d.ts +1 -1
- package/dist/providers/apple.js +6 -8
- package/dist/providers/custom.d.ts +1 -1
- package/dist/providers/custom.js +4 -7
- package/dist/providers/github.d.ts +1 -1
- package/dist/providers/github.js +5 -8
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.js +5 -8
- package/dist/providers/microsoft.d.ts +1 -1
- package/dist/providers/microsoft.js +5 -9
- package/dist/providers/password.d.ts +18 -37
- package/dist/providers/password.js +170 -115
- package/dist/providers/redirect.d.ts +1 -0
- package/dist/providers/redirect.js +20 -0
- package/dist/server/auth.d.ts +6 -7
- package/dist/server/auth.js +3 -2
- package/dist/server/{ctxCache.js → cache/context.js} +2 -2
- package/dist/server/{componentContext.d.ts → component/context.d.ts} +2 -2
- package/dist/server/context.js +3 -10
- package/dist/server/contract.d.ts +2 -87
- package/dist/server/contract.js +1 -1
- package/dist/server/cookies.js +25 -1
- package/dist/server/core.js +1 -1
- package/dist/server/errors.js +24 -1
- package/dist/server/{auth-context.d.ts → facade.d.ts} +3 -45
- package/dist/server/{auth-context.js → facade.js} +11 -12
- package/dist/server/http.d.ts +7 -7
- package/dist/server/http.js +47 -7
- package/dist/server/{convexIdentity.d.ts → identity/convex.d.ts} +3 -3
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +3 -2
- package/dist/server/mounts.d.ts +171 -18
- package/dist/server/mutations/code.js +7 -1
- package/dist/server/mutations/{credentialsSignIn.js → credentials/signin.js} +10 -10
- package/dist/server/mutations/index.js +1 -1
- package/dist/server/mutations/invalidate.js +11 -1
- package/dist/server/mutations/oauth.js +25 -27
- package/dist/server/mutations/signin.js +6 -0
- package/dist/server/mutations/signout.js +5 -0
- package/dist/server/mutations/store.js +1 -1
- package/dist/server/oauth/factory.js +11 -3
- package/dist/server/passkey.js +126 -110
- package/dist/server/prefetch.js +8 -1
- package/dist/server/redirects.js +11 -3
- package/dist/server/refresh.js +6 -1
- package/dist/server/runtime.d.ts +68 -37
- package/dist/server/runtime.js +318 -36
- package/dist/server/services/group.js +4 -0
- package/dist/server/sessions.js +1 -0
- package/dist/server/signin.js +8 -6
- package/dist/server/sso/domain.d.ts +159 -16
- package/dist/server/sso/domain.js +1 -1
- package/dist/server/sso/http.js +144 -60
- package/dist/server/sso/oidc.js +28 -12
- package/dist/server/sso/policy.js +30 -14
- package/dist/server/sso/provision.js +1 -1
- package/dist/server/sso/saml.js +18 -9
- package/dist/server/sso/scim.js +12 -4
- package/dist/server/sso/shared.js +5 -5
- package/dist/server/telemetry.js +3 -0
- package/dist/server/tokens.js +10 -2
- package/dist/server/totp.js +127 -100
- package/dist/server/types.d.ts +224 -151
- package/dist/server/url.js +1 -1
- package/dist/server/users.js +93 -53
- package/dist/server/wellknown.d.ts +75 -0
- package/dist/server/wellknown.js +198 -0
- package/dist/shared/errors.js +0 -1
- package/package.json +36 -4
- package/dist/server/oauth/index.js +0 -12
- package/dist/server/utils/dispatch.js +0 -36
- package/dist/shared/authResults.d.ts +0 -16
- /package/dist/server/{componentContext.js → component/context.js} +0 -0
- /package/dist/server/{convexIdentity.js → identity/convex.js} +0 -0
package/dist/bin.js
CHANGED
|
@@ -6253,6 +6253,15 @@ gradient.pastel = pastel;
|
|
|
6253
6253
|
|
|
6254
6254
|
//#endregion
|
|
6255
6255
|
//#region src/cli/keys.ts
|
|
6256
|
+
/**
|
|
6257
|
+
* Generate a fresh JWT signing keypair, JWKS payload, and secret-encryption key.
|
|
6258
|
+
*
|
|
6259
|
+
* Used by the Convex Auth setup wizard to provision required environment
|
|
6260
|
+
* variables on a target Convex deployment.
|
|
6261
|
+
*
|
|
6262
|
+
* @returns Generated `JWT_PRIVATE_KEY`, `JWKS`, and `AUTH_SECRET_ENCRYPTION_KEY` values.
|
|
6263
|
+
* @internal
|
|
6264
|
+
*/
|
|
6256
6265
|
async function generateKeys() {
|
|
6257
6266
|
try {
|
|
6258
6267
|
const keys = await generateKeyPair("EdDSA", {
|
|
@@ -6408,6 +6417,11 @@ function printHelp() {
|
|
|
6408
6417
|
printBanner();
|
|
6409
6418
|
console.log(" Add code and set environment variables for @robelest/convex-auth.\n");
|
|
6410
6419
|
console.log(" Full docs: https://auth.estifanos.com\n");
|
|
6420
|
+
console.log(" Commands:\n");
|
|
6421
|
+
console.log(" setup Scaffold files and set env vars");
|
|
6422
|
+
console.log(" doctor Verify env, files, and mounted auth endpoints");
|
|
6423
|
+
console.log(" urls Print auth endpoint and provider callback URLs");
|
|
6424
|
+
console.log(" keys Generate signing/encryption keys\n");
|
|
6411
6425
|
console.log(" Options:\n");
|
|
6412
6426
|
for (const [name, def] of flagDefs) {
|
|
6413
6427
|
const flag = def.type === "boolean" ? `--${name}` : `--${name} <value>`;
|
|
@@ -6416,7 +6430,16 @@ function printHelp() {
|
|
|
6416
6430
|
console.log();
|
|
6417
6431
|
}
|
|
6418
6432
|
function parseArgs(argv) {
|
|
6419
|
-
const
|
|
6433
|
+
const rawArgs = argv.slice(2);
|
|
6434
|
+
const knownCommands = new Set([
|
|
6435
|
+
"setup",
|
|
6436
|
+
"doctor",
|
|
6437
|
+
"urls",
|
|
6438
|
+
"keys"
|
|
6439
|
+
]);
|
|
6440
|
+
const firstArg = rawArgs[0];
|
|
6441
|
+
const command = knownCommands.has(firstArg ?? "") ? firstArg : "setup";
|
|
6442
|
+
const args = command === "setup" && !knownCommands.has(firstArg ?? "") ? rawArgs : rawArgs.slice(1);
|
|
6420
6443
|
const strings = /* @__PURE__ */ new Map();
|
|
6421
6444
|
const booleans = /* @__PURE__ */ new Set();
|
|
6422
6445
|
let i = 0;
|
|
@@ -6454,6 +6477,7 @@ function parseArgs(argv) {
|
|
|
6454
6477
|
process$1.exit(0);
|
|
6455
6478
|
}
|
|
6456
6479
|
return {
|
|
6480
|
+
command,
|
|
6457
6481
|
siteUrl: strings.get("site-url"),
|
|
6458
6482
|
secondaryUrl: strings.get("secondary-url"),
|
|
6459
6483
|
variables: strings.get("variables"),
|
|
@@ -6505,16 +6529,80 @@ async function runSetup(options) {
|
|
|
6505
6529
|
await modifyTsConfig(config);
|
|
6506
6530
|
await configureConvexConfig(config);
|
|
6507
6531
|
await initializeAuth(config);
|
|
6532
|
+
await initializeHttp(config);
|
|
6508
6533
|
await initializeAuthCore(config);
|
|
6509
6534
|
await initializeAuthConfig(config);
|
|
6510
|
-
await configureHttp(config);
|
|
6511
6535
|
if (options.variables !== void 0) await configureOtherVariables(config, options.variables);
|
|
6512
6536
|
else printFinalSuccessMessage(config);
|
|
6513
6537
|
gt("Done! Happy building.");
|
|
6514
6538
|
}
|
|
6539
|
+
async function runDoctor(options) {
|
|
6540
|
+
validateDeploymentSelectionOptions(options);
|
|
6541
|
+
printBanner();
|
|
6542
|
+
mt("Checking Convex Auth setup...");
|
|
6543
|
+
const convexFolderPath = readConvexJson().functions ?? "convex";
|
|
6544
|
+
const configPath = existingNonEmptySourcePath(path.join(convexFolderPath, "convex.config"));
|
|
6545
|
+
const authPath = existingNonEmptySourcePath(path.join(convexFolderPath, "auth"));
|
|
6546
|
+
const authConfigPath = existingNonEmptySourcePath(path.join(convexFolderPath, "auth.config"));
|
|
6547
|
+
if (configPath === null) O.warn("Missing convex.config.ts/js.");
|
|
6548
|
+
else O.success(`Found ${configPath}`);
|
|
6549
|
+
if (authPath === null) O.warn("Missing auth.ts/js.");
|
|
6550
|
+
else O.success(`Found ${authPath}`);
|
|
6551
|
+
if (authConfigPath === null) O.warn("Missing auth.config.ts/js.");
|
|
6552
|
+
else O.success(`Found ${authConfigPath}`);
|
|
6553
|
+
gt("Doctor checks complete.");
|
|
6554
|
+
}
|
|
6555
|
+
async function runUrls(options) {
|
|
6556
|
+
validateDeploymentSelectionOptions(options);
|
|
6557
|
+
printBanner();
|
|
6558
|
+
const authSiteUrl = `${(readConvexDeployment(options).options.url ?? process$1.env.CONVEX_SITE_URL ?? "https://<deployment>.convex.site").replace(/\/$/, "")}/auth`;
|
|
6559
|
+
O.info("Convex Auth URLs:");
|
|
6560
|
+
O.message([
|
|
6561
|
+
`Issuer: ${authSiteUrl}`,
|
|
6562
|
+
`OpenID configuration: ${authSiteUrl}/.well-known/openid-configuration`,
|
|
6563
|
+
`JWKS: ${authSiteUrl}/.well-known/jwks.json`,
|
|
6564
|
+
`OAuth sign-in base: ${authSiteUrl}/signin/<provider>`,
|
|
6565
|
+
`OAuth callback base: ${authSiteUrl}/callback/<provider>`,
|
|
6566
|
+
`SSO connections base: ${authSiteUrl}/connections/<connectionId>`
|
|
6567
|
+
].join("\n"));
|
|
6568
|
+
}
|
|
6569
|
+
async function runKeys(options) {
|
|
6570
|
+
validateDeploymentSelectionOptions(options);
|
|
6571
|
+
printBanner();
|
|
6572
|
+
const convexJson = readConvexJson();
|
|
6573
|
+
const deployment = readConvexDeployment(options);
|
|
6574
|
+
await configureKeys({
|
|
6575
|
+
isExpo: false,
|
|
6576
|
+
isNextjs: false,
|
|
6577
|
+
isVite: false,
|
|
6578
|
+
usesTypeScript: true,
|
|
6579
|
+
convexFolderPath: convexJson.functions ?? "convex",
|
|
6580
|
+
deployment,
|
|
6581
|
+
step: 1
|
|
6582
|
+
});
|
|
6583
|
+
}
|
|
6584
|
+
/**
|
|
6585
|
+
* Run the interactive Convex Auth setup wizard.
|
|
6586
|
+
*
|
|
6587
|
+
* Parses CLI flags, detects the target Convex deployment, configures required
|
|
6588
|
+
* environment variables, and scaffolds the expected auth files in the current
|
|
6589
|
+
* project.
|
|
6590
|
+
*
|
|
6591
|
+
* @param argv - Process arguments. Defaults to `process.argv`.
|
|
6592
|
+
* @returns A promise that resolves when setup completes successfully.
|
|
6593
|
+
*/
|
|
6515
6594
|
const runCli = async (argv = process$1.argv) => {
|
|
6516
|
-
|
|
6595
|
+
const options = parseArgs(argv);
|
|
6596
|
+
if (options.command === "setup") await runSetup(options);
|
|
6597
|
+
else if (options.command === "doctor") await runDoctor(options);
|
|
6598
|
+
else if (options.command === "urls") await runUrls(options);
|
|
6599
|
+
else if (options.command === "keys") await runKeys(options);
|
|
6517
6600
|
};
|
|
6601
|
+
/**
|
|
6602
|
+
* Run the Convex Auth CLI and exit the process on failure.
|
|
6603
|
+
*
|
|
6604
|
+
* @param argv - Process arguments. Defaults to `process.argv`.
|
|
6605
|
+
*/
|
|
6518
6606
|
function runCliMain(argv = process$1.argv) {
|
|
6519
6607
|
runCli(argv).catch((err) => {
|
|
6520
6608
|
console.error(err);
|
|
@@ -6790,6 +6878,33 @@ export const { signIn, signOut, store } = auth;
|
|
|
6790
6878
|
O.success(`Created ${newAuthPath}`);
|
|
6791
6879
|
}
|
|
6792
6880
|
}
|
|
6881
|
+
async function initializeHttp(config) {
|
|
6882
|
+
logStep(config, "Initialize HTTP auth routes");
|
|
6883
|
+
const sourceTemplate = `\
|
|
6884
|
+
import { auth } from "./auth";
|
|
6885
|
+
|
|
6886
|
+
export default auth.http();
|
|
6887
|
+
`;
|
|
6888
|
+
const source = templateToSource(sourceTemplate);
|
|
6889
|
+
const httpPath = path.join(config.convexFolderPath, "http");
|
|
6890
|
+
const existingPath = existingNonEmptySourcePath(httpPath);
|
|
6891
|
+
if (existingPath !== null) if (doesAlreadyMatchTemplate(readFileSync(existingPath, "utf8"), sourceTemplate)) O.success(`${existingPath} is already set up.`);
|
|
6892
|
+
else {
|
|
6893
|
+
O.info(`You already have ${existingPath}. Make sure it mounts Convex Auth protocol routes:`);
|
|
6894
|
+
O.message(indent(`\n${source}\n`));
|
|
6895
|
+
const ready = await ot({ message: "Ready to continue?" });
|
|
6896
|
+
handleCancel(ready);
|
|
6897
|
+
if (!ready) {
|
|
6898
|
+
pt("Setup cancelled.");
|
|
6899
|
+
process$1.exit(1);
|
|
6900
|
+
}
|
|
6901
|
+
}
|
|
6902
|
+
else {
|
|
6903
|
+
const newPath = config.usesTypeScript ? `${httpPath}.ts` : `${httpPath}.js`;
|
|
6904
|
+
writeFileSync(newPath, source);
|
|
6905
|
+
O.success(`Created ${newPath}`);
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6793
6908
|
async function initializeAuthCore(config) {
|
|
6794
6909
|
logStep(config, "Initialize auth/core file");
|
|
6795
6910
|
const sourceTemplate = `\
|
|
@@ -6826,7 +6941,7 @@ async function initializeAuthConfig(config) {
|
|
|
6826
6941
|
export default {$$
|
|
6827
6942
|
providers: [$$
|
|
6828
6943
|
{$$
|
|
6829
|
-
domain: process.env.CONVEX_SITE_URL,$$
|
|
6944
|
+
domain: process.env.CONVEX_SITE_URL + "/auth",$$
|
|
6830
6945
|
applicationID: "convex",$$
|
|
6831
6946
|
},$$
|
|
6832
6947
|
],$$
|
|
@@ -6852,38 +6967,6 @@ export default {$$
|
|
|
6852
6967
|
O.success(`Created ${newPath}`);
|
|
6853
6968
|
}
|
|
6854
6969
|
}
|
|
6855
|
-
async function configureHttp(config) {
|
|
6856
|
-
logStep(config, "Configure http file");
|
|
6857
|
-
const sourceTemplate = `\
|
|
6858
|
-
import { httpRouter } from "convex/server";
|
|
6859
|
-
import { auth } from "./auth";
|
|
6860
|
-
|
|
6861
|
-
const http = httpRouter();
|
|
6862
|
-
|
|
6863
|
-
auth.http.add(http);
|
|
6864
|
-
|
|
6865
|
-
export default http;
|
|
6866
|
-
`;
|
|
6867
|
-
const source = templateToSource(sourceTemplate);
|
|
6868
|
-
const httpPath = path.join(config.convexFolderPath, "http");
|
|
6869
|
-
const existingHttpPath = existingNonEmptySourcePath(httpPath);
|
|
6870
|
-
if (existingHttpPath !== null) if (doesAlreadyMatchTemplate(readFileSync(existingHttpPath, "utf8"), sourceTemplate)) O.success(`${existingHttpPath} is already set up.`);
|
|
6871
|
-
else {
|
|
6872
|
-
O.info(`You already have ${existingHttpPath}. Make sure it includes auth.http.add:`);
|
|
6873
|
-
O.message(indent(`\n${source}\n`));
|
|
6874
|
-
const ready = await ot({ message: "Ready to continue?" });
|
|
6875
|
-
handleCancel(ready);
|
|
6876
|
-
if (!ready) {
|
|
6877
|
-
pt("Setup cancelled.");
|
|
6878
|
-
process$1.exit(1);
|
|
6879
|
-
}
|
|
6880
|
-
}
|
|
6881
|
-
else {
|
|
6882
|
-
const newHttpPath = config.usesTypeScript ? `${httpPath}.ts` : `${httpPath}.js`;
|
|
6883
|
-
writeFileSync(newHttpPath, source);
|
|
6884
|
-
O.success(`Created ${newHttpPath}`);
|
|
6885
|
-
}
|
|
6886
|
-
}
|
|
6887
6970
|
function validateVariablesConfig(value) {
|
|
6888
6971
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Expected an object at the top level.");
|
|
6889
6972
|
const obj = value;
|
|
@@ -6947,9 +7030,11 @@ async function configureOtherVariables(config, json) {
|
|
|
6947
7030
|
}
|
|
6948
7031
|
if (variables.success !== void 0) O.success(variables.success);
|
|
6949
7032
|
}
|
|
7033
|
+
/** @internal */
|
|
6950
7034
|
function doesAlreadyMatchTemplate(existing, template) {
|
|
6951
7035
|
return new RegExp(template.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\$\\\$/g, ".*").replace(/;\n/g, ";.*"), "s").test(existing);
|
|
6952
7036
|
}
|
|
7037
|
+
/** @internal */
|
|
6953
7038
|
function templateToSource(template) {
|
|
6954
7039
|
return template.replace(/\$\$/g, "");
|
|
6955
7040
|
}
|
|
@@ -7021,6 +7106,7 @@ function loadEnvFiles() {
|
|
|
7021
7106
|
override: false
|
|
7022
7107
|
});
|
|
7023
7108
|
}
|
|
7109
|
+
/** @internal */
|
|
7024
7110
|
function readConvexDeployment(options) {
|
|
7025
7111
|
const { adminKey, url, prod, previewName, deploymentName } = options;
|
|
7026
7112
|
if (url) return {
|
|
@@ -7063,6 +7149,7 @@ function readConvexDeployment(options) {
|
|
|
7063
7149
|
}
|
|
7064
7150
|
logErrorAndExit("Could not find a configured CONVEX_DEPLOYMENT. Did you forget to run `npx convex dev` first?");
|
|
7065
7151
|
}
|
|
7152
|
+
/** @internal */
|
|
7066
7153
|
function stripDeploymentTypePrefix(deployment) {
|
|
7067
7154
|
const [type, name] = deployment.split(":");
|
|
7068
7155
|
if (type !== "prod" && type !== "dev" && type !== "preview" || !name) logErrorAndExit("Invalid CONVEX_DEPLOYMENT.", "Expected a typed deployment like \"dev:my-deployment\", \"prod:my-deployment\", or \"preview:my-deployment\".");
|
|
@@ -7077,11 +7164,13 @@ function deploymentNameFromAdminKey(adminKey) {
|
|
|
7077
7164
|
const parts = adminKey.split("|");
|
|
7078
7165
|
return parts.length > 1 && !isPreviewDeployKey(adminKey) ? stripDeploymentTypePrefix(parts[0]) : null;
|
|
7079
7166
|
}
|
|
7167
|
+
/** @internal */
|
|
7080
7168
|
function deploymentTypeFromAdminKey(adminKey) {
|
|
7081
7169
|
const type = adminKey.split(":")[0];
|
|
7082
7170
|
if (type === "prod" || type === "dev" || type === "preview") return type;
|
|
7083
7171
|
logErrorAndExit("Invalid admin key.", "Expected a typed key like \"dev:deployment|...\", \"prod:deployment|...\", or \"preview:...\".");
|
|
7084
7172
|
}
|
|
7173
|
+
/** @internal */
|
|
7085
7174
|
function isPreviewDeployKey(adminKey) {
|
|
7086
7175
|
const parts = adminKey.split("|");
|
|
7087
7176
|
if (parts.length === 1) return false;
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthApiRefs,
|
|
1
|
+
import { AuthApiRefs, ClientOptions, PlatformAuthClient } from "../client/core/types.js";
|
|
2
2
|
import "../client/index.js";
|
|
3
3
|
|
|
4
4
|
//#region src/browser/index.d.ts
|
|
@@ -13,18 +13,8 @@ import "../client/index.js";
|
|
|
13
13
|
* @typeParam Api - Auth API references that control which factor helpers are
|
|
14
14
|
* available on the returned client.
|
|
15
15
|
* @returns A browser auth client with the configured auth helpers.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* import { ConvexReactClient } from "convex/react";
|
|
20
|
-
* import { client } from "@robelest/convex-auth/browser";
|
|
21
|
-
* import { api } from "../convex/_generated/api";
|
|
22
|
-
*
|
|
23
|
-
* const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
|
|
24
|
-
* const auth = client({ convex, api: api.auth });
|
|
25
|
-
* ```
|
|
26
16
|
*/
|
|
27
|
-
declare function client<Api extends AuthApiRefs<boolean, boolean, boolean> = AuthApiRefs>(options: ClientOptions<Api>):
|
|
17
|
+
declare function client<Api extends AuthApiRefs<boolean, boolean, boolean> = AuthApiRefs>(options: ClientOptions<Api>): PlatformAuthClient<Api>;
|
|
28
18
|
//#endregion
|
|
29
|
-
export { type AuthApiRefs, type
|
|
19
|
+
export { type AuthApiRefs, type PlatformAuthClient as AuthClient, type ClientOptions, client };
|
|
30
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/browser/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LOG_LEVELS, logMessage } from "../shared/log.js";
|
|
1
2
|
import { ClientAdapterFactoriesLive, ClientAdaptersLive } from "../client/services/adapters.js";
|
|
2
3
|
import { ClientHttpLive } from "../client/services/http.js";
|
|
3
4
|
import { resolveClientServices } from "../client/services/resolve.js";
|
|
@@ -13,7 +14,7 @@ import { ConvexHttpClient } from "convex/browser";
|
|
|
13
14
|
*
|
|
14
15
|
* This entrypoint wraps the framework-agnostic `client(...)`
|
|
15
16
|
* helper with browser defaults such as `ConvexHttpClient`, local storage, URL
|
|
16
|
-
* replacement, and passkey adapters.
|
|
17
|
+
* replacement, OAuth launching, and passkey adapters.
|
|
17
18
|
*
|
|
18
19
|
* @module
|
|
19
20
|
*/
|
|
@@ -28,16 +29,6 @@ import { ConvexHttpClient } from "convex/browser";
|
|
|
28
29
|
* @typeParam Api - Auth API references that control which factor helpers are
|
|
29
30
|
* available on the returned client.
|
|
30
31
|
* @returns A browser auth client with the configured auth helpers.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```ts
|
|
34
|
-
* import { ConvexReactClient } from "convex/react";
|
|
35
|
-
* import { client } from "@robelest/convex-auth/browser";
|
|
36
|
-
* import { api } from "../convex/_generated/api";
|
|
37
|
-
*
|
|
38
|
-
* const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
|
|
39
|
-
* const auth = client({ convex, api: api.auth });
|
|
40
|
-
* ```
|
|
41
32
|
*/
|
|
42
33
|
function client(options) {
|
|
43
34
|
const url = options.proxyPath === void 0 ? options.url ?? inferConvexUrl(options.convex) : void 0;
|
|
@@ -50,7 +41,7 @@ function client(options) {
|
|
|
50
41
|
}),
|
|
51
42
|
http: ClientHttpLive(options.proxyPath !== void 0 ? null : options.httpClient ?? (url ? new ConvexHttpClient(url) : null))
|
|
52
43
|
});
|
|
53
|
-
|
|
44
|
+
const baseClient = client$1({
|
|
54
45
|
...options,
|
|
55
46
|
storage: options.storage === void 0 && options.proxyPath !== void 0 ? null : options.storage,
|
|
56
47
|
runtime: services.runtime,
|
|
@@ -58,6 +49,49 @@ function client(options) {
|
|
|
58
49
|
adapterFactories: services.adapterFactories,
|
|
59
50
|
httpClient: services.httpClient
|
|
60
51
|
});
|
|
52
|
+
const completeOAuth = async (input) => {
|
|
53
|
+
const result = await baseClient.completeOAuth(input);
|
|
54
|
+
if (result.handled && result.cleanupUrl && services.runtime.location) {
|
|
55
|
+
const current = services.runtime.location.get();
|
|
56
|
+
const cleanupUrl = result.cleanupUrl;
|
|
57
|
+
const relativeUrl = current !== null && cleanupUrl.origin === current.origin ? `${cleanupUrl.pathname}${cleanupUrl.search}${cleanupUrl.hash}` : cleanupUrl.toString();
|
|
58
|
+
await services.runtime.location.replace(relativeUrl);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
const initialize = async () => {
|
|
63
|
+
await baseClient.initialize();
|
|
64
|
+
const current = services.runtime.location?.get();
|
|
65
|
+
if (current?.searchParams.has("code") && current.searchParams.has("state")) await completeOAuth(current);
|
|
66
|
+
};
|
|
67
|
+
const signIn = async (provider, ...args) => {
|
|
68
|
+
const params = args[0];
|
|
69
|
+
const result = await baseClient.signIn(provider, params);
|
|
70
|
+
if (result.kind === "redirect") await services.runtime.oauth?.open(result.redirect);
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
const browserClient = {
|
|
74
|
+
get state() {
|
|
75
|
+
return baseClient.state;
|
|
76
|
+
},
|
|
77
|
+
initialize,
|
|
78
|
+
param: baseClient.param,
|
|
79
|
+
get invite() {
|
|
80
|
+
return baseClient.invite;
|
|
81
|
+
},
|
|
82
|
+
completeOAuth,
|
|
83
|
+
signIn,
|
|
84
|
+
signOut: baseClient.signOut,
|
|
85
|
+
onChange: baseClient.onChange,
|
|
86
|
+
destroy: baseClient.destroy,
|
|
87
|
+
..."totp" in baseClient ? { totp: baseClient.totp } : {},
|
|
88
|
+
..."device" in baseClient ? { device: baseClient.device } : {},
|
|
89
|
+
..."passkey" in baseClient ? { passkey: baseClient.passkey } : {}
|
|
90
|
+
};
|
|
91
|
+
initialize().catch((error) => {
|
|
92
|
+
logMessage("convex-auth/browser", LOG_LEVELS.ERROR, ["[convex-auth] Browser client initialization failed:", error]);
|
|
93
|
+
});
|
|
94
|
+
return browserClient;
|
|
61
95
|
}
|
|
62
96
|
function mergeBrowserRuntime(runtime) {
|
|
63
97
|
const defaults = createBrowserRuntime();
|
|
@@ -68,6 +102,7 @@ function mergeBrowserRuntime(runtime) {
|
|
|
68
102
|
proxy: runtime?.proxy ?? defaults.proxy,
|
|
69
103
|
storage: runtime?.storage === void 0 ? defaults.storage : runtime.storage,
|
|
70
104
|
location: runtime?.location ?? defaults.location,
|
|
105
|
+
oauth: runtime?.oauth ?? defaults.oauth,
|
|
71
106
|
sync: runtime?.sync ?? defaults.sync,
|
|
72
107
|
mutex: runtime?.mutex ?? defaults.mutex
|
|
73
108
|
};
|
package/dist/browser/passkey.js
CHANGED
|
@@ -37,7 +37,7 @@ function createPasskeyClient(deps) {
|
|
|
37
37
|
},
|
|
38
38
|
register: async (opts) => {
|
|
39
39
|
const phase1Params = {
|
|
40
|
-
flow: "
|
|
40
|
+
flow: "register",
|
|
41
41
|
email: opts?.email,
|
|
42
42
|
userName: opts?.userName,
|
|
43
43
|
userDisplayName: opts?.userDisplayName
|
|
@@ -79,7 +79,7 @@ function createPasskeyClient(deps) {
|
|
|
79
79
|
const response = credential.response;
|
|
80
80
|
const transports = typeof response.getTransports === "function" ? response.getTransports() : void 0;
|
|
81
81
|
const phase2Params = {
|
|
82
|
-
flow: "
|
|
82
|
+
flow: "verify",
|
|
83
83
|
clientDataJSON: base64urlEncode(response.clientDataJSON),
|
|
84
84
|
attestationObject: base64urlEncode(response.attestationObject),
|
|
85
85
|
transports,
|
|
@@ -100,11 +100,11 @@ function createPasskeyClient(deps) {
|
|
|
100
100
|
params: phase2Params,
|
|
101
101
|
verifier: phase1Result.verifier
|
|
102
102
|
});
|
|
103
|
-
return handleSignedInResult(phase2Result, "
|
|
103
|
+
return handleSignedInResult(phase2Result, "verify");
|
|
104
104
|
},
|
|
105
|
-
|
|
105
|
+
signIn: async (opts) => {
|
|
106
106
|
const phase1Params = {
|
|
107
|
-
flow: "
|
|
107
|
+
flow: "signIn",
|
|
108
108
|
email: opts?.email
|
|
109
109
|
};
|
|
110
110
|
let phase1Result;
|
|
@@ -139,7 +139,7 @@ function createPasskeyClient(deps) {
|
|
|
139
139
|
if (!credential) throw new Error("Passkey authentication was cancelled");
|
|
140
140
|
const response = credential.response;
|
|
141
141
|
const phase2Params = {
|
|
142
|
-
flow: "
|
|
142
|
+
flow: "verify",
|
|
143
143
|
credentialId: base64urlEncode(credential.rawId),
|
|
144
144
|
clientDataJSON: base64urlEncode(response.clientDataJSON),
|
|
145
145
|
authenticatorData: base64urlEncode(response.authenticatorData),
|
|
@@ -159,7 +159,7 @@ function createPasskeyClient(deps) {
|
|
|
159
159
|
params: phase2Params,
|
|
160
160
|
verifier: phase1Result.verifier
|
|
161
161
|
});
|
|
162
|
-
return handleSignedInResult(phase2Result, "
|
|
162
|
+
return handleSignedInResult(phase2Result, "verify");
|
|
163
163
|
}
|
|
164
164
|
};
|
|
165
165
|
}
|
package/dist/browser/runtime.js
CHANGED
|
@@ -11,14 +11,10 @@ const browserStorage = {
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
async setItem(key, value) {
|
|
14
|
-
|
|
15
|
-
localStorage.setItem(key, value);
|
|
16
|
-
} catch {}
|
|
14
|
+
localStorage.setItem(key, value);
|
|
17
15
|
},
|
|
18
16
|
async removeItem(key) {
|
|
19
|
-
|
|
20
|
-
localStorage.removeItem(key);
|
|
21
|
-
} catch {}
|
|
17
|
+
localStorage.removeItem(key);
|
|
22
18
|
}
|
|
23
19
|
};
|
|
24
20
|
/** @internal */
|
|
@@ -30,7 +26,8 @@ function base64urlEncode(buffer) {
|
|
|
30
26
|
}
|
|
31
27
|
/** @internal */
|
|
32
28
|
function base64urlDecode(str) {
|
|
33
|
-
const
|
|
29
|
+
const normalized = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
30
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
34
31
|
const binary = atob(padded);
|
|
35
32
|
const bytes = new Uint8Array(binary.length);
|
|
36
33
|
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
@@ -52,14 +49,15 @@ function createBrowserRuntime() {
|
|
|
52
49
|
replace: (url) => {
|
|
53
50
|
BrowserNavigationLive.replace(url);
|
|
54
51
|
return Promise.resolve();
|
|
55
|
-
},
|
|
56
|
-
redirect: (url) => {
|
|
57
|
-
BrowserNavigationLive.redirect(url);
|
|
58
|
-
return Promise.resolve();
|
|
59
52
|
}
|
|
60
53
|
},
|
|
54
|
+
oauth: { open: (url) => {
|
|
55
|
+
BrowserNavigationLive.open(url);
|
|
56
|
+
return Promise.resolve();
|
|
57
|
+
} },
|
|
61
58
|
mutex: { withKey: (key, callback) => BrowserLocksLive.withKey(key, callback) },
|
|
62
59
|
proxy: { fetch: async (body, proxyPath) => {
|
|
60
|
+
if (typeof window === "undefined" || window.location?.origin === void 0) throw new Error("Browser proxy fetch is unavailable outside the browser runtime.");
|
|
63
61
|
return fetch(new URL(proxyPath, window.location.origin), {
|
|
64
62
|
method: "POST",
|
|
65
63
|
headers: { "Content-Type": "application/json" },
|
|
@@ -70,18 +68,18 @@ function createBrowserRuntime() {
|
|
|
70
68
|
sync: { subscribe: (key, callback) => {
|
|
71
69
|
if (typeof window === "undefined") return null;
|
|
72
70
|
const registry = getStorageListenerRegistry();
|
|
73
|
-
|
|
74
|
-
if (existingSubscription !== void 0) existingSubscription();
|
|
71
|
+
if (registry[key] === void 0) registry[key] = /* @__PURE__ */ new Set();
|
|
75
72
|
const controller = new AbortController();
|
|
76
73
|
window.addEventListener("storage", (event) => {
|
|
77
74
|
if (event.key !== key) return;
|
|
78
75
|
callback(event.newValue ?? null);
|
|
79
76
|
}, { signal: controller.signal });
|
|
80
77
|
const unsubscribe = () => {
|
|
81
|
-
|
|
78
|
+
registry[key]?.delete(unsubscribe);
|
|
79
|
+
if (registry[key]?.size === 0) delete registry[key];
|
|
82
80
|
controller.abort();
|
|
83
81
|
};
|
|
84
|
-
registry[key]
|
|
82
|
+
registry[key].add(unsubscribe);
|
|
85
83
|
return unsubscribe;
|
|
86
84
|
} }
|
|
87
85
|
};
|