Skip to content

[준섭] 1121(화) 개발기록 ‐ SignIn시 RefreshToken 발급 및 Redis에 저장

송준섭 edited this page Nov 21, 2023 · 1 revision

SignInLoginDto에 ClassValidator 적용

// signin-user.dto.ts
import ...

export class SignInUserDto {
	@ApiProperty({
		description: '로그인용 아이디',
		example: 'test user',
		required: true,
	})
	@IsNotEmpty({ message: UserEnum.USERNAME_NOTEMPTY_MESSAGE as string })
	@IsString({ message: UserEnum.USERNAME_ISSTRING_MESSAGE as string })
	@IsUsername()
	username: string;

	@ApiProperty({
		description: '비밀번호',
		example: 'test password',
		required: true,
	})
	@IsNotEmpty({ message: UserEnum.PASSWORD_NOTEMPTY_MESSAGE as string })
	@IsString({ message: UserEnum.PASSWORD_ISSTRING_MESSAGE as string })
	@IsPassword()
	password: string;
}

[준섭] 1121(화) 개발기록 ‐ SignUpDto에 ClassValidator 적용에서 만들었던 데코레이터 @IsUsername()@IsPassword()를 재사용하였습니다.

RefreshToken 발급 및 브라우저에 쿠키 전송

// auth.service.ts
async signIn(signInUserDto: SignInUserDto) {
	const { username, password } = signInUserDto;

	const user = await this.authRepository.findOneBy({ username });

	if (user && (await bcrypt.compare(password, user.password))) {
		const accessTokenPayload = { username, id: user.id, type: 'access' };
		const refreshTokenPayload = { username, id: user.id, type: 'refresh' };
		const [accessToken, refreshToken] = await Promise.all([
			this.jwtService.sign(accessTokenPayload),  // accessToken은 expiresIn이 60 * 60(1시간)
			this.jwtService.sign(refreshTokenPayload, { expiresIn: 60 * 60 * 24 }),  // refreshToken은 하루
		]);

		return { accessToken, refreshToken };
	} else {
		throw new UnauthorizedException('login failed');
	}
}

현재 우리 프로젝트의 jwtConfig에는 원래 expiresIn옵션이 60 * 60으로 1시간으로 되어있다.

그러나 refreshTokenaccessToken보다 더 길게 유효 기간을 잡아야 하므로 하루로 늘렸다.

이 유효 기간에 관해서는 얼마가 좋을 지 생각이 필요할 듯 하다.

그렇게 토큰을 발급하면 AuthController에서 이 토큰을 받아 사용한다.

async signIn(
	@Body() signInUserDto: SignInUserDto,
	@Res({ passthrough: true }) res: Response,
) {
	const tokens = await this.authService.signIn(signInUserDto);
	res.cookie('accessToken', tokens.accessToken, {
		path: '/',
		httpOnly: true,
	});
	res.cookie('refreshToken', tokens.refreshToken, {
		path: '/',
		httpOnly: true,
	});

	return tokens;
}

이렇게 쿠키에 담아서 보내면 다음과 같이 쿠키가 설정된다.

image

이 두 토큰의 값을 디코딩 해보았다.

  1. accessToken

image

  1. refreshToken

image

정보가 잘 담겨있는 모습을 확인했다.

유효 기간도 accessToken은 1시간, refreshToken은 하루가 잘 적용되어 있었다.

RefreshToken을 Redis에 저장하기

// auth.service.ts
async signIn(signInUserDto: SignInUserDto) {
	const { username, password } = signInUserDto;

	const user = await this.authRepository.findOneBy({ username });

	if (!(user && (await bcrypt.compare(password, user.password)))) {
		throw new UnauthorizedException('login failed');
	}

	const accessTokenPayload = { username, id: user.id, type: 'access' };
	const refreshTokenPayload = { username, id: user.id, type: 'refresh' };
	const [accessToken, refreshToken] = await Promise.all([
		this.jwtService.sign(accessTokenPayload),
		this.jwtService.sign(refreshTokenPayload, { expiresIn: 60 * 60 * 24 }),
	]);

	// redis에 저장 (키로 username, 값으로 refreshToken)
	this.redisRepository.set(username, refreshToken);

	return { accessToken, refreshToken };
}

image

처음 명령어는 SignIn 전, 다음 명령어는 SignIn 후.

username이 키로 추가된 모습.

값에는 refreshToken이 그대로 담겨있다.

소개

규칙

학습 기록

[공통] 개발 기록

[재하] 개발 기록

[준섭] 개발 기록

회의록

스크럼 기록

팀 회고

개인 회고

멘토링 일지

Clone this wiki locally