@robelest/convex-auth 0.0.4-preview.21 → 0.0.4-preview.22
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/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/model.d.ts +10 -10
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/schema.d.ts +51 -51
- package/dist/component/server/auth.d.ts +71 -25
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +13 -27
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/runtime.d.ts +2 -2
- package/dist/server/auth.d.ts +70 -24
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +13 -27
- package/dist/server/auth.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/mounts.d.ts +12 -12
- package/dist/server/mutations/account.d.ts +6 -6
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/invalidate.d.ts +4 -4
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/oauth.d.ts +9 -9
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/refresh.d.ts +3 -3
- package/dist/server/mutations/refresh.d.ts.map +1 -1
- package/dist/server/mutations/register.d.ts +11 -11
- package/dist/server/mutations/retrieve.d.ts +6 -6
- package/dist/server/mutations/retrieve.d.ts.map +1 -1
- package/dist/server/mutations/signature.d.ts +4 -4
- package/dist/server/mutations/signin.d.ts +5 -5
- package/dist/server/mutations/store.d.ts +100 -100
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/verify.d.ts +10 -10
- package/dist/server/runtime.d.ts +15 -15
- package/package.json +2 -2
- package/src/cli/index.ts +1 -1
- package/src/component/index.ts +1 -0
- package/src/server/auth.ts +103 -59
- package/src/server/index.ts +2 -0
package/src/server/auth.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type { GenericId } from "convex/values";
|
|
|
9
9
|
|
|
10
10
|
import type { AuthApiRefs } from "../client/index";
|
|
11
11
|
import { Auth as AuthFactory } from "./runtime";
|
|
12
|
-
import { Fx } from "@robelest/fx";
|
|
13
12
|
import { AuthError } from "./authError";
|
|
14
13
|
import type { Doc } from "./types";
|
|
15
14
|
import type {
|
|
@@ -35,6 +34,9 @@ import type {
|
|
|
35
34
|
*/
|
|
36
35
|
export type AuthConfig = Omit<ConvexAuthConfig, "component">;
|
|
37
36
|
|
|
37
|
+
/** Canonical user document type exposed by Convex Auth. */
|
|
38
|
+
export type UserDoc = Doc<"User">;
|
|
39
|
+
|
|
38
40
|
type MemberApiWithAuthorization<
|
|
39
41
|
TAuthorization extends AuthAuthorizationConfig | undefined,
|
|
40
42
|
> = Omit<
|
|
@@ -197,17 +199,32 @@ export type AuthApiBase<
|
|
|
197
199
|
};
|
|
198
200
|
|
|
199
201
|
/**
|
|
200
|
-
* Resolved auth context injected into `ctx.auth` by `auth.ctx()
|
|
202
|
+
* Resolved auth context injected into `ctx.auth` by `auth.ctx()` and
|
|
203
|
+
* {@link AuthCtx}. Also the expected return shape for custom
|
|
204
|
+
* {@link AuthCtxConfig.authResolve | authResolve} hooks.
|
|
201
205
|
*
|
|
202
206
|
* - `null` when unauthenticated.
|
|
203
207
|
* - `groupId` is `null` when the user has no active group set.
|
|
204
208
|
* - `role` / `grants` are `null` / `[]` when no active group or no membership.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```ts
|
|
212
|
+
* import type { AuthResolvedContext } from "@robelest/convex-auth/server";
|
|
213
|
+
*
|
|
214
|
+
* const mockAuth: AuthResolvedContext = {
|
|
215
|
+
* userId: "user123" as Id<"User">,
|
|
216
|
+
* user: { _id: "user123", email: "test@example.com" },
|
|
217
|
+
* groupId: "group456",
|
|
218
|
+
* role: "admin",
|
|
219
|
+
* grants: ["read", "write"],
|
|
220
|
+
* };
|
|
221
|
+
* ```
|
|
205
222
|
*/
|
|
206
223
|
export type AuthResolvedContext = {
|
|
207
224
|
/** The authenticated user's document ID. */
|
|
208
|
-
userId:
|
|
225
|
+
userId: GenericId<"User">;
|
|
209
226
|
/** The authenticated user's full document. */
|
|
210
|
-
user:
|
|
227
|
+
user: UserDoc;
|
|
211
228
|
/** The user's active group ID, or `null` if none set. */
|
|
212
229
|
groupId: string | null;
|
|
213
230
|
/** The user's primary role in the active group, or `null`. */
|
|
@@ -216,6 +233,20 @@ export type AuthResolvedContext = {
|
|
|
216
233
|
grants: string[];
|
|
217
234
|
};
|
|
218
235
|
|
|
236
|
+
type AuthCtxBase = {
|
|
237
|
+
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
type RequiredAuthCtxState = AuthCtxBase & AuthResolvedContext;
|
|
241
|
+
|
|
242
|
+
type OptionalAuthCtxState = AuthCtxBase & {
|
|
243
|
+
userId: GenericId<"User"> | null;
|
|
244
|
+
user: UserDoc | null;
|
|
245
|
+
groupId: string | null;
|
|
246
|
+
role: string | null;
|
|
247
|
+
grants: string[];
|
|
248
|
+
};
|
|
249
|
+
|
|
219
250
|
type InternalSsoApi = ReturnType<typeof AuthFactory>["auth"]["sso"];
|
|
220
251
|
|
|
221
252
|
type PublicSsoAdminApi = {
|
|
@@ -370,7 +401,7 @@ export type InferClientApi<T> =
|
|
|
370
401
|
: AuthApiRefs;
|
|
371
402
|
|
|
372
403
|
/** @internal */
|
|
373
|
-
export type AuthLike = Pick<AuthApiBase, "user">;
|
|
404
|
+
export type AuthLike = Pick<AuthApiBase, "user" | "member">;
|
|
374
405
|
|
|
375
406
|
// ============================================================================
|
|
376
407
|
// Auth setup APIs
|
|
@@ -417,7 +448,7 @@ export type AuthLike = Pick<AuthApiBase, "user">;
|
|
|
417
448
|
* 3. `user.getActiveGroup(ctx, { userId })` → groupId or null
|
|
418
449
|
* 4. If groupId → `member.resolve(ctx, { userId, groupId })` → role + grants
|
|
419
450
|
*/
|
|
420
|
-
async function resolveAuthContext(auth:
|
|
451
|
+
async function resolveAuthContext(auth: AuthLike, ctx: any) {
|
|
421
452
|
const userId = await auth.user.id(ctx);
|
|
422
453
|
if (!userId) return null;
|
|
423
454
|
const user = await auth.user.get(ctx, userId);
|
|
@@ -645,9 +676,6 @@ export function createAuth<
|
|
|
645
676
|
// AuthCtx — ctx enrichment for customQuery / customMutation
|
|
646
677
|
// ============================================================================
|
|
647
678
|
|
|
648
|
-
/** Canonical user document type exposed by Convex Auth. */
|
|
649
|
-
export type UserDoc = Doc<"User">;
|
|
650
|
-
|
|
651
679
|
/**
|
|
652
680
|
* Configuration for {@link AuthCtx} context enrichment.
|
|
653
681
|
*
|
|
@@ -660,17 +688,54 @@ export type AuthCtxConfig<
|
|
|
660
688
|
/** Allow unauthenticated callers and return `userId: null` / `user: null`. */
|
|
661
689
|
optional?: boolean;
|
|
662
690
|
/**
|
|
663
|
-
* Attach additional derived fields to the auth context after the
|
|
691
|
+
* Attach additional derived fields to the auth context after the base auth
|
|
692
|
+
* context is resolved.
|
|
664
693
|
*/
|
|
665
|
-
resolve?: (
|
|
694
|
+
resolve?: (
|
|
695
|
+
ctx: any,
|
|
696
|
+
user: UserDoc,
|
|
697
|
+
auth: AuthResolvedContext,
|
|
698
|
+
) => Promise<TResolve> | TResolve;
|
|
699
|
+
/**
|
|
700
|
+
* Override or wrap the base auth resolution used by {@link AuthCtx}.
|
|
701
|
+
*
|
|
702
|
+
* Return `undefined` to fall back to the built-in resolver,
|
|
703
|
+
* `null` for an explicit unauthenticated state, or an
|
|
704
|
+
* {@link AuthResolvedContext} object to provide a pre-resolved auth state.
|
|
705
|
+
* This is useful for tests, proxy auth, impersonation flows, or any
|
|
706
|
+
* environment that needs to inject auth without depending on the standard
|
|
707
|
+
* Convex auth tables.
|
|
708
|
+
*
|
|
709
|
+
* @param ctx - The Convex function context.
|
|
710
|
+
* @param fallback - The built-in auth resolver used by {@link AuthCtx}.
|
|
711
|
+
* @returns Resolved auth state, `null`, or `undefined` to use the fallback.
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* const authCtx = AuthCtx(auth, {
|
|
716
|
+
* authResolve: async (ctx, fallback) => {
|
|
717
|
+
* const injected = getInjectedAuth(ctx);
|
|
718
|
+
* return injected ?? (await fallback());
|
|
719
|
+
* },
|
|
720
|
+
* });
|
|
721
|
+
* ```
|
|
722
|
+
*/
|
|
723
|
+
authResolve?: (
|
|
724
|
+
ctx: any,
|
|
725
|
+
fallback: () => Promise<AuthResolvedContext | null>,
|
|
726
|
+
) =>
|
|
727
|
+
| Promise<AuthResolvedContext | null | undefined>
|
|
728
|
+
| AuthResolvedContext
|
|
729
|
+
| null
|
|
730
|
+
| undefined;
|
|
666
731
|
};
|
|
667
732
|
|
|
668
733
|
/**
|
|
669
734
|
* Create a context enrichment for `customQuery` / `customMutation` — optional auth.
|
|
670
735
|
*
|
|
671
736
|
* When `optional: true` is set, unauthenticated requests are allowed.
|
|
672
|
-
* The enriched `ctx.auth` will have `userId: null
|
|
673
|
-
* for unauthenticated callers.
|
|
737
|
+
* The enriched `ctx.auth` will have `userId: null`, `user: null`,
|
|
738
|
+
* `groupId: null`, `role: null`, and `grants: []` for unauthenticated callers.
|
|
674
739
|
*
|
|
675
740
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
676
741
|
* @param config - Configuration with `optional: true` and an optional
|
|
@@ -701,11 +766,7 @@ export function AuthCtx<
|
|
|
701
766
|
_extra?: any,
|
|
702
767
|
) => Promise<{
|
|
703
768
|
ctx: {
|
|
704
|
-
auth:
|
|
705
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
706
|
-
userId: GenericId<"User"> | null;
|
|
707
|
-
user: UserDoc | null;
|
|
708
|
-
} & TResolve;
|
|
769
|
+
auth: OptionalAuthCtxState & TResolve;
|
|
709
770
|
};
|
|
710
771
|
args: {};
|
|
711
772
|
}>;
|
|
@@ -715,8 +776,9 @@ export function AuthCtx<
|
|
|
715
776
|
*
|
|
716
777
|
* When `optional` is omitted or `false`, the inferred type is the authenticated
|
|
717
778
|
* auth shape. At runtime this helper still resolves instead of throwing, so if
|
|
718
|
-
* no user is signed in the returned `ctx.auth.userId`
|
|
719
|
-
* `null
|
|
779
|
+
* no user is signed in the returned `ctx.auth.userId` / `ctx.auth.user` are
|
|
780
|
+
* `null`, `ctx.auth.groupId` / `ctx.auth.role` are `null`, and
|
|
781
|
+
* `ctx.auth.grants` is `[]`.
|
|
720
782
|
*
|
|
721
783
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
722
784
|
* @param config - Optional configuration with a `resolve` callback
|
|
@@ -746,11 +808,7 @@ export function AuthCtx<
|
|
|
746
808
|
_extra?: any,
|
|
747
809
|
) => Promise<{
|
|
748
810
|
ctx: {
|
|
749
|
-
auth:
|
|
750
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
751
|
-
userId: GenericId<"User">;
|
|
752
|
-
user: UserDoc;
|
|
753
|
-
} & TResolve;
|
|
811
|
+
auth: RequiredAuthCtxState & TResolve;
|
|
754
812
|
};
|
|
755
813
|
args: {};
|
|
756
814
|
}>;
|
|
@@ -761,39 +819,25 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
|
|
|
761
819
|
args: {},
|
|
762
820
|
input: async (ctx: any, _args: any, _extra?: any) => {
|
|
763
821
|
const nativeAuth = ctx.auth;
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const userId = await auth.user.id(ctx);
|
|
773
|
-
if (!userId) {
|
|
774
|
-
return null;
|
|
775
|
-
}
|
|
776
|
-
const user = await auth.user.get(ctx, userId);
|
|
777
|
-
return { userId, user };
|
|
778
|
-
},
|
|
779
|
-
required: async () => {
|
|
780
|
-
const userId = await auth.user.id(ctx);
|
|
781
|
-
if (!userId) {
|
|
782
|
-
return null;
|
|
783
|
-
}
|
|
784
|
-
const user = await auth.user.get(ctx, userId);
|
|
785
|
-
return { userId, user };
|
|
786
|
-
},
|
|
787
|
-
}),
|
|
788
|
-
);
|
|
822
|
+
const getUserIdentity = nativeAuth.getUserIdentity.bind(nativeAuth);
|
|
823
|
+
const fallback = () => resolveAuthContext(auth, ctx);
|
|
824
|
+
|
|
825
|
+
const authOverride = config?.authResolve
|
|
826
|
+
? await config.authResolve(ctx, fallback)
|
|
827
|
+
: undefined;
|
|
828
|
+
const resolved =
|
|
829
|
+
authOverride === undefined ? await fallback() : authOverride;
|
|
789
830
|
|
|
790
|
-
if (
|
|
831
|
+
if (resolved === null) {
|
|
791
832
|
return {
|
|
792
833
|
ctx: {
|
|
793
834
|
auth: {
|
|
794
|
-
getUserIdentity
|
|
835
|
+
getUserIdentity,
|
|
795
836
|
userId: null,
|
|
796
837
|
user: null,
|
|
838
|
+
groupId: null,
|
|
839
|
+
role: null,
|
|
840
|
+
grants: [],
|
|
797
841
|
},
|
|
798
842
|
},
|
|
799
843
|
args: {},
|
|
@@ -801,15 +845,14 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
|
|
|
801
845
|
}
|
|
802
846
|
|
|
803
847
|
const extra = config?.resolve
|
|
804
|
-
? await config.resolve(ctx,
|
|
848
|
+
? await config.resolve(ctx, resolved.user, resolved)
|
|
805
849
|
: {};
|
|
806
850
|
|
|
807
851
|
return {
|
|
808
852
|
ctx: {
|
|
809
853
|
auth: {
|
|
810
|
-
getUserIdentity
|
|
811
|
-
|
|
812
|
-
user: userContext.user,
|
|
854
|
+
getUserIdentity,
|
|
855
|
+
...resolved,
|
|
813
856
|
...extra,
|
|
814
857
|
},
|
|
815
858
|
},
|
|
@@ -824,9 +867,10 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
|
|
|
824
867
|
*
|
|
825
868
|
* Use this to type function parameters or variables that receive the
|
|
826
869
|
* enriched auth context produced by `AuthCtx`. The inferred type includes
|
|
827
|
-
* `userId`, `user`, `getUserIdentity`, and any
|
|
828
|
-
* by the `resolve` callback. This is the generic
|
|
829
|
-
* enriched auth shape without manually duplicating
|
|
870
|
+
* `userId`, `user`, `groupId`, `role`, `grants`, `getUserIdentity`, and any
|
|
871
|
+
* additional fields added by the `resolve` callback. This is the generic
|
|
872
|
+
* utility for reusing the enriched auth shape without manually duplicating
|
|
873
|
+
* conditional auth types.
|
|
830
874
|
*
|
|
831
875
|
* @typeParam T - An `AuthCtx` return value (must have an `input` method
|
|
832
876
|
* that returns `{ ctx: { auth: ... } }`).
|