A social knowledge tool for researchers built on ATProto
1import { Controller } from '../../../../../shared/infrastructure/http/Controller';
2import { Request, Response } from 'express';
3import { CompleteOAuthSignInUseCase } from '../../../application/use-cases/CompleteOAuthSignInUseCase';
4import { CookieService } from '../../../../../shared/infrastructure/http/services/CookieService';
5import { configService } from 'src/shared/infrastructure/config';
6
7export class CompleteOAuthSignInController extends Controller {
8 constructor(
9 private completeOAuthSignInUseCase: CompleteOAuthSignInUseCase,
10 private cookieService: CookieService,
11 ) {
12 super();
13 }
14
15 async executeImpl(req: Request, res: Response): Promise<any> {
16 const appUrl = configService.getAppConfig().appUrl;
17 try {
18 const { code, state, iss } = req.query;
19
20 if (!code || !state || !iss) {
21 return this.badRequest(res, 'Missing required parameters');
22 }
23
24 const result = await this.completeOAuthSignInUseCase.execute({
25 code: code as string,
26 state: state as string,
27 iss: iss as string,
28 });
29
30 if (result.isErr()) {
31 // Instead of returning JSON, redirect with error
32 return res.redirect(
33 `${appUrl}/login?error=${encodeURIComponent(result.error.message)}`,
34 );
35 }
36
37 // Set tokens in httpOnly cookies
38 this.cookieService.setTokens(res, {
39 accessToken: result.value.accessToken,
40 refreshToken: result.value.refreshToken,
41 });
42
43 // Redirect back to frontend without tokens in URL (more secure)
44 return res.redirect(`${appUrl}/auth/complete`);
45 } catch (error: any) {
46 return res.redirect(
47 `${appUrl}/login?error=${encodeURIComponent(error.message || 'Unknown error')}`,
48 );
49 }
50 }
51}