(
refreshToken: string,
ipAddress?: string,
userAgent?: string,
)
| 112 | } |
| 113 | |
| 114 | async refreshAccessToken( |
| 115 | refreshToken: string, |
| 116 | ipAddress?: string, |
| 117 | userAgent?: string, |
| 118 | ) { |
| 119 | // First validate the OLD refresh token to get the user |
| 120 | const oldTokenEntity = |
| 121 | await this.refreshTokenService.validateRefreshToken(refreshToken); |
| 122 | |
| 123 | if (!oldTokenEntity || !oldTokenEntity.userId) { |
| 124 | throw new UnauthorizedException('Invalid refresh token'); |
| 125 | } |
| 126 | |
| 127 | // Load the user explicitly by ID |
| 128 | const user = await this.usersService.findOne(oldTokenEntity.userId); |
| 129 | if (!user) { |
| 130 | throw new UnauthorizedException('User not found'); |
| 131 | } |
| 132 | |
| 133 | // Now rotate to get new refresh token |
| 134 | const newRefreshToken = await this.refreshTokenService.rotateRefreshToken( |
| 135 | refreshToken, |
| 136 | ipAddress, |
| 137 | userAgent, |
| 138 | ); |
| 139 | |
| 140 | if (!newRefreshToken) { |
| 141 | throw new UnauthorizedException('Failed to rotate refresh token'); |
| 142 | } |
| 143 | |
| 144 | const payload = { |
| 145 | email: user.email, |
| 146 | sub: user.id, |
| 147 | type: 'access', |
| 148 | }; |
| 149 | |
| 150 | // Generate new short-lived access token |
| 151 | const accessToken = this.jwtService.sign(payload, { |
| 152 | secret: |
| 153 | this.configService.get('JWT_ACCESS_SECRET') || |
| 154 | this.configService.get('JWT_SECRET'), |
| 155 | expiresIn: '15m', |
| 156 | }); |
| 157 | |
| 158 | return { |
| 159 | access_token: accessToken, |
| 160 | refresh_token: newRefreshToken, |
| 161 | }; |
| 162 | } |
| 163 | |
| 164 | async logout(refreshToken: string) { |
| 165 | await this.refreshTokenService.revokeToken(refreshToken); |
no test coverage detected