NestJS/GraphQL/Passport - 从警卫那里得到未经授权的错误
NestJS/GraphQL/Passport - getting unauthorised error from guard
我正在努力跟进这个 tutorial 并且我正在努力将实现转换为 GraphQL。
local.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authenticationService: AuthenticationService) {
super();
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authenticationService.getAuthenticatedUser(
email,
password,
);
if (!user) throw new UnauthorizedException();
return user;
}
}
local.guard.ts
@Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext): Promise<boolean> {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
req.body = ctx.getArgs();
await super.canActivate(new ExecutionContextHost([req]));
await super.logIn(req);
return true;
}
}
authentication.type.ts
@InputType()
export class AuthenticationInput {
@Field()
email: string;
@Field()
password: string;
}
authentication.resolver.ts
@UseGuards(LogInWithCredentialsGuard)
@Mutation(() => User, { nullable: true })
logIn(
@Args('variables')
_authenticationInput: AuthenticationInput,
@Context() req: any,
) {
return req.user;
}
变异
mutation {
logIn(variables: {
email: "email@email.com",
password: "123123"
} ) {
id
email
}
}
即使上述凭据正确,我也收到未经授权的错误。
问题出在你的LogInWithCredentialsGuard
。
你不应该覆盖 canAcitavte
方法,你所要做的就是用正确的 graphql args 更新请求,因为在 API 请求的情况下,passport 会自动从 req.body 但 graphql 执行上下文的情况不同,因此您必须在 req.body 中手动设置参数,为此使用 getRequest
方法。
由于 graphql 和 rest api 的执行上下文不同,你必须确保你的守卫在这两种情况下都能正常工作,无论是控制器还是突变。
这是一个有效的代码片段
@Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
// Override this method so it can be used in graphql
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
const { variables } = ctx.getArgs();
gqlReq.body = variables;
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
你的突变会像
@UseGuards(LogInWithCredentialsGuard)
@Mutation(() => User, { nullable: true })
logIn(
@Args('variables')
_authenticationInput: AuthenticationInput,
@Context() context: any, // <----------- it's not request
) {
return context.req.user;
}
我已经能够通过这样的守卫成功登录:
@Injectable()
export class LocalGqlAuthGuard extends AuthGuard('local') {
constructor() {
super();
}
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
req.body = ctx.getArgs();
return req;
}
async canActivate(context: ExecutionContext) {
await super.canActivate(context);
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
await super.logIn(req);
return true;
}
}
我正在努力跟进这个 tutorial 并且我正在努力将实现转换为 GraphQL。
local.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authenticationService: AuthenticationService) {
super();
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authenticationService.getAuthenticatedUser(
email,
password,
);
if (!user) throw new UnauthorizedException();
return user;
}
}
local.guard.ts
@Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext): Promise<boolean> {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
req.body = ctx.getArgs();
await super.canActivate(new ExecutionContextHost([req]));
await super.logIn(req);
return true;
}
}
authentication.type.ts
@InputType()
export class AuthenticationInput {
@Field()
email: string;
@Field()
password: string;
}
authentication.resolver.ts
@UseGuards(LogInWithCredentialsGuard)
@Mutation(() => User, { nullable: true })
logIn(
@Args('variables')
_authenticationInput: AuthenticationInput,
@Context() req: any,
) {
return req.user;
}
变异
mutation {
logIn(variables: {
email: "email@email.com",
password: "123123"
} ) {
id
email
}
}
即使上述凭据正确,我也收到未经授权的错误。
问题出在你的LogInWithCredentialsGuard
。
你不应该覆盖 canAcitavte
方法,你所要做的就是用正确的 graphql args 更新请求,因为在 API 请求的情况下,passport 会自动从 req.body 但 graphql 执行上下文的情况不同,因此您必须在 req.body 中手动设置参数,为此使用 getRequest
方法。
由于 graphql 和 rest api 的执行上下文不同,你必须确保你的守卫在这两种情况下都能正常工作,无论是控制器还是突变。
这是一个有效的代码片段
@Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
// Override this method so it can be used in graphql
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
const { variables } = ctx.getArgs();
gqlReq.body = variables;
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
你的突变会像
@UseGuards(LogInWithCredentialsGuard)
@Mutation(() => User, { nullable: true })
logIn(
@Args('variables')
_authenticationInput: AuthenticationInput,
@Context() context: any, // <----------- it's not request
) {
return context.req.user;
}
我已经能够通过这样的守卫成功登录:
@Injectable()
export class LocalGqlAuthGuard extends AuthGuard('local') {
constructor() {
super();
}
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
req.body = ctx.getArgs();
return req;
}
async canActivate(context: ExecutionContext) {
await super.canActivate(context);
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
await super.logIn(req);
return true;
}
}