ekyc

Electronic KYC process with uploading ID document using OAuth 2.1 (experimental)
Log | Files | Refs | README | LICENSE

auth.ts (5069B)


      1 import { AuthRepository } from "#core/application/authn/auth_repository.ts";
      2 import {
      3   EntityLocked,
      4   EntityNotFound,
      5 } from "#core/application/repository_error.ts";
      6 import { Auth } from "#core/domain/auth.ts";
      7 import { Email } from "#core/domain/email.ts";
      8 import { Token } from "#core/domain/token.ts";
      9 import { UUID } from "#core/domain/uuid.ts";
     10 import {
     11   AuthDto,
     12   mapFromAuth,
     13   mapToAuth,
     14 } from "#infrastructure/memory/mapper/auth.ts";
     15 import { mapError } from "#infrastructure/postgres/error.ts";
     16 import { Pool } from "$postgres";
     17 
     18 export class PostgresAuthRepositoryAdapter implements AuthRepository {
     19   constructor(readonly pool: Pool) {
     20   }
     21 
     22   async find(uuid: UUID): Promise<Auth> {
     23     try {
     24       const connection = await this.pool.connect();
     25       try {
     26         const result = await connection.queryObject<
     27           AuthDto
     28         >`select * from "auth" where "uuid" = ${uuid.toString()} limit 1;`;
     29         if (result.rowCount !== 1) {
     30           throw new EntityNotFound(uuid.toString());
     31         }
     32         return mapToAuth(result.rows[0]);
     33       } finally {
     34         connection.release();
     35       }
     36     } catch (error) {
     37       throw mapError(error);
     38     }
     39   }
     40 
     41   async findByEmail(email: Email): Promise<Auth> {
     42     try {
     43       const connection = await this.pool.connect();
     44       try {
     45         const result = await connection.queryObject<
     46           AuthDto
     47         >`select * from "auth" where "email" = ${email.toString()} limit 1;`;
     48         if (result.rowCount !== 1) {
     49           throw new EntityNotFound(email.toString());
     50         }
     51         return mapToAuth(result.rows[0]);
     52       } finally {
     53         connection.release();
     54       }
     55     } catch (error) {
     56       throw mapError(error);
     57     }
     58   }
     59 
     60   async findBySessionToken(sessionToken: Token): Promise<Auth> {
     61     try {
     62       const connection = await this.pool.connect();
     63       try {
     64         const result = await connection.queryObject<
     65           AuthDto
     66         >`select * from "auth" where "sessionToken" = ${sessionToken.toString()} and "sessionExpire" > current_timestamp limit 1;`;
     67         if (result.rowCount !== 1) {
     68           throw new EntityNotFound(sessionToken.toString());
     69         }
     70         return mapToAuth(result.rows[0]);
     71       } finally {
     72         connection.release();
     73       }
     74     } catch (error) {
     75       throw mapError(error);
     76     }
     77   }
     78 
     79   async store(auth: Auth): Promise<void> {
     80     try {
     81       const dto = mapFromAuth(auth);
     82       const connection = await this.pool.connect();
     83       const transaction = connection.createTransaction(
     84         `txn_${dto.uuid}_${dto.version}`,
     85       );
     86       try {
     87         await transaction.begin();
     88         dto.version++;
     89         if (dto.version === 1) {
     90           await transaction.queryObject<void>`
     91             insert into "auth" (
     92               "uuid", "email", "emailVerified", "emailCode", "emailCodeExpire",
     93               "emailChallengeRequest", "emailChallengeRequestExpire", "emailChallengeAttempt", "emailChallengeAttemptExpire",
     94               "passwordHash", "passwordAttempt", "passwordAttemptExpire",
     95               "sessionToken", "sessionExpire", "version"
     96             ) values (
     97               ${dto.uuid}, ${dto.email}, ${dto.emailVerified}, ${dto.emailCode}, ${dto.emailCodeExpire},
     98               ${dto.emailChallengeRequest}, ${dto.emailChallengeRequestExpire}, ${dto.emailChallengeAttempt}, ${dto.emailChallengeAttemptExpire},
     99               ${dto.passwordHash}, ${dto.passwordAttempt}, ${dto.passwordAttemptExpire},
    100               ${dto.sessionToken}, ${dto.sessionExpire}, ${dto.version}
    101             );
    102           `;
    103           await transaction.commit();
    104           auth.version = dto.version;
    105           return;
    106         }
    107         const result = await transaction.queryObject<{ version: number }>`
    108           update "auth" set
    109             "email" = ${dto.email},
    110             "emailVerified" = ${dto.emailVerified},
    111             "emailCode" = ${dto.emailCode},
    112             "emailCodeExpire" = ${dto.emailCodeExpire},
    113             "emailChallengeRequest" = ${dto.emailChallengeRequest},
    114             "emailChallengeRequestExpire" = ${dto.emailChallengeRequestExpire},
    115             "emailChallengeAttempt" = ${dto.emailChallengeAttempt},
    116             "emailChallengeAttemptExpire" = ${dto.emailChallengeAttemptExpire},
    117             "passwordAttempt" = ${dto.passwordAttempt},
    118             "passwordAttemptExpire" = ${dto.passwordAttemptExpire},
    119             "sessionToken" = ${dto.sessionToken},
    120             "sessionExpire" = ${dto.sessionExpire},
    121             "version" = "version" + 1
    122           where uuid = ${dto.uuid}
    123           returning version;
    124         `;
    125         if (result.rowCount !== 1) {
    126           transaction.rollback();
    127           throw new EntityNotFound(dto.uuid);
    128         }
    129         if (result.rows[0].version === dto.version) {
    130           await transaction.commit();
    131           auth.version = result.rows[0].version;
    132           return;
    133         }
    134         transaction.rollback();
    135         throw new EntityLocked();
    136       } finally {
    137         connection.release();
    138       }
    139     } catch (error) {
    140       throw mapError(error);
    141     }
    142   }
    143 }