Skip to content

Commit c0ab87d

Browse files
committed
[Dashboard] Feature: Send UTM params when logging into dashboard (#5400)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on collecting and managing `utm_` cookies for tracking purposes during user login and middleware processing. It enhances the handling of cookies to ensure `utm_` parameters are captured and forwarded appropriately in API requests and redirects. ### Detailed summary - Added collection of `utm_` cookies in `auth-actions.ts`. - Modified the `doLogin` function to include `utmCookies` in the request body. - Implemented logic in `middleware.ts` to capture `utm_` parameters from URL and set them as cookies. - Updated redirect and rewrite functions to include `cookiesToSet` for managing cookies in responses. - Ensured cookies are set in the response for various redirect scenarios. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent f0c077c commit c0ab87d

File tree

2 files changed

+106
-27
lines changed

2 files changed

+106
-27
lines changed

apps/dashboard/src/app/login/auth-actions.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ export async function doLogin(payload: VerifyLoginPayloadParams) {
4444
}
4545

4646
const cookieStore = await cookies();
47+
const utmCookies = cookieStore
48+
.getAll()
49+
.filter((cookie) => {
50+
return cookie.name.startsWith("utm_");
51+
})
52+
.reduce(
53+
(acc, cookie) => {
54+
acc[cookie.name] = cookie.value;
55+
return acc;
56+
},
57+
{} as Record<string, string>,
58+
);
4759

4860
// forward the request to the API server
4961
const res = await fetch(`${API_SERVER_URL}/v2/siwe/login`, {
@@ -53,7 +65,7 @@ export async function doLogin(payload: VerifyLoginPayloadParams) {
5365
"x-service-api-key": THIRDWEB_API_SECRET,
5466
},
5567
// set the createAccount flag to true to create a new account if it does not exist
56-
body: JSON.stringify({ ...payload, createAccount: true }),
68+
body: JSON.stringify({ ...payload, createAccount: true, utm: utmCookies }),
5769
});
5870

5971
// if the request failed, log the error and throw an error

apps/dashboard/src/middleware.ts

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,40 @@ export const config = {
2222
};
2323

2424
export async function middleware(request: NextRequest) {
25+
let cookiesToSet: Record<string, string> | undefined = undefined;
2526
const { pathname } = request.nextUrl;
2627
const activeAccount = request.cookies.get(COOKIE_ACTIVE_ACCOUNT)?.value;
2728
const authCookie = activeAccount
2829
? request.cookies.get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount))
2930
: null;
3031

32+
// utm collection
33+
// NOTE: this is not working for pages with rewrites in next.config.js - (framer pages)
34+
// if user is already signed in - don't bother capturing utm params
35+
if (!authCookie) {
36+
const searchParamsEntries = request.nextUrl.searchParams.entries();
37+
const utmParams: Map<string, string> = new Map();
38+
for (const param of searchParamsEntries) {
39+
if (param[0].startsWith("utm_")) {
40+
utmParams.set(param[0], param[1]);
41+
}
42+
}
43+
44+
// if we have utm params, set them as cookies
45+
if (utmParams.size) {
46+
for (const [key, value] of utmParams.entries()) {
47+
// if its already set - don't set it again
48+
if (!request.cookies.get(key)) {
49+
if (!cookiesToSet) {
50+
cookiesToSet = {};
51+
}
52+
53+
cookiesToSet[key] = value;
54+
}
55+
}
56+
}
57+
}
58+
3159
// logged in paths
3260
if (isLoginRequired(pathname)) {
3361
// check if the user is logged in (has a valid auth cookie)
@@ -36,12 +64,11 @@ export async function middleware(request: NextRequest) {
3664
const searchParamsString = request.nextUrl.searchParams.toString();
3765

3866
// if not logged in, rewrite to login page
39-
return redirect(
40-
request,
41-
"/login",
42-
`next=${encodeURIComponent(`${pathname}${searchParamsString ? `?${searchParamsString}` : ""}`)}`,
43-
false,
44-
);
67+
return redirect(request, "/login", {
68+
permanent: false,
69+
searchParams: `next=${encodeURIComponent(`${pathname}${searchParamsString ? `?${searchParamsString}` : ""}`)}`,
70+
cookiesToSet,
71+
});
4572
}
4673
}
4774

@@ -50,7 +77,15 @@ export async function middleware(request: NextRequest) {
5077

5178
// if it's the homepage and we have an auth cookie, redirect to the dashboard
5279
if (paths.length === 1 && paths[0] === "" && authCookie) {
53-
return redirect(request, "/team");
80+
return redirect(
81+
request,
82+
"/team",
83+
cookiesToSet
84+
? {
85+
cookiesToSet,
86+
}
87+
: undefined,
88+
);
5489
}
5590

5691
// if the first section of the path is a number, check if it's a valid chain_id and re-write it to the slug
@@ -69,6 +104,7 @@ export async function middleware(request: NextRequest) {
69104
return redirect(
70105
request,
71106
`/${chainMetadata.slug}/${paths.slice(1).join("/")}`,
107+
cookiesToSet ? { cookiesToSet } : undefined,
72108
);
73109
}
74110
} catch {
@@ -83,20 +119,18 @@ export async function middleware(request: NextRequest) {
83119
// special case for "deployer.thirdweb.eth"
84120
// we want to always redirect this to "thirdweb.eth/..."
85121
if (paths[0] === "deployer.thirdweb.eth") {
86-
return redirect(
87-
request,
88-
`/thirdweb.eth/${paths.slice(1).join("/")}`,
89-
undefined,
90-
true,
91-
);
122+
return redirect(request, `/thirdweb.eth/${paths.slice(1).join("/")}`, {
123+
permanent: true,
124+
cookiesToSet,
125+
});
92126
}
93127
// if we have exactly 1 path part, we're in the <address> case -> profile page
94128
if (paths.length === 1) {
95-
return rewrite(request, `/profile${pathname}`);
129+
return rewrite(request, `/profile${pathname}`, cookiesToSet);
96130
}
97131
// if we have more than 1 path part, we're in the <address>/<slug> case -> publish page
98132
if (paths.length > 1) {
99-
return rewrite(request, `/published-contract${pathname}`);
133+
return rewrite(request, `/published-contract${pathname}`, cookiesToSet);
100134
}
101135
}
102136

@@ -108,15 +142,22 @@ export async function middleware(request: NextRequest) {
108142
if (firstTeam) {
109143
const modifiedPaths = [...paths];
110144
modifiedPaths[1] = firstTeam.slug;
111-
return redirect(
112-
request,
113-
`/${modifiedPaths.join("/")}`,
114-
request.nextUrl.searchParams.toString(),
115-
);
145+
return redirect(request, `/${modifiedPaths.join("/")}`, {
146+
searchParams: request.nextUrl.searchParams.toString(),
147+
cookiesToSet,
148+
});
116149
}
117150
}
118151
// END /<address>/... case
119152
// all other cases are handled by the file system router so we just fall through
153+
if (cookiesToSet) {
154+
const defaultResponse = NextResponse.next();
155+
for (const entry of Object.entries(cookiesToSet)) {
156+
defaultResponse.cookies.set(entry[0], entry[1]);
157+
}
158+
159+
return defaultResponse;
160+
}
120161
}
121162

122163
function isPossibleEVMAddress(address: string) {
@@ -125,20 +166,46 @@ function isPossibleEVMAddress(address: string) {
125166

126167
// utils for rewriting and redirecting with relative paths
127168

128-
function rewrite(request: NextRequest, relativePath: string) {
169+
function rewrite(
170+
request: NextRequest,
171+
relativePath: string,
172+
cookiesToSet: Record<string, string> | undefined,
173+
) {
129174
const url = request.nextUrl.clone();
130175
url.pathname = relativePath;
131-
return NextResponse.rewrite(url);
176+
const res = NextResponse.rewrite(url);
177+
178+
if (cookiesToSet) {
179+
for (const entry of Object.entries(cookiesToSet)) {
180+
res.cookies.set(entry[0], entry[1]);
181+
}
182+
}
183+
184+
return res;
132185
}
133186

134187
function redirect(
135188
request: NextRequest,
136189
relativePath: string,
137-
searchParams?: string,
138-
permanent = false,
190+
options:
191+
| {
192+
searchParams?: string;
193+
permanent?: boolean;
194+
cookiesToSet?: Record<string, string> | undefined;
195+
}
196+
| undefined,
139197
) {
198+
const permanent = options?.permanent ?? false;
140199
const url = request.nextUrl.clone();
141200
url.pathname = relativePath;
142-
url.search = searchParams ? `?${searchParams}` : "";
143-
return NextResponse.redirect(url, permanent ? 308 : undefined);
201+
url.search = options?.searchParams ? `?${options.searchParams}` : "";
202+
const res = NextResponse.redirect(url, permanent ? 308 : undefined);
203+
204+
if (options?.cookiesToSet) {
205+
for (const entry of Object.entries(options.cookiesToSet)) {
206+
res.cookies.set(entry[0], entry[1]);
207+
}
208+
}
209+
210+
return res;
144211
}

0 commit comments

Comments
 (0)