@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.
- package/LICENSE +21 -0
- package/README.md +516 -1
- package/dist/identity.d.ts +36 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +12 -0
- package/dist/identity.js.map +1 -0
- package/dist/middleware.d.ts +21 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +20 -0
- package/dist/middleware.js.map +1 -0
- package/dist/permission.d.ts +11 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +17 -0
- package/dist/permission.js.map +1 -0
- package/dist/server/adapters/betterAuthSessions.d.ts +14 -0
- package/dist/server/adapters/betterAuthSessions.d.ts.map +1 -0
- package/dist/server/adapters/betterAuthSessions.js +24 -0
- package/dist/server/adapters/betterAuthSessions.js.map +1 -0
- package/dist/server/better-auth/allowedAccounts.d.ts +10 -0
- package/dist/server/better-auth/allowedAccounts.d.ts.map +1 -0
- package/dist/server/better-auth/allowedAccounts.js +20 -0
- package/dist/server/better-auth/allowedAccounts.js.map +1 -0
- package/dist/server/better-auth/bearerSessions.d.ts +9 -0
- package/dist/server/better-auth/bearerSessions.d.ts.map +1 -0
- package/dist/server/better-auth/bearerSessions.js +38 -0
- package/dist/server/better-auth/bearerSessions.js.map +1 -0
- package/dist/server/better-auth/memoryRateLimit.d.ts +3 -0
- package/dist/server/better-auth/memoryRateLimit.d.ts.map +1 -0
- package/dist/server/better-auth/memoryRateLimit.js +46 -0
- package/dist/server/better-auth/memoryRateLimit.js.map +1 -0
- package/dist/server/better-auth/mount.d.ts +265 -0
- package/dist/server/better-auth/mount.d.ts.map +1 -0
- package/dist/server/better-auth/mount.js +111 -0
- package/dist/server/better-auth/mount.js.map +1 -0
- package/dist/server/better-auth/mountRoutes.d.ts +22 -0
- package/dist/server/better-auth/mountRoutes.d.ts.map +1 -0
- package/dist/server/better-auth/mountRoutes.js +54 -0
- package/dist/server/better-auth/mountRoutes.js.map +1 -0
- package/dist/server/better-auth/mountedSessions.d.ts +15 -0
- package/dist/server/better-auth/mountedSessions.d.ts.map +1 -0
- package/dist/server/better-auth/mountedSessions.js +12 -0
- package/dist/server/better-auth/mountedSessions.js.map +1 -0
- package/dist/server/better-auth/revokeUnadmitted.d.ts +10 -0
- package/dist/server/better-auth/revokeUnadmitted.d.ts.map +1 -0
- package/dist/server/better-auth/revokeUnadmitted.js +29 -0
- package/dist/server/better-auth/revokeUnadmitted.js.map +1 -0
- package/dist/server/better-auth/sessionPermissions.d.ts +3 -0
- package/dist/server/better-auth/sessionPermissions.d.ts.map +1 -0
- package/dist/server/better-auth/sessionPermissions.js +16 -0
- package/dist/server/better-auth/sessionPermissions.js.map +1 -0
- package/dist/server/better-auth/signInGate.d.ts +37 -0
- package/dist/server/better-auth/signInGate.d.ts.map +1 -0
- package/dist/server/better-auth/signInGate.js +38 -0
- package/dist/server/better-auth/signInGate.js.map +1 -0
- package/dist/server/better-auth/signInPolicy.d.ts +193 -0
- package/dist/server/better-auth/signInPolicy.d.ts.map +1 -0
- package/dist/server/better-auth/signInPolicy.js +61 -0
- package/dist/server/better-auth/signInPolicy.js.map +1 -0
- package/dist/server/better-auth/testDatabase.d.ts +19 -0
- package/dist/server/better-auth/testDatabase.d.ts.map +1 -0
- package/dist/server/better-auth/testDatabase.js +24 -0
- package/dist/server/better-auth/testDatabase.js.map +1 -0
- package/dist/server/better-auth/testing.d.ts +24 -0
- package/dist/server/better-auth/testing.d.ts.map +1 -0
- package/dist/server/better-auth/testing.js +21 -0
- package/dist/server/better-auth/testing.js.map +1 -0
- package/dist/server/better-auth/userProfiles.d.ts +14 -0
- package/dist/server/better-auth/userProfiles.d.ts.map +1 -0
- package/dist/server/better-auth/userProfiles.js +20 -0
- package/dist/server/better-auth/userProfiles.js.map +1 -0
- package/dist/server/commandChanges.d.ts +5 -0
- package/dist/server/commandChanges.d.ts.map +1 -0
- package/dist/server/commandChanges.js +44 -0
- package/dist/server/commandChanges.js.map +1 -0
- package/dist/server/local.d.ts +10 -0
- package/dist/server/local.d.ts.map +1 -0
- package/dist/server/local.js +93 -0
- package/dist/server/local.js.map +1 -0
- package/dist/server/origin.d.ts +15 -0
- package/dist/server/origin.d.ts.map +1 -0
- package/dist/server/origin.js +16 -0
- package/dist/server/origin.js.map +1 -0
- package/dist/server/permissions.d.ts +9 -0
- package/dist/server/permissions.d.ts.map +1 -0
- package/dist/server/permissions.js +31 -0
- package/dist/server/permissions.js.map +1 -0
- package/dist/server/proxyRanges.d.ts +4 -0
- package/dist/server/proxyRanges.d.ts.map +1 -0
- package/dist/server/proxyRanges.js +62 -0
- package/dist/server/proxyRanges.js.map +1 -0
- package/dist/server/resolveRole.d.ts +11 -0
- package/dist/server/resolveRole.d.ts.map +1 -0
- package/dist/server/resolveRole.js +6 -0
- package/dist/server/resolveRole.js.map +1 -0
- package/dist/server/revalidateStream.d.ts +6 -0
- package/dist/server/revalidateStream.d.ts.map +1 -0
- package/dist/server/revalidateStream.js +19 -0
- package/dist/server/revalidateStream.js.map +1 -0
- package/dist/server/session.d.ts +39 -0
- package/dist/server/session.d.ts.map +1 -0
- package/dist/server/session.js +73 -0
- package/dist/server/session.js.map +1 -0
- package/dist/server/sessionKeys.d.ts +10 -0
- package/dist/server/sessionKeys.d.ts.map +1 -0
- package/dist/server/sessionKeys.js +22 -0
- package/dist/server/sessionKeys.js.map +1 -0
- package/dist/server/sessionProfile.d.ts +3 -0
- package/dist/server/sessionProfile.d.ts.map +1 -0
- package/dist/server/sessionProfile.js +4 -0
- package/dist/server/sessionProfile.js.map +1 -0
- package/dist/server/tracing.d.ts +9 -0
- package/dist/server/tracing.d.ts.map +1 -0
- package/dist/server/tracing.js +51 -0
- package/dist/server/tracing.js.map +1 -0
- package/dist/server/transportHeaders.d.ts +4 -0
- package/dist/server/transportHeaders.d.ts.map +1 -0
- package/dist/server/transportHeaders.js +6 -0
- package/dist/server/transportHeaders.js.map +1 -0
- package/dist/server/trustedProxy.d.ts +7 -0
- package/dist/server/trustedProxy.d.ts.map +1 -0
- package/dist/server/trustedProxy.js +19 -0
- package/dist/server/trustedProxy.js.map +1 -0
- package/dist/server/trustedProxyRanges.d.ts +2 -0
- package/dist/server/trustedProxyRanges.d.ts.map +1 -0
- package/dist/server/trustedProxyRanges.js +5 -0
- package/dist/server/trustedProxyRanges.js.map +1 -0
- package/package.json +86 -4
- package/src/identity.ts +26 -0
- package/src/middleware.ts +38 -0
- package/src/permission.ts +24 -0
- package/src/server/adapters/betterAuthSessions.ts +44 -0
- package/src/server/better-auth/allowedAccounts.ts +29 -0
- package/src/server/better-auth/bearerSessions.ts +50 -0
- package/src/server/better-auth/memoryRateLimit.ts +56 -0
- package/src/server/better-auth/mount.ts +145 -0
- package/src/server/better-auth/mountRoutes.ts +75 -0
- package/src/server/better-auth/mountedSessions.ts +21 -0
- package/src/server/better-auth/revokeUnadmitted.ts +41 -0
- package/src/server/better-auth/sessionPermissions.ts +18 -0
- package/src/server/better-auth/signInGate.ts +77 -0
- package/src/server/better-auth/signInPolicy.ts +72 -0
- package/src/server/better-auth/testDatabase.ts +27 -0
- package/src/server/better-auth/testing.ts +35 -0
- package/src/server/better-auth/userProfiles.ts +37 -0
- package/src/server/commandChanges.ts +52 -0
- package/src/server/local.ts +100 -0
- package/src/server/origin.ts +26 -0
- package/src/server/permissions.ts +51 -0
- package/src/server/proxyRanges.ts +61 -0
- package/src/server/resolveRole.ts +7 -0
- package/src/server/revalidateStream.ts +25 -0
- package/src/server/session.ts +148 -0
- package/src/server/sessionKeys.ts +25 -0
- package/src/server/sessionProfile.ts +5 -0
- package/src/server/tracing.ts +68 -0
- package/src/server/transportHeaders.ts +8 -0
- package/src/server/trustedProxy.ts +22 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
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
|
}
|
package/src/identity.ts
ADDED
|
@@ -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
|
+
}
|