Skip to content

Commit 82eaa33

Browse files
committed
fix: fix security issue in slack redirect
1 parent 091ce6e commit 82eaa33

File tree

2 files changed

+31
-15
lines changed

2 files changed

+31
-15
lines changed

apps/api/src/index.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,15 @@ app.get("/test", apiLimiter, (req: Request, res: Response) => {
9696
// Slack Community Invite Endpoint (Protected)
9797
app.get("/join-community", apiLimiter, async (req: Request, res: Response) => {
9898
try {
99-
// Get token from Authorization header or query parameter
100-
let token: string | undefined;
10199
const authHeader = req.headers.authorization;
102100

103-
if (authHeader && authHeader.startsWith("Bearer ")) {
104-
token = authHeader.substring(7); // Remove "Bearer " prefix
105-
} else if (req.query.token && typeof req.query.token === "string") {
106-
token = req.query.token;
101+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
102+
return res.status(401).json({
103+
error: "Unauthorized - Authorization header with Bearer token required",
104+
});
107105
}
108106

109-
if (!token) {
110-
return res.status(401).json({ error: "Unauthorized - Missing token" });
111-
}
107+
const token = authHeader.substring(7);
112108

113109
// Verify token and get user
114110
let user;
@@ -142,8 +138,10 @@ app.get("/join-community", apiLimiter, async (req: Request, res: Response) => {
142138
return res.status(500).json({ error: "Community invite not configured" });
143139
}
144140

145-
// Redirect to Slack community
146-
return res.redirect(slackInviteUrl);
141+
return res.status(200).json({
142+
slackInviteUrl,
143+
message: "Subscription verified. You can join the community.",
144+
});
147145
} catch (error: any) {
148146
console.error("Community invite error:", error);
149147
return res.status(500).json({ error: "Internal server error" });

apps/web/src/components/checkout/checkout-confirmation.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const CheckoutConfirmation: React.FC<CheckoutConfirmationProps> = ({
1515
const { data: session } = useSession();
1616
const [error, setError] = useState<string | null>(null);
1717

18-
const handleJoinCommunity = () => {
18+
const handleJoinCommunity = async () => {
1919
if (!session?.user) {
2020
setError("Please sign in to join the community");
2121
return;
@@ -28,9 +28,27 @@ const CheckoutConfirmation: React.FC<CheckoutConfirmationProps> = ({
2828
return;
2929
}
3030

31-
// Redirect to backend endpoint which will validate subscription and redirect to Slack
32-
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000";
33-
window.location.href = `${apiUrl}/join-community?token=${encodeURIComponent(accessToken)}`;
31+
try {
32+
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000";
33+
const response = await fetch(`${apiUrl}/join-community`, {
34+
method: "GET",
35+
headers: {
36+
Authorization: `Bearer ${accessToken}`,
37+
},
38+
});
39+
40+
if (!response.ok) {
41+
const errorData = await response.json();
42+
setError(errorData.error || "Failed to join community");
43+
return;
44+
}
45+
46+
const { slackInviteUrl } = await response.json();
47+
window.location.href = slackInviteUrl;
48+
} catch (err) {
49+
console.error("Failed to join community:", err);
50+
setError("Failed to connect to server");
51+
}
3452
};
3553

3654
return (

0 commit comments

Comments
 (0)