@thecodeorigin/auth 0.0.8 → 0.0.9
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/contract/index.d.mts +62 -2
- package/dist/contract/index.d.ts +62 -2
- package/dist/contract/index.mjs +30 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +1 -0
- package/dist/runtime/app/composables/useAuth.d.ts +13 -0
- package/dist/runtime/app/composables/useAuth.js +7 -0
- package/dist/runtime/app/composables/useCasl.d.ts +9 -6
- package/dist/runtime/app/middleware/auth.global.d.ts +1 -1
- package/dist/runtime/app/middleware/casl.global.d.ts +1 -1
- package/dist/runtime/app/plugins/0.session.d.ts +1 -1
- package/dist/runtime/app/plugins/ability.d.ts +2 -2
- package/dist/runtime/server/api/_auth/impersonate.post.js +1 -0
- package/dist/runtime/server/api/_auth/organizations/[orgId]/users.get.d.ts +10 -0
- package/dist/runtime/server/api/_auth/organizations/[orgId]/users.get.js +25 -0
- package/dist/runtime/server/api/_auth/organizations/switch.post.js +19 -9
- package/dist/runtime/server/index.d.ts +6 -1
- package/dist/runtime/server/index.js +13 -1
- package/dist/runtime/server/routes/callback.get.js +1 -0
- package/dist/runtime/server/utils/ability.d.ts +15 -0
- package/dist/runtime/server/utils/ability.js +25 -0
- package/dist/runtime/server/utils/directory.d.ts +3 -0
- package/dist/runtime/server/utils/directory.js +14 -0
- package/dist/runtime/server/utils/handlers.d.ts +4 -1
- package/dist/runtime/server/utils/handlers.js +34 -4
- package/dist/runtime/server/utils/idp.d.ts +2 -0
- package/dist/runtime/server/utils/idp.js +11 -0
- package/dist/runtime/server/utils/integration-key.d.ts +13 -0
- package/dist/runtime/server/utils/integration-key.js +27 -0
- package/dist/runtime/server/utils/session.d.ts +11 -37
- package/dist/runtime/server/utils/session.js +49 -16
- package/package.json +29 -27
|
@@ -55,6 +55,66 @@ declare const ImpersonationCandidateSchema: z.ZodObject<{
|
|
|
55
55
|
image: z.ZodNullable<z.ZodString>;
|
|
56
56
|
}, z.core.$strip>;
|
|
57
57
|
type ImpersonationCandidate = z.infer<typeof ImpersonationCandidateSchema>;
|
|
58
|
+
declare const DirectoryUserSchema: z.ZodObject<{
|
|
59
|
+
sub: z.ZodString;
|
|
60
|
+
email: z.ZodString;
|
|
61
|
+
name: z.ZodNullable<z.ZodString>;
|
|
62
|
+
picture: z.ZodNullable<z.ZodString>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
type DirectoryUser = z.infer<typeof DirectoryUserSchema>;
|
|
65
|
+
declare const DirectoryUserPageSchema: z.ZodObject<{
|
|
66
|
+
items: z.ZodArray<z.ZodObject<{
|
|
67
|
+
sub: z.ZodString;
|
|
68
|
+
email: z.ZodString;
|
|
69
|
+
name: z.ZodNullable<z.ZodString>;
|
|
70
|
+
picture: z.ZodNullable<z.ZodString>;
|
|
71
|
+
}, z.core.$strip>>;
|
|
72
|
+
hasMore: z.ZodBoolean;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
type DirectoryUserPage = z.infer<typeof DirectoryUserPageSchema>;
|
|
75
|
+
declare const OrganizationContextSchema: z.ZodObject<{
|
|
76
|
+
organization: z.ZodObject<{
|
|
77
|
+
id: z.ZodString;
|
|
78
|
+
slug: z.ZodString;
|
|
79
|
+
name: z.ZodString;
|
|
80
|
+
role: z.ZodString;
|
|
81
|
+
personal: z.ZodBoolean;
|
|
82
|
+
}, z.core.$strip>;
|
|
83
|
+
organizations: z.ZodArray<z.ZodObject<{
|
|
84
|
+
id: z.ZodString;
|
|
85
|
+
slug: z.ZodString;
|
|
86
|
+
name: z.ZodString;
|
|
87
|
+
role: z.ZodString;
|
|
88
|
+
personal: z.ZodBoolean;
|
|
89
|
+
}, z.core.$strip>>;
|
|
90
|
+
abilities: z.ZodArray<z.ZodObject<{
|
|
91
|
+
action: z.ZodString;
|
|
92
|
+
subject: z.ZodString;
|
|
93
|
+
conditions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
94
|
+
}, z.core.$strip>>;
|
|
95
|
+
systemRole: z.ZodNullable<z.ZodString>;
|
|
96
|
+
entitlement: z.ZodNullable<z.ZodObject<{
|
|
97
|
+
product: z.ZodString;
|
|
98
|
+
plan: z.ZodString;
|
|
99
|
+
status: z.ZodString;
|
|
100
|
+
active: z.ZodBoolean;
|
|
101
|
+
currentPeriodEnd: z.ZodNullable<z.ZodNumber>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
type OrganizationContext = z.infer<typeof OrganizationContextSchema>;
|
|
105
|
+
declare const IntegrationKeyContextSchema: z.ZodObject<{
|
|
106
|
+
keyId: z.ZodString;
|
|
107
|
+
organizationId: z.ZodString;
|
|
108
|
+
clientId: z.ZodString;
|
|
109
|
+
audience: z.ZodLiteral<"vault">;
|
|
110
|
+
resourceType: z.ZodLiteral<"environment">;
|
|
111
|
+
resourceId: z.ZodString;
|
|
112
|
+
permissions: z.ZodArray<z.ZodLiteral<"secrets:read">>;
|
|
113
|
+
name: z.ZodString;
|
|
114
|
+
prefix: z.ZodString;
|
|
115
|
+
expiresAt: z.ZodNullable<z.ZodNumber>;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
type IntegrationKeyContext = z.infer<typeof IntegrationKeyContextSchema>;
|
|
58
118
|
/** Server-safe session projection — no tokens, no KV internals. */
|
|
59
119
|
interface ServerAuthSession {
|
|
60
120
|
sub: string;
|
|
@@ -113,5 +173,5 @@ declare const PublicSessionSchema: z.ZodObject<{
|
|
|
113
173
|
}, z.core.$strip>;
|
|
114
174
|
type PublicSession = z.infer<typeof PublicSessionSchema>;
|
|
115
175
|
|
|
116
|
-
export { AbilityRuleSchema, ImpersonationCandidateSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
|
117
|
-
export type { AbilityRule, ImpersonationCandidate, PublicSession, PublicUser, RpOrganization, ServerAuthSession, UserinfoClaims };
|
|
176
|
+
export { AbilityRuleSchema, DirectoryUserPageSchema, DirectoryUserSchema, ImpersonationCandidateSchema, IntegrationKeyContextSchema, OrganizationContextSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
|
177
|
+
export type { AbilityRule, DirectoryUser, DirectoryUserPage, ImpersonationCandidate, IntegrationKeyContext, OrganizationContext, PublicSession, PublicUser, RpOrganization, ServerAuthSession, UserinfoClaims };
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -55,6 +55,66 @@ declare const ImpersonationCandidateSchema: z.ZodObject<{
|
|
|
55
55
|
image: z.ZodNullable<z.ZodString>;
|
|
56
56
|
}, z.core.$strip>;
|
|
57
57
|
type ImpersonationCandidate = z.infer<typeof ImpersonationCandidateSchema>;
|
|
58
|
+
declare const DirectoryUserSchema: z.ZodObject<{
|
|
59
|
+
sub: z.ZodString;
|
|
60
|
+
email: z.ZodString;
|
|
61
|
+
name: z.ZodNullable<z.ZodString>;
|
|
62
|
+
picture: z.ZodNullable<z.ZodString>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
type DirectoryUser = z.infer<typeof DirectoryUserSchema>;
|
|
65
|
+
declare const DirectoryUserPageSchema: z.ZodObject<{
|
|
66
|
+
items: z.ZodArray<z.ZodObject<{
|
|
67
|
+
sub: z.ZodString;
|
|
68
|
+
email: z.ZodString;
|
|
69
|
+
name: z.ZodNullable<z.ZodString>;
|
|
70
|
+
picture: z.ZodNullable<z.ZodString>;
|
|
71
|
+
}, z.core.$strip>>;
|
|
72
|
+
hasMore: z.ZodBoolean;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
type DirectoryUserPage = z.infer<typeof DirectoryUserPageSchema>;
|
|
75
|
+
declare const OrganizationContextSchema: z.ZodObject<{
|
|
76
|
+
organization: z.ZodObject<{
|
|
77
|
+
id: z.ZodString;
|
|
78
|
+
slug: z.ZodString;
|
|
79
|
+
name: z.ZodString;
|
|
80
|
+
role: z.ZodString;
|
|
81
|
+
personal: z.ZodBoolean;
|
|
82
|
+
}, z.core.$strip>;
|
|
83
|
+
organizations: z.ZodArray<z.ZodObject<{
|
|
84
|
+
id: z.ZodString;
|
|
85
|
+
slug: z.ZodString;
|
|
86
|
+
name: z.ZodString;
|
|
87
|
+
role: z.ZodString;
|
|
88
|
+
personal: z.ZodBoolean;
|
|
89
|
+
}, z.core.$strip>>;
|
|
90
|
+
abilities: z.ZodArray<z.ZodObject<{
|
|
91
|
+
action: z.ZodString;
|
|
92
|
+
subject: z.ZodString;
|
|
93
|
+
conditions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
94
|
+
}, z.core.$strip>>;
|
|
95
|
+
systemRole: z.ZodNullable<z.ZodString>;
|
|
96
|
+
entitlement: z.ZodNullable<z.ZodObject<{
|
|
97
|
+
product: z.ZodString;
|
|
98
|
+
plan: z.ZodString;
|
|
99
|
+
status: z.ZodString;
|
|
100
|
+
active: z.ZodBoolean;
|
|
101
|
+
currentPeriodEnd: z.ZodNullable<z.ZodNumber>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
type OrganizationContext = z.infer<typeof OrganizationContextSchema>;
|
|
105
|
+
declare const IntegrationKeyContextSchema: z.ZodObject<{
|
|
106
|
+
keyId: z.ZodString;
|
|
107
|
+
organizationId: z.ZodString;
|
|
108
|
+
clientId: z.ZodString;
|
|
109
|
+
audience: z.ZodLiteral<"vault">;
|
|
110
|
+
resourceType: z.ZodLiteral<"environment">;
|
|
111
|
+
resourceId: z.ZodString;
|
|
112
|
+
permissions: z.ZodArray<z.ZodLiteral<"secrets:read">>;
|
|
113
|
+
name: z.ZodString;
|
|
114
|
+
prefix: z.ZodString;
|
|
115
|
+
expiresAt: z.ZodNullable<z.ZodNumber>;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
type IntegrationKeyContext = z.infer<typeof IntegrationKeyContextSchema>;
|
|
58
118
|
/** Server-safe session projection — no tokens, no KV internals. */
|
|
59
119
|
interface ServerAuthSession {
|
|
60
120
|
sub: string;
|
|
@@ -113,5 +173,5 @@ declare const PublicSessionSchema: z.ZodObject<{
|
|
|
113
173
|
}, z.core.$strip>;
|
|
114
174
|
type PublicSession = z.infer<typeof PublicSessionSchema>;
|
|
115
175
|
|
|
116
|
-
export { AbilityRuleSchema, ImpersonationCandidateSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
|
117
|
-
export type { AbilityRule, ImpersonationCandidate, PublicSession, PublicUser, RpOrganization, ServerAuthSession, UserinfoClaims };
|
|
176
|
+
export { AbilityRuleSchema, DirectoryUserPageSchema, DirectoryUserSchema, ImpersonationCandidateSchema, IntegrationKeyContextSchema, OrganizationContextSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
|
177
|
+
export type { AbilityRule, DirectoryUser, DirectoryUserPage, ImpersonationCandidate, IntegrationKeyContext, OrganizationContext, PublicSession, PublicUser, RpOrganization, ServerAuthSession, UserinfoClaims };
|
package/dist/contract/index.mjs
CHANGED
|
@@ -39,6 +39,35 @@ const ImpersonationCandidateSchema = z.object({
|
|
|
39
39
|
name: z.string().nullable(),
|
|
40
40
|
image: z.string().nullable()
|
|
41
41
|
});
|
|
42
|
+
const DirectoryUserSchema = z.object({
|
|
43
|
+
sub: z.string().min(1),
|
|
44
|
+
email: z.string().email(),
|
|
45
|
+
name: z.string().nullable(),
|
|
46
|
+
picture: z.string().nullable()
|
|
47
|
+
});
|
|
48
|
+
const DirectoryUserPageSchema = z.object({
|
|
49
|
+
items: z.array(DirectoryUserSchema),
|
|
50
|
+
hasMore: z.boolean()
|
|
51
|
+
});
|
|
52
|
+
const OrganizationContextSchema = z.object({
|
|
53
|
+
organization: RpOrganizationSchema,
|
|
54
|
+
organizations: z.array(RpOrganizationSchema),
|
|
55
|
+
abilities: z.array(AbilityRuleSchema),
|
|
56
|
+
systemRole: z.string().nullable(),
|
|
57
|
+
entitlement: UserinfoClaimsSchema.shape.entitlement
|
|
58
|
+
});
|
|
59
|
+
const IntegrationKeyContextSchema = z.object({
|
|
60
|
+
keyId: z.string().min(1),
|
|
61
|
+
organizationId: z.string().min(1),
|
|
62
|
+
clientId: z.string().min(1),
|
|
63
|
+
audience: z.literal("vault"),
|
|
64
|
+
resourceType: z.literal("environment"),
|
|
65
|
+
resourceId: z.string().uuid(),
|
|
66
|
+
permissions: z.array(z.literal("secrets:read")).length(1),
|
|
67
|
+
name: z.string().min(1).max(100),
|
|
68
|
+
prefix: z.string().min(1),
|
|
69
|
+
expiresAt: z.number().int().positive().nullable()
|
|
70
|
+
});
|
|
42
71
|
const PublicSessionSchema = z.object({
|
|
43
72
|
user: PublicUserSchema,
|
|
44
73
|
abilities: z.array(AbilityRuleSchema),
|
|
@@ -49,4 +78,4 @@ const PublicSessionSchema = z.object({
|
|
|
49
78
|
impersonator: PublicUserSchema.nullable()
|
|
50
79
|
});
|
|
51
80
|
|
|
52
|
-
export { AbilityRuleSchema, ImpersonationCandidateSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
|
81
|
+
export { AbilityRuleSchema, DirectoryUserPageSchema, DirectoryUserSchema, ImpersonationCandidateSchema, IntegrationKeyContextSchema, OrganizationContextSchema, PublicSessionSchema, PublicUserSchema, RpOrganizationSchema, UserinfoClaimsSchema };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -46,6 +46,7 @@ const module$1 = defineNuxtModule({
|
|
|
46
46
|
["/api/_auth/sign-out", "sign-out.post"],
|
|
47
47
|
["/api/_auth/organizations", "organizations.get"],
|
|
48
48
|
["/api/_auth/organizations/switch", "organizations/switch.post"],
|
|
49
|
+
["/api/_auth/organizations/:orgId/users", "organizations/[orgId]/users.get"],
|
|
49
50
|
["/api/_auth/impersonatable-users", "impersonatable-users.get"],
|
|
50
51
|
["/api/_auth/impersonate", "impersonate.post"],
|
|
51
52
|
["/api/_auth/stop-impersonating", "stop-impersonating.post"]
|
|
@@ -91,6 +91,19 @@ export declare function useAuth(): {
|
|
|
91
91
|
isImpersonating: import("vue").ComputedRef<boolean>;
|
|
92
92
|
getOrganizations: () => RpOrganization[];
|
|
93
93
|
switchOrganization: (orgId: string) => Promise<void>;
|
|
94
|
+
getOrganizationUsers: (organizationId: string, query?: {
|
|
95
|
+
q?: string;
|
|
96
|
+
limit?: number;
|
|
97
|
+
offset?: number;
|
|
98
|
+
}) => Promise<{
|
|
99
|
+
items: {
|
|
100
|
+
sub: string;
|
|
101
|
+
email: string;
|
|
102
|
+
name: string | null;
|
|
103
|
+
picture: string | null;
|
|
104
|
+
}[];
|
|
105
|
+
hasMore: boolean;
|
|
106
|
+
}>;
|
|
94
107
|
getImpersonatableUsers: (query?: {
|
|
95
108
|
q?: string;
|
|
96
109
|
limit?: number;
|
|
@@ -20,6 +20,12 @@ export function useAuth() {
|
|
|
20
20
|
body: { organizationId: orgId }
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
+
function getOrganizationUsers(organizationId, query) {
|
|
24
|
+
return $fetch(
|
|
25
|
+
`/api/_auth/organizations/${encodeURIComponent(organizationId)}/users`,
|
|
26
|
+
{ query }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
23
29
|
function getImpersonatableUsers(query) {
|
|
24
30
|
return $fetch("/api/_auth/impersonatable-users", { query });
|
|
25
31
|
}
|
|
@@ -56,6 +62,7 @@ export function useAuth() {
|
|
|
56
62
|
isImpersonating,
|
|
57
63
|
getOrganizations,
|
|
58
64
|
switchOrganization,
|
|
65
|
+
getOrganizationUsers,
|
|
59
66
|
getImpersonatableUsers,
|
|
60
67
|
impersonate,
|
|
61
68
|
stopImpersonating,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Subject } from '@casl/ability';
|
|
2
|
+
interface BrowserAbility {
|
|
3
|
+
can: (action: string, subject: Subject, field?: string) => boolean;
|
|
4
|
+
cannot: (action: string, subject: Subject, field?: string) => boolean;
|
|
5
|
+
}
|
|
1
6
|
export declare function useCasl(): {
|
|
2
|
-
ability:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
can: (action: string, subject: string, field?: string) => boolean;
|
|
7
|
-
cannot: (action: string, subject: string, field?: string) => boolean;
|
|
7
|
+
ability: BrowserAbility;
|
|
8
|
+
can: (action: string, subject: Subject, field?: string) => boolean;
|
|
9
|
+
cannot: (action: string, subject: Subject, field?: string) => boolean;
|
|
8
10
|
};
|
|
11
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("
|
|
1
|
+
declare const _default: import("nuxt/app").RouteMiddleware;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("
|
|
1
|
+
declare const _default: import("nuxt/app").RouteMiddleware;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("
|
|
1
|
+
declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MongoAbility } from '@casl/ability';
|
|
2
|
-
declare const _default: import("
|
|
2
|
+
declare const _default: import("nuxt/app").Plugin<{
|
|
3
3
|
ability: MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>;
|
|
4
|
-
}> & import("
|
|
4
|
+
}> & import("nuxt/app").ObjectPlugin<{
|
|
5
5
|
ability: MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>;
|
|
6
6
|
}>;
|
|
7
7
|
export default _default;
|
|
@@ -28,6 +28,7 @@ export default defineEventHandler(async (event) => {
|
|
|
28
28
|
refreshToken: null,
|
|
29
29
|
idToken: null,
|
|
30
30
|
accessExpiresAt: res.expiresAt,
|
|
31
|
+
authorizationRefreshedAt: Date.now(),
|
|
31
32
|
isImpersonation: true,
|
|
32
33
|
impersonator: { sub: s.rec.user.sub, email: s.rec.user.email, name: s.rec.user.name, picture: s.rec.user.picture },
|
|
33
34
|
backupId
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getRouterParam, getValidatedQuery } from "h3";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { idpFetch } from "../../../../utils/idp.js";
|
|
4
|
+
import { readSessionRecord } from "../../../../utils/session.js";
|
|
5
|
+
const querySchema = z.object({
|
|
6
|
+
q: z.string().trim().min(2).max(200),
|
|
7
|
+
limit: z.coerce.number().int().min(1).max(20).default(20),
|
|
8
|
+
offset: z.coerce.number().int().min(0).default(0)
|
|
9
|
+
});
|
|
10
|
+
export default defineEventHandler(async (event) => {
|
|
11
|
+
const session = await readSessionRecord(event);
|
|
12
|
+
if (!session)
|
|
13
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
14
|
+
const orgId = getRouterParam(event, "orgId");
|
|
15
|
+
if (!orgId || orgId !== session.rec.activeOrg)
|
|
16
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
17
|
+
const query = await getValidatedQuery(event, querySchema.parse);
|
|
18
|
+
return idpFetch(
|
|
19
|
+
event,
|
|
20
|
+
session.id,
|
|
21
|
+
session.rec,
|
|
22
|
+
`/api/auth/rp/organizations/${encodeURIComponent(orgId)}/users`,
|
|
23
|
+
{ query }
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import { createError, defineEventHandler, readValidatedBody } from "h3";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { idpFetch } from "../../../utils/idp.js";
|
|
3
4
|
import { readSessionRecord, toPublicSession, writeSessionRecord } from "../../../utils/session.js";
|
|
4
|
-
const bodySchema = z.object({ organizationId: z.string() });
|
|
5
|
+
const bodySchema = z.object({ organizationId: z.string().min(1) });
|
|
5
6
|
export default defineEventHandler(async (event) => {
|
|
6
|
-
const
|
|
7
|
-
if (!
|
|
7
|
+
const session = await readSessionRecord(event);
|
|
8
|
+
if (!session)
|
|
8
9
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
9
10
|
const { organizationId } = await readValidatedBody(event, bodySchema.parse);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
const context = await idpFetch(
|
|
12
|
+
event,
|
|
13
|
+
session.id,
|
|
14
|
+
session.rec,
|
|
15
|
+
"/api/auth/rp/organizations/switch",
|
|
16
|
+
{ method: "POST", body: { organizationId } }
|
|
17
|
+
);
|
|
18
|
+
session.rec.activeOrg = context.organization.id;
|
|
19
|
+
session.rec.organizations = context.organizations;
|
|
20
|
+
session.rec.abilities = context.abilities;
|
|
21
|
+
session.rec.systemRole = context.systemRole;
|
|
22
|
+
session.rec.entitlement = context.entitlement;
|
|
23
|
+
session.rec.authorizationRefreshedAt = Date.now();
|
|
24
|
+
await writeSessionRecord(session.id, session.rec);
|
|
25
|
+
return toPublicSession(session.rec);
|
|
16
26
|
});
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { abilitySubject, canPerformStatic, createServerAbility, requireServerAbility, } from './utils/ability.js';
|
|
2
|
+
export type { ServerAbility } from './utils/ability.js';
|
|
3
|
+
export { resolveOrganizationUser } from './utils/directory.js';
|
|
4
|
+
export { defineAuthorizedHandler, defineSensitiveAuthorizedHandler, } from './utils/handlers.js';
|
|
5
|
+
export { verifyOrganizationIntegrationKey } from './utils/integration-key.js';
|
|
6
|
+
export { getFreshServerAuthSession, getServerAuthSession } from './utils/session.js';
|
|
2
7
|
export type { ServerAuthSession } from './utils/session.js';
|
|
@@ -1 +1,13 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
abilitySubject,
|
|
3
|
+
canPerformStatic,
|
|
4
|
+
createServerAbility,
|
|
5
|
+
requireServerAbility
|
|
6
|
+
} from "./utils/ability.js";
|
|
7
|
+
export { resolveOrganizationUser } from "./utils/directory.js";
|
|
8
|
+
export {
|
|
9
|
+
defineAuthorizedHandler,
|
|
10
|
+
defineSensitiveAuthorizedHandler
|
|
11
|
+
} from "./utils/handlers.js";
|
|
12
|
+
export { verifyOrganizationIntegrationKey } from "./utils/integration-key.js";
|
|
13
|
+
export { getFreshServerAuthSession, getServerAuthSession } from "./utils/session.js";
|
|
@@ -55,6 +55,7 @@ export default defineEventHandler(async (event) => {
|
|
|
55
55
|
refreshToken: tokens.refresh_token ?? null,
|
|
56
56
|
idToken: tokens.id_token ?? null,
|
|
57
57
|
accessExpiresAt: Date.now() + (tokens.expires_in ?? 3600) * 1e3,
|
|
58
|
+
authorizationRefreshedAt: Date.now(),
|
|
58
59
|
isImpersonation: false,
|
|
59
60
|
impersonator: null,
|
|
60
61
|
backupId: null
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MongoAbility, Subject } from '@casl/ability';
|
|
2
|
+
import type { ServerAuthSession } from './session.js';
|
|
3
|
+
export type ServerAbility = MongoAbility<[string, Subject]>;
|
|
4
|
+
export declare function createServerAbility(session: ServerAuthSession, options?: {
|
|
5
|
+
allowSystemAdmin?: boolean;
|
|
6
|
+
}): ServerAbility;
|
|
7
|
+
export declare function abilitySubject<Name extends string, Resource extends Record<string, unknown>>(name: Name, resource: Resource): Resource & import("@casl/ability").ForcedSubject<Name>;
|
|
8
|
+
export declare function requireServerAbility(session: ServerAuthSession, action: string, resource: Subject, options?: {
|
|
9
|
+
allowSystemAdmin?: boolean;
|
|
10
|
+
}): void;
|
|
11
|
+
/**
|
|
12
|
+
* Static checks are safe only for unconditional rules. Conditional rules must
|
|
13
|
+
* be evaluated through requireServerAbility() against a loaded resource.
|
|
14
|
+
*/
|
|
15
|
+
export declare function canPerformStatic(session: ServerAuthSession, action: string, subject: string): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createMongoAbility, subject as tagSubject } from "@casl/ability";
|
|
2
|
+
import { createError } from "h3";
|
|
3
|
+
export function createServerAbility(session, options = {}) {
|
|
4
|
+
const allowSystemAdmin = options.allowSystemAdmin ?? true;
|
|
5
|
+
const rules = allowSystemAdmin && session.systemRole === "admin" ? [{ action: "manage", subject: "all" }] : session.abilities.map((rule) => ({
|
|
6
|
+
action: rule.action,
|
|
7
|
+
subject: rule.subject,
|
|
8
|
+
...rule.conditions ? { conditions: rule.conditions } : {}
|
|
9
|
+
}));
|
|
10
|
+
return createMongoAbility(rules);
|
|
11
|
+
}
|
|
12
|
+
export function abilitySubject(name, resource) {
|
|
13
|
+
return tagSubject(name, resource);
|
|
14
|
+
}
|
|
15
|
+
export function requireServerAbility(session, action, resource, options = {}) {
|
|
16
|
+
if (!createServerAbility(session, options).can(action, resource))
|
|
17
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
18
|
+
}
|
|
19
|
+
export function canPerformStatic(session, action, subject) {
|
|
20
|
+
if (session.systemRole === "admin")
|
|
21
|
+
return true;
|
|
22
|
+
return session.abilities.some(
|
|
23
|
+
(rule) => !rule.conditions && (rule.action === action || rule.action === "manage") && (rule.subject === subject || rule.subject === "all")
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import { idpFetch } from "./idp.js";
|
|
3
|
+
import { readSessionRecord } from "./session.js";
|
|
4
|
+
export async function resolveOrganizationUser(event, organizationId, userId) {
|
|
5
|
+
const session = await readSessionRecord(event);
|
|
6
|
+
if (!session || organizationId !== session.rec.activeOrg)
|
|
7
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
8
|
+
return idpFetch(
|
|
9
|
+
event,
|
|
10
|
+
session.id,
|
|
11
|
+
session.rec,
|
|
12
|
+
`/api/auth/rp/organizations/${encodeURIComponent(organizationId)}/users/${encodeURIComponent(userId)}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
2
|
import type { ServerAuthSession } from './session.js';
|
|
3
3
|
export declare function defineAuthenticatedHandler<T>(handler: (event: H3Event, session: NonNullable<ServerAuthSession>) => Promise<T> | T): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<T>>;
|
|
4
|
-
export declare function defineAuthorizedHandler<T>(
|
|
4
|
+
export declare function defineAuthorizedHandler<T>(checks: string[], handler: (event: H3Event, ctx: {
|
|
5
|
+
session: NonNullable<ServerAuthSession>;
|
|
6
|
+
}) => Promise<T> | T): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<T>>;
|
|
7
|
+
export declare function defineSensitiveAuthorizedHandler<T>(checks: string[], handler: (event: H3Event, ctx: {
|
|
5
8
|
session: NonNullable<ServerAuthSession>;
|
|
6
9
|
}) => Promise<T> | T): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<T>>;
|
|
7
10
|
export declare function defineAdminHandler<T>(_checks: string[], handler: (event: H3Event, ctx: {
|
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import { createError, defineEventHandler } from "h3";
|
|
2
|
-
import {
|
|
2
|
+
import { canPerformStatic } from "./ability.js";
|
|
3
|
+
import { getFreshServerAuthSession } from "./session.js";
|
|
4
|
+
function parseCheck(check) {
|
|
5
|
+
const separator = check.lastIndexOf(":");
|
|
6
|
+
if (separator <= 0 || separator === check.length - 1)
|
|
7
|
+
throw createError({ statusCode: 500, statusMessage: `Invalid ability check: ${check}` });
|
|
8
|
+
return { subject: check.slice(0, separator), action: check.slice(separator + 1) };
|
|
9
|
+
}
|
|
3
10
|
export function defineAuthenticatedHandler(handler) {
|
|
4
11
|
return defineEventHandler(async (event) => {
|
|
5
|
-
const session = await
|
|
12
|
+
const session = await getFreshServerAuthSession(event);
|
|
6
13
|
if (!session)
|
|
7
14
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
8
15
|
event.context.activeOrganizationId = session.activeOrg;
|
|
9
16
|
return handler(event, session);
|
|
10
17
|
});
|
|
11
18
|
}
|
|
12
|
-
export function defineAuthorizedHandler(
|
|
13
|
-
return defineAuthenticatedHandler(async (event, session) =>
|
|
19
|
+
export function defineAuthorizedHandler(checks, handler) {
|
|
20
|
+
return defineAuthenticatedHandler(async (event, session) => {
|
|
21
|
+
const allowed = checks.some((check) => {
|
|
22
|
+
const { action, subject } = parseCheck(check);
|
|
23
|
+
return canPerformStatic(session, action, subject);
|
|
24
|
+
});
|
|
25
|
+
if (!allowed)
|
|
26
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
27
|
+
return handler(event, { session });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function defineSensitiveAuthorizedHandler(checks, handler) {
|
|
31
|
+
return defineEventHandler(async (event) => {
|
|
32
|
+
const session = await getFreshServerAuthSession(event, { force: true });
|
|
33
|
+
if (!session)
|
|
34
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
35
|
+
event.context.activeOrganizationId = session.activeOrg;
|
|
36
|
+
const allowed = checks.some((check) => {
|
|
37
|
+
const { action, subject } = parseCheck(check);
|
|
38
|
+
return canPerformStatic(session, action, subject);
|
|
39
|
+
});
|
|
40
|
+
if (!allowed)
|
|
41
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
42
|
+
return handler(event, { session });
|
|
43
|
+
});
|
|
14
44
|
}
|
|
15
45
|
export function defineAdminHandler(_checks, handler) {
|
|
16
46
|
return defineAuthenticatedHandler(async (event, session) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
+
import type { OrganizationContext } from '../../../contract/index.js';
|
|
2
3
|
import type { SessionRecord } from './session.js';
|
|
4
|
+
export declare function refreshAuthorizationContext(event: H3Event, id: string, rec: SessionRecord): Promise<OrganizationContext>;
|
|
3
5
|
/**
|
|
4
6
|
* Call an IdP path with the session's bearer token. Refreshes once on expiry/401.
|
|
5
7
|
* Persists the rotated record. Returns parsed JSON.
|
|
@@ -4,6 +4,17 @@ import { $fetch } from "ofetch";
|
|
|
4
4
|
import { idpBaseUrl } from "./oidc.js";
|
|
5
5
|
import { readSessionRecordById, writeSessionRecord } from "./session.js";
|
|
6
6
|
const SKEW_MS = 6e4;
|
|
7
|
+
export async function refreshAuthorizationContext(event, id, rec) {
|
|
8
|
+
if (!rec.activeOrg)
|
|
9
|
+
throw createError({ statusCode: 403, statusMessage: "No active organization" });
|
|
10
|
+
return idpFetch(
|
|
11
|
+
event,
|
|
12
|
+
id,
|
|
13
|
+
rec,
|
|
14
|
+
"/api/auth/rp/organizations/context",
|
|
15
|
+
{ method: "POST", body: { organizationId: rec.activeOrg } }
|
|
16
|
+
);
|
|
17
|
+
}
|
|
7
18
|
async function refresh(rec) {
|
|
8
19
|
if (rec.isImpersonation || !rec.refreshToken)
|
|
9
20
|
return false;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
export declare function verifyOrganizationIntegrationKey(_event: H3Event, key: string): Promise<{
|
|
3
|
+
keyId: string;
|
|
4
|
+
organizationId: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
audience: "vault";
|
|
7
|
+
resourceType: "environment";
|
|
8
|
+
resourceId: string;
|
|
9
|
+
permissions: "secrets:read"[];
|
|
10
|
+
name: string;
|
|
11
|
+
prefix: string;
|
|
12
|
+
expiresAt: number | null;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
import { $fetch } from "ofetch";
|
|
4
|
+
import { IntegrationKeyContextSchema } from "../../../contract";
|
|
5
|
+
import { idpBaseUrl } from "./oidc.js";
|
|
6
|
+
export async function verifyOrganizationIntegrationKey(_event, key) {
|
|
7
|
+
const config = useRuntimeConfig();
|
|
8
|
+
try {
|
|
9
|
+
const raw = await $fetch(`${idpBaseUrl()}/api/auth/rp/integration-keys/verify`, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: {
|
|
12
|
+
Authorization: `Basic ${btoa(`${config.public.auth.clientId}:${config.auth.clientSecret}`)}`
|
|
13
|
+
},
|
|
14
|
+
body: { key }
|
|
15
|
+
});
|
|
16
|
+
return IntegrationKeyContextSchema.parse(raw);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const status = error.response?.status;
|
|
19
|
+
if (status === 401 || status === 403 || status === 429) {
|
|
20
|
+
throw createError({
|
|
21
|
+
statusCode: status,
|
|
22
|
+
statusMessage: status === 429 ? "Rate limited" : "Invalid API token"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
throw createError({ statusCode: 503, statusMessage: "Authorization service unavailable" });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
-
import type { AbilityRule, PublicSession } from '../../../contract/index.js';
|
|
2
|
+
import type { AbilityRule, PublicSession, ServerAuthSession as ServerAuthSessionContract } from '../../../contract/index.js';
|
|
3
|
+
export declare const AUTHORIZATION_TTL_MS = 120000;
|
|
3
4
|
export interface SessionRecord {
|
|
4
5
|
sub: string;
|
|
5
6
|
user: {
|
|
@@ -17,6 +18,7 @@ export interface SessionRecord {
|
|
|
17
18
|
refreshToken: string | null;
|
|
18
19
|
idToken: string | null;
|
|
19
20
|
accessExpiresAt: number;
|
|
21
|
+
authorizationRefreshedAt: number;
|
|
20
22
|
isImpersonation: boolean;
|
|
21
23
|
impersonator: {
|
|
22
24
|
sub: string;
|
|
@@ -35,41 +37,13 @@ export declare function readSessionRecord(event: H3Event): Promise<{
|
|
|
35
37
|
export declare function setSessionCookie(event: H3Event, id: string): Promise<void>;
|
|
36
38
|
export declare function destroySession(event: H3Event): Promise<SessionRecord | null>;
|
|
37
39
|
export declare function readSessionRecordById(id: string): Promise<SessionRecord | null>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
action: string;
|
|
47
|
-
subject: string;
|
|
48
|
-
conditions?: Record<string, string> | undefined;
|
|
49
|
-
}[];
|
|
50
|
-
organizations: {
|
|
51
|
-
id: string;
|
|
52
|
-
slug: string;
|
|
53
|
-
name: string;
|
|
54
|
-
role: string;
|
|
55
|
-
personal: boolean;
|
|
56
|
-
}[];
|
|
57
|
-
activeOrg: string | null;
|
|
58
|
-
entitlement: {
|
|
59
|
-
product: string;
|
|
60
|
-
plan: string;
|
|
61
|
-
status: string;
|
|
62
|
-
active: boolean;
|
|
63
|
-
currentPeriodEnd: number | null;
|
|
64
|
-
} | null;
|
|
65
|
-
isImpersonation: boolean;
|
|
66
|
-
impersonator: {
|
|
67
|
-
sub: string;
|
|
68
|
-
email: string;
|
|
69
|
-
name: string | null;
|
|
70
|
-
picture: string | null;
|
|
71
|
-
} | null;
|
|
72
|
-
} | null>;
|
|
73
|
-
export type ServerAuthSession = NonNullable<Awaited<ReturnType<typeof getServerAuthSession>>>;
|
|
40
|
+
export type ServerAuthSession = ServerAuthSessionContract;
|
|
41
|
+
/** Server-safe session projection — no tokens or private refresh metadata. */
|
|
42
|
+
export declare function toServerAuthSession(rec: SessionRecord): ServerAuthSession;
|
|
43
|
+
/** Server-safe session for callers that explicitly do not need a live refresh. */
|
|
44
|
+
export declare function getServerAuthSession(event: H3Event): Promise<ServerAuthSession | null>;
|
|
45
|
+
export declare function getFreshServerAuthSession(event: H3Event, options?: {
|
|
46
|
+
force?: boolean;
|
|
47
|
+
}): Promise<ServerAuthSession | null>;
|
|
74
48
|
/** Browser-safe projection — tokens are excluded. */
|
|
75
49
|
export declare function toPublicSession(rec: SessionRecord): PublicSession;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { deleteCookie, getCookie, setCookie } from "h3";
|
|
1
|
+
import { createError, deleteCookie, getCookie, setCookie } from "h3";
|
|
2
2
|
import { useRuntimeConfig, useStorage } from "nitropack/runtime";
|
|
3
|
+
import { refreshAuthorizationContext } from "./idp.js";
|
|
4
|
+
export const AUTHORIZATION_TTL_MS = 12e4;
|
|
3
5
|
function key(id) {
|
|
4
6
|
return `session:${id}`;
|
|
5
7
|
}
|
|
@@ -44,24 +46,55 @@ export async function readSessionRecordById(id) {
|
|
|
44
46
|
const { auth: runtimeConfig } = useRuntimeConfig();
|
|
45
47
|
return useStorage(runtimeConfig.sessionStorageBase).getItem(key(id));
|
|
46
48
|
}
|
|
47
|
-
export
|
|
48
|
-
const s = await readSessionRecord(event);
|
|
49
|
-
if (!s)
|
|
50
|
-
return null;
|
|
49
|
+
export function toServerAuthSession(rec) {
|
|
51
50
|
return {
|
|
52
|
-
sub:
|
|
53
|
-
email:
|
|
54
|
-
name:
|
|
55
|
-
picture:
|
|
56
|
-
systemRole:
|
|
57
|
-
abilities:
|
|
58
|
-
organizations:
|
|
59
|
-
activeOrg:
|
|
60
|
-
entitlement:
|
|
61
|
-
isImpersonation:
|
|
62
|
-
impersonator:
|
|
51
|
+
sub: rec.sub,
|
|
52
|
+
email: rec.user.email,
|
|
53
|
+
name: rec.user.name,
|
|
54
|
+
picture: rec.user.picture,
|
|
55
|
+
systemRole: rec.systemRole,
|
|
56
|
+
abilities: rec.abilities,
|
|
57
|
+
organizations: rec.organizations,
|
|
58
|
+
activeOrg: rec.activeOrg,
|
|
59
|
+
entitlement: rec.entitlement,
|
|
60
|
+
isImpersonation: rec.isImpersonation,
|
|
61
|
+
impersonator: rec.impersonator
|
|
63
62
|
};
|
|
64
63
|
}
|
|
64
|
+
export async function getServerAuthSession(event) {
|
|
65
|
+
const session = await readSessionRecord(event);
|
|
66
|
+
return session ? toServerAuthSession(session.rec) : null;
|
|
67
|
+
}
|
|
68
|
+
export async function getFreshServerAuthSession(event, options = {}) {
|
|
69
|
+
const session = await readSessionRecord(event);
|
|
70
|
+
if (!session)
|
|
71
|
+
return null;
|
|
72
|
+
if (!options.force && Date.now() - session.rec.authorizationRefreshedAt <= AUTHORIZATION_TTL_MS)
|
|
73
|
+
return toServerAuthSession(session.rec);
|
|
74
|
+
try {
|
|
75
|
+
const context = await refreshAuthorizationContext(event, session.id, session.rec);
|
|
76
|
+
session.rec.activeOrg = context.organization.id;
|
|
77
|
+
session.rec.organizations = context.organizations;
|
|
78
|
+
session.rec.abilities = context.abilities;
|
|
79
|
+
session.rec.systemRole = context.systemRole;
|
|
80
|
+
session.rec.entitlement = context.entitlement;
|
|
81
|
+
session.rec.authorizationRefreshedAt = Date.now();
|
|
82
|
+
await writeSessionRecord(session.id, session.rec);
|
|
83
|
+
return toServerAuthSession(session.rec);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const status = error.statusCode ?? error.response?.status;
|
|
86
|
+
if (status === 401 || status === 403) {
|
|
87
|
+
session.rec.abilities = [];
|
|
88
|
+
session.rec.organizations = [];
|
|
89
|
+
session.rec.activeOrg = null;
|
|
90
|
+
session.rec.systemRole = null;
|
|
91
|
+
session.rec.authorizationRefreshedAt = Date.now();
|
|
92
|
+
await writeSessionRecord(session.id, session.rec);
|
|
93
|
+
throw createError({ statusCode: 403, statusMessage: "Authorization changed; sign in again" });
|
|
94
|
+
}
|
|
95
|
+
throw createError({ statusCode: 503, statusMessage: "Authorization service unavailable" });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
65
98
|
export function toPublicSession(rec) {
|
|
66
99
|
return {
|
|
67
100
|
user: rec.user,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thecodeorigin/auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.9",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "THECODEORIGIN Authentication Portal client module",
|
|
7
7
|
"repository": "https://github.com/thecodeorigin/auth",
|
|
@@ -39,32 +39,8 @@
|
|
|
39
39
|
"workspaces": [
|
|
40
40
|
"playground"
|
|
41
41
|
],
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@casl/ability": "^6.8.1",
|
|
44
|
-
"@casl/vue": "^2.2.6",
|
|
45
|
-
"@nuxt/kit": "^4.4.8",
|
|
46
|
-
"defu": "^6.1.4",
|
|
47
|
-
"h3": "^1.14.0",
|
|
48
|
-
"ofetch": "^1.4.1",
|
|
49
|
-
"ufo": "^1.5.4",
|
|
50
|
-
"zod": "^4.3.6"
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
"@nuxt/devtools": "^3.2.4",
|
|
54
|
-
"@nuxt/eslint-config": "^1.15.2",
|
|
55
|
-
"@nuxt/module-builder": "^1.0.2",
|
|
56
|
-
"@nuxt/schema": "^4.4.8",
|
|
57
|
-
"@nuxt/test-utils": "^4.0.3",
|
|
58
|
-
"@types/node": "latest",
|
|
59
|
-
"changelogen": "^0.6.2",
|
|
60
|
-
"eslint": "^10.4.1",
|
|
61
|
-
"nuxt": "^4.4.8",
|
|
62
|
-
"typescript": "~6.0.3",
|
|
63
|
-
"unbuild": "^3.6.1",
|
|
64
|
-
"vitest": "^4.1.8",
|
|
65
|
-
"vue-tsc": "^3.3.3"
|
|
66
|
-
},
|
|
67
42
|
"scripts": {
|
|
43
|
+
"prepack": "nuxt-module-build build",
|
|
68
44
|
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
69
45
|
"dev:build": "nuxt build playground",
|
|
70
46
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
@@ -72,5 +48,31 @@
|
|
|
72
48
|
"test": "vitest run",
|
|
73
49
|
"test:watch": "vitest watch",
|
|
74
50
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@casl/ability": "catalog:",
|
|
54
|
+
"@casl/vue": "catalog:",
|
|
55
|
+
"@nuxt/kit": "catalog:",
|
|
56
|
+
"defu": "catalog:",
|
|
57
|
+
"h3": "catalog:",
|
|
58
|
+
"ofetch": "catalog:",
|
|
59
|
+
"ufo": "catalog:",
|
|
60
|
+
"zod": "catalog:"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@antfu/eslint-config": "catalog:",
|
|
64
|
+
"@nuxt/devtools": "catalog:",
|
|
65
|
+
"@nuxt/eslint-config": "catalog:",
|
|
66
|
+
"@nuxt/module-builder": "catalog:",
|
|
67
|
+
"@nuxt/schema": "catalog:",
|
|
68
|
+
"@nuxt/test-utils": "catalog:",
|
|
69
|
+
"@types/node": "catalog:",
|
|
70
|
+
"changelogen": "catalog:",
|
|
71
|
+
"eslint": "catalog:",
|
|
72
|
+
"nuxt": "catalog:",
|
|
73
|
+
"typescript": "catalog:",
|
|
74
|
+
"unbuild": "catalog:",
|
|
75
|
+
"vitest": "catalog:",
|
|
76
|
+
"vue-tsc": "catalog:"
|
|
75
77
|
}
|
|
76
|
-
}
|
|
78
|
+
}
|