1- import crypto from 'node: crypto'
1+ import crypto from 'crypto'
22import { type Connection , type Password , type User } from '@prisma/client'
3- import bcrypt from 'bcryptjs'
43import { redirect } from 'react-router'
54import { Authenticator } from 'remix-auth'
65import { safeRedirect } from 'remix-utils/safe-redirect'
@@ -11,6 +10,14 @@ import { type ProviderUser } from './providers/provider.ts'
1110import { authSessionStorage } from './session.server.ts'
1211import { uploadProfileImage } from './storage.server.ts'
1312
13+ const SCRYPT_PARAMS = {
14+ N : 2 ** 14 ,
15+ r : 16 ,
16+ p : 1 ,
17+ keyLength : 64 ,
18+ saltLength : 16 ,
19+ }
20+
1421export const SESSION_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30
1522export const getSessionExpirationDate = ( ) =>
1623 new Date ( Date . now ( ) + SESSION_EXPIRATION_TIME )
@@ -231,8 +238,9 @@ export async function logout(
231238}
232239
233240export async function getPasswordHash ( password : string ) {
234- const hash = await bcrypt . hash ( password , 10 )
235- return hash
241+ const salt = crypto . randomBytes ( SCRYPT_PARAMS . saltLength ) . toString ( 'hex' )
242+ const hash = await generateKey ( password , salt )
243+ return `${ salt } :${ hash . toString ( 'hex' ) } `
236244}
237245
238246export async function verifyUserPassword (
@@ -244,11 +252,16 @@ export async function verifyUserPassword(
244252 select : { id : true , password : { select : { hash : true } } } ,
245253 } )
246254
247- if ( ! userWithPassword || ! userWithPassword . password ) {
248- return null
249- }
255+ if ( ! userWithPassword || ! userWithPassword . password ) return null
250256
251- const isValid = await bcrypt . compare ( password , userWithPassword . password . hash )
257+ const [ salt , key ] = userWithPassword . password . hash . split ( ':' )
258+
259+ if ( ! key || ! salt ) return null
260+
261+ const isValid = crypto . timingSafeEqual (
262+ Buffer . from ( key , 'hex' ) ,
263+ await generateKey ( password , salt ) ,
264+ )
252265
253266 if ( ! isValid ) {
254267 return null
@@ -292,3 +305,25 @@ export async function checkIsCommonPassword(password: string) {
292305 return false
293306 }
294307}
308+
309+ async function generateKey (
310+ password : string ,
311+ salt : string ,
312+ ) : Promise < Buffer < ArrayBufferLike > > {
313+ return new Promise < Buffer < ArrayBufferLike > > ( ( resolve , reject ) => {
314+ crypto . scrypt (
315+ password . normalize ( 'NFKC' ) ,
316+ salt ,
317+ SCRYPT_PARAMS . keyLength ,
318+ {
319+ N : SCRYPT_PARAMS . N ,
320+ r : SCRYPT_PARAMS . r ,
321+ p : SCRYPT_PARAMS . p ,
322+ } ,
323+ ( err , key ) => {
324+ if ( err ) reject ( err )
325+ else resolve ( key )
326+ } ,
327+ )
328+ } )
329+ }
0 commit comments