Aws Cognito, Passport 및 NestJs를 사용한 인증(파트 III)
31575 단어 awstutorialtypescriptwebdev
엔드포인트 보호
인증을 위해 JWT 전략을 사용할 것입니다. 보호된 엔드포인트 리소스에 액세스하려면 Cognito에서 생성한 베어러 토큰을 사용해야 합니다.
먼저
PassportModule
를 가져오고 JWT 전략을 기본값으로 지정해 보겠습니다.auth.module.ts
...
import { PassportModule } from '@nestjs/passport';
@Module({
imports: [PassportModule.register({ defaultStrategy: 'jwt' })],
...
})
export class AuthModule {}
이제
jwt.strategy.ts
와 같은 디렉토리에 auth.controller.ts
라는 파일을 만듭니다.이 클래스는
PassportStrategy
에서 확장되며 선택한 전략을 통과합니다. 또한 validate()
콜백을 통해 요청이 유효한지 확인합니다. 우리는 또한 AWS_COGNITO_AUTHORITY
이어야 하는 새로운 환경 변수https://cognito-idp.YOUR_POOL_REGION.amazonaws.com/AWS_COGNITO_USER_POOL_ID
를 사용합니다. 내 풀 지역은 North Virginia이므로 us-east-1
도 마찬가지입니다.auth.module.ts
공급자이므로 @Injectable
공급자에 나열하는 것을 잊지 마십시오.jwt.전략.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { passportJwtSecret } from 'jwks-rsa';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
_audience: process.env.AWS_COGNITO_COGNITO_CLIENT_ID,
issuer: process.env.AWS_COGNITO_AUTHORITY,
algorithms: ['RS256'],
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: process.env.AWS_COGNITO_AUTHORITY + '/.well-known/jwks.json',
}),
});
}
async validate(payload: any) {
return { idUser: payload.sub, email: payload.email };
}
}
마지막으로 네스트 가드를 사용하여 포켓몬의 목록 끝점을 보호합니다.
pokemon.controller.ts
로 가서 @UseGuards()
데코레이터 전달AuthGuard('jwt')
을 매개변수로 추가해 보겠습니다.이 같은:
포켓몬.컨트롤러.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
...
@Controller('api/v1/pokemons')
export class PokemonController {
constructor(private readonly pokemonService: PokemonService) {}
@UseGuards(AuthGuard('jwt'))
@Get()
listAllPokemons(): Array<Pokemon> {
return this.pokemonService.listAllPokemons();
}
}
이제 로그인할 때 생성된 베어러 토큰을 전달하지 않으면 엔드포인트에서
401 Unauthorized
를 받게 됩니다.이제 엔드포인트가 원치 않는 액세스로부터 보호됩니다.
비밀번호 변경
암호를 변경하려면
changeUserPassword
에서 aws-cognito.service.ts
라는 다른 방법을 수행해야 합니다. 이 메서드는 현재 및 새 암호를 얻기 위해 다른 DTO를 받습니다. 이 방법에서 Cognito는 나중에 changePassword
방법을 사용하여 암호를 변경하기 위해 먼저 사용자를 인증해야 합니다.인증-변경-비밀번호-user.dto.ts
import { IsEmail, Matches } from 'class-validator';
export class AuthChangePasswordUserDto {
@IsEmail()
email: string;
/* Minimum eight characters, at least one uppercase letter, one lowercase letter, one number, and one special character */
@Matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$&+,:;=?@#|'<>.^*()%!-])[A-Za-z\d@$&+,:;=?@#|'<>.^*()%!-]{8,}$/,
{ message: 'invalid password' },
)
currentPassword: string;
@Matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$&+,:;=?@#|'<>.^*()%!-])[A-Za-z\d@$&+,:;=?@#|'<>.^*()%!-]{8,}$/,
{ message: 'invalid password' },
)
newPassword: string;
}
aws-cognito.service.ts
...
import { AuthChangePasswordUserDto } from './dtos/auth-change-password-user.dto';
...
async changeUserPassword(
authChangePasswordUserDto: AuthChangePasswordUserDto,
) {
const { email, currentPassword, newPassword } = authChangePasswordUserDto;
const userData = {
Username: email,
Pool: this.userPool,
};
const authenticationDetails = new AuthenticationDetails({
Username: email,
Password: currentPassword,
});
const userCognito = new CognitoUser(userData);
return new Promise((resolve, reject) => {
userCognito.authenticateUser(authenticationDetails, {
onSuccess: () => {
userCognito.changePassword(
currentPassword,
newPassword,
(err, result) => {
if (err) {
reject(err);
return;
}
resolve(result);
},
);
},
onFailure: (err) => {
reject(err);
},
});
});
}
...
auth.controller
에서 AuthChangePasswordUserDto
를 통과하고 changeUserPassword
에서 aws.cognito.service.ts
를 호출하는 새 경로만 수행하면 됩니다.auth.controller.ts
...
@Post('/change-password')
@UsePipes(ValidationPipe)
async changePassword(
@Body() authChangePasswordUserDto: AuthChangePasswordUserDto,
) {
await this.awsCognitoService.changeUserPassword(authChangePasswordUserDto);
}
...
테스트 후 모든 것이 올바르게 작동하는 것 같습니다.
잊어버린 비밀번호 재설정
잊어버린 암호를 재설정하려면 두 개의 새로운 엔드포인트가 필요합니다. 하나는 고유한 코드를 요청하고 다른 하나는 암호를 전환하기 위한 것입니다.
두 개의 새로운 DTO를 정의하는 것으로 시작합니다.
auth-forgot-password-user.dto.ts
...
import { IsEmail } from 'class-validator';
export class AuthForgotPasswordUserDto {
@IsEmail()
email: string;
}
...
auth-confirm-password-user.dto.ts
...
import { IsEmail, IsString, Matches } from 'class-validator';
export class AuthConfirmPasswordUserDto {
@IsEmail()
email: string;
@IsString()
confirmationCode: string;
@Matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$&+,:;=?@#|'<>.^*()%!-])[A-Za-z\d@$&+,:;=?@#|'<>.^*()%!-]{8,}$/,
{ message: 'invalid password' },
)
newPassword: string;
}
또한
aws.service.ts
에서 Cognito에 새 재설정 코드를 요청하고 암호를 변경하기 위해 두 가지 새로운 메서드를 만들어야 합니다.aws-cognito.service.ts
...
import { AuthConfirmPasswordUserDto } from './dtos/auth-confirm-password-user.dto';
import { AuthForgotPasswordUserDto } from './dtos/auth-forgot-password-user.dto';
...
async forgotUserPassword(
authForgotPasswordUserDto: AuthForgotPasswordUserDto,
) {
const { email } = authForgotPasswordUserDto;
const userData = {
Username: email,
Pool: this.userPool,
};
const userCognito = new CognitoUser(userData);
return new Promise((resolve, reject) => {
userCognito.forgotPassword({
onSuccess: (result) => {
resolve(result);
},
onFailure: (err) => {
reject(err);
},
});
});
}
async confirmUserPassword(
authConfirmPasswordUserDto: AuthConfirmPasswordUserDto,
) {
const { email, confirmationCode, newPassword } = authConfirmPasswordUserDto;
const userData = {
Username: email,
Pool: this.userPool,
};
const userCognito = new CognitoUser(userData);
return new Promise((resolve, reject) => {
userCognito.confirmPassword(confirmationCode, newPassword, {
onSuccess: () => {
resolve({ status: 'success' });
},
onFailure: (err) => {
reject(err);
},
});
});
}
...
각각의 서비스 메서드를 호출하여 API에 대한 두 개의 새 경로를 추가하는 컨트롤러에서 거의 동일한 상황입니다.
auth.controller.ts
...
@Post('/forgot-password')
@UsePipes(ValidationPipe)
async forgotPassword(
@Body() authForgotPasswordUserDto: AuthForgotPasswordUserDto,
) {
return await this.awsCognitoService.forgotUserPassword(
authForgotPasswordUserDto,
);
}
@Post('/confirm-password')
@UsePipes(ValidationPipe)
async confirmPassword(
@Body() authConfirmPasswordUserDto: AuthConfirmPasswordUserDto,
) {
return await this.awsCognitoService.confirmUserPassword(
authConfirmPasswordUserDto,
);
}
...
이제 새 끝점을 테스트해 보겠습니다.
팔!! 모든 것이 완벽하게 작동하는 것 같습니다.
그게 다야.
이 시리즈가 이 시리즈를 좋아했던 Cognito 및 javascript 통합을 더 잘 이해하는 데 도움이 되기를 바랍니다. 그리고 repohere를 찾을 수 있습니다.
Medium에서 저를 팔로우하거나 제 기술 여정에 대해 자세히 알아보세요.
Reference
이 문제에 관하여(Aws Cognito, Passport 및 NestJs를 사용한 인증(파트 III)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/fstbraz/authentication-with-aws-cognito-passport-and-nestjs-part-iii-2da5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)