Skip to content

Commit 9120bdd

Browse files
committed
Forgot the add the file
1 parent 04b5207 commit 9120bdd

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

api/sockets/authHandler.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { ApiGatewayManagementApiClient, DeleteConnectionCommand } from "@aws-sdk/client-apigatewaymanagementapi";
2+
import { CognitoJwtVerifier } from "aws-jwt-verify";
3+
import { APIGatewayProxyEventV2 } from "aws-lambda";
4+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
5+
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
6+
7+
type WebSocketRequestContext = APIGatewayProxyEventV2["requestContext"] & {
8+
connectionId: string;
9+
routeKey: string;
10+
eventType: "CONNECT" | "MESSAGE" | "DISCONNECT";
11+
};
12+
13+
interface WebSocketEvent extends Omit<APIGatewayProxyEventV2, "requestContext"> {
14+
requestContext: WebSocketRequestContext;
15+
}
16+
17+
const REGION = "us-east-1";
18+
const clnt = new DynamoDBClient({ region: REGION });
19+
const marshallOptions = {
20+
// Whether to automatically convert empty strings, blobs, and sets to `null`.
21+
convertEmptyValues: false, // false, by default.
22+
// Whether to remove undefined values while marshalling.
23+
removeUndefinedValues: true, // false, by default.
24+
// Whether to convert typeof object to map attribute.
25+
convertClassInstanceToMap: false, // false, by default.
26+
};
27+
const unmarshallOptions = {
28+
// Whether to return numbers as a string instead of converting them to native JavaScript numbers.
29+
wrapNumbers: false, // false, by default.
30+
};
31+
const translateConfig = { marshallOptions, unmarshallOptions };
32+
const ddbDocClient = DynamoDBDocumentClient.from(clnt, translateConfig);
33+
34+
const verifier = CognitoJwtVerifier.create({
35+
userPoolId: process.env.userpoolId!,
36+
tokenUse: "id",
37+
clientId: process.env.userpoolClient!,
38+
});
39+
40+
export const wsAuthorizer = async (event: WebSocketEvent) => {
41+
42+
// Parse the incoming message body
43+
const body = JSON.parse(event.body ?? "{}");
44+
const token: string | undefined = body.token;
45+
46+
if (!token) {
47+
console.error("Missing token in auth message");
48+
return { statusCode: 400, body: "Missing token" };
49+
}
50+
51+
try {
52+
console.log(`Verifying JWT: ${token}`);
53+
const payload = await verifier.verify(token);
54+
console.log(`Validated: ${JSON.stringify(payload)}`);
55+
const { connectionId } = event.requestContext;
56+
57+
const userId = payload.sub;
58+
59+
await ddbDocClient.send(
60+
new PutCommand({
61+
TableName: process.env.ABSTRACT_PLAY_TABLE!,
62+
Item: {
63+
pk: "wsConnections",
64+
sk: connectionId,
65+
66+
connectionId,
67+
userId,
68+
69+
// Optional TTL for auto-cleanup
70+
ttl: Math.floor(Date.now() / 1000) + 3600,
71+
},
72+
})
73+
);
74+
} catch (ex) {
75+
console.log("Connect error:", JSON.stringify(ex));
76+
const domain = event.requestContext.domainName;
77+
const stage = event.requestContext.stage;
78+
const connectionId = event.requestContext.connectionId;
79+
80+
const mgmt = new ApiGatewayManagementApiClient({
81+
endpoint: `https://${domain}/${stage}`,
82+
});
83+
84+
await mgmt.send(new DeleteConnectionCommand({ ConnectionId: connectionId }));
85+
return { statusCode: 401 };
86+
}
87+
};

0 commit comments

Comments
 (0)