@robelest/convex-auth 0.0.4-preview.27 → 0.0.4-preview.28
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 +3 -5
- package/dist/bin.js +6488 -1571
- package/dist/browser/index.js +10 -7
- package/dist/browser/locks.js +3 -5
- package/dist/browser/navigation.js +7 -10
- package/dist/browser/runtime.js +35 -33
- package/dist/client/core/types.js +17 -0
- package/dist/client/factors/device.js +26 -19
- package/dist/client/index.js +151 -163
- package/dist/client/runtime/proxy.js +6 -6
- package/dist/client/services/adapters.js +3 -7
- package/dist/client/services/http.js +2 -5
- package/dist/client/services/resolve.js +5 -11
- package/dist/client/services/runtime.js +2 -5
- package/dist/component/_generated/component.d.ts +46 -0
- package/dist/component/index.d.ts +3 -3
- package/dist/component/model.d.ts +25 -25
- package/dist/component/public/identity/sessions.js +38 -1
- package/dist/component/public/identity/tokens.js +81 -3
- package/dist/component/public/identity/verifiers.js +9 -3
- package/dist/component/public.js +3 -3
- package/dist/component/schema.d.ts +320 -320
- package/dist/core/index.d.ts +380 -0
- package/dist/core/index.js +83 -0
- package/dist/otel.d.ts +13 -17
- package/dist/otel.js +39 -49
- package/dist/providers/email.d.ts +2 -2
- package/dist/providers/password.js +8 -16
- package/dist/providers/phone.js +2 -9
- package/dist/server/auth-context.d.ts +204 -0
- package/dist/server/auth-context.js +76 -0
- package/dist/server/auth.d.ts +25 -187
- package/dist/server/auth.js +5 -96
- package/dist/server/componentContext.d.ts +12 -0
- package/dist/server/componentContext.js +1 -0
- package/dist/server/config.js +1 -12
- package/dist/server/constants.js +6 -0
- package/dist/server/contract.d.ts +1 -1
- package/dist/server/core.js +5 -14
- package/dist/server/crypto.js +26 -18
- package/dist/server/db.js +6 -1
- package/dist/server/device.js +88 -78
- package/dist/server/http.d.ts +4 -3
- package/dist/server/http.js +74 -86
- package/dist/server/index.d.ts +2 -1
- package/dist/server/limits.js +22 -15
- package/dist/server/mounts.d.ts +103 -103
- package/dist/server/mutations/account.js +6 -4
- package/dist/server/mutations/invalidate.js +3 -6
- package/dist/server/mutations/oauth.js +86 -88
- package/dist/server/mutations/refresh.js +45 -87
- package/dist/server/mutations/register.js +19 -19
- package/dist/server/mutations/retrieve.js +17 -15
- package/dist/server/mutations/signature.js +9 -13
- package/dist/server/mutations/signin.js +7 -3
- package/dist/server/mutations/signout.js +10 -15
- package/dist/server/mutations/store.js +22 -12
- package/dist/server/mutations/verifier.js +11 -6
- package/dist/server/mutations/verify.js +55 -46
- package/dist/server/oauth/runtime.js +27 -25
- package/dist/server/passkey.js +299 -250
- package/dist/server/prefetch.js +283 -281
- package/dist/server/refresh.js +7 -60
- package/dist/server/runtime.d.ts +82 -206
- package/dist/server/runtime.js +63 -56
- package/dist/server/services/config.js +5 -3
- package/dist/server/services/logger.js +2 -4
- package/dist/server/services/providers.js +2 -4
- package/dist/server/services/refresh.js +2 -4
- package/dist/server/services/resolve.js +15 -14
- package/dist/server/services/signin.js +2 -4
- package/dist/server/sessions.js +32 -33
- package/dist/server/signin.js +177 -142
- package/dist/server/sso/domain.d.ts +20 -68
- package/dist/server/sso/domain.js +444 -413
- package/dist/server/sso/http.js +53 -59
- package/dist/server/sso/oidc.js +94 -80
- package/dist/server/tokens.js +13 -3
- package/dist/server/totp.js +153 -116
- package/dist/server/types.d.ts +2 -2
- package/dist/server/users.js +18 -23
- package/dist/server/utils/cache.js +51 -0
- package/dist/server/utils/dispatch.js +36 -0
- package/dist/server/utils/retry.js +24 -0
- package/dist/server/utils/span.js +32 -0
- package/dist/shared/errors.js +9 -3
- package/dist/shared/log.js +20 -22
- package/package.json +41 -33
package/dist/server/signin.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { envOptionalString, readConfigSync, requireEnv } from "./env.js";
|
|
2
1
|
import { generateRandomString } from "./random.js";
|
|
3
|
-
import {
|
|
4
|
-
import { toConvexError } from "./errors.js";
|
|
2
|
+
import { envOptionalString, readConfigSync, requireEnv } from "./env.js";
|
|
5
3
|
import { log } from "./log.js";
|
|
6
4
|
import { callCreateVerificationCode } from "./mutations/code.js";
|
|
5
|
+
import { withSpan } from "./utils/span.js";
|
|
7
6
|
import { callRefreshSession } from "./mutations/refresh.js";
|
|
8
|
-
import { callVerifierSignature } from "./mutations/signature.js";
|
|
9
7
|
import { callSignIn } from "./mutations/signin.js";
|
|
10
8
|
import { callVerifier } from "./mutations/verifier.js";
|
|
11
9
|
import { callVerifyCodeAndSignIn } from "./mutations/verify.js";
|
|
10
|
+
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
|
|
11
|
+
import { authFlowError } from "../shared/errors.js";
|
|
12
|
+
import { toConvexError } from "./errors.js";
|
|
12
13
|
import { queryTotpVerifiedByUserId } from "./types.js";
|
|
13
14
|
import { handleDevice } from "./device.js";
|
|
14
15
|
import { handlePasskeyFx } from "./passkey.js";
|
|
15
|
-
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
|
|
16
16
|
import { handleTotp } from "./totp.js";
|
|
17
17
|
import { ConvexError } from "convex/values";
|
|
18
|
-
import { Effect, Match } from "effect";
|
|
19
18
|
|
|
20
19
|
//#region src/server/signin.ts
|
|
21
20
|
const DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 3600 * 24;
|
|
@@ -46,15 +45,16 @@ const asCredentialsError = (error) => {
|
|
|
46
45
|
return toConvexError(authFlowError("INTERNAL_ERROR", "Failed to authorize credentials."));
|
|
47
46
|
};
|
|
48
47
|
async function signInImpl(ctx, provider, args, options) {
|
|
49
|
-
return
|
|
48
|
+
return signInFx(ctx, provider, args, options);
|
|
50
49
|
}
|
|
51
|
-
function signInFx(ctx, provider, args, options) {
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
async function signInFx(ctx, provider, args, options) {
|
|
51
|
+
return withSpan("convex-auth.signin", {
|
|
52
|
+
hasProvider: provider !== null,
|
|
53
|
+
hasCode: args.params?.code !== void 0,
|
|
54
|
+
hasRefreshToken: args.refreshToken !== void 0
|
|
55
|
+
}, async () => {
|
|
56
|
+
if (provider === null && args.refreshToken) try {
|
|
57
|
+
const tokens = await callRefreshSession(ctx, { refreshToken: args.refreshToken });
|
|
58
58
|
if (tokens === null) return {
|
|
59
59
|
kind: "signedIn",
|
|
60
60
|
signedIn: null
|
|
@@ -63,171 +63,201 @@ function signInFx(ctx, provider, args, options) {
|
|
|
63
63
|
kind: "refreshTokens",
|
|
64
64
|
signedIn: { tokens }
|
|
65
65
|
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to refresh session.");
|
|
66
68
|
}
|
|
67
|
-
if (provider === null && args.params?.code !== void 0)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (provider === null && args.params?.code !== void 0) try {
|
|
70
|
+
return {
|
|
71
|
+
kind: "signedIn",
|
|
72
|
+
signedIn: await callVerifyCodeAndSignIn(ctx, {
|
|
71
73
|
params: args.params,
|
|
72
74
|
verifier: args.verifier,
|
|
73
75
|
generateTokens: true,
|
|
74
76
|
allowExtraProviders: options.allowExtraProviders
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
})
|
|
78
|
+
};
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to verify sign-in code.");
|
|
81
|
+
}
|
|
79
82
|
const resolvedProvider = provider;
|
|
80
|
-
if (resolvedProvider === null)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
if (resolvedProvider === null) throw toConvexError(authFlowError("SIGN_IN_MISSING_PARAMS", "Cannot sign in: missing provider, code, or refresh token."));
|
|
84
|
+
const handler = {
|
|
85
|
+
email: () => handleEmailAndPhoneProviderFx(ctx, resolvedProvider, args, options),
|
|
86
|
+
phone: () => handleEmailAndPhoneProviderFx(ctx, resolvedProvider, args, options),
|
|
87
|
+
credentials: () => handleCredentialsFx(ctx, resolvedProvider, args, options),
|
|
88
|
+
oauth: () => handleOAuthProviderFx(ctx, resolvedProvider, args, options),
|
|
89
|
+
passkey: () => handlePasskeyFx(ctx, resolvedProvider, args),
|
|
90
|
+
totp: () => handleTotp(ctx, resolvedProvider, args),
|
|
91
|
+
device: () => handleDevice(ctx, resolvedProvider, args),
|
|
92
|
+
sso: () => handleSsoProviderFx(ctx, args, options)
|
|
93
|
+
}[resolvedProvider.type];
|
|
94
|
+
if (!handler) throw toConvexError(authFlowError("SIGN_IN_MISSING_PARAMS", `Unknown provider type: ${resolvedProvider.type}`));
|
|
95
|
+
return handler();
|
|
96
|
+
});
|
|
87
97
|
}
|
|
88
|
-
function handleEmailAndPhoneProviderFx(ctx, provider, args, options) {
|
|
89
|
-
return
|
|
98
|
+
async function handleEmailAndPhoneProviderFx(ctx, provider, args, options) {
|
|
99
|
+
return withSpan(`convex-auth.signin.${provider.type}`, {}, async () => {
|
|
90
100
|
const normalizedParams = normalizeVerificationParams(args.params);
|
|
91
101
|
if (args.params?.code !== void 0) {
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
let result;
|
|
103
|
+
try {
|
|
104
|
+
result = await callVerifyCodeAndSignIn(ctx, {
|
|
94
105
|
params: args.params,
|
|
95
106
|
provider: provider.id,
|
|
96
107
|
generateTokens: options.generateTokens,
|
|
97
108
|
allowExtraProviders: options.allowExtraProviders
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to verify email or phone code.");
|
|
112
|
+
}
|
|
113
|
+
if (result === null) throw toConvexError(authFlowError("INVALID_VERIFICATION_CODE", "Invalid or expired verification code."));
|
|
102
114
|
return {
|
|
103
115
|
kind: "signedIn",
|
|
104
116
|
signedIn: result
|
|
105
117
|
};
|
|
106
118
|
}
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
120
|
+
let code;
|
|
121
|
+
if (provider.generateVerificationToken) try {
|
|
122
|
+
code = await provider.generateVerificationToken();
|
|
123
|
+
} catch {
|
|
124
|
+
throw toConvexError(authFlowError("INTERNAL_ERROR", "Failed to generate verification token"));
|
|
125
|
+
}
|
|
126
|
+
else code = generateRandomString(32, alphabet);
|
|
111
127
|
const expirationTime = Date.now() + (provider.maxAge ?? DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S) * 1e3;
|
|
128
|
+
let identifier;
|
|
129
|
+
try {
|
|
130
|
+
identifier = await callCreateVerificationCode(ctx, {
|
|
131
|
+
provider: provider.id,
|
|
132
|
+
accountId: args.accountId,
|
|
133
|
+
email: normalizedParams.email,
|
|
134
|
+
phone: normalizedParams.phone,
|
|
135
|
+
code,
|
|
136
|
+
expirationTime,
|
|
137
|
+
allowExtraProviders: options.allowExtraProviders
|
|
138
|
+
});
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to create verification code.");
|
|
141
|
+
}
|
|
142
|
+
let destination;
|
|
143
|
+
try {
|
|
144
|
+
destination = await redirectAbsoluteUrl(ctx.auth.config, args.params ?? {});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
throw asConvexError(error, "INVALID_REDIRECT", "Failed to resolve redirect URL.");
|
|
147
|
+
}
|
|
112
148
|
const verificationArgs = {
|
|
113
|
-
identifier
|
|
114
|
-
|
|
115
|
-
provider: provider.id,
|
|
116
|
-
accountId: args.accountId,
|
|
117
|
-
email: normalizedParams.email,
|
|
118
|
-
phone: normalizedParams.phone,
|
|
119
|
-
code,
|
|
120
|
-
expirationTime,
|
|
121
|
-
allowExtraProviders: options.allowExtraProviders
|
|
122
|
-
}),
|
|
123
|
-
catch: (error) => asConvexError(error, "INTERNAL_ERROR", "Failed to create verification code.")
|
|
124
|
-
}),
|
|
125
|
-
url: setURLSearchParam(yield* Effect.tryPromise({
|
|
126
|
-
try: () => redirectAbsoluteUrl(ctx.auth.config, args.params ?? {}),
|
|
127
|
-
catch: (error) => asConvexError(error, "INVALID_REDIRECT", "Failed to resolve redirect URL.")
|
|
128
|
-
}), "code", code),
|
|
149
|
+
identifier,
|
|
150
|
+
url: setURLSearchParam(destination, "code", code),
|
|
129
151
|
token: code,
|
|
130
152
|
expires: new Date(expirationTime)
|
|
131
153
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
})), Match.exhaustive);
|
|
154
|
+
if (provider.type === "email") try {
|
|
155
|
+
await provider.sendVerificationRequest({
|
|
156
|
+
...verificationArgs,
|
|
157
|
+
provider,
|
|
158
|
+
request: new Request("http://localhost")
|
|
159
|
+
}, ctx);
|
|
160
|
+
} catch {
|
|
161
|
+
throw toConvexError(authFlowError("INTERNAL_ERROR", "Failed to send email code"));
|
|
162
|
+
}
|
|
163
|
+
else try {
|
|
164
|
+
await provider.sendVerificationRequest({
|
|
165
|
+
...verificationArgs,
|
|
166
|
+
provider
|
|
167
|
+
}, ctx);
|
|
168
|
+
} catch {
|
|
169
|
+
throw toConvexError(authFlowError("INTERNAL_ERROR", "Failed to send phone code"));
|
|
170
|
+
}
|
|
150
171
|
return {
|
|
151
172
|
kind: "started",
|
|
152
173
|
started: true
|
|
153
174
|
};
|
|
154
|
-
})
|
|
175
|
+
});
|
|
155
176
|
}
|
|
156
|
-
function handleCredentialsFx(ctx, provider, args, options) {
|
|
157
|
-
return
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
})
|
|
177
|
+
async function handleCredentialsFx(ctx, provider, args, options) {
|
|
178
|
+
return withSpan("convex-auth.signin.credentials", {}, async () => {
|
|
179
|
+
let result;
|
|
180
|
+
try {
|
|
181
|
+
result = await provider.authorize(args.params ?? {}, ctx);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
throw asCredentialsError(error);
|
|
184
|
+
}
|
|
162
185
|
if (result === null) return {
|
|
163
186
|
kind: "signedIn",
|
|
164
187
|
signedIn: null
|
|
165
188
|
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
let hasTotpEnrolled;
|
|
190
|
+
try {
|
|
191
|
+
hasTotpEnrolled = await queryTotpVerifiedByUserId(ctx, result.userId) !== null;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to load TOTP enrollment.");
|
|
194
|
+
}
|
|
195
|
+
if (hasTotpEnrolled) {
|
|
196
|
+
try {
|
|
197
|
+
await callSignIn(ctx, {
|
|
174
198
|
userId: result.userId,
|
|
175
199
|
sessionId: result.sessionId,
|
|
176
200
|
generateTokens: false
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
signature: JSON.stringify({ userId: result.userId })
|
|
188
|
-
}),
|
|
189
|
-
catch: (error) => asConvexError(error, "INTERNAL_ERROR", "Failed to store verifier signature.")
|
|
190
|
-
});
|
|
201
|
+
});
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to start TOTP sign-in.");
|
|
204
|
+
}
|
|
205
|
+
let verifier;
|
|
206
|
+
try {
|
|
207
|
+
verifier = await callVerifier(ctx, JSON.stringify({ userId: result.userId }));
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to create verifier.");
|
|
210
|
+
}
|
|
191
211
|
return {
|
|
192
212
|
kind: "totpRequired",
|
|
193
213
|
verifier
|
|
194
214
|
};
|
|
195
215
|
}
|
|
216
|
+
let idsAndTokens;
|
|
217
|
+
try {
|
|
218
|
+
idsAndTokens = await callSignIn(ctx, {
|
|
219
|
+
userId: result.userId,
|
|
220
|
+
sessionId: result.sessionId,
|
|
221
|
+
generateTokens: options.generateTokens
|
|
222
|
+
});
|
|
223
|
+
} catch (error) {
|
|
224
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to complete sign-in.");
|
|
225
|
+
}
|
|
196
226
|
return {
|
|
197
227
|
kind: "signedIn",
|
|
198
|
-
signedIn:
|
|
199
|
-
try: () => callSignIn(ctx, {
|
|
200
|
-
userId: result.userId,
|
|
201
|
-
sessionId: result.sessionId,
|
|
202
|
-
generateTokens: options.generateTokens
|
|
203
|
-
}),
|
|
204
|
-
catch: (error) => asConvexError(error, "INTERNAL_ERROR", "Failed to complete sign-in.")
|
|
205
|
-
})
|
|
228
|
+
signedIn: idsAndTokens
|
|
206
229
|
};
|
|
207
|
-
})
|
|
230
|
+
});
|
|
208
231
|
}
|
|
209
|
-
function handleOAuthProviderFx(ctx, provider, args, options) {
|
|
210
|
-
return
|
|
211
|
-
if (args.params?.code !== void 0)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
232
|
+
async function handleOAuthProviderFx(ctx, provider, args, options) {
|
|
233
|
+
return withSpan(`convex-auth.signin.oauth`, { provider: provider.id }, async () => {
|
|
234
|
+
if (args.params?.code !== void 0) {
|
|
235
|
+
let result;
|
|
236
|
+
try {
|
|
237
|
+
result = await callVerifyCodeAndSignIn(ctx, {
|
|
215
238
|
params: args.params,
|
|
216
239
|
verifier: args.verifier,
|
|
217
240
|
generateTokens: true,
|
|
218
241
|
allowExtraProviders: options.allowExtraProviders
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to verify OAuth sign-in.");
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
kind: "signedIn",
|
|
248
|
+
signedIn: result
|
|
249
|
+
};
|
|
250
|
+
}
|
|
223
251
|
const redirect = new URL((readConfigSync(envOptionalString("CUSTOM_AUTH_SITE_URL")) ?? requireEnv("CONVEX_SITE_URL")) + `/api/auth/signin/${provider.id}`);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
})
|
|
252
|
+
let verifier;
|
|
253
|
+
try {
|
|
254
|
+
verifier = await callVerifier(ctx);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to create verifier.");
|
|
257
|
+
}
|
|
228
258
|
redirect.searchParams.set("code", verifier);
|
|
229
259
|
if (args.params?.redirectTo !== void 0) {
|
|
230
|
-
if (typeof args.params.redirectTo !== "string")
|
|
260
|
+
if (typeof args.params.redirectTo !== "string") throw toConvexError(authFlowError("INVALID_REDIRECT", `Expected \`redirectTo\` to be a string, got ${describeUnknown(args.params.redirectTo)}`));
|
|
231
261
|
redirect.searchParams.set("redirectTo", args.params.redirectTo);
|
|
232
262
|
}
|
|
233
263
|
return {
|
|
@@ -235,27 +265,32 @@ function handleOAuthProviderFx(ctx, provider, args, options) {
|
|
|
235
265
|
redirect: redirect.toString(),
|
|
236
266
|
verifier
|
|
237
267
|
};
|
|
238
|
-
})
|
|
268
|
+
});
|
|
239
269
|
}
|
|
240
|
-
function handleSsoProviderFx(ctx, args, options) {
|
|
241
|
-
return
|
|
270
|
+
async function handleSsoProviderFx(ctx, args, options) {
|
|
271
|
+
return withSpan("convex-auth.signin.sso", {}, async () => {
|
|
242
272
|
const normalizedParams = normalizeVerificationParams(args.params);
|
|
243
273
|
const connectionId = normalizedParams.connectionId;
|
|
244
|
-
if (!connectionId || typeof connectionId !== "string")
|
|
245
|
-
|
|
246
|
-
try
|
|
247
|
-
|
|
248
|
-
|
|
274
|
+
if (!connectionId || typeof connectionId !== "string") throw toConvexError(authFlowError("SIGN_IN_MISSING_PARAMS", "connectionId is required for SSO sign-in."));
|
|
275
|
+
let protocol = (normalizedParams.protocol === "oidc" || normalizedParams.protocol === "saml" ? normalizedParams.protocol : void 0) ?? (options.resolveSsoProtocol ? await (async () => {
|
|
276
|
+
try {
|
|
277
|
+
return await options.resolveSsoProtocol(ctx, connectionId);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to resolve SSO protocol.");
|
|
280
|
+
}
|
|
281
|
+
})() : "oidc");
|
|
249
282
|
log("DEBUG", "[group-sso] signin:resolved", {
|
|
250
283
|
connectionId,
|
|
251
284
|
protocol,
|
|
252
285
|
redirectTo: typeof args.params?.redirectTo === "string" ? args.params.redirectTo : void 0
|
|
253
286
|
});
|
|
254
|
-
if (protocol !== "oidc" && protocol !== "saml")
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
})
|
|
287
|
+
if (protocol !== "oidc" && protocol !== "saml") throw toConvexError(authFlowError("SIGN_IN_MISSING_PARAMS", `Invalid SSO protocol: ${protocol}. Expected "oidc" or "saml".`));
|
|
288
|
+
let verifier;
|
|
289
|
+
try {
|
|
290
|
+
verifier = await callVerifier(ctx);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
throw asConvexError(error, "INTERNAL_ERROR", "Failed to create verifier.");
|
|
293
|
+
}
|
|
259
294
|
const siteUrl = readConfigSync(envOptionalString("CUSTOM_AUTH_SITE_URL")) ?? requireEnv("CONVEX_SITE_URL");
|
|
260
295
|
const redirect = new URL(`${siteUrl}/api/auth/connections/${connectionId}/${protocol}/signin`);
|
|
261
296
|
redirect.searchParams.set("code", verifier);
|
|
@@ -271,7 +306,7 @@ function handleSsoProviderFx(ctx, args, options) {
|
|
|
271
306
|
redirect: redirect.toString(),
|
|
272
307
|
verifier
|
|
273
308
|
};
|
|
274
|
-
})
|
|
309
|
+
});
|
|
275
310
|
}
|
|
276
311
|
|
|
277
312
|
//#endregion
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { ComponentCtx, ComponentReadCtx } from "../componentContext.js";
|
|
1
2
|
import { ConvexAuthMaterializedConfig, GroupConnectionDeprovisionMode, GroupConnectionPolicy, GroupConnectionPolicyPatch, OIDCClaimMapping } from "../types.js";
|
|
2
3
|
import { AuditEventRecord, ConnectionDomainRecord, GroupConnectionDomainLookupRecord, GroupConnectionListResult, GroupConnectionRecord, ScimConfigRecord, ScimIdentityRecord, WebhookDeliveryRecord, WebhookEndpointRecord } from "../contract.js";
|
|
3
|
-
import * as convex_server320 from "convex/server";
|
|
4
4
|
import { GenericActionCtx, GenericDataModel } from "convex/server";
|
|
5
5
|
|
|
6
6
|
//#region src/server/sso/domain.d.ts
|
|
7
|
-
type ComponentCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">;
|
|
8
|
-
type ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery">;
|
|
9
7
|
type DomainDeps = {
|
|
10
8
|
config: ConvexAuthMaterializedConfig & {
|
|
11
9
|
extraProviders?: unknown[];
|
|
@@ -309,16 +307,9 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
309
307
|
}>;
|
|
310
308
|
};
|
|
311
309
|
policy: {
|
|
312
|
-
get: (ctx:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
update: (ctx: {
|
|
316
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
317
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
318
|
-
}, groupId: string, patch: GroupConnectionPolicyPatch) => Promise<GroupConnectionPolicy>;
|
|
319
|
-
validate: (ctx: {
|
|
320
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
321
|
-
}, groupId: string) => Promise<{
|
|
310
|
+
get: (ctx: ComponentReadCtx, groupId: string) => Promise<GroupConnectionPolicy>;
|
|
311
|
+
update: (ctx: ComponentCtx, groupId: string, patch: GroupConnectionPolicyPatch) => Promise<GroupConnectionPolicy>;
|
|
312
|
+
validate: (ctx: ComponentReadCtx, groupId: string) => Promise<{
|
|
322
313
|
ok: boolean;
|
|
323
314
|
groupId: string;
|
|
324
315
|
checks: {
|
|
@@ -429,10 +420,7 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
429
420
|
}>;
|
|
430
421
|
};
|
|
431
422
|
scim: {
|
|
432
|
-
configure: (ctx: {
|
|
433
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
434
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
435
|
-
}, data: {
|
|
423
|
+
configure: (ctx: ComponentCtx, data: {
|
|
436
424
|
connectionId: string;
|
|
437
425
|
status?: "draft" | "active" | "disabled";
|
|
438
426
|
security?: {
|
|
@@ -459,9 +447,7 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
459
447
|
basePath: string;
|
|
460
448
|
token: string;
|
|
461
449
|
}>;
|
|
462
|
-
get: (ctx: {
|
|
463
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
464
|
-
}, connectionId: string) => Promise<{
|
|
450
|
+
get: (ctx: ComponentReadCtx, connectionId: string) => Promise<{
|
|
465
451
|
security: {
|
|
466
452
|
maxRequestSize?: number;
|
|
467
453
|
} | undefined;
|
|
@@ -490,9 +476,7 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
490
476
|
lastRotatedAt?: number;
|
|
491
477
|
extend?: unknown;
|
|
492
478
|
} | null>;
|
|
493
|
-
status: (ctx: {
|
|
494
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
495
|
-
}, connectionId: string) => Promise<{
|
|
479
|
+
status: (ctx: ComponentReadCtx, connectionId: string) => Promise<{
|
|
496
480
|
connectionId: string;
|
|
497
481
|
configured: boolean;
|
|
498
482
|
ready: boolean;
|
|
@@ -516,12 +500,8 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
516
500
|
etag: boolean;
|
|
517
501
|
} | undefined;
|
|
518
502
|
}>;
|
|
519
|
-
getConfigByToken: (ctx:
|
|
520
|
-
|
|
521
|
-
}, token: string) => Promise<ScimConfigRecord | null>;
|
|
522
|
-
validate: (ctx: {
|
|
523
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
524
|
-
}, connectionId: string) => Promise<{
|
|
503
|
+
getConfigByToken: (ctx: ComponentReadCtx, token: string) => Promise<ScimConfigRecord | null>;
|
|
504
|
+
validate: (ctx: ComponentReadCtx, connectionId: string) => Promise<{
|
|
525
505
|
ok: boolean;
|
|
526
506
|
connectionId: string;
|
|
527
507
|
checks: {
|
|
@@ -553,17 +533,12 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
553
533
|
}[];
|
|
554
534
|
}>;
|
|
555
535
|
identity: {
|
|
556
|
-
get: (ctx: {
|
|
557
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
558
|
-
}, data: {
|
|
536
|
+
get: (ctx: ComponentReadCtx, data: {
|
|
559
537
|
connectionId: string;
|
|
560
538
|
resourceType: "user" | "group";
|
|
561
539
|
externalId: string;
|
|
562
540
|
}) => Promise<ScimIdentityRecord | null>;
|
|
563
|
-
upsert: (ctx: {
|
|
564
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
565
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
566
|
-
}, data: {
|
|
541
|
+
upsert: (ctx: ComponentCtx, data: {
|
|
567
542
|
connectionId: string;
|
|
568
543
|
groupId: string;
|
|
569
544
|
resourceType: "user" | "group";
|
|
@@ -597,13 +572,8 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
597
572
|
};
|
|
598
573
|
webhook: {
|
|
599
574
|
endpoint: {
|
|
600
|
-
get: (ctx:
|
|
601
|
-
|
|
602
|
-
}, endpointId: string) => Promise<WebhookEndpointRecord | null>;
|
|
603
|
-
create: (ctx: {
|
|
604
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
605
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
606
|
-
}, data: {
|
|
575
|
+
get: (ctx: ComponentReadCtx, endpointId: string) => Promise<WebhookEndpointRecord | null>;
|
|
576
|
+
create: (ctx: ComponentCtx, data: {
|
|
607
577
|
connectionId: string;
|
|
608
578
|
url: string;
|
|
609
579
|
secret: string;
|
|
@@ -612,43 +582,25 @@ declare function createGroupConnectionDomain<TDeps extends DomainDeps>(deps: TDe
|
|
|
612
582
|
}) => Promise<{
|
|
613
583
|
endpointId: string;
|
|
614
584
|
}>;
|
|
615
|
-
list: (ctx:
|
|
616
|
-
|
|
617
|
-
}, connectionId: string) => Promise<WebhookEndpointRecord[]>;
|
|
618
|
-
disable: (ctx: {
|
|
619
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
620
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
621
|
-
}, endpointId: string) => Promise<{
|
|
585
|
+
list: (ctx: ComponentReadCtx, connectionId: string) => Promise<WebhookEndpointRecord[]>;
|
|
586
|
+
disable: (ctx: ComponentCtx, endpointId: string) => Promise<{
|
|
622
587
|
endpointId: string;
|
|
623
588
|
}>;
|
|
624
589
|
};
|
|
625
|
-
emit: (ctx: {
|
|
626
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
627
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
628
|
-
}, data: {
|
|
590
|
+
emit: (ctx: ComponentCtx, data: {
|
|
629
591
|
connectionId: string;
|
|
630
592
|
eventType: string;
|
|
631
593
|
payload: Record<string, unknown>;
|
|
632
594
|
auditEventId?: string;
|
|
633
595
|
}) => Promise<void>;
|
|
634
596
|
delivery: {
|
|
635
|
-
list: (ctx: {
|
|
636
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
637
|
-
}, data: {
|
|
597
|
+
list: (ctx: ComponentReadCtx, data: {
|
|
638
598
|
connectionId: string;
|
|
639
599
|
limit?: number;
|
|
640
600
|
}) => Promise<WebhookDeliveryRecord[]>;
|
|
641
|
-
listReady: (ctx:
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
markDelivered: (ctx: {
|
|
645
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
646
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
647
|
-
}, deliveryId: string, responseStatus?: number) => Promise<void>;
|
|
648
|
-
markFailed: (ctx: {
|
|
649
|
-
runQuery: <Query extends convex_server320.FunctionReference<"query", "public" | "internal">>(query: Query, ...args: convex_server320.OptionalRestArgs<Query>) => Promise<convex_server320.FunctionReturnType<Query>>;
|
|
650
|
-
runMutation: <Mutation extends convex_server320.FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: convex_server320.OptionalRestArgs<Mutation>) => Promise<convex_server320.FunctionReturnType<Mutation>>;
|
|
651
|
-
}, deliveryId: string, data: {
|
|
601
|
+
listReady: (ctx: ComponentReadCtx, limit?: number) => Promise<WebhookDeliveryRecord[]>;
|
|
602
|
+
markDelivered: (ctx: ComponentCtx, deliveryId: string, responseStatus?: number) => Promise<void>;
|
|
603
|
+
markFailed: (ctx: ComponentCtx, deliveryId: string, data: {
|
|
652
604
|
attemptCount: number;
|
|
653
605
|
responseStatus?: number;
|
|
654
606
|
error?: string;
|