← back to Norma
app/api/gmail/oauth/callback/route.ts
61 lines
import { NextRequest, NextResponse } from 'next/server';
import { getOAuth2Client, storeTokens } from '@/lib/gmail';
/**
* GET /api/gmail/oauth/callback — Google OAuth2 redirect target.
* Exchanges the authorization code for tokens and stores them on the mailbox
* named by the `state` parameter (we set state=mailboxEmail in /api/gmail/oauth).
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
const state = searchParams.get('state'); // mailbox email
const error = searchParams.get('error');
if (error) {
return new NextResponse(
`<html><body><h2>OAuth Error</h2><p>${error}</p><p><a href="/">Back to Norma</a></p></body></html>`,
{ headers: { 'Content-Type': 'text/html' } },
);
}
if (!code) {
return new NextResponse(
`<html><body><h2>Missing code</h2><p>No authorization code received.</p><p><a href="/">Back to Norma</a></p></body></html>`,
{ headers: { 'Content-Type': 'text/html' } },
);
}
const mailboxEmail = state && state.includes('@') ? state : '';
if (!mailboxEmail) {
return new NextResponse(
`<html><body><h2>Missing mailbox</h2><p>OAuth flow returned without a target mailbox in <code>state</code>. Restart the connect flow from inside Norma.</p><p><a href="/">Back to Norma</a></p></body></html>`,
{ headers: { 'Content-Type': 'text/html' } },
);
}
try {
const client = getOAuth2Client();
const { tokens } = await client.getToken(code);
await storeTokens(tokens as unknown as Record<string, unknown>, mailboxEmail);
console.log(`[gmail-oauth] Tokens stored for ${mailboxEmail}`);
return new NextResponse(
`<html><body style="font-family:system-ui;padding:40px;text-align:center">
<h2 style="color:#10b981">Gmail Connected!</h2>
<p>${mailboxEmail} is now linked to Norma.</p>
<p>You can close this tab and return to Norma. Inbox will populate after the next sync.</p>
<script>setTimeout(()=>window.close(),3000)</script>
</body></html>`,
{ headers: { 'Content-Type': 'text/html' } },
);
} catch (err) {
console.error('[gmail-oauth] Token exchange failed:', (err as Error).message);
return new NextResponse(
`<html><body><h2>Token Exchange Failed</h2><p>${(err as Error).message}</p><p><a href="/">Back to Norma</a></p></body></html>`,
{ headers: { 'Content-Type': 'text/html' } },
);
}
}