# User authentication

Now that we can get the links saved, let's add a layer of security by using [jsonwebtoken](https://jwt.io/introduction/).

There are 2 important things you should always do:

* Hash password before storing them in the database. If you leak them, you won't expose the actual passwords.

![Source: http://www.commitstrip.com/en/2018/03/22/basic-functionality](/files/-LqHA4l-m1iGHTUTNHxS)

* Only store enough information in the JWT to identify the user. Anyone can read its content.

Try it yourself, take this token and paste it in [jwt.io](https://jwt.io).

```
eyJzZWNyZXQiOiIxMjMiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.OZ-CLQhIwvruoziWza1XEB4PwCNfMnlcljRt14_Zckw
```

![Anyone can read the content of a token](/files/-LqBwTmf73FJztKB0XvD)

{% hint style="danger" %}
Never store password in plain text.
{% endhint %}

{% hint style="danger" %}
Never put sensitive data in a JWT.
{% endhint %}

Install the required dependencies:

```
$ yarn add jsonwebtoken bcryptjs @types/jsonwebtoken @types/bcryptjs
```

We first going to create a GraphQL input to structure the data we receive:

{% code title="resolvers/UserAuthInput.ts" %}

```typescript
import { InputType, Field } from "type-graphql";

@InputType()
class UserAuthInput {
  @Field()
  emailAddress: string;

  @Field()
  password: string;
}

export { UserAuthInput };
```

{% endcode %}

Update the `User` entity to return the `jwt` with:

{% code title="entities/User.ts" %}

```typescript
class User extends Model<User> {
  // ...
  @Field({ nullable: true })
  jwt?: string;
}
```

{% endcode %}

Add your `jwtSecret` to the shared config:

```typescript
export interface SharedConfig {
  jwtSecret: string;
  // ...
}

export const sharedConfig: SharedConfig = {
  // ...
  jwtSecret: process.env.JWT_SECRET || "",
  // ...
};

```

And finally create the resolver:

```typescript
import { Resolver, Query, Mutation, Arg } from "type-graphql";
import * as bcrypt from "bcryptjs";
import { User } from "../../../../entities";
import { getUserEntity } from "../../../../containers";
import { UserAuthInput } from "./UserAuthInput";
import * as jwt from "jsonwebtoken";
import { config } from "../../../../config";

@Resolver(of => User)
class UserResolver {
  private readonly _UserEntity = getUserEntity();
  @Mutation(returns => User)
  async signIn(@Arg("input")
  {
    emailAddress,
    password
  }: UserAuthInput): Promise<User> {
    const user = await this._UserEntity.findOne({ where: { emailAddress } });
    if (!user) {
      throw new Error("User not found");
    }
    if (!bcrypt.compare(password, user.password)) {
      throw new Error("Invalid password");
    }
    user.jwt = jwt.sign({ id: user.id }, config.jwtSecret);
    return user;
  }
  @Mutation(returns => User)
  async signUp(@Arg("input")
  {
    emailAddress,
    password
  }: UserAuthInput): Promise<User> {
    if (await this._UserEntity.findOne({ where: { emailAddress } })) {
      throw new Error("User with this email address already exists.");
    }
    const salt = bcrypt.genSaltSync(10);
    try {
      const user = await this._UserEntity.create({
        emailAddress,
        password: bcrypt.hashSync(password, salt)
      });
      user.jwt = jwt.sign({ id: user.id }, config.jwtSecret);
      return user;
    } catch (error) {
      console.error("Cannot create user.", error);
      throw error;
    }
  }
}

export { UserResolver };
```

### Writing tests

Update `setupTests.ts` to require the environment variables:

{% code title="src/setupTests.ts" %}

```typescript
require('dotenv').config()
```

{% endcode %}

When building the schema, GraphQL will complain if we don't have any queries. For this reason, let's create a `Health` resolver and add it to our schema when testing.

{% code title="resolvers/Health.ts" %}

```typescript
import { Int, ObjectType, Query, Resolver, Field } from "type-graphql";

@ObjectType()
class Health {
  @Field(() => Int)
  ok: number;
}

@Resolver(of => Health)
class HealthResolver {
  @Query(returns => Health)
  async health(): Promise<Health> {
    return { ok: 1 };
  }
}

export { HealthResolver };
```

{% endcode %}

Finally, test the resolver:

```typescript
import { ApolloServerBase, gql } from "apollo-server-core";
import {
  createTestClient
} from "apollo-server-testing";
import { buildSchema } from "type-graphql";
import { UserResolver } from "./User";
import { setUserEntity } from "../../../../containers";
import { User } from "../../../../entities";
import { HealthResolver } from "..";
import * as jwt from 'jsonwebtoken'

describe("routers/GraphQL/Resolvers/User", () => {
  describe("mutation/signIn", () => {
    it("should return a jwt for this user", async () => {
      const schema = await buildSchema({
        resolvers: [HealthResolver, UserResolver]
      });
      const server = new ApolloServerBase({ schema });
      const testClient = createTestClient(server);
      const user = { emailAddress: "test1@mock.com", password: "123" };
      const findOne = jest.fn();
      findOne.mockResolvedValue({ ...user, id: 'userid', });
      setUserEntity(({ findOne } as unknown) as typeof User);

      const { data, errors } = await testClient.mutate({
        mutation: gql`
          mutation signIn($input: UserAuthInput!) {
            signIn(input: $input) {
              id
              emailAddress
              jwt
            }
          }
        `,
        variables: { input: user }
      });
      expect(errors).toBeUndefined();
      expect(data!.signIn).toMatchObject({ id: "userid", emailAddress: user.emailAddress })
      expect(jwt.decode(data!.signIn.jwt)).toMatchObject({ id: 'userid' })
    });
  });
  describe("mutation/signUp", () => {
    it("should create a user and return the jwt", async () => {
      const schema = await buildSchema({
        resolvers: [HealthResolver, UserResolver]
      });
      const server = new ApolloServerBase({ schema });
      const testClient = createTestClient(server);
      const user = { emailAddress: "test2@mock.com", password: "123" };
      const findOne = jest.fn();
      const create = jest.fn();
      create.mockResolvedValue({ id: "userid", ...user });
      findOne.mockResolvedValue(null);
      setUserEntity(({ findOne, create } as unknown) as typeof User);

      const { data, errors } = await testClient.mutate({
        mutation: gql`
          mutation signUp($input: UserAuthInput!) {
            signUp(input: $input) {
              id
              emailAddress
              jwt
            }
          }
        `,
        variables: { input: user }
      });
      expect(errors).toBeUndefined();
      expect(data!.signUp).toMatchObject({ id: "userid", emailAddress: user.emailAddress })
      expect(jwt.decode(data!.signUp.jwt)).toMatchObject({ id: 'userid' })
    });
  });
});
```

{% hint style="info" %}
Tests are running in parallel. We recreate the server for each test to avoid conflicts.
{% endhint %}

### Adding security to the links

How do we know which user just sent a request? GraphQL uses `context` to pass down information to all resolvers and we will use the `Authorization` header to send the JWT which each request.

### Populating the GraphQL context

{% code title="routers/GraphQL/index.ts" %}

```typescript
import "reflect-metadata";
import { ApolloServer } from "apollo-server-express";

import { Router } from "express";

import { buildSchema } from "type-graphql";
import * as resolvers from "./resolvers";

import * as jwt from "jsonwebtoken";
import { config } from "../../config";
import { User } from "../../entities";

export interface GraphQLContext {
  user?: User;
}

export class GraphQLRouter {
  public readonly router = Router();
  constructor() {
    this.buildSchema();
  }
  async buildSchema() {
    const schema = await buildSchema({
      resolvers: Object.values(resolvers),
      validate: false
    });
    const apolloServer = new ApolloServer({
      schema,
      context: ({ req }): GraphQLContext => {
        if (!req.headers.authorization) {
          return {};
        }
        if (req.headers.authorization.slice(0, 7).trim() !== "Bearer") {
          return {};
        }
        try {
          return {
            user: jwt.verify(
              req.headers.authorization.slice(7),
              config.jwtSecret
            ) as User
          };
        } catch (error) {
          return {};
        }
      }
    });
    apolloServer.applyMiddleware({ app: this.router, path: "/" });
  }
}
```

{% endcode %}

### Testing

We can mock the context when creating the test server:

```typescript
const server = new ApolloServerBase({
  schema,
  context: { user: { id: "userid" } }
});
```

### Getting the context from resolvers

Add the argument `@Ctx() context` to a resolver:

```typescript
@Mutation(returns => Link)
  async addLink(
    @Arg("input") input: LinkInput,
    @Ctx() context: GraphQLContext
  ): Promise<Link> {
    if (!context.user) {
      throw new Error("Unauthorised");
    }
    const link = await this._LinkEntity.create({
      ...input,
      userId: context.user.id
    });
    return link;
  }
```

### Adding headers in the playground

Use the tab `HTTP HEADERS` at the bottom left.

![](/files/-LqCKKanD3SmjCLwwEtE)

### Conclusion

The backend is now ready to interact with the frontend. We can interact safely with the database and we have enough to implement anything we might need.

A lot is going on in this chapter. Have look at this [Pull Request](https://github.com/florianherrengt/book-code/pull/2) to see all the changes with comments.

{% hint style="info" %}
[user-auth](https://github.com/florianherrengt/book-code/tree/user-auth) branch available on GitHub.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tutorial.specian.co.uk/backend/user-authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
