Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ ENV NEXT_PUBLIC_COMMIT_SHA=$NEXT_PUBLIC_COMMIT_SHA
ENV NEXT_PUBLIC_APP_VERSION=$NEXT_PUBLIC_APP_VERSION

RUN chown -R node:node .
USER node
RUN npm run build

EXPOSE 3000
USER node
ENV NEXT_TELEMETRY_DISABLED 1

ENTRYPOINT ["./scripts/launch.sh"]
CMD [ "npm", "start" ]
CMD npx prisma migrate deploy && npm start
8 changes: 0 additions & 8 deletions scripts/launch.sh

This file was deleted.

44 changes: 43 additions & 1 deletion src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
import { handlers } from "@src/auth";
import { type NextRequest } from "next/server";

export const { GET, POST } = handlers;
export const GET = handlers.GET;

/**
* Wrap the Auth.js POST handler to fix Apple Sign In's redirect after OAuth.
*
* Apple uses response_mode=form_post, which is a cross-site POST. The browser
* does not send SameSite=Lax cookies with cross-site POSTs, so Auth.js never
* sees the callbackUrl cookie and falls back to url.origin (homepage). The
* redirect callback in auth.ts is NOT called in this path — it only runs when
* Auth.js has a callbackUrl value to validate, which it doesn't here.
*
* We intercept the 302 response before it leaves the server: if it would send
* the user to the bare homepage (no ?error param, meaning sign-in succeeded),
* we rewrite the Location to /desktop-oauth/complete. That page recovers the
* nonce from sessionStorage (stored by DesktopOAuthStart) for the Tauri flow,
* or redirects to /projects for plain web users.
*/
export async function POST(req: NextRequest, context: unknown) {
const response = await handlers.POST(req, context as never);

Check failure on line 22 in src/app/api/auth/[...nextauth]/route.ts

View workflow job for this annotation

GitHub Actions / lint

Expected 1 arguments, but got 2.

if (new URL(req.url).pathname === "/api/auth/callback/apple") {
const location = response.headers.get("Location");
if (location) {
try {
const dest = new URL(location);
const isHomepageFallback =
dest.pathname === "/" && !dest.searchParams.has("error");
if (isHomepageFallback) {
const headers = new Headers(response.headers);
headers.set("Location", `${dest.origin}/desktop-oauth/complete`);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}
} catch {}
}
}

return response;
}
18 changes: 8 additions & 10 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,15 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
],
callbacks: {
// Apple uses response_mode=form_post; the cross-site POST back from
// appleid.apple.com drops the `callbackUrl` cookie (Auth.js promotes state/nonce
// to SameSite=None for form_post but not callbackUrl), so NextAuth falls back to
// the homepage — passed to this callback as either the absolute baseUrl or the
// relative "/". Send those cases to /desktop-oauth/complete, which recovers the
// nonce from sessionStorage (set by DesktopOAuthStart before the OAuth handoff)
// and finishes the desktop bridge. Web users who signed in with Apple and have
// no nonce are redirected to /projects from within that page.
// appleid.apple.com drops the `callbackUrl` cookie, so Auth.js falls back to
// url.origin and never calls this callback for that case. The actual Apple
// redirect fix lives in /api/auth/[...nextauth]/route.ts which intercepts
// the 302 response before it leaves the server.
//
// This callback still runs for explicit callbackUrl values (e.g. Google OAuth,
// magic-link flows). Apple form_post with no callbackUrl bypasses it entirely.
redirect: async ({ url, baseUrl }) => {
const isHomepage =
url === baseUrl || url === `${baseUrl}/` || url === "/" || url === "";
if (isHomepage) return `${baseUrl}/desktop-oauth/complete`;
if (url === baseUrl || url === `${baseUrl}/`) return `${baseUrl}/projects`;
if (url.startsWith("/")) return `${baseUrl}${url}`;
try {
if (new URL(url).origin === baseUrl) return url;
Expand Down
Loading