Skip to content

Commit 41b68ec

Browse files
authored
Merge pull request #25 from GreenEarthX/feat/userRegistration
Feat/user registration
2 parents 854406c + 8a73979 commit 41b68ec

6 files changed

Lines changed: 324 additions & 5 deletions

File tree

package-lock.json

Lines changed: 160 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"next-auth": "^4.24.11",
2323
"nodemailer": "^6.10.1",
2424
"oauth2": "^0.0.1",
25+
"pg": "^8.16.3",
2526
"prisma": "^6.10.1",
2627
"qrcode": "^1.5.4",
2728
"qrcode.react": "^4.2.0",
@@ -41,6 +42,7 @@
4142
"@types/jsonwebtoken": "^9.0.10",
4243
"@types/node": "20.19.9",
4344
"@types/nodemailer": "^6.4.17",
45+
"@types/pg": "^8.16.0",
4446
"@types/qrcode": "^1.5.5",
4547
"@types/qrcode.react": "^1.0.5",
4648
"@types/react": "19.1.8",

src/app/lib/auth/nextAuth.ts

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import bcrypt from 'bcryptjs';
66
import { db } from '@/app/lib/prisma';
77
import { v4 as uuid } from 'uuid';
88
import { sendVerificationEmail } from '@/app/lib/email/email';
9+
import { upsertCertificationUser, upsertCert2User } from '@/app/lib/user-provisioning';
910
import speakeasy from 'speakeasy';
1011

1112
// Extend NextAuth types
@@ -42,6 +43,9 @@ declare module 'next-auth/jwt' {
4243
}
4344
}
4445

46+
const isProd = process.env.NODE_ENV === 'production';
47+
const cookieDomain = process.env.NEXTAUTH_COOKIE_DOMAIN;
48+
4549
export const authOptions: AuthOptions = {
4650
providers: [
4751
GoogleProvider({
@@ -74,9 +78,10 @@ export const authOptions: AuthOptions = {
7478
// Register new user
7579
const hashedPassword = await bcrypt.hash(credentials.password, 10);
7680
const verificationToken = uuid();
77-
const newUser = await db.user.create({
81+
const userId = uuid();
82+
await db.user.create({
7883
data: {
79-
id: uuid(),
84+
id: userId,
8085
email: credentials.email,
8186
name: credentials.name,
8287
password: hashedPassword,
@@ -86,6 +91,14 @@ export const authOptions: AuthOptions = {
8691
twoFactorSecret: null,
8792
},
8893
});
94+
try {
95+
await upsertCertificationUser({ email: credentials.email, name: credentials.name, authId: userId });
96+
await upsertCert2User({ email: credentials.email, name: credentials.name, authId: userId });
97+
} catch (err) {
98+
console.error('Provisioning to certification/cert2 failed:', err);
99+
await db.user.delete({ where: { id: userId } }).catch(() => null);
100+
throw new Error('Signup failed while provisioning accounts. Please try again.');
101+
}
89102
await sendVerificationEmail(credentials.email, verificationToken);
90103
return null; // must verify before login
91104
}
@@ -122,6 +135,19 @@ export const authOptions: AuthOptions = {
122135
strategy: 'jwt',
123136
maxAge: 60 * 60 * 24 * 30,
124137
},
138+
useSecureCookies: isProd,
139+
cookies: {
140+
sessionToken: {
141+
name: `${isProd ? '__Secure-' : ''}next-auth.session-token`,
142+
options: {
143+
httpOnly: true,
144+
sameSite: 'lax',
145+
path: '/',
146+
secure: isProd,
147+
...(cookieDomain ? { domain: cookieDomain } : {}),
148+
},
149+
},
150+
},
125151
callbacks: {
126152
async jwt({ token, user, account }) {
127153
if (account?.provider) {
@@ -134,21 +160,44 @@ export const authOptions: AuthOptions = {
134160
});
135161

136162
if (!existingUser) {
163+
const userId = uuid();
137164
const newUser = await db.user.create({
138165
data: {
139-
id: uuid(),
166+
id: userId,
140167
email: user.email as string,
141-
name: user.name as string,
168+
name: (user.name as string) ?? '',
142169
emailVerified: true,
143170
twoFactorEnabled: false,
144171
twoFactorSecret: null,
145172
},
146173
});
174+
try {
175+
await upsertCertificationUser({ email: newUser.email, name: newUser.name ?? '', authId: userId });
176+
await upsertCert2User({ email: newUser.email, name: newUser.name ?? '', authId: userId });
177+
} catch (err) {
178+
console.error('Provisioning to certification/cert2 failed:', err);
179+
await db.user.delete({ where: { id: userId } }).catch(() => null);
180+
throw new Error('Signup failed while provisioning accounts. Please try again.');
181+
}
147182
token.id = newUser.id;
148183
token.twoFactorEnabled = false;
149184
} else {
150185
token.id = existingUser.id;
151186
token.twoFactorEnabled = existingUser.twoFactorEnabled;
187+
try {
188+
await upsertCertificationUser({
189+
email: existingUser.email,
190+
name: existingUser.name ?? '',
191+
authId: existingUser.id,
192+
});
193+
await upsertCert2User({
194+
email: existingUser.email,
195+
name: existingUser.name ?? '',
196+
authId: existingUser.id,
197+
});
198+
} catch (err) {
199+
console.error('Provisioning to certification/cert2 failed for existing user:', err);
200+
}
152201
}
153202
} else if (user) {
154203
token.id = user.id;

src/app/lib/external-dbs.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Pool, PoolConfig } from "pg";
2+
3+
function normalizeConnectionString(url: string, preferExplicitSsl: boolean) {
4+
if (!preferExplicitSsl) {
5+
return url;
6+
}
7+
8+
try {
9+
const parsed = new URL(url);
10+
if (parsed.searchParams.has("sslmode")) {
11+
parsed.searchParams.delete("sslmode");
12+
return parsed.toString();
13+
}
14+
} catch {
15+
return url;
16+
}
17+
18+
return url;
19+
}
20+
21+
function makePool(url: string | undefined, name: string, extraConfig: Partial<PoolConfig> = {}) {
22+
if (!url) throw new Error(`${name} is not defined`);
23+
const connectionString = normalizeConnectionString(url, Boolean(extraConfig.ssl));
24+
return new Pool({
25+
connectionString,
26+
...extraConfig,
27+
max: 5,
28+
idleTimeoutMillis: 30_000,
29+
connectionTimeoutMillis: 10_000,
30+
});
31+
}
32+
33+
export const certificationPool = makePool(process.env.CERTIFICATION_DB_URL, "CERTIFICATION_DB_URL");
34+
export const cert2Pool = makePool(process.env.CERT2_DB_URL, "CERT2_DB_URL", {
35+
ssl: { rejectUnauthorized: false },
36+
});

0 commit comments

Comments
 (0)