A social knowledge tool for researchers built on ATProto
at main 118 lines 3.4 kB view raw
1import jwt from 'jsonwebtoken'; 2import { v4 as uuidv4 } from 'uuid'; 3import { Result, ok, err } from 'src/shared/core/Result'; 4import { ITokenService } from '../../application/services/ITokenService'; 5import { ITokenRepository } from '../../domain/repositories/ITokenRepository'; 6import { TokenPair } from '@semble/types'; 7import { EnvironmentConfigService } from 'src/shared/infrastructure/config/EnvironmentConfigService'; 8 9export class FakeJwtTokenService implements ITokenService { 10 private jwtSecret: string; 11 private accessTokenExpiresIn: number = 12 new EnvironmentConfigService().getAuthConfig().accessTokenExpiresIn || 3600; // 1 hour 13 private refreshTokenExpiresIn: number = 14 new EnvironmentConfigService().getAuthConfig().refreshTokenExpiresIn || 15 2592000; // 30 days 16 17 constructor(private tokenRepository: ITokenRepository) { 18 this.jwtSecret = process.env.MOCK_ACCESS_TOKEN || 'mock-access-token-123'; 19 } 20 21 async generateToken(did: string): Promise<Result<TokenPair>> { 22 try { 23 // Generate actual JWT access token 24 const accessToken = jwt.sign( 25 { did, iat: Math.floor(Date.now() / 1000) }, 26 this.jwtSecret, 27 { expiresIn: this.accessTokenExpiresIn }, 28 ); 29 30 // Generate refresh token 31 const refreshToken = uuidv4(); 32 const tokenId = uuidv4(); 33 const now = new Date(); 34 const expiresAt = new Date( 35 now.getTime() + this.refreshTokenExpiresIn * 1000, 36 ); 37 38 // Store refresh token 39 const saveResult = await this.tokenRepository.saveRefreshToken({ 40 tokenId, 41 userDid: did, 42 refreshToken, 43 issuedAt: now, 44 expiresAt, 45 revoked: false, 46 }); 47 48 if (saveResult.isErr()) { 49 return err(saveResult.error); 50 } 51 52 return ok({ 53 accessToken, 54 refreshToken, 55 expiresIn: this.accessTokenExpiresIn, 56 }); 57 } catch (error: any) { 58 return err(error); 59 } 60 } 61 62 async validateToken(token: string): Promise<Result<string | null>> { 63 try { 64 const decoded = jwt.verify(token, this.jwtSecret) as { did: string }; 65 return ok(decoded.did); 66 } catch (error) { 67 return ok(null); // Token is invalid or expired 68 } 69 } 70 71 async refreshToken(refreshToken: string): Promise<Result<TokenPair | null>> { 72 try { 73 // Find the refresh token 74 const findResult = 75 await this.tokenRepository.findRefreshToken(refreshToken); 76 77 if (findResult.isErr()) { 78 return err(findResult.error); 79 } 80 81 const tokenData = findResult.unwrap(); 82 if (!tokenData) { 83 return ok(null); 84 } 85 86 // Check if token is expired 87 if (new Date() > tokenData.expiresAt) { 88 await this.revokeToken(refreshToken); 89 return ok(null); 90 } 91 92 // Generate new tokens 93 const newTokens = await this.generateToken(tokenData.userDid); 94 95 // Revoke old token 96 await this.revokeToken(refreshToken); 97 98 return newTokens; 99 } catch (error: any) { 100 return err(error); 101 } 102 } 103 104 async revokeToken(refreshToken: string): Promise<Result<void>> { 105 try { 106 const revokeResult = 107 await this.tokenRepository.revokeRefreshToken(refreshToken); 108 109 if (revokeResult.isErr()) { 110 return err(revokeResult.error); 111 } 112 113 return ok(undefined); 114 } catch (error: any) { 115 return err(error); 116 } 117 } 118}