NestJs와 graphql과 Cognito를 사용해 로그인 로직을 구현중에 mutation을 사용할 때 에러가 발생하였고 다음과 같이 해결하였다.
발생 에러 : "Cannot return null for non-nullable field LoginResponse.id_token."
import { Field, ObjectType } from '@nestjs/graphql';
@ObjectType()
export class LoginResponse {
@Field()
id_token: string;
}
import { BadRequestException, UseGuards } from '@nestjs/common';
import { Args, Context, Mutation, Query, Resolver } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
import { User } from 'src/users/entities/user.entity';
import { AuthService } from './auth.service';
import { CognitoAuthGuard } from './cognito.guard';
import { CurrentUser } from './currentUser.decorator';
import { LoginResponse } from './dto/login-response';
import { LoginUserInput } from './dto/login-user.input';
import { SignUpUserInput } from './dto/signup-user.input';
import { SignUpInfo } from './entities/signup.entity';
@Resolver()
export class AuthResolver {
constructor(private authService: AuthService) {}
@Mutation(() => LoginResponse)
login(
@Args('loginUserInput')
loginUserInput: LoginUserInput,
@Context() context,
) {
try {
return this.authService.login(loginUserInput);
} catch (err) {
throw new BadRequestException(err.message);
}
}
@Mutation(() => SignUpInfo)
signup(@Args('signUpUserInput') signUpUserInput: SignUpUserInput) {
return this.authService.signup(signUpUserInput);
}
}
LoginResponse에서 id_token을 return 해줘야하는데, null로 return을 해서 발생하는 에러였다.
해결방법 : LoginResponse가 @ObjectType으로 선언되었으므로 객체 타입인데, String 타입으로 받으려고 했기 때문이다.
넘겨줄 때 객체로 변환해서 넘겨주었다.
auth.service.ts
import { JwtService } from '@nestjs/jwt';
import { UsersService } from './../users/users.service';
import { Inject, Injectable } from '@nestjs/common';
import { User } from 'src/users/entities/user.entity';
// import * as bcrypt from 'bcrypt';
import {
AuthenticationDetails,
CognitoUser,
CognitoUserAttribute,
CognitoUserPool,
} from 'amazon-cognito-identity-js';
import { AuthConfiguration } from './auth.configuration';
import { SignUpUserInput } from './dto/signup-user.input';
import { LoginUserInput } from './dto/login-user.input';
@Injectable()
export class AuthService {
private userPool: CognitoUserPool;
constructor(
private readonly authConfig: AuthConfiguration,
private usersService: UsersService,
private jwtService: JwtService,
) {
this.userPool = new CognitoUserPool({
UserPoolId: this.authConfig.userPoolId,
ClientId: this.authConfig.clientId,
});
}
signup(registerRequest: SignUpUserInput) {
return new Promise((resolve, reject) => {
return this.userPool.signUp(
registerRequest.username,
registerRequest.password,
[
new CognitoUserAttribute({
Name: 'email',
Value: registerRequest.email,
}),
],
null,
(err, result) => {
if (!result) {
console.log(err);
reject(err);
} else {
resolve(result.user);
}
},
);
});
}
login(user: LoginUserInput) {
const authenticationDetails = new AuthenticationDetails({
Username: user.username,
Password: user.password,
});
const userData = {
Username: user.username,
Pool: this.userPool,
};
const newUser = new CognitoUser(userData);
return new Promise((resolve, reject) => {
return newUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
// 원래 코드 : resolve(result.getIdToken().getJwtToken());
resolve({ id_token: result.getIdToken().getJwtToken() }); // 변경 후 코드 -> id_token을 객체로 변환하여 넘겨주었다.
},
onFailure: (err) => {
reject(err);
},
});
});
}
}