@wabot-dev/framework 0.2.0-beta.2 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,17 +17,18 @@ let Jwt = class Jwt {
17
17
  this.jwtRefreshTokenRepository = jwtRefreshTokenRepository;
18
18
  this.config = config;
19
19
  }
20
- async createToken() {
20
+ async createToken(metadata) {
21
21
  const authInfo = this.auth.require();
22
22
  const refreshToken = new JwtRefreshToken({
23
+ metadata,
23
24
  authInfo,
24
- expirationTime: new Date().getTime() + this.config.refreshExpirationSeconds * 1000,
25
+ expirationTime: Date.now() + this.config.refreshExpirationSeconds * 1000,
25
26
  });
26
- const refreshPassword = refreshToken.generatePassword();
27
+ const refreshSecret = refreshToken.generateSecret();
27
28
  await this.jwtRefreshTokenRepository.create(refreshToken);
28
29
  const access = await this.jwtSigner.signAccessToken(refreshToken);
29
30
  const refresh = {
30
- token: JwtRefreshToken.deflate({ id: refreshToken.id, pass: refreshPassword }),
31
+ token: refreshSecret,
31
32
  expiration: new Date(refreshToken.expirationTime),
32
33
  };
33
34
  return {
@@ -35,11 +36,8 @@ let Jwt = class Jwt {
35
36
  refresh,
36
37
  };
37
38
  }
38
- async refreshToken(refreshSecret) {
39
- const { id, pass } = JwtRefreshToken.inflate(refreshSecret);
40
- const refreshToken = await this.jwtRefreshTokenRepository.findOrThrow(id);
41
- refreshToken.validatePassword(pass);
42
- return this.jwtSigner.signAccessToken(refreshToken);
39
+ async findRefreshTokenAuthInfo(secret) {
40
+ return await this.jwtRefreshTokenRepository.findAndValidate(secret);
43
41
  }
44
42
  };
45
43
  Jwt = __decorate([
@@ -1,55 +1,60 @@
1
1
  import { Entity } from '../../../core/entity/Entity.js';
2
2
  import { CustomError } from '../../../core/error/CustomError.js';
3
- import { Password } from '../../../core/password/Password.js';
3
+ import crypto from 'node:crypto';
4
4
 
5
5
  class JwtRefreshToken extends Entity {
6
+ static PREFIX = 'rt_';
7
+ static hashSecret(secret) {
8
+ return crypto.createHash('sha256').update(secret).digest('hex');
9
+ }
6
10
  get authInfo() {
7
11
  return this.data.authInfo;
8
12
  }
13
+ get metadata() {
14
+ return this.data.metadata ?? {};
15
+ }
9
16
  get expirationTime() {
10
17
  return new Date(this.data.expirationTime);
11
18
  }
12
- generatePassword() {
13
- if (this.data.passwordHash) {
14
- throw new Error('This api key, already has a secret');
19
+ isExpired() {
20
+ return Date.now() > this.data.expirationTime;
21
+ }
22
+ revoke() {
23
+ this.data.revokedAt = Date.now();
24
+ }
25
+ isRevoked() {
26
+ return this.data.revokedAt != null;
27
+ }
28
+ generateSecret() {
29
+ if (this.data.secretHash) {
30
+ throw new Error('This Token key already has a secret');
15
31
  }
16
- const password = Password.generate(64);
17
- this.data.passwordHash = Password.hash({ password: password });
18
- return password;
32
+ const secret = `${JwtRefreshToken.PREFIX}${crypto.randomBytes(32).toString('hex')}`;
33
+ this.data.secretHash = JwtRefreshToken.hashSecret(secret);
34
+ return secret;
19
35
  }
20
- isValidPassword(password) {
21
- if (new Date().getTime() > this.data.expirationTime) {
36
+ isValidSecret(secret) {
37
+ if (!secret.startsWith(JwtRefreshToken.PREFIX))
22
38
  return false;
23
- }
24
- if (!this.data.passwordHash)
39
+ if (!this.data.secretHash)
25
40
  return false;
26
- return Password.isValid({ password: password, hash: this.data.passwordHash });
41
+ const hashed = JwtRefreshToken.hashSecret(secret);
42
+ const stored = this.data.secretHash;
43
+ const hashedBuf = Buffer.from(hashed, 'hex');
44
+ const storedBuf = Buffer.from(stored, 'hex');
45
+ return hashedBuf.length === storedBuf.length && crypto.timingSafeEqual(hashedBuf, storedBuf);
27
46
  }
28
- validatePassword(password) {
29
- if (!this.isValidPassword(password)) {
30
- throw new CustomError({ message: 'Invalid Api key', httpCode: 401 });
31
- }
32
- }
33
- static inflate(secret) {
34
- try {
35
- const json = Buffer.from(secret, 'base64').toString('utf-8');
36
- const data = JSON.parse(json);
37
- if (!data.id || !data.pass) {
38
- throw new Error('invalid secret structure');
39
- }
40
- return data;
41
- }
42
- catch (err) {
43
- throw new Error('fail to inflate secret: ' + err.message);
44
- }
47
+ isValidToken(secret) {
48
+ if (this.isExpired())
49
+ return false;
50
+ if (this.isRevoked())
51
+ return false;
52
+ return this.isValidSecret(secret);
45
53
  }
46
- static deflate(data) {
47
- const { id, pass } = data;
48
- if (!id || !pass) {
49
- throw new Error('id and pass required');
54
+ validateToken(secret) {
55
+ if (!this.isValidToken(secret)) {
56
+ throw new CustomError({ message: 'Invalid Token', httpCode: 401 });
50
57
  }
51
- const json = JSON.stringify({ id, pass });
52
- return Buffer.from(json, 'utf-8').toString('base64');
53
58
  }
54
59
  }
55
60
 
@@ -1,4 +1,10 @@
1
1
  class JwtRefreshTokenRepository {
2
+ findByMetadata(metadata) {
3
+ throw new Error('Method not implemented.');
4
+ }
5
+ findAndValidate(secret) {
6
+ throw new Error('Method not implemented.');
7
+ }
2
8
  find(id) {
3
9
  throw new Error('Method not implemented.');
4
10
  }
@@ -3,6 +3,7 @@ import { singleton } from '../../../core/injection/index.js';
3
3
  import { Pool } from 'pg';
4
4
  import { PgCrudRepository } from '../../../feature/pg/PgCrudRepository.js';
5
5
  import { JwtRefreshToken } from './JwtRefreshToken.js';
6
+ import { CustomError } from '../../../core/error/CustomError.js';
6
7
 
7
8
  let PgJwtRefreshTokenRepository = class PgJwtRefreshTokenRepository extends PgCrudRepository {
8
9
  constructor(pool) {
@@ -12,6 +13,32 @@ let PgJwtRefreshTokenRepository = class PgJwtRefreshTokenRepository extends PgCr
12
13
  constructor: JwtRefreshToken,
13
14
  });
14
15
  }
16
+ async findAndValidate(secret) {
17
+ const apiKey = await this.findBySecret(secret);
18
+ if (!apiKey) {
19
+ throw new CustomError({ message: 'Invalid Token', httpCode: 401 });
20
+ }
21
+ return apiKey.authInfo;
22
+ }
23
+ async findBySecret(secret) {
24
+ const secretHash = JwtRefreshToken.hashSecret(secret);
25
+ const query = `
26
+ SELECT ${this.columns}
27
+ FROM ${this.table}
28
+ WHERE data @> $1::jsonb
29
+ LIMIT 1
30
+ `;
31
+ const items = await this.query(query, [JSON.stringify({ secretHash })]);
32
+ return items[0] ?? null;
33
+ }
34
+ async findByMetadata(metadata) {
35
+ const query = `
36
+ SELECT ${this.columns}
37
+ FROM ${this.table}
38
+ WHERE data @> $1::jsonb
39
+ `;
40
+ return await this.query(query, [JSON.stringify({ metadata })]);
41
+ }
15
42
  };
16
43
  PgJwtRefreshTokenRepository = __decorate([
17
44
  singleton(),
@@ -1091,27 +1091,30 @@ declare function jwtConnectionGuard(): (target: object, propertyKey: string | sy
1091
1091
  declare function jwtGuard(): (target: object, propertyKey: string | symbol) => void;
1092
1092
 
1093
1093
  interface IJwtRefreshTokenData<A extends IStorableData> extends IEntityData {
1094
+ secretHash?: string;
1095
+ metadata?: Record<string, string>;
1094
1096
  authInfo: A;
1095
- passwordHash?: string;
1096
1097
  expirationTime: number;
1098
+ revokedAt?: number;
1097
1099
  }
1098
1100
  declare class JwtRefreshToken<A extends IStorableData> extends Entity<IJwtRefreshTokenData<A>> {
1101
+ static PREFIX: string;
1102
+ static hashSecret(secret: string): string;
1099
1103
  get authInfo(): A;
1104
+ get metadata(): Record<string, string>;
1100
1105
  get expirationTime(): Date;
1101
- generatePassword(): string;
1102
- isValidPassword(password: string): boolean;
1103
- validatePassword(password: string): void;
1104
- static inflate(secret: string): {
1105
- id: string;
1106
- pass: string;
1107
- };
1108
- static deflate(data: {
1109
- id: string;
1110
- pass: string;
1111
- }): string;
1106
+ isExpired(): boolean;
1107
+ revoke(): void;
1108
+ isRevoked(): boolean;
1109
+ generateSecret(): string;
1110
+ isValidSecret(secret: string): boolean;
1111
+ isValidToken(secret: string): boolean;
1112
+ validateToken(secret: string): void;
1112
1113
  }
1113
1114
 
1114
1115
  interface IJwtRefreshTokenRepository<D extends IStorableData> extends ICrudRepository<JwtRefreshToken<D>> {
1116
+ findAndValidate(secret: string): Promise<D>;
1117
+ findByMetadata(metadata: Record<string, string>): Promise<JwtRefreshToken<D>[]>;
1115
1118
  }
1116
1119
 
1117
1120
  declare class JwtTokenDto {
@@ -1141,6 +1144,8 @@ declare class JwtSigner {
1141
1144
  }
1142
1145
 
1143
1146
  declare class JwtRefreshTokenRepository<D extends IStorableData> implements IJwtRefreshTokenRepository<D> {
1147
+ findByMetadata(metadata: Record<string, string>): Promise<JwtRefreshToken<D>[]>;
1148
+ findAndValidate(secret: string): Promise<D>;
1144
1149
  find(id: string): Promise<JwtRefreshToken<D> | null>;
1145
1150
  findOrThrow(id: string): Promise<JwtRefreshToken<D>>;
1146
1151
  findByIds(ids: string[]): Promise<JwtRefreshToken<D>[]>;
@@ -1156,8 +1161,8 @@ declare class Jwt {
1156
1161
  private jwtRefreshTokenRepository;
1157
1162
  private config;
1158
1163
  constructor(auth: Auth<any>, jwtSigner: JwtSigner, jwtRefreshTokenRepository: JwtRefreshTokenRepository<any>, config: JwtConfig);
1159
- createToken(): Promise<JwtAccessAndRefreshTokenDto>;
1160
- refreshToken(refreshSecret: string): Promise<JwtTokenDto>;
1164
+ createToken(metadata?: Record<string, string>): Promise<JwtAccessAndRefreshTokenDto>;
1165
+ findRefreshTokenAuthInfo(secret: string): Promise<any>;
1161
1166
  }
1162
1167
 
1163
1168
  declare class JwtConnectionGuardMiddleware implements IConnectionMiddleware {
@@ -1176,6 +1181,9 @@ declare class JwtGuardMiddleware implements IMiddleware {
1176
1181
 
1177
1182
  declare class PgJwtRefreshTokenRepository<D extends IStorableData> extends PgCrudRepository<JwtRefreshToken<D>> implements IJwtRefreshTokenRepository<D> {
1178
1183
  constructor(pool: Pool);
1184
+ findAndValidate(secret: string): Promise<D>;
1185
+ findBySecret(secret: string): Promise<JwtRefreshToken<D> | null>;
1186
+ findByMetadata(metadata: Record<string, string>): Promise<JwtRefreshToken<D>[]>;
1179
1187
  }
1180
1188
 
1181
1189
  declare class AnthropicChatAdapter implements IChatAdapter {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wabot-dev/framework",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.3",
4
4
  "description": "Framework for IA Chat Bots",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",