sst 3.0.1-8 → 3.0.1

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 (58) hide show
  1. package/bin/sst.mjs +28 -0
  2. package/dist/auth/adapter/adapter.d.ts +24 -0
  3. package/dist/auth/adapter/adapter.js +4 -0
  4. package/dist/auth/adapter/apple.d.ts +5 -0
  5. package/dist/auth/adapter/apple.js +22 -0
  6. package/dist/auth/adapter/code.d.ts +8 -0
  7. package/dist/auth/adapter/code.js +47 -0
  8. package/dist/auth/adapter/facebook.d.ts +5 -0
  9. package/dist/auth/adapter/facebook.js +27 -0
  10. package/dist/auth/adapter/github.d.ts +12 -0
  11. package/dist/auth/adapter/github.js +23 -0
  12. package/dist/auth/adapter/google.d.ts +17 -0
  13. package/dist/auth/adapter/google.js +22 -0
  14. package/dist/auth/adapter/index.d.ts +11 -0
  15. package/dist/auth/adapter/index.js +10 -0
  16. package/dist/auth/adapter/link.d.ts +6 -0
  17. package/dist/auth/adapter/link.js +27 -0
  18. package/dist/auth/adapter/microsoft.d.ts +11 -0
  19. package/dist/auth/adapter/microsoft.js +16 -0
  20. package/dist/auth/adapter/oauth.d.ts +33 -0
  21. package/dist/auth/adapter/oauth.js +82 -0
  22. package/dist/auth/adapter/oidc.d.ts +19 -0
  23. package/dist/auth/adapter/oidc.js +48 -0
  24. package/dist/auth/adapter/spotify.d.ts +12 -0
  25. package/dist/auth/adapter/spotify.js +22 -0
  26. package/dist/auth/example/bun.d.ts +2 -0
  27. package/dist/auth/example/bun.js +46 -0
  28. package/dist/auth/handler.d.ts +57 -0
  29. package/dist/auth/handler.js +207 -0
  30. package/dist/auth/index.d.ts +10 -0
  31. package/dist/auth/index.js +10 -0
  32. package/dist/auth/session.d.ts +25 -0
  33. package/dist/auth/session.js +28 -0
  34. package/dist/aws/auth.d.ts +9 -0
  35. package/dist/aws/auth.js +12 -0
  36. package/dist/aws/bus.d.ts +29 -0
  37. package/dist/aws/bus.js +69 -0
  38. package/dist/aws/client.d.ts +3 -0
  39. package/dist/aws/client.js +36 -0
  40. package/dist/aws/realtime.d.ts +84 -0
  41. package/dist/aws/realtime.js +82 -0
  42. package/dist/event/index.d.ts +72 -0
  43. package/dist/event/index.js +35 -0
  44. package/dist/event/validator.d.ts +4 -0
  45. package/dist/event/validator.js +11 -0
  46. package/dist/index.d.ts +2 -1
  47. package/dist/index.js +2 -1
  48. package/dist/realtime/index.d.ts +25 -0
  49. package/dist/realtime/index.js +24 -0
  50. package/dist/resource.d.ts +6 -4
  51. package/dist/resource.js +54 -5
  52. package/dist/util/prettify.d.ts +3 -0
  53. package/dist/util/prettify.js +1 -0
  54. package/dist/vector/index.d.ts +238 -0
  55. package/dist/vector/index.js +68 -0
  56. package/package.json +46 -11
  57. package/dist/vector-client.d.ts +0 -126
  58. package/dist/vector-client.js +0 -38
@@ -0,0 +1,57 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Adapter } from "./adapter/adapter.js";
3
+ import { JWTPayload } from "jose";
4
+ import { SessionBuilder } from "./session.js";
5
+ import { Hono } from "hono/tiny";
6
+ export interface OnSuccessResponder<T extends {
7
+ type: any;
8
+ properties: any;
9
+ }> {
10
+ session(input: T & JWTPayload): Promise<Response>;
11
+ }
12
+ export declare class UnknownProviderError extends Error {
13
+ provider?: string | undefined;
14
+ constructor(provider?: string | undefined);
15
+ }
16
+ export declare class MissingParameterError extends Error {
17
+ parameter: string;
18
+ constructor(parameter: string);
19
+ }
20
+ export declare class UnknownStateError extends Error {
21
+ constructor();
22
+ }
23
+ export declare class UnauthorizedClientError extends Error {
24
+ client: string;
25
+ redirect_uri: string;
26
+ constructor(client: string, redirect_uri: string);
27
+ }
28
+ export declare class InvalidSessionError extends Error {
29
+ constructor();
30
+ }
31
+ export type Prettify<T> = {
32
+ [K in keyof T]: T[K];
33
+ } & {};
34
+ export declare const aws: <E extends import("hono").Env = import("hono").Env, S extends import("hono").Schema = {}, BasePath extends string = "/">(app: import("hono").Hono<E, S, BasePath>) => (event: import("hono/aws-lambda").LambdaEvent, lambdaContext?: import("hono/aws-lambda").LambdaContext | undefined) => Promise<import("hono/aws-lambda").APIGatewayProxyResult>;
35
+ export declare function AuthHandler<Providers extends Record<string, Adapter<any>>, Sessions extends SessionBuilder = SessionBuilder, Result = {
36
+ [key in keyof Providers]: Prettify<{
37
+ provider: key;
38
+ } & (Providers[key] extends Adapter<infer T> ? T : {})>;
39
+ }[keyof Providers]>(input: {
40
+ session?: Sessions;
41
+ providers: Providers;
42
+ callbacks: {
43
+ index?(req: Request): Promise<Response>;
44
+ error?(error: UnknownStateError, req: Request): Promise<Response | undefined>;
45
+ auth: {
46
+ error?(error: MissingParameterError | UnauthorizedClientError | UnknownProviderError, req: Request): Promise<Response>;
47
+ start?(event: Request): Promise<void>;
48
+ allowClient(clientID: string, redirect: string, req: Request): Promise<boolean>;
49
+ success(response: OnSuccessResponder<Sessions["$typeValues"]>, input: Result, req: Request): Promise<Response>;
50
+ };
51
+ connect?: {
52
+ error?(error: InvalidSessionError | UnknownProviderError, req: Request): Promise<Response | undefined>;
53
+ start?(session: Sessions["$typeValues"], req: Request): Promise<void>;
54
+ success?(session: Sessions["$typeValues"], input: {}): Promise<Response>;
55
+ };
56
+ };
57
+ }): Hono<import("hono").Env, import("hono/types").BlankSchema, "/">;
@@ -0,0 +1,207 @@
1
+ import { SignJWT, importPKCS8, importSPKI, jwtVerify } from "jose";
2
+ import { Hono } from "hono/tiny";
3
+ import { handle as awsHandle } from "hono/aws-lambda";
4
+ import { deleteCookie, getCookie, setCookie } from "hono/cookie";
5
+ export class UnknownProviderError extends Error {
6
+ provider;
7
+ constructor(provider) {
8
+ super("Unknown provider: " + provider);
9
+ this.provider = provider;
10
+ }
11
+ }
12
+ export class MissingParameterError extends Error {
13
+ parameter;
14
+ constructor(parameter) {
15
+ super("Missing parameter: " + parameter);
16
+ this.parameter = parameter;
17
+ }
18
+ }
19
+ export class UnknownStateError extends Error {
20
+ constructor() {
21
+ super("The browser was in an unknown state. This could be because certain cookies expired or the browser was switched in the middle of an authentication flow");
22
+ }
23
+ }
24
+ export class UnauthorizedClientError extends Error {
25
+ client;
26
+ redirect_uri;
27
+ constructor(client, redirect_uri) {
28
+ super("Unauthorized client");
29
+ this.client = client;
30
+ this.redirect_uri = redirect_uri;
31
+ }
32
+ }
33
+ export class InvalidSessionError extends Error {
34
+ constructor() {
35
+ super("Invalid session");
36
+ }
37
+ }
38
+ import process from "node:process";
39
+ import { Resource } from "../resource.js";
40
+ export const aws = awsHandle;
41
+ export function AuthHandler(input) {
42
+ const app = new Hono();
43
+ if (!input.callbacks.auth.error) {
44
+ input.callbacks.auth.error = async (err) => {
45
+ return new Response(err.message, {
46
+ status: 400,
47
+ headers: {
48
+ "Content-Type": "text/plain",
49
+ },
50
+ });
51
+ };
52
+ }
53
+ const options = {
54
+ signing: {
55
+ privateKey: () => importPKCS8(
56
+ // @ts-expect-error
57
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512"),
58
+ publicKey: () => importSPKI(
59
+ // @ts-expect-error
60
+ process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RS512"),
61
+ },
62
+ encryption: {
63
+ privateKey: () => importPKCS8(
64
+ // @ts-expect-error
65
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RSA-OAEP-512"),
66
+ publicKey: () => importSPKI(
67
+ // @ts-expect-error
68
+ process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RSA-OAEP-512"),
69
+ },
70
+ algorithm: "RS512",
71
+ async success(ctx, properties) {
72
+ const redirect_uri = getCookie(ctx, "redirect_uri");
73
+ const response_type = getCookie(ctx, "response_type");
74
+ if (!redirect_uri) {
75
+ return options.forward(ctx, await input.callbacks.auth.error(new UnknownStateError(), ctx.req.raw));
76
+ }
77
+ return await input.callbacks.auth.success({
78
+ async session(session) {
79
+ const token = await new SignJWT(session)
80
+ .setProtectedHeader({ alg: "RS512" })
81
+ .setExpirationTime("1yr")
82
+ .sign(await options.signing.privateKey());
83
+ deleteCookie(ctx, "provider");
84
+ deleteCookie(ctx, "response_type");
85
+ deleteCookie(ctx, "redirect_uri");
86
+ deleteCookie(ctx, "state");
87
+ const client_id = getCookie(ctx, "client_id");
88
+ const state = getCookie(ctx, "state");
89
+ if (response_type === "token") {
90
+ const location = new URL(redirect_uri);
91
+ location.hash = `access_token=${token}&state=${state || ""}`;
92
+ return ctx.redirect(location.toString(), 302);
93
+ }
94
+ if (response_type === "code") {
95
+ // This allows the code to be reused within a 30 second window
96
+ // The code should be single use but we're making this tradeoff to remain stateless
97
+ // In the future can store this in a dynamo table to ensure single use
98
+ const code = await new SignJWT({
99
+ client_id,
100
+ redirect_uri,
101
+ token,
102
+ })
103
+ .setProtectedHeader({ alg: "RS512" })
104
+ .setExpirationTime("30s")
105
+ .sign(await options.signing.privateKey());
106
+ const location = new URL(redirect_uri);
107
+ location.searchParams.set("code", code);
108
+ location.searchParams.set("state", state || "");
109
+ return ctx.redirect(location.toString(), 302);
110
+ }
111
+ ctx.status(400);
112
+ return ctx.text(`Unsupported response_type: ${response_type}`);
113
+ },
114
+ }, {
115
+ provider: ctx.get("provider"),
116
+ ...properties,
117
+ }, ctx.req.raw);
118
+ },
119
+ forward(ctx, response) {
120
+ return ctx.newResponse(response.body, response.status, Object.fromEntries(response.headers.entries()));
121
+ },
122
+ cookie(c, key, value, maxAge) {
123
+ setCookie(c, key, value, {
124
+ maxAge,
125
+ httpOnly: true,
126
+ ...(c.req.url.startsWith("https://")
127
+ ? { secure: true, sameSite: "None" }
128
+ : {}),
129
+ });
130
+ },
131
+ };
132
+ app.post("/token", async (c) => {
133
+ console.log("token request");
134
+ const form = await c.req.formData();
135
+ if (form.get("grant_type") !== "authorization_code") {
136
+ c.status(400);
137
+ return c.text("Invalid grant_type");
138
+ }
139
+ const code = form.get("code");
140
+ if (!code) {
141
+ c.status(400);
142
+ return c.text("Missing code");
143
+ }
144
+ const { payload } = await jwtVerify(code, await options.signing.publicKey());
145
+ if (payload.redirect_uri !== form.get("redirect_uri")) {
146
+ c.status(400);
147
+ return c.text("redirect_uri mismatch");
148
+ }
149
+ if (payload.client_id !== form.get("client_id")) {
150
+ c.status(400);
151
+ return c.text("client_id mismatch");
152
+ }
153
+ return c.json({
154
+ access_token: payload.token,
155
+ });
156
+ });
157
+ app.use("/:provider/authorize", async (c, next) => {
158
+ const provider = c.req.param("provider");
159
+ console.log("authorize request for", provider);
160
+ const response_type = c.req.query("response_type") || getCookie(c, "response_type");
161
+ const redirect_uri = c.req.query("redirect_uri") || getCookie(c, "redirect_uri");
162
+ const state = c.req.query("state") || getCookie(c, "state");
163
+ const client_id = c.req.query("client_id") || getCookie(c, "client_id");
164
+ if (!provider) {
165
+ c.status(400);
166
+ return c.text("Missing provider");
167
+ }
168
+ if (!redirect_uri) {
169
+ c.status(400);
170
+ return c.text("Missing redirect_uri");
171
+ }
172
+ if (!response_type) {
173
+ c.status(400);
174
+ return c.text("Missing response_type");
175
+ }
176
+ if (!client_id) {
177
+ c.status(400);
178
+ return c.text("Missing client_id");
179
+ }
180
+ options.cookie(c, "provider", provider, 60 * 10);
181
+ options.cookie(c, "response_type", response_type, 60 * 10);
182
+ options.cookie(c, "redirect_uri", redirect_uri, 60 * 10);
183
+ options.cookie(c, "state", state || "", 60 * 10);
184
+ options.cookie(c, "client_id", client_id || "", 60 * 10);
185
+ if (input.callbacks.auth.start) {
186
+ await input.callbacks.auth.start(c.req.raw);
187
+ }
188
+ await next();
189
+ });
190
+ for (const [name, value] of Object.entries(input.providers)) {
191
+ const route = new Hono();
192
+ route.use(async (c, next) => {
193
+ c.set("provider", name);
194
+ await next();
195
+ });
196
+ value(route, {
197
+ name,
198
+ ...options,
199
+ });
200
+ app.route(`/${name}`, route);
201
+ }
202
+ app.all("/*", async (c) => {
203
+ return c.notFound();
204
+ });
205
+ console.log(app.routes);
206
+ return app;
207
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./session.js";
2
+ export * from "./handler.js";
3
+ export { Issuer } from "openid-client";
4
+ import { AuthHandler } from "./handler.js";
5
+ import { createSessionBuilder } from "./session.js";
6
+ export declare namespace auth {
7
+ type Issuer = import("openid-client").Issuer;
8
+ const authorizer: typeof AuthHandler;
9
+ const sessions: typeof createSessionBuilder;
10
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./session.js";
2
+ export * from "./handler.js";
3
+ export { Issuer } from "openid-client";
4
+ import { AuthHandler } from "./handler.js";
5
+ import { createSessionBuilder } from "./session.js";
6
+ export var auth;
7
+ (function (auth) {
8
+ auth.authorizer = AuthHandler;
9
+ auth.sessions = createSessionBuilder;
10
+ })(auth || (auth = {}));
@@ -0,0 +1,25 @@
1
+ export type SessionBuilder = ReturnType<typeof createSessionBuilder>;
2
+ export declare function createSessionBuilder<SessionTypes extends Record<string, any> = {}>(): {
3
+ verify(token: string): Promise<{ [type in keyof SessionTypes]: {
4
+ type: type;
5
+ properties: SessionTypes[type];
6
+ }; }[keyof SessionTypes] | {
7
+ type: "public";
8
+ properties: {};
9
+ }>;
10
+ create(session: { [type in keyof SessionTypes]: {
11
+ type: type;
12
+ properties: SessionTypes[type];
13
+ }; }[keyof SessionTypes] | {
14
+ type: "public";
15
+ properties: {};
16
+ }): Promise<string>;
17
+ $type: SessionTypes;
18
+ $typeValues: { [type in keyof SessionTypes]: {
19
+ type: type;
20
+ properties: SessionTypes[type];
21
+ }; }[keyof SessionTypes] | {
22
+ type: "public";
23
+ properties: {};
24
+ };
25
+ };
@@ -0,0 +1,28 @@
1
+ import { SignJWT, importPKCS8, importSPKI, jwtVerify } from "jose";
2
+ import { Resource } from "../resource.js";
3
+ import process from "node:process";
4
+ export function createSessionBuilder() {
5
+ return {
6
+ async verify(token) {
7
+ const auth = Object.values(Resource).find((value) => value.publicKey);
8
+ if (!auth) {
9
+ throw new Error("No auth resource found. Make sure to link the auth resource to this function.");
10
+ }
11
+ const publicKey = auth.publicKey;
12
+ const result = await jwtVerify(token, await importSPKI(publicKey, "RS512"));
13
+ return result.payload;
14
+ },
15
+ async create(session) {
16
+ const privateKey = await importPKCS8(
17
+ // @ts-expect-error
18
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512");
19
+ const token = await new SignJWT(session)
20
+ .setProtectedHeader({ alg: "RS512" })
21
+ .setExpirationTime("1yr")
22
+ .sign(privateKey);
23
+ return token;
24
+ },
25
+ $type: {},
26
+ $typeValues: {},
27
+ };
28
+ }
@@ -0,0 +1,9 @@
1
+ import { Handler } from "aws-lambda";
2
+ import { Adapter } from "../auth/adapter/adapter.js";
3
+ import { AuthHandler } from "../auth/handler.js";
4
+ import { SessionBuilder, createSessionBuilder } from "../auth/session.js";
5
+ export declare namespace auth {
6
+ type Issuer = import("openid-client").Issuer;
7
+ function authorizer<Providers extends Record<string, Adapter<any>>, Sessions extends SessionBuilder>(...args: Parameters<typeof AuthHandler<Providers, Sessions>>): Handler<any, any>;
8
+ const sessions: typeof createSessionBuilder;
9
+ }
@@ -0,0 +1,12 @@
1
+ import { AuthHandler } from "../auth/handler.js";
2
+ import { createSessionBuilder } from "../auth/session.js";
3
+ import { handle, streamHandle } from "hono/aws-lambda";
4
+ export var auth;
5
+ (function (auth) {
6
+ function authorizer(...args) {
7
+ const hono = AuthHandler(...args);
8
+ return (process.env.SST_LIVE ? handle(hono) : streamHandle(hono));
9
+ }
10
+ auth.authorizer = authorizer;
11
+ auth.sessions = createSessionBuilder;
12
+ })(auth || (auth = {}));
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { AwsOptions } from "../aws/client.js";
3
+ import { Resource } from "../resource.js";
4
+ import { event } from "../event/index.js";
5
+ import { EventBridgeEvent, EventBridgeHandler } from "aws-lambda";
6
+ export declare namespace bus {
7
+ type Name = Extract<typeof Resource, {
8
+ type: "sst.aws.Bus";
9
+ }>["name"];
10
+ function subscriber<Events extends event.Definition>(_events: Events | Events[], cb: (input: {
11
+ [K in Events["type"]]: Extract<Events, {
12
+ type: K;
13
+ }>["$payload"];
14
+ }[Events["type"]], raw: EventBridgeEvent<string, any>) => Promise<void>): EventBridgeHandler<string, any, void>;
15
+ /** @deprecated
16
+ * use bus.subscriber instead
17
+ * */
18
+ const handler: typeof subscriber;
19
+ function publish<Definition extends event.Definition = any>(name: string | {
20
+ name: string;
21
+ }, def: Definition | string, properties: Definition["$input"], options?: {
22
+ metadata?: Definition["$metadata"];
23
+ aws?: AwsOptions;
24
+ }): Promise<any>;
25
+ class PublishError extends Error {
26
+ readonly response: Response;
27
+ constructor(response: Response);
28
+ }
29
+ }
@@ -0,0 +1,69 @@
1
+ import { client } from "../aws/client.js";
2
+ import { Resource } from "../resource.js";
3
+ export var bus;
4
+ (function (bus) {
5
+ function url(region, options) {
6
+ if (options?.region)
7
+ region = options.region;
8
+ return `https://events.${region}.amazonaws.com/`;
9
+ }
10
+ function subscriber(_events, cb) {
11
+ return async function (event) {
12
+ const payload = {
13
+ type: event["detail-type"],
14
+ properties: event.detail.properties,
15
+ metadata: event.detail.metadata,
16
+ };
17
+ return cb(payload, event);
18
+ };
19
+ }
20
+ bus.subscriber = subscriber;
21
+ /** @deprecated
22
+ * use bus.subscriber instead
23
+ * */
24
+ bus.handler = subscriber;
25
+ async function publish(name, def, properties, options) {
26
+ const c = await client();
27
+ const u = url(c.region, options?.aws);
28
+ const evt = typeof def === "string"
29
+ ? {
30
+ type: def,
31
+ properties,
32
+ metadata: options?.metadata || {},
33
+ }
34
+ : await def.create(properties, options?.metadata);
35
+ const res = await c.fetch(u, {
36
+ method: "POST",
37
+ aws: options?.aws,
38
+ headers: {
39
+ "X-Amz-Target": "AWSEvents.PutEvents",
40
+ "Content-Type": "application/x-amz-json-1.1",
41
+ },
42
+ body: JSON.stringify({
43
+ Entries: [
44
+ {
45
+ Source: [Resource.App.name, Resource.App.stage].join("."),
46
+ DetailType: evt.type,
47
+ Detail: JSON.stringify({
48
+ metadata: evt.metadata,
49
+ properties: evt.properties,
50
+ }),
51
+ EventBusName: typeof name === "string" ? name : name.name,
52
+ },
53
+ ],
54
+ }),
55
+ });
56
+ if (!res.ok)
57
+ throw new PublishError(res);
58
+ return res.json();
59
+ }
60
+ bus.publish = publish;
61
+ class PublishError extends Error {
62
+ response;
63
+ constructor(response) {
64
+ super("Failed to publish event to bus");
65
+ this.response = response;
66
+ }
67
+ }
68
+ bus.PublishError = PublishError;
69
+ })(bus || (bus = {}));
@@ -0,0 +1,3 @@
1
+ import { AwsClient } from "aws4fetch";
2
+ export declare function client(): Promise<AwsClient>;
3
+ export type AwsOptions = Exclude<Parameters<AwsClient["fetch"]>[1], null | undefined>["aws"];
@@ -0,0 +1,36 @@
1
+ import { AwsClient } from "aws4fetch";
2
+ let cachedCredentials = null;
3
+ async function getCredentials(url) {
4
+ if (cachedCredentials) {
5
+ const currentTime = new Date();
6
+ const fiveMinutesFromNow = new Date(currentTime.getTime() + 5 * 60000);
7
+ const expirationTime = new Date(cachedCredentials.Expiration);
8
+ if (expirationTime > fiveMinutesFromNow) {
9
+ return cachedCredentials;
10
+ }
11
+ }
12
+ const credentials = (await fetch(url).then((res) => res.json()));
13
+ cachedCredentials = credentials;
14
+ return credentials;
15
+ }
16
+ export async function client() {
17
+ if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
18
+ return new AwsClient({
19
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
20
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
21
+ sessionToken: process.env.AWS_SESSION_TOKEN,
22
+ region: process.env.AWS_REGION,
23
+ });
24
+ }
25
+ if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
26
+ const credentials = await getCredentials("http://169.254.170.2" +
27
+ process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
28
+ return new AwsClient({
29
+ accessKeyId: credentials.AccessKeyId,
30
+ secretAccessKey: credentials.SecretAccessKey,
31
+ sessionToken: credentials.Token,
32
+ region: process.env.AWS_REGION,
33
+ });
34
+ }
35
+ throw new Error("No AWS credentials found");
36
+ }
@@ -0,0 +1,84 @@
1
+ import { Context, IoTCustomAuthorizerEvent } from "aws-lambda";
2
+ /**
3
+ * The `realtime` client SDK is available through the following.
4
+ *
5
+ * @example
6
+ * ```js title="src/authorizer.ts"
7
+ * import { realtime } from "sst/aws/realtime";
8
+ * ```
9
+ */
10
+ export declare namespace realtime {
11
+ interface AuthResult {
12
+ /**
13
+ * The topics the client can subscribe to.
14
+ * @example
15
+ * For example, this subscribes to two specific topics.
16
+ * ```js
17
+ * {
18
+ * subscribe: ["chat/room1", "chat/room2"]
19
+ * }
20
+ * ```
21
+ *
22
+ * And to subscribe to all topics under a given prefix.
23
+ * ```js
24
+ * {
25
+ * subscribe: ["chat/*"]
26
+ * }
27
+ * ```
28
+ */
29
+ subscribe?: string[];
30
+ /**
31
+ * The topics the client can publish to.
32
+ * @example
33
+ * For example, this publishes to two specific topics.
34
+ * ```js
35
+ * {
36
+ * publish: ["chat/room1", "chat/room2"]
37
+ * }
38
+ * ```
39
+ * And to publish to all topics under a given prefix.
40
+ * ```js
41
+ * {
42
+ * publish: ["chat/*"]
43
+ * }
44
+ * ```
45
+ */
46
+ publish?: string[];
47
+ }
48
+ /**
49
+ * Creates an authorization handler for the `Realtime` component. It validates
50
+ * the token and grants permissions for the topics the client can subscribe and publish to.
51
+ *
52
+ * @example
53
+ * ```js title="src/authorizer.ts" "realtime.authorizer"
54
+ * export const handler = realtime.authorizer(async (token) => {
55
+ * // Validate the token
56
+ * console.log(token);
57
+ *
58
+ * // Return the topics to subscribe and publish
59
+ * return {
60
+ * subscribe: ["*"],
61
+ * publish: ["*"],
62
+ * };
63
+ * });
64
+ * ```
65
+ */
66
+ function authorizer(input: (token: string) => Promise<AuthResult>): (evt: IoTCustomAuthorizerEvent, context: Context) => Promise<{
67
+ isAuthenticated: boolean;
68
+ principalId: string;
69
+ disconnectAfterInSeconds: number;
70
+ refreshAfterInSeconds: number;
71
+ policyDocuments: {
72
+ Version: string;
73
+ Statement: ({
74
+ Action: string;
75
+ Effect: string;
76
+ Resource: string;
77
+ } | {
78
+ Action: string;
79
+ Effect: string;
80
+ Resource: string[];
81
+ })[];
82
+ }[];
83
+ }>;
84
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The `realtime` client SDK is available through the following.
3
+ *
4
+ * @example
5
+ * ```js title="src/authorizer.ts"
6
+ * import { realtime } from "sst/aws/realtime";
7
+ * ```
8
+ */
9
+ export var realtime;
10
+ (function (realtime) {
11
+ /**
12
+ * Creates an authorization handler for the `Realtime` component. It validates
13
+ * the token and grants permissions for the topics the client can subscribe and publish to.
14
+ *
15
+ * @example
16
+ * ```js title="src/authorizer.ts" "realtime.authorizer"
17
+ * export const handler = realtime.authorizer(async (token) => {
18
+ * // Validate the token
19
+ * console.log(token);
20
+ *
21
+ * // Return the topics to subscribe and publish
22
+ * return {
23
+ * subscribe: ["*"],
24
+ * publish: ["*"],
25
+ * };
26
+ * });
27
+ * ```
28
+ */
29
+ function authorizer(input) {
30
+ return async (evt, context) => {
31
+ const [, , , region, accountId] = context.invokedFunctionArn.split(":");
32
+ const token = Buffer.from(evt.protocolData.mqtt?.password ?? "", "base64").toString();
33
+ const ret = await input(token);
34
+ return {
35
+ isAuthenticated: true,
36
+ principalId: Date.now().toString(),
37
+ disconnectAfterInSeconds: 86400,
38
+ refreshAfterInSeconds: 300,
39
+ policyDocuments: [
40
+ {
41
+ Version: "2012-10-17",
42
+ Statement: [
43
+ {
44
+ Action: "iot:Connect",
45
+ Effect: "Allow",
46
+ Resource: "*",
47
+ },
48
+ ...(ret.subscribe
49
+ ? [
50
+ {
51
+ Action: "iot:Receive",
52
+ Effect: "Allow",
53
+ Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
54
+ },
55
+ ]
56
+ : []),
57
+ ...(ret.subscribe
58
+ ? [
59
+ {
60
+ Action: "iot:Subscribe",
61
+ Effect: "Allow",
62
+ Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topicfilter/${t}`),
63
+ },
64
+ ]
65
+ : []),
66
+ ...(ret.publish
67
+ ? [
68
+ {
69
+ Action: "iot:Publish",
70
+ Effect: "Allow",
71
+ Resource: ret.publish.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
72
+ },
73
+ ]
74
+ : []),
75
+ ],
76
+ },
77
+ ],
78
+ };
79
+ };
80
+ }
81
+ realtime.authorizer = authorizer;
82
+ })(realtime || (realtime = {}));