Skip to content

Commit

Permalink
feat: delete all sessions if password was changed
Browse files Browse the repository at this point in the history
  • Loading branch information
stonith404 committed Jan 10, 2023
1 parent 74e8956 commit 02e41e2
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 20 deletions.
49 changes: 31 additions & 18 deletions backend/src/auth/auth.controller.ts
Expand Up @@ -21,6 +21,7 @@ import { AuthRegisterDTO } from "./dto/authRegister.dto";
import { AuthSignInDTO } from "./dto/authSignIn.dto";
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
import { EnableTotpDTO } from "./dto/enableTotp.dto";
import { TokenDTO } from "./dto/token.dto";
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
import { JwtGuard } from "./guard/jwt.guard";
Expand All @@ -45,8 +46,8 @@ export class AuthController {

response = this.addTokensToResponse(
response,
result.accessToken,
result.refreshToken
result.refreshToken,
result.accessToken
);

return result;
Expand All @@ -64,8 +65,8 @@ export class AuthController {
if (result.accessToken && result.refreshToken) {
response = this.addTokensToResponse(
response,
result.accessToken,
result.refreshToken
result.refreshToken,
result.accessToken
);
}

Expand All @@ -83,17 +84,28 @@ export class AuthController {

response = this.addTokensToResponse(
response,
result.accessToken,
result.refreshToken
result.refreshToken,
result.accessToken
);

return result;
return new TokenDTO().from(result);
}

@Patch("password")
@UseGuards(JwtGuard)
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) {
await this.authService.updatePassword(user, dto.oldPassword, dto.password);
async updatePassword(
@GetUser() user: User,
@Res({ passthrough: true }) response: Response,
@Body() dto: UpdatePasswordDTO
) {
const result = await this.authService.updatePassword(
user,
dto.oldPassword,
dto.password
);

response = this.addTokensToResponse(response, result.refreshToken);
return new TokenDTO().from(result);
}

@Post("token")
Expand All @@ -108,7 +120,7 @@ export class AuthController {
request.cookies.refresh_token
);
response.cookie("access_token", accessToken);
return { accessToken };
return new TokenDTO().from({ accessToken });
}

@Post("signOut")
Expand Down Expand Up @@ -146,15 +158,16 @@ export class AuthController {

private addTokensToResponse(
response: Response,
accessToken: string,
refreshToken: string
refreshToken?: string,
accessToken?: string
) {
response.cookie("access_token", accessToken);
response.cookie("refresh_token", refreshToken, {
path: "/api/auth/token",
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
});
if (accessToken) response.cookie("access_token", accessToken);
if (refreshToken)
response.cookie("refresh_token", refreshToken, {
path: "/api/auth/token",
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
});

return response;
}
Expand Down
15 changes: 13 additions & 2 deletions backend/src/auth/auth.service.ts
Expand Up @@ -87,10 +87,16 @@ export class AuthService {

const hash = await argon.hash(newPassword);

return await this.prisma.user.update({
await this.prisma.refreshToken.deleteMany({
where: { userId: user.id },
});

await this.prisma.user.update({
where: { id: user.id },
data: { password: hash },
});

return this.createRefreshToken(user.id);
}

async createAccessToken(user: User, refreshTokenId: string) {
Expand All @@ -112,7 +118,12 @@ export class AuthService {
refreshTokenId: string;
};

await this.prisma.refreshToken.delete({ where: { id: refreshTokenId } });
await this.prisma.refreshToken
.delete({ where: { id: refreshTokenId } })
.catch((e) => {
// Ignore error if refresh token doesn't exist
if (e.code != "P2025") throw e;
});
}

async refreshAccessToken(refreshToken: string) {
Expand Down
15 changes: 15 additions & 0 deletions backend/src/auth/dto/token.dto.ts
@@ -0,0 +1,15 @@
import { Expose, plainToClass } from "class-transformer";

export class TokenDTO {
@Expose()
accessToken: string;

@Expose()
refreshToken: string;

from(partial: Partial<TokenDTO>) {
return plainToClass(TokenDTO, partial, {
excludeExtraneousValues: true,
});
}
}

0 comments on commit 02e41e2

Please sign in to comment.