(refreshToken: unknown)
| 89 | } |
| 90 | |
| 91 | refresh(refreshToken: unknown): AuthTokens { |
| 92 | if (typeof refreshToken !== "string" || !refreshToken) { |
| 93 | throw new UnauthorizedException({ error: "missing refresh token" }); |
| 94 | } |
| 95 | const row = this.store.refreshTokens.findByHash(hashRefreshToken(refreshToken)); |
| 96 | if (!row || row.expiresAt < Date.now()) { |
| 97 | throw new UnauthorizedException({ error: "invalid refresh token" }); |
| 98 | } |
| 99 | // Reuse of an already-rotated token (or losing the atomic claim) signals a |
| 100 | // possible theft → revoke every session for that user and reject. |
| 101 | if (row.revoked || !this.store.refreshTokens.revokeIfActive(row.tokenHash)) { |
| 102 | this.store.refreshTokens.revokeAllForUser(row.userId); |
| 103 | throw new UnauthorizedException({ error: "invalid refresh token" }); |
| 104 | } |
| 105 | const u = this.store.users.findById(row.userId); |
| 106 | if (!u) throw new UnauthorizedException({ error: "invalid refresh token" }); |
| 107 | return this.issue(u); |
| 108 | } |
| 109 | |
| 110 | logout(refreshToken: unknown): void { |
| 111 | if (typeof refreshToken === "string" && refreshToken) { |
nothing calls this directly
no test coverage detected