@shivaedev/identity 0.0.0 → 0.1.0

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.
Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +516 -1
  3. package/dist/identity.d.ts +36 -0
  4. package/dist/identity.d.ts.map +1 -0
  5. package/dist/identity.js +12 -0
  6. package/dist/identity.js.map +1 -0
  7. package/dist/middleware.d.ts +21 -0
  8. package/dist/middleware.d.ts.map +1 -0
  9. package/dist/middleware.js +20 -0
  10. package/dist/middleware.js.map +1 -0
  11. package/dist/permission.d.ts +11 -0
  12. package/dist/permission.d.ts.map +1 -0
  13. package/dist/permission.js +17 -0
  14. package/dist/permission.js.map +1 -0
  15. package/dist/server/adapters/betterAuthSessions.d.ts +14 -0
  16. package/dist/server/adapters/betterAuthSessions.d.ts.map +1 -0
  17. package/dist/server/adapters/betterAuthSessions.js +24 -0
  18. package/dist/server/adapters/betterAuthSessions.js.map +1 -0
  19. package/dist/server/better-auth/allowedAccounts.d.ts +10 -0
  20. package/dist/server/better-auth/allowedAccounts.d.ts.map +1 -0
  21. package/dist/server/better-auth/allowedAccounts.js +20 -0
  22. package/dist/server/better-auth/allowedAccounts.js.map +1 -0
  23. package/dist/server/better-auth/bearerSessions.d.ts +9 -0
  24. package/dist/server/better-auth/bearerSessions.d.ts.map +1 -0
  25. package/dist/server/better-auth/bearerSessions.js +38 -0
  26. package/dist/server/better-auth/bearerSessions.js.map +1 -0
  27. package/dist/server/better-auth/memoryRateLimit.d.ts +3 -0
  28. package/dist/server/better-auth/memoryRateLimit.d.ts.map +1 -0
  29. package/dist/server/better-auth/memoryRateLimit.js +46 -0
  30. package/dist/server/better-auth/memoryRateLimit.js.map +1 -0
  31. package/dist/server/better-auth/mount.d.ts +265 -0
  32. package/dist/server/better-auth/mount.d.ts.map +1 -0
  33. package/dist/server/better-auth/mount.js +111 -0
  34. package/dist/server/better-auth/mount.js.map +1 -0
  35. package/dist/server/better-auth/mountRoutes.d.ts +22 -0
  36. package/dist/server/better-auth/mountRoutes.d.ts.map +1 -0
  37. package/dist/server/better-auth/mountRoutes.js +54 -0
  38. package/dist/server/better-auth/mountRoutes.js.map +1 -0
  39. package/dist/server/better-auth/mountedSessions.d.ts +15 -0
  40. package/dist/server/better-auth/mountedSessions.d.ts.map +1 -0
  41. package/dist/server/better-auth/mountedSessions.js +12 -0
  42. package/dist/server/better-auth/mountedSessions.js.map +1 -0
  43. package/dist/server/better-auth/revokeUnadmitted.d.ts +10 -0
  44. package/dist/server/better-auth/revokeUnadmitted.d.ts.map +1 -0
  45. package/dist/server/better-auth/revokeUnadmitted.js +29 -0
  46. package/dist/server/better-auth/revokeUnadmitted.js.map +1 -0
  47. package/dist/server/better-auth/sessionPermissions.d.ts +3 -0
  48. package/dist/server/better-auth/sessionPermissions.d.ts.map +1 -0
  49. package/dist/server/better-auth/sessionPermissions.js +16 -0
  50. package/dist/server/better-auth/sessionPermissions.js.map +1 -0
  51. package/dist/server/better-auth/signInGate.d.ts +37 -0
  52. package/dist/server/better-auth/signInGate.d.ts.map +1 -0
  53. package/dist/server/better-auth/signInGate.js +38 -0
  54. package/dist/server/better-auth/signInGate.js.map +1 -0
  55. package/dist/server/better-auth/signInPolicy.d.ts +193 -0
  56. package/dist/server/better-auth/signInPolicy.d.ts.map +1 -0
  57. package/dist/server/better-auth/signInPolicy.js +61 -0
  58. package/dist/server/better-auth/signInPolicy.js.map +1 -0
  59. package/dist/server/better-auth/testDatabase.d.ts +19 -0
  60. package/dist/server/better-auth/testDatabase.d.ts.map +1 -0
  61. package/dist/server/better-auth/testDatabase.js +24 -0
  62. package/dist/server/better-auth/testDatabase.js.map +1 -0
  63. package/dist/server/better-auth/testing.d.ts +24 -0
  64. package/dist/server/better-auth/testing.d.ts.map +1 -0
  65. package/dist/server/better-auth/testing.js +21 -0
  66. package/dist/server/better-auth/testing.js.map +1 -0
  67. package/dist/server/better-auth/userProfiles.d.ts +14 -0
  68. package/dist/server/better-auth/userProfiles.d.ts.map +1 -0
  69. package/dist/server/better-auth/userProfiles.js +20 -0
  70. package/dist/server/better-auth/userProfiles.js.map +1 -0
  71. package/dist/server/commandChanges.d.ts +5 -0
  72. package/dist/server/commandChanges.d.ts.map +1 -0
  73. package/dist/server/commandChanges.js +44 -0
  74. package/dist/server/commandChanges.js.map +1 -0
  75. package/dist/server/local.d.ts +10 -0
  76. package/dist/server/local.d.ts.map +1 -0
  77. package/dist/server/local.js +93 -0
  78. package/dist/server/local.js.map +1 -0
  79. package/dist/server/origin.d.ts +15 -0
  80. package/dist/server/origin.d.ts.map +1 -0
  81. package/dist/server/origin.js +16 -0
  82. package/dist/server/origin.js.map +1 -0
  83. package/dist/server/permissions.d.ts +9 -0
  84. package/dist/server/permissions.d.ts.map +1 -0
  85. package/dist/server/permissions.js +31 -0
  86. package/dist/server/permissions.js.map +1 -0
  87. package/dist/server/proxyRanges.d.ts +4 -0
  88. package/dist/server/proxyRanges.d.ts.map +1 -0
  89. package/dist/server/proxyRanges.js +62 -0
  90. package/dist/server/proxyRanges.js.map +1 -0
  91. package/dist/server/resolveRole.d.ts +11 -0
  92. package/dist/server/resolveRole.d.ts.map +1 -0
  93. package/dist/server/resolveRole.js +6 -0
  94. package/dist/server/resolveRole.js.map +1 -0
  95. package/dist/server/revalidateStream.d.ts +6 -0
  96. package/dist/server/revalidateStream.d.ts.map +1 -0
  97. package/dist/server/revalidateStream.js +19 -0
  98. package/dist/server/revalidateStream.js.map +1 -0
  99. package/dist/server/session.d.ts +39 -0
  100. package/dist/server/session.d.ts.map +1 -0
  101. package/dist/server/session.js +73 -0
  102. package/dist/server/session.js.map +1 -0
  103. package/dist/server/sessionKeys.d.ts +10 -0
  104. package/dist/server/sessionKeys.d.ts.map +1 -0
  105. package/dist/server/sessionKeys.js +22 -0
  106. package/dist/server/sessionKeys.js.map +1 -0
  107. package/dist/server/sessionProfile.d.ts +3 -0
  108. package/dist/server/sessionProfile.d.ts.map +1 -0
  109. package/dist/server/sessionProfile.js +4 -0
  110. package/dist/server/sessionProfile.js.map +1 -0
  111. package/dist/server/tracing.d.ts +9 -0
  112. package/dist/server/tracing.d.ts.map +1 -0
  113. package/dist/server/tracing.js +51 -0
  114. package/dist/server/tracing.js.map +1 -0
  115. package/dist/server/transportHeaders.d.ts +4 -0
  116. package/dist/server/transportHeaders.d.ts.map +1 -0
  117. package/dist/server/transportHeaders.js +6 -0
  118. package/dist/server/transportHeaders.js.map +1 -0
  119. package/dist/server/trustedProxy.d.ts +7 -0
  120. package/dist/server/trustedProxy.d.ts.map +1 -0
  121. package/dist/server/trustedProxy.js +19 -0
  122. package/dist/server/trustedProxy.js.map +1 -0
  123. package/dist/server/trustedProxyRanges.d.ts +2 -0
  124. package/dist/server/trustedProxyRanges.d.ts.map +1 -0
  125. package/dist/server/trustedProxyRanges.js +5 -0
  126. package/dist/server/trustedProxyRanges.js.map +1 -0
  127. package/package.json +86 -4
  128. package/src/identity.ts +26 -0
  129. package/src/middleware.ts +38 -0
  130. package/src/permission.ts +24 -0
  131. package/src/server/adapters/betterAuthSessions.ts +44 -0
  132. package/src/server/better-auth/allowedAccounts.ts +29 -0
  133. package/src/server/better-auth/bearerSessions.ts +50 -0
  134. package/src/server/better-auth/memoryRateLimit.ts +56 -0
  135. package/src/server/better-auth/mount.ts +145 -0
  136. package/src/server/better-auth/mountRoutes.ts +75 -0
  137. package/src/server/better-auth/mountedSessions.ts +21 -0
  138. package/src/server/better-auth/revokeUnadmitted.ts +41 -0
  139. package/src/server/better-auth/sessionPermissions.ts +18 -0
  140. package/src/server/better-auth/signInGate.ts +77 -0
  141. package/src/server/better-auth/signInPolicy.ts +72 -0
  142. package/src/server/better-auth/testDatabase.ts +27 -0
  143. package/src/server/better-auth/testing.ts +35 -0
  144. package/src/server/better-auth/userProfiles.ts +37 -0
  145. package/src/server/commandChanges.ts +52 -0
  146. package/src/server/local.ts +100 -0
  147. package/src/server/origin.ts +26 -0
  148. package/src/server/permissions.ts +51 -0
  149. package/src/server/proxyRanges.ts +61 -0
  150. package/src/server/resolveRole.ts +7 -0
  151. package/src/server/revalidateStream.ts +25 -0
  152. package/src/server/session.ts +148 -0
  153. package/src/server/sessionKeys.ts +25 -0
  154. package/src/server/sessionProfile.ts +5 -0
  155. package/src/server/tracing.ts +68 -0
  156. package/src/server/transportHeaders.ts +8 -0
  157. package/src/server/trustedProxy.ts +22 -0
  158. package/src/server/trustedProxyRanges.ts +5 -0
@@ -0,0 +1,6 @@
1
+ import { Effect } from "effect";
2
+ import { AuthUnavailable } from "@shivaedev/errors/taxonomy.ts";
3
+ export function resolveRole(roles, stored) {
4
+ return roles.resolve(stored).pipe(Effect.mapError(() => new AuthUnavailable({ message: "Stored user role is not declared by the application" })));
5
+ }
6
+ //# sourceMappingURL=resolveRole.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveRole.js","sourceRoot":"","sources":["../../src/server/resolveRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,MAAe;IAC1D,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC,CAAC,CAAC,CAAC;AACnJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { Effect, Option } from "effect";
2
+ import { type Rpc } from "effect/rpc";
3
+ import { type AuthUnavailable, type Forbidden, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
4
+ import type { SessionPolicy, SessionShape } from "./session.ts";
5
+ export declare function revalidateStream<TSession extends SessionShape>(policy: SessionPolicy<TSession>, rpc: Rpc.AnyWithProps, id: string, lookup: Effect.Effect<Option.Option<TSession>, AuthUnavailable | Forbidden>): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | Forbidden | Unauthorized, R>;
6
+ //# sourceMappingURL=revalidateStream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidateStream.d.ts","sourceRoot":"","sources":["../../src/server/revalidateStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,GAAG,EAAa,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhE,wBAAgB,gBAAgB,CAAC,QAAQ,SAAS,YAAY,EAC7D,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,GAAG,EAAE,GAAG,CAAC,YAAY,EACrB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,IAEnE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,uDAa/C"}
@@ -0,0 +1,19 @@
1
+ import { Effect, Option } from "effect";
2
+ import { RpcSchema } from "effect/rpc";
3
+ import { Unauthorized } from "@shivaedev/errors/taxonomy.ts";
4
+ export function revalidateStream(policy, rpc, id, lookup) {
5
+ return (effect) => {
6
+ if (!RpcSchema.isStreamSchema(rpc.successSchema)) {
7
+ return effect;
8
+ }
9
+ const check = Effect.gen(function* () {
10
+ yield* Effect.sleep(policy.streamRevalidationInterval ?? "30 seconds");
11
+ const session = yield* lookup.pipe(Effect.catchTag("AuthUnavailable", () => Effect.succeed(undefined)));
12
+ if (session !== undefined && (Option.isNone(session) || session.value.user.id !== id)) {
13
+ return yield* new Unauthorized({ message: "Authentication required" });
14
+ }
15
+ });
16
+ return Effect.raceFirst(effect, Effect.forever(check));
17
+ };
18
+ }
19
+ //# sourceMappingURL=revalidateStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidateStream.js","sourceRoot":"","sources":["../../src/server/revalidateStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAY,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAwC,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGnG,MAAM,UAAU,gBAAgB,CAC/B,MAA+B,EAC/B,GAAqB,EACrB,EAAU,EACV,MAA2E;IAE3E,OAAO,CAAU,MAA8B,EAAE,EAAE;QAClD,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YAClD,OAAO,MAAM,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACjC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,IAAI,YAAY,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACxG,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBACvF,OAAO,KAAK,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAC;YACxE,CAAC;QACF,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { Duration, Effect, Layer, Option } from "effect";
2
+ import type { Headers } from "effect/http";
3
+ import type { Permission } from "@shivaedev/access/permission.ts";
4
+ import type { RoleSet } from "@shivaedev/access/roleSet.ts";
5
+ import { type AuthUnavailable, Forbidden, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
6
+ import { Identity, SessionUser, type SessionUserValue, type SessionUserWithProviderAccountsValue } from "#identity.ts";
7
+ import { Authenticated, AuthenticatedUser, AuthenticatedUserWithProviderAccounts, MaybeAuthenticated } from "#middleware.ts";
8
+ import { type OriginPolicy } from "./origin.ts";
9
+ export interface SessionShape {
10
+ readonly user: {
11
+ readonly id: string;
12
+ };
13
+ }
14
+ export interface SessionProvider<TSession extends SessionShape> {
15
+ readonly get: (headers: Headers.Headers) => Effect.Effect<Option.Option<TSession>, AuthUnavailable>;
16
+ }
17
+ export interface SessionPolicy<TSession extends SessionShape> {
18
+ readonly origin: OriginPolicy;
19
+ readonly provider: SessionProvider<TSession>;
20
+ readonly roles?: RoleSet;
21
+ readonly streamRevalidationInterval?: Duration.Input | undefined;
22
+ }
23
+ export declare const resolveSession: <TSession extends SessionShape>(policy: SessionPolicy<TSession>, headers: Headers.Headers, rpc: string) => Effect.Effect<Option.Option<TSession & {
24
+ readonly user: {
25
+ readonly permissions?: readonly Permission[];
26
+ };
27
+ }>, AuthUnavailable | Forbidden>;
28
+ export declare function authenticatedLayer<TSession extends SessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<Authenticated>;
29
+ export declare function maybeAuthenticatedLayer<TSession extends SessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<MaybeAuthenticated>;
30
+ export interface UserSessionShape extends SessionShape {
31
+ readonly user: SessionUserValue;
32
+ }
33
+ export declare function withSessionUser<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>, headers: Headers.Headers, operation: string): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | AuthUnavailable | Forbidden | Unauthorized, Exclude<Exclude<R, Identity>, SessionUser>>;
34
+ export declare function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser>;
35
+ export interface ProviderAccountsSessionShape extends UserSessionShape {
36
+ readonly user: SessionUserWithProviderAccountsValue;
37
+ }
38
+ export declare function authenticatedUserWithProviderAccountsLayer<TSession extends ProviderAccountsSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUserWithProviderAccounts>;
39
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { Duration, Effect, Exit, Layer, Option } from "effect";
2
+ import { Forbidden, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
3
+ import { Identity, OptionalIdentity, SessionUser, SessionUserWithProviderAccounts, } from "#identity.ts";
4
+ import { Authenticated, AuthenticatedUser, AuthenticatedUserWithProviderAccounts, MaybeAuthenticated } from "#middleware.ts";
5
+ import { sessionCommandChanges } from "#server/commandChanges.ts";
6
+ import { originRequest } from "./origin.js";
7
+ import { resolveRole } from "./resolveRole.js";
8
+ import { revalidateStream } from "./revalidateStream.js";
9
+ import { sessionProfile } from "./sessionProfile.js";
10
+ import { transportHeaders } from "./transportHeaders.js";
11
+ export const resolveSession = Effect.fn("PlatformRpc.resolveSession")(function* (policy, headers, rpc) {
12
+ const transport = yield* transportHeaders(headers);
13
+ if (!policy.origin(originRequest(transport, rpc))) {
14
+ return yield* new Forbidden({ message: "Origin not allowed" });
15
+ }
16
+ const session = yield* policy.provider.get(transport);
17
+ if (Option.isNone(session) || policy.roles === undefined) {
18
+ return session;
19
+ }
20
+ const access = yield* resolveRole(policy.roles, Reflect.get(session.value.user, "role"));
21
+ return Option.some({ ...session.value, user: { ...session.value.user, ...access } });
22
+ });
23
+ function identified(id) {
24
+ return Effect.andThen(Effect.annotateCurrentSpan("user.id", id), Effect.succeed(id));
25
+ }
26
+ export function authenticatedLayer(policy) {
27
+ return Layer.succeed(Authenticated, (effect, { headers, rpc, payload }) => Effect.gen(function* () {
28
+ const session = yield* resolveSession(policy, headers, rpc._tag);
29
+ if (Option.isNone(session)) {
30
+ return yield* new Unauthorized({ message: "Authentication required" });
31
+ }
32
+ const id = yield* identified(session.value.user.id);
33
+ return yield* sessionCommandChanges(effect, rpc, payload).pipe(revalidateStream(policy, rpc, id, resolveSession(policy, headers, rpc._tag)), Effect.provideService(Identity, { id }), Effect.annotateLogs({ userId: id }));
34
+ }));
35
+ }
36
+ export function maybeAuthenticatedLayer(policy) {
37
+ return Layer.succeed(MaybeAuthenticated, (effect, { headers, rpc }) => Effect.gen(function* () {
38
+ const session = yield* resolveSession(policy, headers, rpc._tag);
39
+ if (Option.isNone(session)) {
40
+ return yield* Effect.provideService(effect, OptionalIdentity, Option.none());
41
+ }
42
+ const id = yield* identified(session.value.user.id);
43
+ return yield* effect.pipe(revalidateStream(policy, rpc, id, resolveSession(policy, headers, rpc._tag)), Effect.provideService(OptionalIdentity, Option.some({ id })), Effect.annotateLogs({ userId: id }));
44
+ }));
45
+ }
46
+ export function withSessionUser(policy, headers, operation) {
47
+ return (effect) => Effect.gen(function* () {
48
+ const session = yield* resolveSession(policy, headers, operation);
49
+ if (Option.isNone(session)) {
50
+ return yield* new Unauthorized({ message: "Authentication required" });
51
+ }
52
+ const user = sessionProfile(session.value.user);
53
+ const id = yield* identified(session.value.user.id);
54
+ return yield* effect.pipe(Effect.provideService(Identity, { id }), Effect.provideService(SessionUser, user), Effect.annotateLogs({ userId: id }));
55
+ });
56
+ }
57
+ export function authenticatedUserLayer(policy) {
58
+ return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc, payload }) => sessionCommandChanges(effect, rpc, payload).pipe((invocation) => Effect.flatMap(Identity, ({ id }) => invocation.pipe(revalidateStream(policy, rpc, id, resolveSession(policy, headers, rpc._tag)))), withSessionUser(policy, headers, rpc._tag)));
59
+ }
60
+ export function authenticatedUserWithProviderAccountsLayer(policy) {
61
+ return Layer.succeed(AuthenticatedUserWithProviderAccounts, (effect, { headers, rpc, payload }) => Effect.gen(function* () {
62
+ const session = yield* resolveSession(policy, headers, rpc._tag);
63
+ if (Option.isNone(session)) {
64
+ return yield* new Unauthorized({ message: "Authentication required" });
65
+ }
66
+ const { id, providerAccounts } = session.value.user;
67
+ const accounts = yield* Effect.cachedWithTTL(Effect.map(providerAccounts, (values) => values.map(({ accountId, providerId }) => ({ accountId, providerId }))), (exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero));
68
+ const user = sessionProfile(session.value.user);
69
+ yield* identified(id);
70
+ return yield* sessionCommandChanges(effect, rpc, payload).pipe(revalidateStream(policy, rpc, id, resolveSession(policy, headers, rpc._tag)), Effect.provideService(Identity, { id }), Effect.provideService(SessionUser, user), Effect.provideService(SessionUserWithProviderAccounts, { ...user, providerAccounts: accounts }), Effect.annotateLogs({ userId: id }));
71
+ }));
72
+ }
73
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Effect } from "effect";
2
+ import type { Rpc } from "effect/rpc";
3
+ import type { KeyContext } from "@shivaedev/contract/keys.ts";
4
+ import { type QueryReference } from "@shivaedev/contract/serverMetadata.ts";
5
+ export declare function sessionKeyContext(rpc: Pick<Rpc.AnyWithProps, "annotations">): Effect.Effect<KeyContext | undefined>;
6
+ export declare function resolveChanges(rpc: Pick<Rpc.AnyWithProps, "annotations">, references: readonly QueryReference[]): Effect.Effect<{
7
+ context: KeyContext | undefined;
8
+ keys: readonly import("@shivaedev/contract/keys.ts").Key[];
9
+ }, never, never>;
10
+ //# sourceMappingURL=sessionKeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionKeys.d.ts","sourceRoot":"","sources":["../../src/server/sessionKeys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAU,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAGpG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC,CAWnH;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,UAAU,EAAE,SAAS,cAAc,EAAE;;;iBAK/G"}
@@ -0,0 +1,22 @@
1
+ import { Context, Effect, Option } from "effect";
2
+ import { ChangesReads, KeyScope } from "@shivaedev/contract/serverMetadata.ts";
3
+ import { Identity } from "#identity.ts";
4
+ export function sessionKeyContext(rpc) {
5
+ if (Option.isNone(Context.getOption(rpc.annotations, KeyScope))) {
6
+ return Effect.succeed(undefined);
7
+ }
8
+ return Effect.gen(function* () {
9
+ const identity = yield* Effect.serviceOption(Identity);
10
+ if (Option.isNone(identity)) {
11
+ return yield* Effect.die(new Error("Session-scoped contract keys require request-local Identity"));
12
+ }
13
+ return { scope: identity.value.id };
14
+ });
15
+ }
16
+ export function resolveChanges(rpc, references) {
17
+ return Effect.map(sessionKeyContext(rpc), (context) => ({
18
+ context,
19
+ keys: Context.getUnsafe(rpc.annotations, ChangesReads)(references),
20
+ }));
21
+ }
22
+ //# sourceMappingURL=sessionKeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionKeys.js","sourceRoot":"","sources":["../../src/server/sessionKeys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAuB,MAAM,uCAAuC,CAAC;AACpG,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,UAAU,iBAAiB,CAAC,GAA0C;IAC3E,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACjE,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC,CAAC;QACpG,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAA0C,EAAE,UAAqC;IAC/G,OAAO,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACvD,OAAO;QACP,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,UAAU,CAAC;KAClE,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SessionUserValue } from "#identity.ts";
2
+ export declare function sessionProfile({ id, name, email, role, permissions }: SessionUserValue): SessionUserValue;
3
+ //# sourceMappingURL=sessionProfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionProfile.d.ts","sourceRoot":"","sources":["../../src/server/sessionProfile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,gBAAgB,GAAG,gBAAgB,CAEzG"}
@@ -0,0 +1,4 @@
1
+ export function sessionProfile({ id, name, email, role, permissions }) {
2
+ return { email, id, name, ...(role === undefined ? {} : { role }), ...(permissions === undefined ? {} : { permissions }) };
3
+ }
4
+ //# sourceMappingURL=sessionProfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionProfile.js","sourceRoot":"","sources":["../../src/server/sessionProfile.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAoB;IACtF,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC5H,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Layer } from "effect";
2
+ import { type SensitiveKey } from "@shivaedev/errors/sensitive.ts";
3
+ import { RequestTracing } from "#middleware.ts";
4
+ export interface RequestTracingOptions {
5
+ readonly header?: string;
6
+ readonly sensitive?: SensitiveKey;
7
+ }
8
+ export declare function requestTracingLayer(options?: RequestTracingOptions): Layer.Layer<RequestTracing>;
9
+ //# sourceMappingURL=tracing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/server/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,EAA6B,MAAM,QAAQ,CAAC;AAKxF,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEzG,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC;CAClC;AAwCD,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,qBAA0B,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAepG"}
@@ -0,0 +1,51 @@
1
+ import { Cause, Context, Effect, Exit, Layer, Option, Predicate, Random } from "effect";
2
+ import { Headers } from "effect/http";
3
+ import { KeyScope } from "@shivaedev/contract/serverMetadata.ts";
4
+ import { redact } from "@shivaedev/errors/redact.ts";
5
+ import { redactCause } from "@shivaedev/errors/redactCause.ts";
6
+ import { isSensitiveKey, REDACTED, redactText } from "@shivaedev/errors/sensitive.ts";
7
+ import { RequestId } from "#identity.ts";
8
+ import { RequestTracing } from "#middleware.ts";
9
+ import { commandChanges } from "#server/commandChanges.ts";
10
+ const ACCEPTED_REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/u;
11
+ function hex(byte) {
12
+ return byte.toString(16).padStart(2, "0");
13
+ }
14
+ const generateRequestId = Effect.map(Effect.all(Array.from({ length: 16 }, () => Random.nextIntBetween(0, 255))), (bytes) => bytes.map(hex).join(""));
15
+ function requestIdOf(headers, header) {
16
+ return Option.match(Option.filter(Headers.get(headers, header), (value) => ACCEPTED_REQUEST_ID.test(value)), {
17
+ onNone: () => generateRequestId,
18
+ onSome: Effect.succeed,
19
+ });
20
+ }
21
+ function failureTag(cause, sensitive = isSensitiveKey) {
22
+ return Option.match(Cause.findErrorOption(cause), {
23
+ onNone: () => "unknown",
24
+ onSome: (error) => {
25
+ if (!(Predicate.hasProperty(error, "_tag") && Predicate.isString(error._tag))) {
26
+ return "unknown";
27
+ }
28
+ return sensitive("_tag") ? REDACTED : redactText(error._tag, sensitive);
29
+ },
30
+ });
31
+ }
32
+ function logFailure(cause, payload, sensitive) {
33
+ if (Cause.hasInterruptsOnly(cause)) {
34
+ return Effect.annotateCurrentSpan("rpc.interrupted", true).pipe(Effect.andThen(Effect.logDebug("RPC interrupted")), Effect.annotateLogs({ "rpc.payload": redact(payload, sensitive) }));
35
+ }
36
+ const annotations = { "rpc.payload": redact(payload, sensitive) };
37
+ if (!Cause.hasDies(cause)) {
38
+ return Effect.annotateLogs(Effect.logInfo("RPC failure"), { ...annotations, "rpc.failure": failureTag(cause, sensitive) });
39
+ }
40
+ const defects = cause.reasons.filter(Cause.isDieReason).map((reason) => redact(reason.defect, sensitive));
41
+ return Effect.annotateLogs(Effect.logError("RPC defect"), { ...annotations, "rpc.defect": defects });
42
+ }
43
+ export function requestTracingLayer(options = {}) {
44
+ return Layer.succeed(RequestTracing, (effect, { headers, payload, rpc }) => Effect.gen(function* () {
45
+ const requestId = yield* requestIdOf(headers, options.header ?? "x-request-id");
46
+ yield* Effect.annotateCurrentSpan({ "request.id": requestId, "rpc.method": rpc._tag });
47
+ const invocation = Option.isSome(Context.getOption(rpc.annotations, KeyScope)) ? effect : commandChanges(effect, rpc, payload);
48
+ return yield* invocation.pipe(Effect.catchCauseIf(Cause.hasDies, (cause) => Effect.failCause(redactCause(cause, options.sensitive))), Effect.onExit((exit) => (Exit.isFailure(exit) ? logFailure(exit.cause, payload, options.sensitive) : Effect.void)), Effect.provideService(RequestId, requestId), Effect.annotateLogs({ requestId, "rpc.method": rpc._tag }), Effect.withLogSpan("rpc.duration"));
49
+ }));
50
+ }
51
+ //# sourceMappingURL=tracing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../../src/server/tracing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Effect } from "effect";
2
+ import { type Headers } from "effect/http";
3
+ export declare function transportHeaders(rpcHeaders: Headers.Headers): Effect.Effect<Headers.Headers>;
4
+ //# sourceMappingURL=transportHeaders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transportHeaders.d.ts","sourceRoot":"","sources":["../../src/server/transportHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,OAAO,EAAqB,MAAM,aAAa,CAAC;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAK5F"}
@@ -0,0 +1,6 @@
1
+ import { Effect, Option } from "effect";
2
+ import { HttpServerRequest } from "effect/http";
3
+ export function transportHeaders(rpcHeaders) {
4
+ return Effect.map(Effect.serviceOption(HttpServerRequest.HttpServerRequest), Option.match({ onNone: () => rpcHeaders, onSome: (request) => request.headers }));
5
+ }
6
+ //# sourceMappingURL=transportHeaders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transportHeaders.js","sourceRoot":"","sources":["../../src/server/transportHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAgB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,MAAM,UAAU,gBAAgB,CAAC,UAA2B;IAC3D,OAAO,MAAM,CAAC,GAAG,CAChB,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,EACzD,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAChF,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { BlockList } from "node:net";
2
+ import { Schema } from "effect";
3
+ export declare function proxyRanges(value: string): string[] | undefined;
4
+ export declare function proxyAddresses(value: string): BlockList | undefined;
5
+ export declare const TrustedProxy: Schema.String;
6
+ export declare function proxyMatches(addresses: BlockList, address: string): boolean;
7
+ //# sourceMappingURL=trustedProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustedProxy.d.ts","sourceRoot":"","sources":["../../src/server/trustedProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAQ,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAE/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAGnE;AACD,eAAO,MAAM,YAAY,eAIxB,CAAC;AACF,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAI3E"}
@@ -0,0 +1,19 @@
1
+ import { BlockList, isIP } from "node:net";
2
+ import { Schema } from "effect";
3
+ import { addProxyAddress, proxyRanges as normalizedRanges } from "./proxyRanges.js";
4
+ export function proxyRanges(value) {
5
+ return normalizedRanges(value);
6
+ }
7
+ export function proxyAddresses(value) {
8
+ const addresses = new BlockList();
9
+ return proxyRanges(value)?.every((entry) => addProxyAddress(addresses, entry)) ? addresses : undefined;
10
+ }
11
+ export const TrustedProxy = Schema.String.check(Schema.makeFilter((value) => proxyAddresses(value) !== undefined, {
12
+ message: "Expected a comma-separated list of IPv4 or IPv6 addresses or CIDR ranges",
13
+ }));
14
+ export function proxyMatches(addresses, address) {
15
+ const unmapped = address.replace(/^::ffff:(?=\d+\.)/iu, "");
16
+ const family = isIP(unmapped);
17
+ return family !== 0 && !unmapped.includes("%") && addresses.check(unmapped, family === 4 ? "ipv4" : "ipv6");
18
+ }
19
+ //# sourceMappingURL=trustedProxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustedProxy.js","sourceRoot":"","sources":["../../src/server/trustedProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpF,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC3C,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAClC,OAAO,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACxG,CAAC;AACD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAC9C,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE;IACjE,OAAO,EAAE,0EAA0E;CACnF,CAAC,CACF,CAAC;AACF,MAAM,UAAU,YAAY,CAAC,SAAoB,EAAE,OAAe;IACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7G,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function trustedProxyRanges(trustedProxy?: string): string[];
2
+ //# sourceMappingURL=trustedProxyRanges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustedProxyRanges.d.ts","sourceRoot":"","sources":["../../src/server/trustedProxyRanges.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAElE"}
@@ -0,0 +1,5 @@
1
+ import { proxyRanges } from "./proxyRanges.js";
2
+ export function trustedProxyRanges(trustedProxy) {
3
+ return trustedProxy === undefined ? [] : (proxyRanges(trustedProxy) ?? []);
4
+ }
5
+ //# sourceMappingURL=trustedProxyRanges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustedProxyRanges.js","sourceRoot":"","sources":["../../src/server/trustedProxyRanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAAC,YAAqB;IACvD,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5E,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,92 @@
1
1
  {
2
2
  "name": "@shivaedev/identity",
3
- "version": "0.0.0",
4
- "description": "Placeholder for the ShivaeDev identity package.",
5
- "license": "MIT",
3
+ "version": "0.1.0",
4
+ "description": "Request identity, sessions, transport trust and permission enforcement for Effect RPC servers",
5
+ "keywords": [
6
+ "effect"
7
+ ],
8
+ "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/identity#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/ShivaeDev/platform/issues"
11
+ },
6
12
  "repository": {
7
13
  "type": "git",
8
- "url": "git+https://github.com/ShivaeDev/platform.git"
14
+ "url": "git+https://github.com/ShivaeDev/platform.git",
15
+ "directory": "packages/identity"
16
+ },
17
+ "license": "MIT",
18
+ "sideEffects": false,
19
+ "type": "module",
20
+ "imports": {
21
+ "#test/*": "./src/test-support/*",
22
+ "#*.ts": {
23
+ "source": "./src/*.ts",
24
+ "types": "./dist/*.d.ts",
25
+ "default": "./dist/*.js"
26
+ }
27
+ },
28
+ "exports": {
29
+ "./*.ts": {
30
+ "source": "./src/*.ts",
31
+ "types": "./dist/*.d.ts",
32
+ "default": "./dist/*.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "src",
39
+ "!src/**/*.test.ts",
40
+ "!src/**/*.test.tsx",
41
+ "!src/**/*.spec.ts",
42
+ "!src/**/*.spec.tsx",
43
+ "!src/**/test-support",
44
+ "README.md"
45
+ ],
46
+ "dependencies": {
47
+ "@shivaedev/access": "0.1.1",
48
+ "@shivaedev/contract": "0.17.0",
49
+ "@shivaedev/errors": "0.1.0",
50
+ "@shivaedev/sql": "0.22.0"
51
+ },
52
+ "devDependencies": {
53
+ "@effect/platform-node": "4.0.5",
54
+ "@effect/sql-pg": "4.0.5",
55
+ "@shivaedev/quality": "0.42.0",
56
+ "@shivaedev/test-sql": "0.10.0",
57
+ "@shivaedev/test-story": "0.21.0",
58
+ "@types/node": "24.19.2",
59
+ "@types/pg": "8.20.0",
60
+ "better-auth": "1.7.11",
61
+ "effect": "4.0.5",
62
+ "pg": "8.23.0",
63
+ "typescript": "7.0.2",
64
+ "vitest": "5.0.5"
65
+ },
66
+ "peerDependencies": {
67
+ "better-auth": "1.7.11",
68
+ "effect": "4.0.5",
69
+ "pg": "8.23.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "better-auth": {
73
+ "optional": true
74
+ },
75
+ "pg": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "engines": {
80
+ "node": ">=24"
81
+ },
82
+ "publishConfig": {
83
+ "access": "public",
84
+ "provenance": true
85
+ },
86
+ "scripts": {
87
+ "build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc --project tsconfig.build.json",
88
+ "ready": "pnpm typecheck && pnpm test && pnpm build",
89
+ "test": "vitest run",
90
+ "typecheck": "tsc --noEmit"
9
91
  }
10
92
  }
@@ -0,0 +1,26 @@
1
+ import { Context, type Effect, type Option } from "effect";
2
+ import type { Permission } from "@shivaedev/access/permission.ts";
3
+ import type { AuthUnavailable, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
4
+ export class RequestId extends Context.Service<RequestId, string>()("@shivaedev/identity/RequestId") {}
5
+ export interface IdentityValue {
6
+ readonly id: string;
7
+ }
8
+ export class Identity extends Context.Service<Identity, IdentityValue>()("@shivaedev/identity/Identity") {}
9
+ export class OptionalIdentity extends Context.Service<OptionalIdentity, Option.Option<IdentityValue>>()("@shivaedev/identity/OptionalIdentity") {}
10
+ export interface ProviderAccountValue {
11
+ readonly accountId: string;
12
+ readonly providerId: string;
13
+ }
14
+ export interface SessionUserValue extends IdentityValue {
15
+ readonly email: string;
16
+ readonly name: string;
17
+ readonly permissions?: readonly Permission[];
18
+ readonly role?: string | null | undefined;
19
+ }
20
+ export class SessionUser extends Context.Service<SessionUser, SessionUserValue>()("@shivaedev/identity/SessionUser") {}
21
+ export interface SessionUserWithProviderAccountsValue extends SessionUserValue {
22
+ readonly providerAccounts: Effect.Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>;
23
+ }
24
+ export class SessionUserWithProviderAccounts extends Context.Service<SessionUserWithProviderAccounts, SessionUserWithProviderAccountsValue>()(
25
+ "@shivaedev/identity/SessionUserWithProviderAccounts",
26
+ ) {}
@@ -0,0 +1,38 @@
1
+ import { Schema } from "effect";
2
+ import { RpcMiddleware } from "effect/rpc";
3
+ import { AuthUnavailable, Forbidden, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
4
+ import type { Identity, OptionalIdentity, RequestId, SessionUser, SessionUserWithProviderAccounts } from "./identity.ts";
5
+ export class RequestTracing extends RpcMiddleware.Service<
6
+ RequestTracing,
7
+ {
8
+ provides: RequestId;
9
+ }
10
+ >()("@shivaedev/identity/RequestTracing") {}
11
+ export class Authenticated extends RpcMiddleware.Service<
12
+ Authenticated,
13
+ {
14
+ provides: Identity;
15
+ }
16
+ >()("@shivaedev/identity/Authenticated", {
17
+ error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
18
+ }) {}
19
+ export class MaybeAuthenticated extends RpcMiddleware.Service<
20
+ MaybeAuthenticated,
21
+ {
22
+ provides: OptionalIdentity;
23
+ }
24
+ >()("@shivaedev/identity/MaybeAuthenticated", { error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]) }) {}
25
+ export class AuthenticatedUser extends RpcMiddleware.Service<
26
+ AuthenticatedUser,
27
+ {
28
+ provides: Identity | SessionUser;
29
+ }
30
+ >()("@shivaedev/identity/AuthenticatedUser", {
31
+ error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
32
+ }) {}
33
+ export class AuthenticatedUserWithProviderAccounts extends RpcMiddleware.Service<
34
+ AuthenticatedUserWithProviderAccounts,
35
+ { provides: Identity | SessionUser | SessionUserWithProviderAccounts }
36
+ >()("@shivaedev/identity/AuthenticatedUserWithProviderAccounts", {
37
+ error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
38
+ }) {}
@@ -0,0 +1,24 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { RpcMiddleware } from "effect/rpc";
3
+ import type { Permission } from "@shivaedev/access/permission.ts";
4
+ import { AuthUnavailable, Forbidden, Unauthorized } from "@shivaedev/errors/taxonomy.ts";
5
+ import { type Identity, SessionUser, type SessionUserValue } from "./identity.ts";
6
+
7
+ const Failures = Schema.Union([Unauthorized, AuthUnavailable, Forbidden]);
8
+
9
+ export function permissionMiddleware<TSelf>() {
10
+ return <const TName extends string>(name: TName, permission: Permission) =>
11
+ Object.assign(RpcMiddleware.Service<TSelf, { provides: Identity | SessionUser }>()(name, { error: Failures }), { permission });
12
+ }
13
+
14
+ export function hasPermission(user: Pick<SessionUserValue, "id" | "permissions">, permission: Permission): boolean {
15
+ return user.permissions?.some((grant) => grant.name === permission.name && grant.strength === permission.strength) ?? false;
16
+ }
17
+
18
+ export function requirePermission(permission: Permission) {
19
+ return Effect.flatMap(SessionUser, (user) =>
20
+ hasPermission(user, permission)
21
+ ? Effect.succeed(user)
22
+ : Effect.fail(new Forbidden({ message: `Permission required: ${permission.name} (${permission.strength})` })),
23
+ );
24
+ }