@robelest/convex-auth 0.0.4-preview.29 → 0.0.4-preview.31
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 +143 -21
- 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 +9 -9
- package/dist/browser/runtime.js +13 -15
- package/dist/client/core/types.d.ts +177 -72
- package/dist/client/core/types.js +6 -0
- package/dist/client/factors/device.js +3 -3
- package/dist/client/factors/totp.js +9 -9
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +122 -63
- package/dist/client/runtime/mutex.js +3 -2
- package/dist/component/_generated/component.d.ts +40 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/http.js +9 -0
- package/dist/component/index.d.ts +1 -1
- package/dist/component/model.d.ts +25 -25
- 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 +10 -24
- package/dist/core/index.js +8 -16
- 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 +9 -24
- package/dist/server/auth.js +4 -8
- 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/config.js +10 -0
- package/dist/server/context.js +5 -14
- 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 -14
- package/dist/server/device.js +13 -12
- package/dist/server/env.js +10 -2
- package/dist/server/errors.js +24 -1
- package/dist/server/{auth-context.d.ts → facade.d.ts} +4 -46
- package/dist/server/{auth-context.js → facade.js} +23 -11
- package/dist/server/http.d.ts +7 -7
- package/dist/server/http.js +36 -7
- package/dist/server/identity/convex.d.ts +15 -0
- package/dist/server/identity/convex.js +1 -0
- package/dist/server/identity.js +30 -4
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.js +3 -1
- package/dist/server/mounts.d.ts +246 -93
- 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/refresh.js +38 -7
- package/dist/server/mutations/signin.js +18 -2
- package/dist/server/mutations/signout.js +32 -10
- package/dist/server/mutations/store.js +2 -2
- package/dist/server/oauth/factory.js +13 -4
- package/dist/server/passkey.js +130 -115
- package/dist/server/prefetch.js +16 -9
- package/dist/server/redirects.js +11 -3
- package/dist/server/refresh.js +6 -1
- package/dist/server/runtime.d.ts +56 -40
- package/dist/server/runtime.js +340 -76
- package/dist/server/services/group.js +4 -0
- package/dist/server/sessions.d.ts +2 -1
- package/dist/server/sessions.js +22 -11
- package/dist/server/signin.js +25 -22
- 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 +61 -0
- package/dist/server/tokens.js +34 -6
- package/dist/server/totp.js +135 -106
- package/dist/server/types.d.ts +281 -152
- package/dist/server/url.js +1 -1
- package/dist/server/users.js +93 -53
- package/dist/server/utils/span.js +10 -1
- package/dist/server/wellknown.d.ts +130 -0
- package/dist/server/wellknown.js +195 -0
- package/dist/shared/errors.js +0 -1
- package/package.json +39 -4
- package/dist/server/constants.js +0 -6
- package/dist/server/oauth/index.js +0 -12
- package/dist/server/utils/dispatch.js +0 -36
- /package/dist/server/{componentContext.js → component/context.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", {
|
|
@@ -6266,7 +6275,7 @@ async function generateKeys() {
|
|
|
6266
6275
|
...publicKey
|
|
6267
6276
|
}] });
|
|
6268
6277
|
return {
|
|
6269
|
-
JWT_PRIVATE_KEY:
|
|
6278
|
+
JWT_PRIVATE_KEY: privateKey.trimEnd(),
|
|
6270
6279
|
JWKS: jwks,
|
|
6271
6280
|
AUTH_SECRET_ENCRYPTION_KEY: randomBytes(32).toString("base64url")
|
|
6272
6281
|
};
|
|
@@ -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,15 +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
|
-
await
|
|
6534
|
+
await initializeAuthConfig(config);
|
|
6510
6535
|
if (options.variables !== void 0) await configureOtherVariables(config, options.variables);
|
|
6511
6536
|
else printFinalSuccessMessage(config);
|
|
6512
6537
|
gt("Done! Happy building.");
|
|
6513
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
|
+
*/
|
|
6514
6594
|
const runCli = async (argv = process$1.argv) => {
|
|
6515
|
-
|
|
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);
|
|
6516
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
|
+
*/
|
|
6517
6606
|
function runCliMain(argv = process$1.argv) {
|
|
6518
6607
|
runCli(argv).catch((err) => {
|
|
6519
6608
|
console.error(err);
|
|
@@ -6789,6 +6878,33 @@ export const { signIn, signOut, store } = auth;
|
|
|
6789
6878
|
O.success(`Created ${newAuthPath}`);
|
|
6790
6879
|
}
|
|
6791
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
|
+
}
|
|
6792
6908
|
async function initializeAuthCore(config) {
|
|
6793
6909
|
logStep(config, "Initialize auth/core file");
|
|
6794
6910
|
const sourceTemplate = `\
|
|
@@ -6819,24 +6935,24 @@ export const auth = createAuthContext(components.auth);
|
|
|
6819
6935
|
O.success(`Created ${newPath}`);
|
|
6820
6936
|
}
|
|
6821
6937
|
}
|
|
6822
|
-
async function
|
|
6823
|
-
logStep(config, "
|
|
6938
|
+
async function initializeAuthConfig(config) {
|
|
6939
|
+
logStep(config, "Initialize auth.config file");
|
|
6824
6940
|
const sourceTemplate = `\
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6941
|
+
export default {$$
|
|
6942
|
+
providers: [$$
|
|
6943
|
+
{$$
|
|
6944
|
+
domain: process.env.CONVEX_SITE_URL + "/auth",$$
|
|
6945
|
+
applicationID: "convex",$$
|
|
6946
|
+
},$$
|
|
6947
|
+
],$$
|
|
6948
|
+
};
|
|
6833
6949
|
`;
|
|
6834
6950
|
const source = templateToSource(sourceTemplate);
|
|
6835
|
-
const
|
|
6836
|
-
const
|
|
6837
|
-
if (
|
|
6951
|
+
const authConfigPath = path.join(config.convexFolderPath, "auth.config");
|
|
6952
|
+
const existingPath = existingNonEmptySourcePath(authConfigPath);
|
|
6953
|
+
if (existingPath !== null) if (doesAlreadyMatchTemplate(readFileSync(existingPath, "utf8"), sourceTemplate)) O.success(`${existingPath} is already set up.`);
|
|
6838
6954
|
else {
|
|
6839
|
-
O.info(`You already have ${
|
|
6955
|
+
O.info(`You already have ${existingPath}. Make sure it trusts CONVEX_SITE_URL as the Convex auth issuer:`);
|
|
6840
6956
|
O.message(indent(`\n${source}\n`));
|
|
6841
6957
|
const ready = await ot({ message: "Ready to continue?" });
|
|
6842
6958
|
handleCancel(ready);
|
|
@@ -6846,9 +6962,9 @@ export default http;
|
|
|
6846
6962
|
}
|
|
6847
6963
|
}
|
|
6848
6964
|
else {
|
|
6849
|
-
const
|
|
6850
|
-
writeFileSync(
|
|
6851
|
-
O.success(`Created ${
|
|
6965
|
+
const newPath = config.usesTypeScript ? `${authConfigPath}.ts` : `${authConfigPath}.js`;
|
|
6966
|
+
writeFileSync(newPath, source);
|
|
6967
|
+
O.success(`Created ${newPath}`);
|
|
6852
6968
|
}
|
|
6853
6969
|
}
|
|
6854
6970
|
function validateVariablesConfig(value) {
|
|
@@ -6914,9 +7030,11 @@ async function configureOtherVariables(config, json) {
|
|
|
6914
7030
|
}
|
|
6915
7031
|
if (variables.success !== void 0) O.success(variables.success);
|
|
6916
7032
|
}
|
|
7033
|
+
/** @internal */
|
|
6917
7034
|
function doesAlreadyMatchTemplate(existing, template) {
|
|
6918
7035
|
return new RegExp(template.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\$\\\$/g, ".*").replace(/;\n/g, ";.*"), "s").test(existing);
|
|
6919
7036
|
}
|
|
7037
|
+
/** @internal */
|
|
6920
7038
|
function templateToSource(template) {
|
|
6921
7039
|
return template.replace(/\$\$/g, "");
|
|
6922
7040
|
}
|
|
@@ -6988,6 +7106,7 @@ function loadEnvFiles() {
|
|
|
6988
7106
|
override: false
|
|
6989
7107
|
});
|
|
6990
7108
|
}
|
|
7109
|
+
/** @internal */
|
|
6991
7110
|
function readConvexDeployment(options) {
|
|
6992
7111
|
const { adminKey, url, prod, previewName, deploymentName } = options;
|
|
6993
7112
|
if (url) return {
|
|
@@ -7030,6 +7149,7 @@ function readConvexDeployment(options) {
|
|
|
7030
7149
|
}
|
|
7031
7150
|
logErrorAndExit("Could not find a configured CONVEX_DEPLOYMENT. Did you forget to run `npx convex dev` first?");
|
|
7032
7151
|
}
|
|
7152
|
+
/** @internal */
|
|
7033
7153
|
function stripDeploymentTypePrefix(deployment) {
|
|
7034
7154
|
const [type, name] = deployment.split(":");
|
|
7035
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\".");
|
|
@@ -7044,11 +7164,13 @@ function deploymentNameFromAdminKey(adminKey) {
|
|
|
7044
7164
|
const parts = adminKey.split("|");
|
|
7045
7165
|
return parts.length > 1 && !isPreviewDeployKey(adminKey) ? stripDeploymentTypePrefix(parts[0]) : null;
|
|
7046
7166
|
}
|
|
7167
|
+
/** @internal */
|
|
7047
7168
|
function deploymentTypeFromAdminKey(adminKey) {
|
|
7048
7169
|
const type = adminKey.split(":")[0];
|
|
7049
7170
|
if (type === "prod" || type === "dev" || type === "preview") return type;
|
|
7050
7171
|
logErrorAndExit("Invalid admin key.", "Expected a typed key like \"dev:deployment|...\", \"prod:deployment|...\", or \"preview:...\".");
|
|
7051
7172
|
}
|
|
7173
|
+
/** @internal */
|
|
7052
7174
|
function isPreviewDeployKey(adminKey) {
|
|
7053
7175
|
const parts = adminKey.split("|");
|
|
7054
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
|
@@ -8,7 +8,7 @@ function createPasskeyClient(deps) {
|
|
|
8
8
|
if (result.kind !== "signedIn") return { kind: "started" };
|
|
9
9
|
return await setTokenAndMaybeWait(proxy ? {
|
|
10
10
|
shouldStore: false,
|
|
11
|
-
tokens: result.
|
|
11
|
+
tokens: result.session === null ? null : { token: result.session.token },
|
|
12
12
|
waitForHandshake: true,
|
|
13
13
|
context: {
|
|
14
14
|
provider: "passkey",
|
|
@@ -16,7 +16,7 @@ function createPasskeyClient(deps) {
|
|
|
16
16
|
}
|
|
17
17
|
} : {
|
|
18
18
|
shouldStore: true,
|
|
19
|
-
tokens: result.
|
|
19
|
+
tokens: result.session,
|
|
20
20
|
waitForHandshake: true,
|
|
21
21
|
context: {
|
|
22
22
|
provider: "passkey",
|
|
@@ -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
|
};
|