Skip to content

Commit de1a4cb

Browse files
committed
ADD - localhost auth bypass
1 parent 723a4be commit de1a4cb

2 files changed

Lines changed: 65 additions & 9 deletions

File tree

server/authRoutes.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,50 @@ const oauthCallbackLimiter = rateLimit({
2727
message: "Too many callback attempts. Please try again shortly.",
2828
});
2929

30+
function isLocalhostRequest(req) {
31+
const hostHeader = (req.get("X-Forwarded-Host") || req.get("Host") || "")
32+
.split(",")[0]
33+
.trim()
34+
.toLowerCase();
35+
const hostname = hostHeader.split(":")[0];
36+
return hostname === "localhost" || hostname === "127.0.0.1";
37+
}
38+
39+
function shouldBypassAuth(req) {
40+
return process.env.NODE_ENV !== "production" && isLocalhostRequest(req);
41+
}
42+
43+
function getLocalBypassUser() {
44+
return {
45+
id: "local-dev",
46+
hackclubSub: "local-dev",
47+
email: "local@stack.dev",
48+
name: "Local Dev",
49+
slug: "local-dev",
50+
profileImageUrl: null,
51+
slackId: null,
52+
verificationStatus: "unverified",
53+
role: "admin",
54+
};
55+
}
56+
57+
function clearBypassSession(req) {
58+
if (!req.session) return;
59+
delete req.session.devBypassUser;
60+
delete req.session.devBypassLoggedOut;
61+
}
62+
3063
export function createAuthRouter() {
3164
const router = express.Router();
3265

3366
router.get("/hackclub/login", oauthStartLimiter, (req, res) => {
67+
if (shouldBypassAuth(req)) {
68+
clearBypassSession(req);
69+
req.session.devBypassUser = getLocalBypassUser();
70+
res.redirect(302, `${getAppOrigin()}/main`);
71+
return;
72+
}
73+
3474
let redirectUri;
3575
try {
3676
redirectUri = resolveOAuthRedirectUri(req);
@@ -110,6 +150,20 @@ export function createAuthRouter() {
110150

111151
router.get("/me", async (req, res) => {
112152
try {
153+
if (shouldBypassAuth(req)) {
154+
if (req.session?.devBypassLoggedOut) {
155+
res.json({ user: null });
156+
return;
157+
}
158+
159+
const bypassUser = req.session?.devBypassUser || getLocalBypassUser();
160+
if (req.session) {
161+
req.session.devBypassUser = bypassUser;
162+
}
163+
res.json({ user: bypassUser });
164+
return;
165+
}
166+
113167
const userId = req.session?.userId;
114168
if (!userId) {
115169
res.json({ user: null });
@@ -125,6 +179,15 @@ export function createAuthRouter() {
125179
});
126180

127181
router.post("/logout", (req, res) => {
182+
if (shouldBypassAuth(req) && req.session) {
183+
req.session.devBypassLoggedOut = true;
184+
delete req.session.devBypassUser;
185+
delete req.session.userId;
186+
delete req.session.hackclubSub;
187+
res.json({ ok: true });
188+
return;
189+
}
190+
128191
req.session.destroy((err) => {
129192
if (err) {
130193
console.error("[auth] logout failed:", err);

server/hackclubAuth.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ function requiredEnv(name) {
1414
return value;
1515
}
1616

17-
/**
18-
* Every URI here must appear exactly in Hack Club → Developer → your app → redirect URIs.
19-
* HC_REDIRECT_URI is always included; OAUTH_ALLOWED_REDIRECT_URIS adds more (comma-separated).
20-
* For dev we also add the twin host (localhost ↔ 127.0.0.1) when HC_REDIRECT_URI uses one of them.
21-
*/
17+
2218
export function getAllowedRedirectUris() {
2319
const primary = process.env.HC_REDIRECT_URI?.trim();
2420
const set = new Set();
@@ -61,7 +57,6 @@ function parseRequestOrigin(req) {
6157
const parsed = new URL(originHeader);
6258
return parsed.origin;
6359
} catch {
64-
// ignore invalid origin header
6560
}
6661
}
6762

@@ -88,9 +83,7 @@ function parseRequestOrigin(req) {
8883
return null;
8984
}
9085

91-
/**
92-
* Picks redirect_uri for authorize + token exchange. Must match a URI registered at Hack Club exactly.
93-
*/
86+
9487
export function resolveOAuthRedirectUri(req) {
9588
const allowed = getAllowedRedirectUris();
9689
const origin = parseRequestOrigin(req);

0 commit comments

Comments
 (0)