sst 3.0.20 → 3.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +7 -1
- package/dist/src/auth/adapter/adapter.d.ts +24 -0
- package/dist/src/auth/adapter/adapter.js +4 -0
- package/dist/src/auth/adapter/apple.d.ts +5 -0
- package/dist/src/auth/adapter/apple.js +22 -0
- package/dist/src/auth/adapter/code.d.ts +8 -0
- package/dist/src/auth/adapter/code.js +47 -0
- package/dist/src/auth/adapter/facebook.d.ts +5 -0
- package/dist/src/auth/adapter/facebook.js +27 -0
- package/dist/src/auth/adapter/github.d.ts +12 -0
- package/dist/src/auth/adapter/github.js +23 -0
- package/dist/src/auth/adapter/google.d.ts +17 -0
- package/dist/src/auth/adapter/google.js +22 -0
- package/dist/src/auth/adapter/index.d.ts +11 -0
- package/dist/src/auth/adapter/index.js +10 -0
- package/dist/src/auth/adapter/link.d.ts +6 -0
- package/dist/src/auth/adapter/link.js +27 -0
- package/dist/src/auth/adapter/microsoft.d.ts +11 -0
- package/dist/src/auth/adapter/microsoft.js +16 -0
- package/dist/src/auth/adapter/oauth.d.ts +33 -0
- package/dist/src/auth/adapter/oauth.js +79 -0
- package/dist/src/auth/adapter/oidc.d.ts +19 -0
- package/dist/src/auth/adapter/oidc.js +45 -0
- package/dist/src/auth/adapter/spotify.d.ts +12 -0
- package/dist/src/auth/adapter/spotify.js +22 -0
- package/dist/src/auth/example/bun.d.ts +2 -0
- package/dist/src/auth/example/bun.js +46 -0
- package/dist/src/auth/handler.d.ts +58 -0
- package/dist/src/auth/handler.js +207 -0
- package/dist/src/auth/index.d.ts +10 -0
- package/dist/src/auth/index.js +10 -0
- package/dist/src/auth/session.d.ts +25 -0
- package/dist/src/auth/session.js +28 -0
- package/dist/src/aws/bus.d.ts +29 -0
- package/dist/src/aws/bus.js +67 -0
- package/dist/src/aws/client.d.ts +3 -0
- package/dist/src/aws/client.js +7 -0
- package/dist/src/aws/realtime.d.ts +61 -0
- package/dist/src/aws/realtime.js +76 -0
- package/dist/src/event/index.d.ts +74 -0
- package/dist/src/event/index.js +41 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/realtime/index.d.ts +25 -0
- package/dist/src/realtime/index.js +24 -0
- package/dist/src/resource.d.ts +9 -0
- package/dist/src/resource.js +50 -0
- package/dist/src/util/prettify.d.ts +3 -0
- package/dist/src/util/prettify.js +1 -0
- package/dist/src/vector/index.d.ts +193 -0
- package/dist/src/vector/index.js +62 -0
- package/dist/test/event.test.d.ts +1 -0
- package/dist/test/event.test.js +12 -0
- package/package.json +2 -1
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
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, "/">;
|
|
58
|
+
export {};
|
|
@@ -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,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,67 @@
|
|
|
1
|
+
import { client } from "../aws/client.js";
|
|
2
|
+
import { Resource } from "../resource.js";
|
|
3
|
+
export var bus;
|
|
4
|
+
(function (bus) {
|
|
5
|
+
function url(options) {
|
|
6
|
+
const region = options?.region || client.region;
|
|
7
|
+
return `https://events.${region}.amazonaws.com/`;
|
|
8
|
+
}
|
|
9
|
+
function subscriber(_events, cb) {
|
|
10
|
+
return async function (event) {
|
|
11
|
+
const payload = {
|
|
12
|
+
type: event["detail-type"],
|
|
13
|
+
properties: event.detail.properties,
|
|
14
|
+
metadata: event.detail.metadata,
|
|
15
|
+
};
|
|
16
|
+
return cb(payload, event);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
bus.subscriber = subscriber;
|
|
20
|
+
/** @deprecated
|
|
21
|
+
* use bus.subscriber instead
|
|
22
|
+
* */
|
|
23
|
+
bus.handler = subscriber;
|
|
24
|
+
async function publish(name, def, properties, options) {
|
|
25
|
+
const u = url(options?.aws);
|
|
26
|
+
const evt = typeof def === "string"
|
|
27
|
+
? {
|
|
28
|
+
type: def,
|
|
29
|
+
properties,
|
|
30
|
+
metadata: options?.metadata || {},
|
|
31
|
+
}
|
|
32
|
+
: await def.create(properties);
|
|
33
|
+
const res = await client.fetch(u, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
aws: options?.aws,
|
|
36
|
+
headers: {
|
|
37
|
+
"X-Amz-Target": "AWSEvents.PutEvents",
|
|
38
|
+
"Content-Type": "application/x-amz-json-1.1",
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
Entries: [
|
|
42
|
+
{
|
|
43
|
+
Source: [Resource.App.name, Resource.App.stage].join("."),
|
|
44
|
+
DetailType: evt.type,
|
|
45
|
+
Detail: JSON.stringify({
|
|
46
|
+
metadata: evt.metadata,
|
|
47
|
+
payload: evt.properties,
|
|
48
|
+
}),
|
|
49
|
+
EventBusName: typeof name === "string" ? name : name.name,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
throw new PublishError(res);
|
|
56
|
+
return res.json();
|
|
57
|
+
}
|
|
58
|
+
bus.publish = publish;
|
|
59
|
+
class PublishError extends Error {
|
|
60
|
+
response;
|
|
61
|
+
constructor(response) {
|
|
62
|
+
super("Failed to publish event to bus");
|
|
63
|
+
this.response = response;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
bus.PublishError = PublishError;
|
|
67
|
+
})(bus || (bus = {}));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { IoTCustomAuthorizerHandler } from "aws-lambda";
|
|
2
|
+
export declare namespace realtime {
|
|
3
|
+
interface AuthResult {
|
|
4
|
+
/**
|
|
5
|
+
* The topics the client can subscribe to.
|
|
6
|
+
* @example
|
|
7
|
+
* For example, this subscribes to specific topics.
|
|
8
|
+
* ```js
|
|
9
|
+
* {
|
|
10
|
+
* subscribe: ["chat/room1", "chat/room2"]
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* And to subscribe to all topics under a specific prefix.
|
|
15
|
+
* ```js
|
|
16
|
+
* {
|
|
17
|
+
* subscribe: ["chat/*"]
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
subscribe?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* The topics the client can publish to.
|
|
24
|
+
* @example
|
|
25
|
+
* For example, this publishes to specific topics.
|
|
26
|
+
* ```js
|
|
27
|
+
* {
|
|
28
|
+
* publish: ["chat/room1", "chat/room2"]
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
* And to publish to all topics under a specific prefix.
|
|
32
|
+
* ```js
|
|
33
|
+
* {
|
|
34
|
+
* publish: ["chat/*"]
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
publish?: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates an authorization handler for the `Realtime` component, that validates
|
|
42
|
+
* the token and grants permissions for the topics the client can subscribe and publish to.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```js
|
|
46
|
+
* import { realtime } from "sst/aws/realtime";
|
|
47
|
+
*
|
|
48
|
+
* export const handler = realtime.authorizer(async (token) => {
|
|
49
|
+
* // Validate the token
|
|
50
|
+
* console.log(token);
|
|
51
|
+
*
|
|
52
|
+
* // Return the topics to subscribe and publish
|
|
53
|
+
* return {
|
|
54
|
+
* subscribe: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
|
|
55
|
+
* publish: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
|
|
56
|
+
* };
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
function authorizer(input: (token: string) => Promise<AuthResult>): IoTCustomAuthorizerHandler;
|
|
61
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export var realtime;
|
|
2
|
+
(function (realtime) {
|
|
3
|
+
/**
|
|
4
|
+
* Creates an authorization handler for the `Realtime` component, that validates
|
|
5
|
+
* the token and grants permissions for the topics the client can subscribe and publish to.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```js
|
|
9
|
+
* import { realtime } from "sst/aws/realtime";
|
|
10
|
+
*
|
|
11
|
+
* export const handler = realtime.authorizer(async (token) => {
|
|
12
|
+
* // Validate the token
|
|
13
|
+
* console.log(token);
|
|
14
|
+
*
|
|
15
|
+
* // Return the topics to subscribe and publish
|
|
16
|
+
* return {
|
|
17
|
+
* subscribe: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
|
|
18
|
+
* publish: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
|
|
19
|
+
* };
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function authorizer(input) {
|
|
24
|
+
return async (evt, context) => {
|
|
25
|
+
const [, , , region, accountId] = context.invokedFunctionArn.split(":");
|
|
26
|
+
const token = Buffer.from(evt.protocolData.mqtt?.password ?? "", "base64").toString();
|
|
27
|
+
const ret = await input(token);
|
|
28
|
+
return {
|
|
29
|
+
isAuthenticated: true,
|
|
30
|
+
principalId: Date.now().toString(),
|
|
31
|
+
disconnectAfterInSeconds: 86400,
|
|
32
|
+
refreshAfterInSeconds: 300,
|
|
33
|
+
policyDocuments: [
|
|
34
|
+
{
|
|
35
|
+
Version: "2012-10-17",
|
|
36
|
+
Statement: [
|
|
37
|
+
{
|
|
38
|
+
Action: "iot:Connect",
|
|
39
|
+
Effect: "Allow",
|
|
40
|
+
Resource: "*",
|
|
41
|
+
},
|
|
42
|
+
...(ret.subscribe
|
|
43
|
+
? [
|
|
44
|
+
{
|
|
45
|
+
Action: "iot:Receive",
|
|
46
|
+
Effect: "Allow",
|
|
47
|
+
Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
: []),
|
|
51
|
+
...(ret.subscribe
|
|
52
|
+
? [
|
|
53
|
+
{
|
|
54
|
+
Action: "iot:Subscribe",
|
|
55
|
+
Effect: "Allow",
|
|
56
|
+
Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topicfilter/${t}`),
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
: []),
|
|
60
|
+
...(ret.publish
|
|
61
|
+
? [
|
|
62
|
+
{
|
|
63
|
+
Action: "iot:Publish",
|
|
64
|
+
Effect: "Allow",
|
|
65
|
+
Resource: ret.publish.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
: []),
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
realtime.authorizer = authorizer;
|
|
76
|
+
})(realtime || (realtime = {}));
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ZodSchema, z } from "zod";
|
|
2
|
+
export declare namespace event {
|
|
3
|
+
export type Definition = {
|
|
4
|
+
type: string;
|
|
5
|
+
$input: any;
|
|
6
|
+
$output: any;
|
|
7
|
+
$metadata: any;
|
|
8
|
+
$payload: any;
|
|
9
|
+
create: (...args: any[]) => Promise<any>;
|
|
10
|
+
};
|
|
11
|
+
export function builder<Metadata extends ((type: string, properties: any) => any) | Parameters<Validator>[0], Validator extends (schema: any) => (input: any) => any = typeof ZodValidator>(input: {
|
|
12
|
+
validator?: Validator;
|
|
13
|
+
metadata?: Metadata;
|
|
14
|
+
}): {
|
|
15
|
+
<Type extends string, Schema extends Parameters<Validator>[0]>(type: Type, schema: Schema): {
|
|
16
|
+
create: Metadata extends (type: string, properties: any) => any ? (properties: inferParser<Schema>["in"]) => Promise<{
|
|
17
|
+
type: Type;
|
|
18
|
+
properties: inferParser<Schema>["out"];
|
|
19
|
+
metadata: Metadata extends (type: string, properties: any) => any ? ReturnType<Metadata> : inferParser<Metadata>["out"];
|
|
20
|
+
}> : (properties: inferParser<Schema>["in"], metadata: inferParser<Metadata>["in"]) => Promise<{
|
|
21
|
+
type: Type;
|
|
22
|
+
properties: inferParser<Schema>["out"];
|
|
23
|
+
metadata: Metadata extends (type: string, properties: any) => any ? ReturnType<Metadata> : inferParser<Metadata>["out"];
|
|
24
|
+
}>;
|
|
25
|
+
type: Type;
|
|
26
|
+
$input: inferParser<Schema>["in"];
|
|
27
|
+
$output: inferParser<Schema>["out"];
|
|
28
|
+
$payload: {
|
|
29
|
+
type: Type;
|
|
30
|
+
properties: inferParser<Schema>["out"];
|
|
31
|
+
metadata: Metadata extends (type: string, properties: any) => any ? ReturnType<Metadata> : inferParser<Metadata>["out"];
|
|
32
|
+
};
|
|
33
|
+
$metadata: Metadata extends (type: string, properties: any) => any ? ReturnType<Metadata> : inferParser<Metadata>["out"];
|
|
34
|
+
};
|
|
35
|
+
coerce<Events extends Definition>(_events: Events | Events[], raw: any): { [K in Events["type"]]: Extract<Events, {
|
|
36
|
+
type: K;
|
|
37
|
+
}>["$payload"]; }[Events["type"]];
|
|
38
|
+
};
|
|
39
|
+
export function ZodValidator<Schema extends ZodSchema>(schema: Schema): (input: z.input<Schema>) => z.output<Schema>;
|
|
40
|
+
type ParserZodEsque<TInput, TParsedInput> = {
|
|
41
|
+
_input: TInput;
|
|
42
|
+
_output: TParsedInput;
|
|
43
|
+
};
|
|
44
|
+
type ParserValibotEsque<TInput, TParsedInput> = {
|
|
45
|
+
_types?: {
|
|
46
|
+
input: TInput;
|
|
47
|
+
output: TParsedInput;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
type ParserMyZodEsque<TInput> = {
|
|
51
|
+
parse: (input: any) => TInput;
|
|
52
|
+
};
|
|
53
|
+
type ParserSuperstructEsque<TInput> = {
|
|
54
|
+
create: (input: unknown) => TInput;
|
|
55
|
+
};
|
|
56
|
+
type ParserCustomValidatorEsque<TInput> = (input: unknown) => Promise<TInput> | TInput;
|
|
57
|
+
type ParserYupEsque<TInput> = {
|
|
58
|
+
validateSync: (input: unknown) => TInput;
|
|
59
|
+
};
|
|
60
|
+
type ParserScaleEsque<TInput> = {
|
|
61
|
+
assert(value: unknown): asserts value is TInput;
|
|
62
|
+
};
|
|
63
|
+
export type ParserWithoutInput<TInput> = ParserCustomValidatorEsque<TInput> | ParserMyZodEsque<TInput> | ParserScaleEsque<TInput> | ParserSuperstructEsque<TInput> | ParserYupEsque<TInput>;
|
|
64
|
+
export type ParserWithInputOutput<TInput, TParsedInput> = ParserZodEsque<TInput, TParsedInput> | ParserValibotEsque<TInput, TParsedInput>;
|
|
65
|
+
export type Parser = ParserWithInputOutput<any, any> | ParserWithoutInput<any>;
|
|
66
|
+
export type inferParser<TParser extends Parser> = TParser extends ParserWithInputOutput<infer $TIn, infer $TOut> ? {
|
|
67
|
+
in: $TIn;
|
|
68
|
+
out: $TOut;
|
|
69
|
+
} : TParser extends ParserWithoutInput<infer $InOut> ? {
|
|
70
|
+
in: $InOut;
|
|
71
|
+
out: $InOut;
|
|
72
|
+
} : never;
|
|
73
|
+
export {};
|
|
74
|
+
}
|