@shivaedev/platform 0.17.0 → 0.18.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/CHANGELOG.md +22 -0
- package/README.md +130 -12
- package/dist/rpc/identity.d.ts +8 -2
- package/dist/rpc/identity.d.ts.map +1 -1
- package/dist/rpc/identity.js +2 -0
- package/dist/rpc/identity.js.map +1 -1
- package/dist/rpc/middleware.d.ts +4 -1
- package/dist/rpc/middleware.d.ts.map +1 -1
- package/dist/rpc/middleware.js +4 -0
- package/dist/rpc/middleware.js.map +1 -1
- package/dist/rpc/publicConfig.d.ts +3 -0
- package/dist/rpc/publicConfig.d.ts.map +1 -0
- package/dist/rpc/publicConfig.js +8 -0
- package/dist/rpc/publicConfig.js.map +1 -0
- package/dist/server/better-auth/mount.d.ts +61 -0
- package/dist/server/better-auth/mount.d.ts.map +1 -0
- package/dist/server/better-auth/mount.js +59 -0
- package/dist/server/better-auth/mount.js.map +1 -0
- package/dist/server/better-auth/testing.d.ts +50 -0
- package/dist/server/better-auth/testing.d.ts.map +1 -0
- package/dist/server/better-auth/testing.js +18 -0
- package/dist/server/better-auth/testing.js.map +1 -0
- package/dist/server/databaseLayer.d.ts +2 -1
- package/dist/server/databaseLayer.d.ts.map +1 -1
- package/dist/server/databaseLayer.js +2 -0
- package/dist/server/databaseLayer.js.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts +3 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.js +13 -22
- package/dist/server/rpc-server/adapters/betterAuthSessions.js.map +1 -1
- package/dist/server/rpc-server/publicConfigLayer.d.ts +4 -0
- package/dist/server/rpc-server/publicConfigLayer.d.ts.map +1 -0
- package/dist/server/rpc-server/publicConfigLayer.js +8 -0
- package/dist/server/rpc-server/publicConfigLayer.js.map +1 -0
- package/dist/server/rpc-server/session.d.ts +6 -2
- package/dist/server/rpc-server/session.d.ts.map +1 -1
- package/dist/server/rpc-server/session.js +16 -6
- package/dist/server/rpc-server/session.js.map +1 -1
- package/package.json +10 -6
- package/src/rpc/identity.ts +8 -2
- package/src/rpc/middleware.ts +7 -1
- package/src/rpc/publicConfig.ts +9 -0
- package/src/server/better-auth/mount.ts +68 -0
- package/src/server/better-auth/testing.ts +21 -0
- package/src/server/databaseLayer.ts +4 -1
- package/src/server/rpc-server/adapters/betterAuthSessions.ts +19 -23
- package/src/server/rpc-server/publicConfigLayer.ts +11 -0
- package/src/server/rpc-server/session.ts +38 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databaseLayer.js","sourceRoot":"","sources":["../../src/server/databaseLayer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAiB,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACxE,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAC;
|
|
1
|
+
{"version":3,"file":"databaseLayer.js","sourceRoot":"","sources":["../../src/server/databaseLayer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { AuthUnavailable, Unauthorized } from "#errors/taxonomy.ts";
|
|
1
3
|
import type { ProviderAccountValue } from "#rpc/identity.ts";
|
|
2
4
|
import type { SessionProvider, SessionShape } from "#server/rpc-server/session.ts";
|
|
3
5
|
export type GetSession<TSession extends SessionShape> = (headers: globalThis.Headers) => Promise<TSession | null>;
|
|
4
6
|
export type GetProviderAccounts = (headers: globalThis.Headers) => Promise<readonly ProviderAccountValue[]>;
|
|
5
7
|
export type SessionWithProviderAccounts<TSession extends SessionShape> = TSession & {
|
|
6
8
|
readonly user: TSession["user"] & {
|
|
7
|
-
readonly providerAccounts: readonly ProviderAccountValue[]
|
|
9
|
+
readonly providerAccounts: Effect.Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>;
|
|
8
10
|
};
|
|
9
11
|
};
|
|
10
12
|
export declare function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>): SessionProvider<TSession>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"betterAuthSessions.d.ts","sourceRoot":"","sources":["../../../../src/server/rpc-server/adapters/betterAuthSessions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"betterAuthSessions.d.ts","sourceRoot":"","sources":["../../../../src/server/rpc-server/adapters/betterAuthSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,QAAQ,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACnF,MAAM,MAAM,UAAU,CAAC,QAAQ,SAAS,YAAY,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAClH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,KAAK,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;AAC5G,MAAM,MAAM,2BAA2B,CAAC,QAAQ,SAAS,YAAY,IAAI,QAAQ,GAAG;IACnF,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG;QAAE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,oBAAoB,EAAE,EAAE,eAAe,GAAG,YAAY,CAAC,CAAA;KAAE,CAAC;CAChJ,CAAC;AAOF,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,YAAY,EAAE,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;AAC/H,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,YAAY,EAC/D,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,EAChC,mBAAmB,EAAE,mBAAmB,GACtC,eAAe,CAAC,2BAA2B,CAAC,QAAQ,CAAC,CAAC,CAAC"}
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
import { Effect, Option, Predicate } from "effect";
|
|
2
|
-
import { AuthUnavailable } from "#errors/taxonomy.ts";
|
|
2
|
+
import { AuthUnavailable, Unauthorized } from "#errors/taxonomy.ts";
|
|
3
3
|
import { redact } from "#server/rpc-server/redact.ts";
|
|
4
4
|
function unavailable(cause) {
|
|
5
5
|
return Effect.andThen(Effect.logError("Authentication provider failed", redact(cause)), Effect.fail(new AuthUnavailable({ message: "Authentication is temporarily unavailable" })));
|
|
6
6
|
}
|
|
7
7
|
export function betterAuthSessions(getSession, getProviderAccounts) {
|
|
8
8
|
const betterAuthSession = Effect.fn("PlatformRpc.betterAuthSession")(function* (headers) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (accounts === null) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
...resolved,
|
|
26
|
-
user: { ...resolved.user, providerAccounts: accounts },
|
|
27
|
-
};
|
|
28
|
-
}).pipe(Effect.catch((error) => unavailable(error.cause)));
|
|
29
|
-
return Option.fromNullishOr(session);
|
|
9
|
+
const transport = new globalThis.Headers(headers);
|
|
10
|
+
const session = yield* Effect.tryPromise(() => getSession(transport)).pipe(Effect.catch((error) => unavailable(error.cause)));
|
|
11
|
+
if (session === null || getProviderAccounts === undefined) {
|
|
12
|
+
return Option.fromNullishOr(session);
|
|
13
|
+
}
|
|
14
|
+
const providerAccounts = Effect.tryPromise({
|
|
15
|
+
catch: (cause) => cause,
|
|
16
|
+
try: () => getProviderAccounts(transport),
|
|
17
|
+
}).pipe(Effect.catch((cause) => Predicate.hasProperty(cause, "statusCode") && cause.statusCode === 401
|
|
18
|
+
? Effect.fail(new Unauthorized({ message: "Authentication required" }))
|
|
19
|
+
: unavailable(cause)));
|
|
20
|
+
return Option.some({ ...session, user: { ...session.user, providerAccounts } });
|
|
30
21
|
});
|
|
31
22
|
return { get: betterAuthSession };
|
|
32
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"betterAuthSessions.js","sourceRoot":"","sources":["../../../../src/server/rpc-server/adapters/betterAuthSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"betterAuthSessions.js","sourceRoot":"","sources":["../../../../src/server/rpc-server/adapters/betterAuthSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAOtD,SAAS,WAAW,CAAC,KAAc;IAClC,OAAO,MAAM,CAAC,OAAO,CACpB,MAAM,CAAC,QAAQ,CAAC,gCAAgC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAChE,MAAM,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC,CAAC,CAC1F,CAAC;AACH,CAAC;AAMD,MAAM,UAAU,kBAAkB,CAAgC,UAAgC,EAAE,mBAAyC;IAC5I,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAwB;QACvG,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9H,IAAI,OAAO,KAAK,IAAI,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC3D,OAAO,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAAC;YAC1C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;YACvB,GAAG,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC;SACzC,CAAC,CAAC,IAAI,CACN,MAAM,CAAC,KAAK,CACX,CAAC,KAAK,EAAwD,EAAE,CAC/D,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG;YACrE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAC;YACvE,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CACtB,CACD,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Config, type ConfigProvider } from "effect";
|
|
2
|
+
import type { publicConfig } from "#rpc/publicConfig.ts";
|
|
3
|
+
export declare function publicConfigLayer<T, E>(declaration: ReturnType<typeof publicConfig<T, E>>, path?: string | ConfigProvider.Path): import("effect/Layer").Layer<import("effect/rpc/Rpc").Handler<"platform.publicConfig.get">, Config.ConfigError, never>;
|
|
4
|
+
//# sourceMappingURL=publicConfigLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicConfigLayer.d.ts","sourceRoot":"","sources":["../../../src/server/rpc-server/publicConfigLayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,cAAc,EAAU,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,IAAI,0HAO9H"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config, Effect } from "effect";
|
|
2
|
+
export function publicConfigLayer(declaration, path) {
|
|
3
|
+
return declaration.toLayer(Effect.gen(function* () {
|
|
4
|
+
const value = yield* Config.schema(declaration.declaration.queries[0].success, path);
|
|
5
|
+
return declaration.of({ "platform.publicConfig.get": () => Effect.succeed(value) });
|
|
6
|
+
}));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=publicConfigLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicConfigLayer.js","sourceRoot":"","sources":["../../../src/server/rpc-server/publicConfigLayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAuB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG7D,MAAM,UAAU,iBAAiB,CAAO,WAAkD,EAAE,IAAmC;IAC9H,OAAO,WAAW,CAAC,OAAO,CACzB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrF,OAAO,WAAW,CAAC,EAAE,CAAC,EAAE,2BAA2B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC,CACF,CAAC;AACH,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
3
|
import { type AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import { Identity, SessionUser, type SessionUserValue } from "#rpc/identity.ts";
|
|
5
|
-
import { Authenticated, AuthenticatedUser, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
4
|
+
import { Identity, SessionUser, type SessionUserValue, type SessionUserWithProviderAccountsValue } from "#rpc/identity.ts";
|
|
5
|
+
import { Authenticated, AuthenticatedUser, AuthenticatedUserWithProviderAccounts, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
6
6
|
import { type OriginPolicy } from "./origin.ts";
|
|
7
7
|
export interface SessionShape {
|
|
8
8
|
readonly user: {
|
|
@@ -24,4 +24,8 @@ export interface UserSessionShape extends SessionShape {
|
|
|
24
24
|
}
|
|
25
25
|
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>>;
|
|
26
26
|
export declare function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser>;
|
|
27
|
+
export interface ProviderAccountsSessionShape extends UserSessionShape {
|
|
28
|
+
readonly user: SessionUserWithProviderAccountsValue;
|
|
29
|
+
}
|
|
30
|
+
export declare function authenticatedUserWithProviderAccountsLayer<TSession extends ProviderAccountsSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUserWithProviderAccounts>;
|
|
27
31
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EACN,QAAQ,EAER,WAAW,EACX,KAAK,gBAAgB,EAErB,KAAK,oCAAoC,EACzC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,qCAAqC,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjI,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE;QACd,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;KACpB,CAAC;CACF;AACD,MAAM,WAAW,eAAe,CAAC,QAAQ,SAAS,YAAY;IAC7D,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;CACpG;AACD,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,YAAY;IAC3D,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;CAC7C;AACD,eAAO,MAAM,cAAc,EAAE,CAAC,QAAQ,SAAS,YAAY,EAC1D,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,GAAG,EAAE,MAAM,KACP,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,SAAS,CAQrE,CAAC;AAIH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAW7H;AACD,wBAAgB,uBAAuB,CAAC,QAAQ,SAAS,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAWvI;AACD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACrD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CAChC;AACD,wBAAgB,eAAe,CAAC,QAAQ,SAAS,gBAAgB,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,IACtI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,kHAmB/C;AACD,wBAAgB,sBAAsB,CAAC,QAAQ,SAAS,gBAAgB,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAEzI;AAED,MAAM,WAAW,4BAA6B,SAAQ,gBAAgB;IACrE,QAAQ,CAAC,IAAI,EAAE,oCAAoC,CAAC;CACpD;AACD,wBAAgB,0CAA0C,CAAC,QAAQ,SAAS,4BAA4B,EACvG,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAC7B,KAAK,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAqBpD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import { Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
3
|
-
import { Identity, OptionalIdentity, SessionUser } from "#rpc/identity.ts";
|
|
4
|
-
import { Authenticated, AuthenticatedUser, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
3
|
+
import { Identity, OptionalIdentity, SessionUser, SessionUserWithProviderAccounts, } from "#rpc/identity.ts";
|
|
4
|
+
import { Authenticated, AuthenticatedUser, AuthenticatedUserWithProviderAccounts, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
5
5
|
import { originRequest } from "./origin.js";
|
|
6
6
|
import { transportHeaders } from "./transportHeaders.js";
|
|
7
7
|
export const resolveSession = Effect.fn("PlatformRpc.resolveSession")(function* (policy, headers, rpc) {
|
|
@@ -40,20 +40,30 @@ export function withSessionUser(policy, headers, operation) {
|
|
|
40
40
|
if (Option.isNone(session)) {
|
|
41
41
|
return yield* new Unauthorized({ message: "Authentication required" });
|
|
42
42
|
}
|
|
43
|
-
const { name, email, role
|
|
43
|
+
const { name, email, role } = session.value.user;
|
|
44
44
|
const id = yield* identified(session.value.user.id);
|
|
45
45
|
return yield* effect.pipe(Effect.provideService(Identity, { id }), Effect.provideService(SessionUser, {
|
|
46
46
|
email,
|
|
47
47
|
id,
|
|
48
48
|
name,
|
|
49
49
|
...(role === undefined ? {} : { role }),
|
|
50
|
-
...(providerAccounts === undefined
|
|
51
|
-
? {}
|
|
52
|
-
: { providerAccounts: providerAccounts.map(({ accountId, providerId }) => ({ accountId, providerId })) }),
|
|
53
50
|
}), Effect.annotateLogs({ userId: id }));
|
|
54
51
|
});
|
|
55
52
|
}
|
|
56
53
|
export function authenticatedUserLayer(policy) {
|
|
57
54
|
return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc }) => effect.pipe(withSessionUser(policy, headers, rpc._tag)));
|
|
58
55
|
}
|
|
56
|
+
export function authenticatedUserWithProviderAccountsLayer(policy) {
|
|
57
|
+
return Layer.succeed(AuthenticatedUserWithProviderAccounts, (effect, { headers, rpc }) => Effect.gen(function* () {
|
|
58
|
+
const session = yield* resolveSession(policy, headers, rpc._tag);
|
|
59
|
+
if (Option.isNone(session)) {
|
|
60
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
61
|
+
}
|
|
62
|
+
const { id, name, email, role, providerAccounts } = session.value.user;
|
|
63
|
+
const accounts = yield* Effect.cached(Effect.map(providerAccounts, (values) => values.map(({ accountId, providerId }) => ({ accountId, providerId }))));
|
|
64
|
+
const user = { email, id, name, ...(role === undefined ? {} : { role }) };
|
|
65
|
+
yield* identified(id);
|
|
66
|
+
return yield* effect.pipe(Effect.provideService(Identity, { id }), Effect.provideService(SessionUser, user), Effect.provideService(SessionUserWithProviderAccounts, { ...user, providerAccounts: accounts }), Effect.annotateLogs({ userId: id }));
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
59
69
|
//# sourceMappingURL=session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAwB,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Opinionated Effect application infrastructure for ShivaeDev services",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/platform#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@shivaedev/effect-contract": "0.5.1",
|
|
44
|
-
"@shivaedev/effect-sql": "0.
|
|
44
|
+
"@shivaedev/effect-sql": "0.6.0",
|
|
45
45
|
"@shivaedev/types": "0.2.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"@prisma-next/sql-orm-client": "0.16.0",
|
|
56
56
|
"@prisma-next/target-postgres": "0.16.0",
|
|
57
57
|
"@shivaedev/effect-prisma": "0.10.0",
|
|
58
|
-
"@shivaedev/effect-test-sql": "0.
|
|
58
|
+
"@shivaedev/effect-test-sql": "0.6.0",
|
|
59
59
|
"@shivaedev/effect-trpc": "0.8.0",
|
|
60
|
-
"@shivaedev/quality": "0.27.
|
|
61
|
-
"@shivaedev/test-story": "0.
|
|
60
|
+
"@shivaedev/quality": "0.27.1",
|
|
61
|
+
"@shivaedev/test-story": "0.10.0",
|
|
62
62
|
"@trpc/client": "11.19.0",
|
|
63
63
|
"@trpc/server": "11.19.0",
|
|
64
64
|
"@types/node": "24.19.2",
|
|
@@ -79,7 +79,8 @@
|
|
|
79
79
|
"@shivaedev/effect-trpc": "0.8.0",
|
|
80
80
|
"@trpc/server": "11.19.0",
|
|
81
81
|
"better-auth": "1.7.7",
|
|
82
|
-
"effect": "4.0.4"
|
|
82
|
+
"effect": "4.0.4",
|
|
83
|
+
"pg": "8.23.0"
|
|
83
84
|
},
|
|
84
85
|
"peerDependenciesMeta": {
|
|
85
86
|
"@effect/platform-node": {
|
|
@@ -105,6 +106,9 @@
|
|
|
105
106
|
},
|
|
106
107
|
"better-auth": {
|
|
107
108
|
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"pg": {
|
|
111
|
+
"optional": true
|
|
108
112
|
}
|
|
109
113
|
},
|
|
110
114
|
"engines": {
|
package/src/rpc/identity.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context, type Option } from "effect";
|
|
1
|
+
import { Context, type Effect, type Option } from "effect";
|
|
2
|
+
import type { AuthUnavailable, Unauthorized } from "#errors/taxonomy.ts";
|
|
2
3
|
export class RequestId extends Context.Service<RequestId, string>()("@shivaedev/platform/rpc/RequestId") {}
|
|
3
4
|
export interface IdentityValue {
|
|
4
5
|
readonly id: string;
|
|
@@ -12,7 +13,12 @@ export interface ProviderAccountValue {
|
|
|
12
13
|
export interface SessionUserValue extends IdentityValue {
|
|
13
14
|
readonly email: string;
|
|
14
15
|
readonly name: string;
|
|
15
|
-
readonly providerAccounts?: readonly ProviderAccountValue[];
|
|
16
16
|
readonly role?: string | null | undefined;
|
|
17
17
|
}
|
|
18
18
|
export class SessionUser extends Context.Service<SessionUser, SessionUserValue>()("@shivaedev/platform/rpc/SessionUser") {}
|
|
19
|
+
export interface SessionUserWithProviderAccountsValue extends SessionUserValue {
|
|
20
|
+
readonly providerAccounts: Effect.Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>;
|
|
21
|
+
}
|
|
22
|
+
export class SessionUserWithProviderAccounts extends Context.Service<SessionUserWithProviderAccounts, SessionUserWithProviderAccountsValue>()(
|
|
23
|
+
"@shivaedev/platform/rpc/SessionUserWithProviderAccounts",
|
|
24
|
+
) {}
|
package/src/rpc/middleware.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { RpcMiddleware } from "effect/rpc";
|
|
3
3
|
import { AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import type { Identity, OptionalIdentity, RequestId, SessionUser } from "./identity.ts";
|
|
4
|
+
import type { Identity, OptionalIdentity, RequestId, SessionUser, SessionUserWithProviderAccounts } from "./identity.ts";
|
|
5
5
|
export class RequestTracing extends RpcMiddleware.Service<
|
|
6
6
|
RequestTracing,
|
|
7
7
|
{
|
|
@@ -30,3 +30,9 @@ export class AuthenticatedUser extends RpcMiddleware.Service<
|
|
|
30
30
|
>()("@shivaedev/platform/rpc/AuthenticatedUser", {
|
|
31
31
|
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
32
32
|
}) {}
|
|
33
|
+
export class AuthenticatedUserWithProviderAccounts extends RpcMiddleware.Service<
|
|
34
|
+
AuthenticatedUserWithProviderAccounts,
|
|
35
|
+
{ provides: Identity | SessionUser | SessionUserWithProviderAccounts }
|
|
36
|
+
>()("@shivaedev/platform/rpc/AuthenticatedUserWithProviderAccounts", {
|
|
37
|
+
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
38
|
+
}) {}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Schema } from "effect";
|
|
2
|
+
import { contract } from "@shivaedev/effect-contract/contract.ts";
|
|
3
|
+
import { query } from "@shivaedev/effect-contract/operation.ts";
|
|
4
|
+
|
|
5
|
+
export function publicConfig<T, E>(schema: Schema.Codec<T, E>) {
|
|
6
|
+
return contract("platform.publicConfig", {
|
|
7
|
+
queries: [query("get", { reads: () => [], success: schema })],
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type BetterAuthOptions, betterAuth } from "better-auth";
|
|
2
|
+
import { Config, Effect, Option, Redacted } from "effect";
|
|
3
|
+
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
4
|
+
import { Pool, type PoolClient } from "pg";
|
|
5
|
+
import { trustedProxyPeer, trustedProxyRanges } from "#server/rpc-server/local.ts";
|
|
6
|
+
|
|
7
|
+
export const betterAuthMount = Effect.fn("Platform.betterAuthMount")(function* <const TOptions extends BetterAuthOptions>(options: TOptions) {
|
|
8
|
+
const secret = options.secret ?? Redacted.value(yield* Config.Redacted("BETTER_AUTH_SECRET"));
|
|
9
|
+
const publicUrl = yield* Config.URL("PUBLIC_URL").pipe(Config.option);
|
|
10
|
+
const origin = Option.map(publicUrl, (url) => url.origin);
|
|
11
|
+
const trustedProxy = Option.getOrUndefined(yield* Config.String("TRUSTED_PROXY").pipe(Config.option));
|
|
12
|
+
const clientIpHeader = yield* Config.String("AUTH_CLIENT_IP_HEADER").pipe(Config.withDefault("x-forwarded-for"));
|
|
13
|
+
const rateLimitEnabled = yield* Config.Boolean("AUTH_RATE_LIMIT_ENABLED").pipe(Config.withDefault(true));
|
|
14
|
+
const isTrustedProxy = trustedProxyPeer(trustedProxy);
|
|
15
|
+
const trustedProxies = trustedProxyRanges(trustedProxy);
|
|
16
|
+
const auth = betterAuth({
|
|
17
|
+
...options,
|
|
18
|
+
advanced: {
|
|
19
|
+
useSecureCookies: Option.isSome(publicUrl) && publicUrl.value.protocol === "https:",
|
|
20
|
+
...options.advanced,
|
|
21
|
+
ipAddress: {
|
|
22
|
+
...options.advanced?.ipAddress,
|
|
23
|
+
ipAddressHeaders: trustedProxies.length === 0 ? [] : [clientIpHeader],
|
|
24
|
+
trustedProxies,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
"baseURL": options.baseURL ?? Option.getOrElse(origin, () => ({ allowedHosts: ["127.0.0.1:*"], protocol: "http" as const })),
|
|
28
|
+
rateLimit: { ...options.rateLimit, enabled: rateLimitEnabled },
|
|
29
|
+
secret,
|
|
30
|
+
trustedOrigins: options.trustedOrigins ?? [Option.getOrElse(origin, () => "http://127.0.0.1:*")],
|
|
31
|
+
});
|
|
32
|
+
const routes = HttpRouter.use((router) =>
|
|
33
|
+
router.add("*", "/api/auth/*", (request) =>
|
|
34
|
+
Effect.gen(function* () {
|
|
35
|
+
const web = yield* HttpServerRequest.toWeb(request);
|
|
36
|
+
if (!Option.exists(request.remoteAddress, isTrustedProxy)) {
|
|
37
|
+
web.headers.delete("x-forwarded-for");
|
|
38
|
+
web.headers.delete(clientIpHeader);
|
|
39
|
+
}
|
|
40
|
+
return HttpServerResponse.fromWeb(yield* Effect.tryPromise(() => auth.handler(web)));
|
|
41
|
+
}).pipe(Effect.catch(() => Effect.succeed(HttpServerResponse.text("Authentication is temporarily unavailable", { status: 503 })))),
|
|
42
|
+
),
|
|
43
|
+
);
|
|
44
|
+
return { auth, routes };
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const authDatabase = Effect.fn("Platform.authDatabase")(function* (url: Redacted.Redacted<string>) {
|
|
48
|
+
const clients = new Set<PoolClient>();
|
|
49
|
+
const run = Effect.runForkWith(yield* Effect.context<never>());
|
|
50
|
+
return yield* Effect.acquireRelease(
|
|
51
|
+
Effect.sync(() => {
|
|
52
|
+
const pool = new Pool({ connectionString: Redacted.value(url) });
|
|
53
|
+
pool.on("connect", (client) => clients.add(client));
|
|
54
|
+
pool.on("remove", (client) => clients.delete(client));
|
|
55
|
+
pool.on("error", (error) => {
|
|
56
|
+
run(Effect.logDebug({ event: "auth_database_idle_client_failed", message: error.message }));
|
|
57
|
+
});
|
|
58
|
+
return pool;
|
|
59
|
+
}),
|
|
60
|
+
(pool) =>
|
|
61
|
+
Effect.promise(async () => {
|
|
62
|
+
// pg-pool resolves end before its clients finish disconnecting.
|
|
63
|
+
const disconnected = [...clients].map((client) => new Promise<void>((resolve) => client.once("end", resolve)));
|
|
64
|
+
await pool.end();
|
|
65
|
+
await Promise.all(disconnected);
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { testUtils } from "better-auth/plugins";
|
|
2
|
+
|
|
3
|
+
export function authTestUtils(options?: Parameters<typeof testUtils>[0]) {
|
|
4
|
+
const plugin = testUtils(options);
|
|
5
|
+
return {
|
|
6
|
+
...plugin,
|
|
7
|
+
init(context: Parameters<typeof plugin.init>[0]) {
|
|
8
|
+
const result = plugin.init(context);
|
|
9
|
+
// Remove this wrapper once Better Auth's testUtils omits undefined options.
|
|
10
|
+
return result.options === undefined ? { context: result.context } : { context: result.context, options: result.options };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type TestContext = ReturnType<ReturnType<typeof authTestUtils>["init"]>["context"];
|
|
16
|
+
|
|
17
|
+
export async function signedInUser(auth: { readonly $context: Promise<TestContext> }, fields: Parameters<TestContext["test"]["createUser"]>[0]) {
|
|
18
|
+
const { test } = await auth.$context;
|
|
19
|
+
const user = await test.saveUser(test.createUser(fields));
|
|
20
|
+
return test.login({ userId: user.id });
|
|
21
|
+
}
|
|
@@ -3,12 +3,13 @@ import { Config, Effect, Layer, type Redacted, Schedule } from "effect";
|
|
|
3
3
|
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
4
4
|
import { makeLiveServer } from "@shivaedev/effect-contract/makeLiveServer.ts";
|
|
5
5
|
import { assertPostgresMigrations } from "@shivaedev/effect-sql/assertPostgresMigrations.ts";
|
|
6
|
+
import type { SqlNameTransforms } from "@shivaedev/effect-sql/clientOptions.ts";
|
|
6
7
|
import { migratePostgres } from "@shivaedev/effect-sql/migrations.ts";
|
|
7
8
|
import { type PostgresChangesOptions, postgresChanges } from "@shivaedev/effect-sql/postgresChanges.ts";
|
|
8
9
|
import type { AppChanges, AppDatabaseOptions } from "#server/appHost.ts";
|
|
9
10
|
import { databaseAddress } from "./databaseAddress.ts";
|
|
10
11
|
|
|
11
|
-
export interface DatabaseConfig {
|
|
12
|
+
export interface DatabaseConfig extends SqlNameTransforms {
|
|
12
13
|
readonly applicationName?: string;
|
|
13
14
|
readonly changes?: Omit<PostgresChangesOptions, "client" | "resync">;
|
|
14
15
|
readonly databaseUrl?: Redacted.Redacted<string>;
|
|
@@ -20,6 +21,8 @@ export function databaseLayer<R>(options: AppDatabaseOptions<R>, changesService:
|
|
|
20
21
|
Effect.gen(function* () {
|
|
21
22
|
const url = options.config?.databaseUrl ?? (yield* Config.Redacted("DATABASE_URL"));
|
|
22
23
|
return PgClient.layer({
|
|
24
|
+
transformQueryNames: options.config?.transformQueryNames,
|
|
25
|
+
transformResultNames: options.config?.transformResultNames,
|
|
23
26
|
url,
|
|
24
27
|
...databaseAddress(url),
|
|
25
28
|
...(options.config?.maxConnections === undefined ? {} : { maxConnections: options.config.maxConnections }),
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Effect, Option, Predicate } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
|
-
import { AuthUnavailable } from "#errors/taxonomy.ts";
|
|
3
|
+
import { AuthUnavailable, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
4
|
import type { ProviderAccountValue } from "#rpc/identity.ts";
|
|
5
5
|
import { redact } from "#server/rpc-server/redact.ts";
|
|
6
6
|
import type { SessionProvider, SessionShape } from "#server/rpc-server/session.ts";
|
|
7
7
|
export type GetSession<TSession extends SessionShape> = (headers: globalThis.Headers) => Promise<TSession | null>;
|
|
8
8
|
export type GetProviderAccounts = (headers: globalThis.Headers) => Promise<readonly ProviderAccountValue[]>;
|
|
9
9
|
export type SessionWithProviderAccounts<TSession extends SessionShape> = TSession & {
|
|
10
|
-
readonly user: TSession["user"] & { readonly providerAccounts: readonly ProviderAccountValue[] };
|
|
10
|
+
readonly user: TSession["user"] & { readonly providerAccounts: Effect.Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized> };
|
|
11
11
|
};
|
|
12
12
|
function unavailable(cause: unknown) {
|
|
13
13
|
return Effect.andThen(
|
|
@@ -22,27 +22,23 @@ export function betterAuthSessions<TSession extends SessionShape>(
|
|
|
22
22
|
): SessionProvider<SessionWithProviderAccounts<TSession>>;
|
|
23
23
|
export function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>, getProviderAccounts?: GetProviderAccounts) {
|
|
24
24
|
const betterAuthSession = Effect.fn("PlatformRpc.betterAuthSession")(function* (headers: Headers.Headers) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
user: { ...resolved.user, providerAccounts: accounts },
|
|
43
|
-
};
|
|
44
|
-
}).pipe(Effect.catch((error) => unavailable(error.cause)));
|
|
45
|
-
return Option.fromNullishOr(session);
|
|
25
|
+
const transport = new globalThis.Headers(headers);
|
|
26
|
+
const session = yield* Effect.tryPromise(() => getSession(transport)).pipe(Effect.catch((error) => unavailable(error.cause)));
|
|
27
|
+
if (session === null || getProviderAccounts === undefined) {
|
|
28
|
+
return Option.fromNullishOr(session);
|
|
29
|
+
}
|
|
30
|
+
const providerAccounts = Effect.tryPromise({
|
|
31
|
+
catch: (cause) => cause,
|
|
32
|
+
try: () => getProviderAccounts(transport),
|
|
33
|
+
}).pipe(
|
|
34
|
+
Effect.catch(
|
|
35
|
+
(cause): Effect.Effect<never, AuthUnavailable | Unauthorized> =>
|
|
36
|
+
Predicate.hasProperty(cause, "statusCode") && cause.statusCode === 401
|
|
37
|
+
? Effect.fail(new Unauthorized({ message: "Authentication required" }))
|
|
38
|
+
: unavailable(cause),
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
return Option.some({ ...session, user: { ...session.user, providerAccounts } });
|
|
46
42
|
});
|
|
47
43
|
return { get: betterAuthSession };
|
|
48
44
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Config, type ConfigProvider, Effect } from "effect";
|
|
2
|
+
import type { publicConfig } from "#rpc/publicConfig.ts";
|
|
3
|
+
|
|
4
|
+
export function publicConfigLayer<T, E>(declaration: ReturnType<typeof publicConfig<T, E>>, path?: string | ConfigProvider.Path) {
|
|
5
|
+
return declaration.toLayer(
|
|
6
|
+
Effect.gen(function* () {
|
|
7
|
+
const value = yield* Config.schema(declaration.declaration.queries[0].success, path);
|
|
8
|
+
return declaration.of({ "platform.publicConfig.get": () => Effect.succeed(value) });
|
|
9
|
+
}),
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
3
|
import { type AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
Identity,
|
|
6
|
+
OptionalIdentity,
|
|
7
|
+
SessionUser,
|
|
8
|
+
type SessionUserValue,
|
|
9
|
+
SessionUserWithProviderAccounts,
|
|
10
|
+
type SessionUserWithProviderAccountsValue,
|
|
11
|
+
} from "#rpc/identity.ts";
|
|
12
|
+
import { Authenticated, AuthenticatedUser, AuthenticatedUserWithProviderAccounts, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
6
13
|
import { type OriginPolicy, originRequest } from "./origin.ts";
|
|
7
14
|
import { transportHeaders } from "./transportHeaders.ts";
|
|
8
15
|
export interface SessionShape {
|
|
@@ -67,7 +74,7 @@ export function withSessionUser<TSession extends UserSessionShape>(policy: Sessi
|
|
|
67
74
|
if (Option.isNone(session)) {
|
|
68
75
|
return yield* new Unauthorized({ message: "Authentication required" });
|
|
69
76
|
}
|
|
70
|
-
const { name, email, role
|
|
77
|
+
const { name, email, role } = session.value.user;
|
|
71
78
|
const id = yield* identified(session.value.user.id);
|
|
72
79
|
return yield* effect.pipe(
|
|
73
80
|
Effect.provideService(Identity, { id }),
|
|
@@ -76,9 +83,6 @@ export function withSessionUser<TSession extends UserSessionShape>(policy: Sessi
|
|
|
76
83
|
id,
|
|
77
84
|
name,
|
|
78
85
|
...(role === undefined ? {} : { role }),
|
|
79
|
-
...(providerAccounts === undefined
|
|
80
|
-
? {}
|
|
81
|
-
: { providerAccounts: providerAccounts.map(({ accountId, providerId }) => ({ accountId, providerId })) }),
|
|
82
86
|
}),
|
|
83
87
|
Effect.annotateLogs({ userId: id }),
|
|
84
88
|
);
|
|
@@ -87,3 +91,31 @@ export function withSessionUser<TSession extends UserSessionShape>(policy: Sessi
|
|
|
87
91
|
export function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser> {
|
|
88
92
|
return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc }) => effect.pipe(withSessionUser(policy, headers, rpc._tag)));
|
|
89
93
|
}
|
|
94
|
+
|
|
95
|
+
export interface ProviderAccountsSessionShape extends UserSessionShape {
|
|
96
|
+
readonly user: SessionUserWithProviderAccountsValue;
|
|
97
|
+
}
|
|
98
|
+
export function authenticatedUserWithProviderAccountsLayer<TSession extends ProviderAccountsSessionShape>(
|
|
99
|
+
policy: SessionPolicy<TSession>,
|
|
100
|
+
): Layer.Layer<AuthenticatedUserWithProviderAccounts> {
|
|
101
|
+
return Layer.succeed(AuthenticatedUserWithProviderAccounts, (effect, { headers, rpc }) =>
|
|
102
|
+
Effect.gen(function* () {
|
|
103
|
+
const session = yield* resolveSession(policy, headers, rpc._tag);
|
|
104
|
+
if (Option.isNone(session)) {
|
|
105
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
106
|
+
}
|
|
107
|
+
const { id, name, email, role, providerAccounts } = session.value.user;
|
|
108
|
+
const accounts = yield* Effect.cached(
|
|
109
|
+
Effect.map(providerAccounts, (values) => values.map(({ accountId, providerId }) => ({ accountId, providerId }))),
|
|
110
|
+
);
|
|
111
|
+
const user = { email, id, name, ...(role === undefined ? {} : { role }) };
|
|
112
|
+
yield* identified(id);
|
|
113
|
+
return yield* effect.pipe(
|
|
114
|
+
Effect.provideService(Identity, { id }),
|
|
115
|
+
Effect.provideService(SessionUser, user),
|
|
116
|
+
Effect.provideService(SessionUserWithProviderAccounts, { ...user, providerAccounts: accounts }),
|
|
117
|
+
Effect.annotateLogs({ userId: id }),
|
|
118
|
+
);
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
}
|