NestJS Authentication with OAuth2.0: Apollo Local OAuth GraphQL API
Tags: typescript, nestjs, node, graphql
Published: 2023-03-26 12:26:27

Hey there, dear readers! I apologise for the delayed Article. I had been traveling abroad for almost a month, and upon returning, this series slipped my conscious. I am sorry for any frustration that this may have caused.

We are nearing the end of this series, with only two more articles to go (including this one). I am committed to delivering them before mid-April, so you will not have to wait too long. But hold on, the excitement does not end there! I have got big plans for the future of my blog.

In fact, I am thrilled to announce that I'll be introducing two new series:

- _Rust Algorithm Development_: In this series, I will be guiding you through implementing some basic NLP algorithms from scratch. Specifically, TF-IDF, Co-occurrence Matrix and RAKE. I know it might seem a bit random, but since machine learning is becoming more prevalent in our lives, I thought it would be fun to dip my toes into the topic since I used to be a Data Analyst (technically an Econometrician)
- _Advance GraphQL with Mercurius and NestJS_: Throughout this series, I will be your guide as we develop a Discord clone. We will start by creating a monolithic back-end and then divide it into microservices to make it more scalable and future proof.

I hope you all enjoy the remainder of the series, and thank you for your understanding!

## Series Intro

This series will cover the full implementation of OAuth2.0 Authentication in [NestJS](https://nestjs.com/) for the following types of APIs:

- [Express](https://expressjs.com/) REST API;
- [Fastify](https://www.fastify.io/) REST API;
- [Apollo GraphQL](https://www.apollographql.com/docs/apollo-server/) API.

And it is divided in 5 parts:

- Configuration and operations;
- Express Local OAuth REST API;
- Fastify Local OAuth REST API;
- Apollo Local OAuth GraphQL API;
- Adding External OAuth Providers to our API;

Lets start the fourth part of this series.

## Tutorial Intro

On this tutorial we will look at the previous [Express REST API](https://dev.to/tugascript/nestjs-authentication-with-oauth20-express-local-oauth-rest-api-3hfj) tutorial, and transform it to an [Apollo](https://www.apollographql.com/docs/apollo-server/) GraphQL API.

**DISCLAIMER:** Although it is possible to have an OAuth System in GraphQL, it is recommended to use REST architecture instead. This article is for educational purposes only. Please do not use this code in Production. **To discourage the use of this code in production I removed all unit tests from it**

<small>**NOTE:** if you do not have time to read the entire article the repository can be found [here](https://github.com/tugascript/nestjs-oauth/tree/apollo-graphql-api). Do not use this code in production, and please remember that there is nothing wrong with having a Hybrid API, with REST for Authentication and GraphQL for the rest of the API, that is what is recommended. While you at it consider [buying me a coffee](https://www.buymeacoffee.com/barracha).</small>

## Set up

Disclaimers aside, start by installing the required libraries for GraphQL development:

```bash
$ yarn add @nestjs/graphql @nestjs/apollo @apollo/server graphql @apollo/federation @apollo/subgraph
```

On the `config` directory create a new file for the GraphQL configuration:

```ts
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { ConfigService } from '@nestjs/config';
import { GqlOptionsFactory } from '@nestjs/graphql';
import { IContext } from './interfaces/context.interface';

export class GraphQLConfig implements GqlOptionsFactory {
  private readonly testing: boolean;

  constructor(private readonly configService: ConfigService) {
    this.testing = this.configService.get('testing');
  }

  public createGqlOptions(): ApolloDriverConfig {
    return {
      driver: ApolloDriver,
      context: ({ req, res }): IContext => ({
        req,
        res,
      }),
      path: '/api/graphql',
      autoSchemaFile: './schema.gql',
      sortSchema: true,
      playground: this.testing,
      introspection: true,
    };
  }
}
```

And register the `GraphQLModule` with it in the `AppModule`:

```ts
// ...
import { GraphQLConfig } from './config/graphql.config';
// ...

@Module({
  imports: [
    // ...
    GraphQLModule.forRootAsync({
      imports: [ConfigModule],
      driver: ApolloDriver,
      useClass: GraphQLConfig,
    }),
    // ...
  ],
  // ...
})
export class AppModule {}
```

## Differences overview

If you are reading this article, I will assume that you already have experience developing GraphQL APIs using NestJS, but to summarise there are a few differences from REST to GraphQL:

- We use Resolvers and not Controllers to write Mutations (equivalent to POST, PATCH & Delete) and Queries (GET equivalent);
- Request and Response are not passed directly to the Resolver, but through the Context parameter.

Having this in mind, going from REST to GraphQL is not that hard.

## Resolvers

In general before developing our resolvers we need to create:

- _Object Types_:
    - How our entities maps to the GraphQL schema;
    - _Impl_: add `@ObjectType()` decorator on the entity.
- _Args Types_: 
    - The arguments that go into our Queries (like array fields in Python `*args`);
    - _Impl_: add `@ArgsType()` decorator on DTOs.
- _Input Types_: 
    - This are Input Objects where a single argument will accept an object (like named parameters in Python `**kwargs`);
    - _Impl_: add `@InputType()` decorator on DTOs.
- _Fields_:
    - For any field that should be mapped on any kind of object or DTO;
    - _Impl_: add `@Field` decorator on each parameter.

As for standards and rules we will use the Apollo [recommendations](https://www.apollographql.com/docs/apollo-server/schema/schema/) and be explicit with our queries and mutations.

## Updates

### Common

On the common service start by creating a new `entities` folder with a `gql` folder inside and add a `MessageType` there, based on the `MessageMapper`:

`message.type.ts`:

```ts
import { Field, ObjectType } from '@nestjs/graphql';
import { ApiProperty } from '@nestjs/swagger';
import { v4 } from 'uuid';
import { IMessage } from '../../interfaces/message.interface';

@ObjectType('Message')
export class MessageType implements IMessage {
  @Field(() => String)
  public id: string;

  @Field(() => String)
  @ApiProperty({
    description: 'Message',
    example: 'Hello World',
    type: String,
  })
  public message: string;

  constructor(message: string) {
    this.id = v4();
    this.message = message;
  }
}
```

We need a new DTO as well, the `IdDto`, start by creating a `dtos` folder and add the `id.dto.ts` there:

```ts
import { ArgsType, Field, Int } from '@nestjs/graphql';
import { IsInt, Min } from 'class-validator';

@ArgsType()
export abstract class IdDto {
  @Field(() => Int)
  @IsInt()
  @Min(1)
  public id: number;
}
```

<small>Note that if your service should allow to filter users consider reading [my article on cursor pagination](https://dev.to/tugascript/cursor-pagination-for-nestjs-graphql-with-mikro-orm-and-sql-m68). Shameless plug I know</small>

Lastly since we are creating a micro-service we need to Federate our schema, so create a `IFederatedInstance` interface on the `interfaces` folder:

```ts
export interface IFederatedInstance<T extends string> {
  readonly __typename: T;
  readonly id: number;
}
```

Read more about apollo federation on the [apollo docs](https://www.apollographql.com/docs/federation/).

### Users

**Entities**

Start by adding the `@ObjectType` decorator to the `UserEntity`, with the `@Field` decorator for every field you want to be queryable:

```ts
import { Embedded, Entity, PrimaryKey, Property } from '@mikro-orm/core';
import { Directive, Field, Int, ObjectType } from '@nestjs/graphql';
import { IsBoolean, IsEmail, IsString, Length, Matches } from 'class-validator';
import {
  BCRYPT_HASH,
  NAME_REGEX,
  SLUG_REGEX,
} from '../../common/consts/regex.const';
import { CredentialsEmbeddable } from '../embeddables/credentials.embeddable';
import { IUser } from '../interfaces/user.interface';
import { privateMiddleware } from '../middleware/private.middleware';

@ObjectType('User')
@Directive('@key(fields: "id")')
@Entity({ tableName: 'users' })
export class UserEntity implements IUser {
  @Field(() => Int)
  @PrimaryKey()
  public id: number;

  @Field(() => String)
  @Property({ columnType: 'varchar', length: 100 })
  @IsString()
  @Length(3, 100)
  @Matches(NAME_REGEX, {
    message: 'Name must not have special characters',
  })
  public name: string;

  @Field(() => String)
  @Property({ columnType: 'varchar', length: 106 })
  @IsString()
  @Length(3, 106)
  @Matches(SLUG_REGEX, {
    message: 'Username must be a valid slug',
  })
  public username: string;

  @Field(() => String, { nullable: true, middleware: [privateMiddleware] })
  @Property({ columnType: 'varchar', length: 255 })
  @IsString()
  @IsEmail()
  @Length(5, 255)
  public email: string;

  @Property({ columnType: 'varchar', length: 60 })
  @IsString()
  @Length(59, 60)
  @Matches(BCRYPT_HASH)
  public password: string;

  @Property({ columnType: 'boolean', default: false })
  @IsBoolean()
  public confirmed: true | false = false;

  @Embedded(() => CredentialsEmbeddable)
  public credentials: CredentialsEmbeddable = new CredentialsEmbeddable();

  @Property({ onCreate: () => new Date() })
  public createdAt: Date = new Date();

  @Property({ onUpdate: () => new Date() })
  public updatedAt: Date = new Date();
}
```

In order for only the current logged user to have access to his own email we need to create a `privateMiddleware`, add it to new a `middleware` directory:

```ts
import { FieldMiddleware, MiddlewareContext } from '@nestjs/graphql';
import { isNull, isUndefined } from '../../common/utils/validation.util';
import { IContext } from '../../config/interfaces/context.interface';
import { IUser } from '../interfaces/user.interface';

export const privateMiddleware: FieldMiddleware = async (
  ctx: MiddlewareContext<IUser, IContext, unknown>,
  next,
) => {
  const user = ctx.context.req.user;

  if (isUndefined(user) || isNull(user) || ctx.source.id !== user) {
    return null;
  }

  return next();
};
```

**Users Service**

Before defining the new `users.resolver.ts` file we need to do some changes to `UsersService` based on being explicit with our mutation, hence we need to divide the `update` method into each user's parameter:

- `updateName`:
    ```ts
    @Injectable()
    export class UsersService {
      // ...

      public async updateName(userId: number, name: string): Promise<UserEntity> {
        const user = await this.findOneById(userId);
        const formattedName = this.commonService.formatName(name);

        if (user.name === formattedName) {
          throw new BadRequestException('Name must be different');
        }

        user.name = formattedName;
        await this.commonService.saveEntity(this.usersRepository, user);
        return user;
      }

      // ...
    }
    ```
- `updateUsername`:
    ```ts
    @Injectable()
    export class UsersService {
      // ...

      public async updateUsername(
        userId: number,
        username: string,
      ): Promise<UserEntity> {
        const user = await this.findOneById(userId);
        const formattedUsername = username.toLowerCase();

        if (user.username === formattedUsername) {
          throw new BadRequestException('Username should be different');
        }

        await this.checkUsernameUniqueness(formattedUsername);
        user.username = formattedUsername;
        await this.commonService.saveEntity(this.usersRepository, user);
        return user;
      }

      // ...
    }
    ```

**DTOs**

On the DTOs we created in the previous tutorials, and new DTOs you will need to add the `@ArgsType()` decorator. So the old decorators would be something like this:

- `PasswordDto` on `password.dto.ts`:
    ```ts
    import { ArgsType, Field } from '@nestjs/graphql';
    import { ApiProperty } from '@nestjs/swagger';
    import { IsString, MinLength } from 'class-validator';

    @ArgsType()
    export abstract class PasswordDto {
      @Field(() => String)
      @ApiProperty({
        description: 'The password of the user',
        minLength: 1,
        type: String,
      })
      @IsString()
      @MinLength(1)
      public password: string;
    }
    ```
- `ChangeEmailDto` on `change-email.dto.ts`;
    ```ts
    import { ArgsType, Field } from '@nestjs/graphql';
    import { ApiProperty } from '@nestjs/swagger';
    import { IsEmail, IsString, Length } from 'class-validator';
    import { PasswordDto } from './password.dto';

    @ArgsType()
    export abstract class ChangeEmailDto extends PasswordDto {
      @Field(() => String)
      @ApiProperty({
        description: 'The email of the user',
        example: 'someone@gmail.com',
        minLength: 5,
        maxLength: 255,
        type: String,
      })
      @IsString()
      @IsEmail()
      @Length(5, 255)
      public email: string;
    }
    ```

As for new create a `NameDto` and `UsernameDto`:

- `name.dto.ts`:
    ```ts
    import { ArgsType, Field } from '@nestjs/graphql';
    import { IsString, Length, Matches } from 'class-validator';
    import { NAME_REGEX } from '../../common/consts/regex.const';

    @ArgsType()
    export abstract class NameDto {
      @Field(() => String)
      @IsString()
      @Length(3, 100)
      @Matches(NAME_REGEX, {
        message: 'Name must not have special characters',
      })
      public name: string;
    }
    ```
- `username.dto.ts`:
    ```ts
    import { ArgsType, Field } from '@nestjs/graphql';
    import { IsString, Length, Matches } from 'class-validator';
    import { SLUG_REGEX } from '../../common/consts/regex.const';

    @ArgsType()
    export abstract class UsernameDto {
      @Field(() => String)
      @IsString()
      @Length(3, 106)
      @Matches(SLUG_REGEX, {
        message: 'Username must be a valid slug',
      })
      public username: string;
    }
    ```

**Resolver**

To create the `users.resolver.ts` run the following command:

```bash
$ nest g r users
```

Now add a return type for our resolver and import the user service:

```ts
import { Resolver } from '@nestjs/graphql';
import { UserEntity } from './entities/user.entity';
import { UsersService } from './users.service';

@Resolver(() => UserEntity)
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}
}
```

On the resolver we need to add two queries and four mutations.

_Queries_:

- User by ID:
    ```ts
    import {
      Args,
      Query,
      // ...
    } from '@nestjs/graphql';
    import { Public } from '../auth/decorators/public.decorator';
    import { UserEntity } from './entities/user.entity';
    // ...

    @Resolver(() => UserEntity)
    export class UsersResolver {
      // ...

      @Public()
      @Query(() => UserEntity)
      public async userById(@Args() idDto: IdDto): Promise<UserEntity> {
        return await this.usersService.findOneById(idDto.id);
      }

    }
    ```
- User by username:
    ```ts
    import {
      Args,
      Query,
      // ...
    } from '@nestjs/graphql';
    import { Public } from '../auth/decorators/public.decorator';
    import { UserEntity } from './entities/user.entity';
    // ...

    @Resolver(() => UserEntity)
    export class UsersResolver {
      // ...

      @Public()
      @Query(() => UserEntity)
      public async userByUsername(
        @Args() usernameDto: UsernameDto,
      ): Promise<UserEntity> {
        return await this.usersService.findOneByUsername(usernameDto.username);
      }

    }
    ```

_Mutations_

For mutations is a bit more complicated, specially because of delete. First we need to change the cookiePath to the GraphQL path '/api/graphl' so the cookie is blacklisted when the user deletes his or her account.

```ts
import { ConfigService } from '@nestjs/config';
// ...

@Resolver(() => UserEntity)
export class UsersResolver {
  private readonly cookiePath = '/api/graphql';
  private readonly cookieName: string;

  constructor(
    private readonly usersService: UsersService,
    private readonly configService: ConfigService,
  ) {
    this.cookieName = this.configService.get<string>('REFRESH_COOKIE');
  }

  // ...
}
```

Finally, start adding the delete mutation:

```ts
// ...
import {
  // ...
  Context,
  Mutation,
  // ...
} from '@nestjs/graphql';
import { MessageType } from '../common/entities/gql/message.type';
import { PasswordDto } from './dtos/password.dto';
// ...

@Resolver(() => UserEntity)
export class UsersResolver {
  // ...

  @Mutation(() => MessageType)
  public async deleteUser(
    @Context('res') res: Response,
    @CurrentUser() id: number,
    @Args() passwordDto: PasswordDto,
  ): Promise<MessageType> {
    await this.usersService.delete(id, passwordDto);
    res.clearCookie(this.cookieName, { path: this.cookiePath });
    return new MessageType('User deleted successfully');
  }
}
```

As you can see we use the context to pass the `Response` object into our mutation.

Next, create an `update` mutation for each editable parameter:

- Update Email:
    ```ts
    // ...
    import { ChangeEmailDto } from './dtos/change-email.dto';
    // ...

    @Resolver(() => UserEntity)
    export class UsersResolver {
      // ...

      @Mutation(() => UserEntity)
      public async updateUserEmail(
        @CurrentUser() id: number,
        @Args() changeEmailDto: ChangeEmailDto,
      ): Promise<UserEntity> {
        return await this.usersService.updateEmail(id, changeEmailDto);
      }
    }
    ```
- Update Name:
    ```ts
    // ...
    import { NameDto } from './dtos/name.dto';
    // ...

    @Resolver(() => UserEntity)
    export class UsersResolver {
      // ...

      @Mutation(() => UserEntity)
      public async updateUserName(
        @CurrentUser() id: number,
        @Args() nameDto: NameDto,
      ): Promise<UserEntity> {
        return await this.usersService.updateName(id, nameDto.name);
      }
    }
    ```
- Update Username:
    ```ts
    // ...
    import { UsernameDto } from './dtos/username.dto';
    // ...

    @Resolver(() => UserEntity)
    export class UsersResolver {
      // ...

      @Mutation(() => UserEntity)
      public async updateUserUsername(
        @CurrentUser() id: number,
        @Args() usernameDto: UsernameDto,
      ): Promise<UserEntity> {
        return await this.usersService.updateUsername(id, usernameDto.username);
      }
    }
    ```

Finally since we are using micro-services, we need to add a `@ResolveReference` decorator so other sub-graphs can have access to the `UserEntity`:

```ts
// ...
import {
  // ...
  ResolveReference,
} from '@nestjs/graphql';
import { IFederatedInstance } from '../common/interfaces/federated-instance.interface';

@Resolver(() => UserEntity)
export class UsersResolver {
  // ...

  @ResolveReference()
  public async resolveReference(
    reference: IFederatedInstance<'User'>,
  ): Promise<UserEntity> {
    return this.usersService.findOneById(reference.id);
  }
}
```

### Auth

**Entities**

Create a `gql` directory and add an `auth.type.ts` type:

```ts
import { Field, ObjectType } from '@nestjs/graphql';
import { UserEntity } from '../../../users/entities/user.entity';
import { IUser } from '../../../users/interfaces/user.interface';

@ObjectType('Auth')
export abstract class AuthType {
  @Field(() => UserEntity)
  public user: IUser;

  @Field(() => String)
  public accessToken: string;
}
```

**DTOs**

Start by updating the:

- Email DTO:
    ```ts
    import { ArgsType, Field } from '@nestjs/graphql';
    import { ApiProperty } from '@nestjs/swagger';
    import { IsEmail, IsString, Length } from 'class-validator';

    @ArgsType()
    export abstract class EmailDto {
      @Field(() => String)
      @ApiProperty({
        description: 'The email of the user',
        example: 'someone@gmail.com',
        minLength: 5,
        maxLength: 255,
        type: String,
      })
      @IsString()
      @IsEmail()
      @Length(5, 255)
      public email: string;
    }
    ```
- Passwords DTO:
    ```ts
    import { ArgsType, Field, InputType } from '@nestjs/graphql';
    import { ApiProperty } from '@nestjs/swagger';
    import { IsString, Length, Matches, MinLength } from 'class-validator';
    import { PASSWORD_REGEX } from '../../common/consts/regex.const';

    @InputType({ isAbstract: true })
    @ArgsType()
    export abstract class PasswordsDto {
      @Field(() => String)
      @ApiProperty({
        description: 'New password',
        minLength: 8,
        maxLength: 35,
        type: String,
      })
      @IsString()
      @Length(8, 35)
      @Matches(PASSWORD_REGEX, {
        message:
          'Password requires a lowercase letter, an uppercase letter, and a number or symbol',
      })
      public password1!: string;

      @Field(() => String)
      @ApiProperty({
        description: 'Password confirmation',
        minLength: 1,
        type: String,
      })
      @IsString()
      @MinLength(1)
      public password2!: string;
    }
    ```

Since we are going to use the `AuthResolver` to create new users and do updates that require complex inputs (e.g. updating the Password). We need to create an `inputs` folder and add the following inputs:

- Sign In Input:
    ```ts
    import { Field, InputType } from '@nestjs/graphql';
    import { IsString, Length, MinLength } from 'class-validator';

    @InputType('SignInInput')
    export abstract class SignInInput {
      @Field(() => String)
      @IsString()
      @Length(3, 255)
      public emailOrUsername!: string;

      @Field(() => String)
      @IsString()
      @MinLength(1)
      public password!: string;
    }
    ```
- Sign Up Input:
    ```ts
    import { Field, InputType } from '@nestjs/graphql';
    import { IsEmail, IsString, Length, Matches } from 'class-validator';
    import { NAME_REGEX } from '../../common/consts/regex.const';
    import { PasswordsDto } from '../dtos/passwords.dto';

    @InputType('SignUpInput')
    export abstract class SignUpInput extends PasswordsDto {
      @Field(() => String)
      @IsString()
      @Length(3, 100, {
        message: 'Name has to be between 3 and 100 characters.',
      })
      @Matches(NAME_REGEX, {
        message: 'Name can not contain special characters.',
      })
      public name!: string;

      @Field(() => String)
      @IsString()
      @IsEmail()
      @Length(5, 255)
      public email!: string;
    }
    ```
- Reset Password Input:
    ```ts
    import { Field, InputType } from '@nestjs/graphql';
    import { IsJWT, IsString } from 'class-validator';
    import { PasswordsDto } from '../dtos/passwords.dto';

    @InputType('ResetPasswordInput')
    export abstract class ResetPasswordInput extends PasswordsDto {
      @Field(() => String)
      @IsString()
      @IsJWT()
      public resetToken!: string;
    }
    ```
- Update Password Input:
    ```ts
    import { Field, InputType } from '@nestjs/graphql';
    import { IsString, MinLength } from 'class-validator';
    import { PasswordsDto } from '../dtos/passwords.dto';

    @InputType('UpdatePasswordInput')
    export abstract class UpdatePasswordInput extends PasswordsDto {
      @Field(() => String)
      @IsString()
      @MinLength(1)
      public password!: string;
    }
    ```

**Decorators**

The decorators need to be updated to accept GraphQL requests:

- Current User Decorator:
    ```ts
    import { createParamDecorator, ExecutionContext } from '@nestjs/common';
    import { GqlExecutionContext } from '@nestjs/graphql';
    import { Request } from 'express-serve-static-core';
    import { IContext } from '../../config/interfaces/context.interface';

    export const CurrentUser = createParamDecorator(
      (_, context: ExecutionContext): number | undefined => {
        if (context.getType() === 'http') {
          return context.switchToHttp().getRequest<Request>()?.user;
        }

        return GqlExecutionContext.create(context).getContext<IContext>().req?.user;
      },
    );
    ```
- Origin Decorator:
    ```ts
    import { createParamDecorator, ExecutionContext } from '@nestjs/common';
    import { GqlExecutionContext } from '@nestjs/graphql';
    import { Request } from 'express-serve-static-core';
    import { IContext } from '../../config/interfaces/context.interface';

    export const Origin = createParamDecorator(
      (_, context: ExecutionContext): string | undefined => {
        if (context.getType() === 'http') {
          return context.switchToHttp().getRequest<Request>().headers?.origin;
        }

        return GqlExecutionContext.create(context).getContext<IContext>().req
      .headers?.origin;
      },
    );
    ```

You can determine if you are using GraphQL or HTTP simply by using the context.getType() method.

**Guards**

The Auth Guard right now only accepts REST requests, so lets change that:

```ts
import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { isJWT } from 'class-validator';
import { Request } from 'express-serve-static-core';
import { isNull, isUndefined } from '../../common/utils/validation.util';
import { TokenTypeEnum } from '../../jwt/enums/token-type.enum';
import { JwtService } from '../../jwt/jwt.service';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly jwtService: JwtService,
  ) {}

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    const activate = await this.setHttpHeader(
      context.switchToHttp().getRequest(),
      isPublic,
    );

    if (!activate) {
      throw new UnauthorizedException();
    }

    return activate;
  }

  /**
   * Sets HTTP Header
   *
   * Checks if the header has a valid Bearer token, validates it and sets the User ID as the user.
   */
  private async setHttpHeader(
    req: Request,
    isPublic: boolean,
  ): Promise<boolean> {
    const auth = req.headers?.authorization;

    if (isUndefined(auth) || isNull(auth) || auth.length === 0) {
      return isPublic;
    }

    const authArr = auth.split(' ');
    const bearer = authArr[0];
    const token = authArr[1];

    if (isUndefined(bearer) || isNull(bearer) || bearer !== 'Bearer') {
      return isPublic;
    }
    if (isUndefined(token) || isNull(token) || !isJWT(token)) {
      return isPublic;
    }

    try {
      const { id } = await this.jwtService.verifyToken(
        token,
        TokenTypeEnum.ACCESS,
      );
      req.user = id;
      return true;
    } catch (_) {
      return isPublic;
    }
  }
}
```

**Resolver**

The `AuthResolver` and `AuthController` are not that different, they do exactly the same thing, just change the routes to Queries and Mutations.

Start by creating the resolver:

```bash
$ nest g r auth
```

And copy the `AuthController` constructor to the resolver:

```ts
import { ConfigService } from '@nestjs/config';
import { Resolver } from '@nestjs/graphql';
import { UsersService } from '../users/users.service';
import { AuthService } from './auth.service';

@Resolver(() => AuthType)
export class AuthResolver {
  private readonly cookiePath = '/api/graphql';
  private readonly cookieName: string;
  private readonly refreshTime: number;
  private readonly testing: boolean;

  constructor(
    private readonly authService: AuthService,
    private readonly usersService: UsersService,
    private readonly configService: ConfigService,
  ) {
    this.cookieName = this.configService.get<string>('REFRESH_COOKIE');
    this.refreshTime = this.configService.get<number>('jwt.refresh.time');
    this.testing = this.configService.get<boolean>('testing');
  }
}
```

As you can see now our cookie path is "/api/graphql", so create a new `saveRefreshCookie`:

```ts
// ...
import { Response } from 'express-serve-static-core';

@Resolver(() => AuthType)
export class AuthResolver {
  // ...

  private saveRefreshCookie(res: Response, refreshToken: string): void {
    res.cookie(this.cookieName, refreshToken, {
      secure: !this.testing,
      httpOnly: true,
      signed: true,
      path: this.cookiePath,
      expires: new Date(Date.now() + this.refreshTime * 1000),
    });
  }
}
```

And copy the private `refreshTokenFromReq` method as well:

```ts
import { UnauthorizedException } from '@nestjs/common';
// ...
import { Request, Response } from 'express-serve-static-core';

@Resolver(() => AuthType)
export class AuthResolver {
  // ...

  private refreshTokenFromReq(req: Request): string {
    const token: string | undefined = req.signedCookies[this.cookieName];

    if (isUndefined(token)) {
      throw new UnauthorizedException();
    }

    return token;
  }
}
```

Taking in mind that all the routes are the same but now we use Mutations and Queries, hence we get the following mutations:

```ts
import { UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
import { Request, Response } from 'express-serve-static-core';
import { MessageType } from '../common/entities/gql/message.type';
import { IMessage } from '../common/interfaces/message.interface';
import { isUndefined } from '../common/utils/validation.util';
import { UsersService } from '../users/users.service';
import { AuthService } from './auth.service';
import { CurrentUser } from './decorators/current-user.decorator';
import { Origin } from './decorators/origin.decorator';
import { Public } from './decorators/public.decorator';
import { ConfirmEmailDto } from './dtos/confirm-email.dto';
import { EmailDto } from './dtos/email.dto';
import { AuthType } from './entities/gql/auth.type';
import { ResetPasswordInput } from './inputs/reset-password.input';
import { SignInInput } from './inputs/sign-in.input';
import { SignUpInput } from './inputs/sign-up.input';
import { UpdatePasswordInput } from './inputs/update-password.input';

@Resolver(() => AuthType)
export class AuthResolver {
  // ...

  @Public()
  @Mutation(() => MessageType)
  public async signUp(
    @Origin() origin: string | undefined,
    @Args('input') signUpInput: SignUpInput,
  ): Promise<IMessage> {
    return await this.authService.signUp(signUpInput, origin);
  }

  @Public()
  @Mutation(() => AuthType)
  public async signIn(
    @Context('res') res: Response,
    @Origin() origin: string | undefined,
    @Args('input') signInInput: SignInInput,
  ): Promise<AuthType> {
    const { refreshToken, ...authType } = await this.authService.signIn(
      signInInput,
      origin,
    );
    this.saveRefreshCookie(res, refreshToken);
    return authType;
  }

  @Public()
  @Mutation(() => AuthType)
  public async refreshAccess(
    @Context('req') req: Request,
    @Context('res') res: Response,
  ): Promise<AuthType> {
    const token = this.refreshTokenFromReq(req);
    const { refreshToken, ...authType } =
      await this.authService.refreshTokenAccess(token, req.headers.origin);
    this.saveRefreshCookie(res, refreshToken);
    return authType;
  }

  @Mutation(() => MessageType)
  public async logout(
    @Context('req') req: Request,
    @Context('res') res: Response,
  ): Promise<IMessage> {
    const token = this.refreshTokenFromReq(req);
    res.clearCookie(this.cookieName);
    return this.authService.logout(token);
  }

  @Public()
  @Mutation(() => AuthType)
  public async confirmEmail(
    @Origin() origin: string | undefined,
    @Args() confirmEmailDto: ConfirmEmailDto,
    @Context('res') res: Response,
  ) {
    const { refreshToken, ...authType } = await this.authService.confirmEmail(
      confirmEmailDto,
      origin,
    );
    this.saveRefreshCookie(res, refreshToken);
    return authType;
  }

  @Public()
  @Mutation(() => MessageType)
  public async forgotPassword(
    @Origin() origin: string | undefined,
    @Args() emailDto: EmailDto,
  ): Promise<IMessage> {
    return await this.authService.resetPasswordEmail(emailDto, origin);
  }

  @Public()
  @Mutation(() => MessageType)
  public async resetPassword(
    @Args('input') input: ResetPasswordInput,
  ): Promise<IMessage> {
    return await this.authService.resetPassword(input);
  }

  @Mutation(() => AuthType)
  public async updatePassword(
    @CurrentUser() id: number,
    @Origin() origin: string | undefined,
    @Args('input') input: UpdatePasswordInput,
    @Context('res') res: Response,
  ): Promise<AuthType> {
    const { refreshToken, ...authType } = await this.authService.updatePassword(
      id,
      input,
      origin,
    );
    this.saveRefreshCookie(res, refreshToken);
    return authType;
  }

  //...
}
```

And the following `me` query:

```ts
// ...
import { Query } from '@nestjs/graphql';
// ...
import { UserEntity } from '../users/entities/user.entity';
// ...

@Resolver(() => AuthType)
export class AuthResolver {
  // ...

  @Query(() => UserEntity)
  public async me(@CurrentUser() id: number): Promise<UserEntity> {
    return await this.usersService.findOneById(id);
  }

  // ...
}
```

## Conclusion

With this you are now able to implement (although again, you should not) a Local OAuth API in GraphQL.

The repository for the code can be found [here](https://github.com/tugascript/nestjs-oauth/tree/apollo-graphql-api).


## About the Author

Hey there! I am Afonso Barracha, your go-to econometrician who found his way into the world of back-end development with a soft spot for GraphQL. If you enjoyed reading this article, why not show some love by [buying me a coffee](https://www.buymeacoffee.com/barracha)?

Lately, I have been diving deep into more advanced subjects. As a result, I have switched from sharing my thoughts every week to posting once or twice a month. This way, I can make sure to bring you the highest quality content possible.

Do not miss out on any of my latest articles – follow me here on [dev](https://dev.to/tugascript) and [LinkedIn](https://www.linkedin.com/in/afonso-barracha/) to stay updated. I would be thrilled to welcome you to our ever-growing community! See you around!