# API Key Authentication & Authorization

With the [API Key Authentication Policy](../policies/api-key-inbound.mdx)
configured on your API routes you can build additional policies that run after
the API Key Authentication policy to perform additional checks or authorization
on the consumer.

## Request User Object

After each successful authentication the policy will set the `request.user`
object. The name of the API Key consumer is set to the `request.user.sub`
property. Any `metadata` attached to the consumer is set to the
`request.user.data` property. The interface of `request.user` is shown below.

```ts
/**
 * The User object set by the API Key Authentication policy
 */
interface User {
  /**
   * The name of the API Key consumer
   */
  sub: string;
  /**
   * The metadata attached to the API Key consumer
   */
  data: any;
}
```

So if you created a consumer with the following configuration:

```json
{
  "name": "my-consumer",
  "metadata": {
    "companyId": 12345,
    "plan": "gold"
  }
}
```

The request object would be the following:

```ts
context.log.debug(request.user);
// Outputs:
// {
//   sub: "my-consumer",
//   data: {
//     companyId: 12345,
//     plan: "gold"
//   }
// }
```

:::note

One question you might have is why is the `request.user` object not the same
shape as the API Key Consumer object. for example why doesn't it has
`request.user.name` and `request.user.metadata` properties.

The reason is because the `request.user` object is reused by many different
kinds of authentication policies and they all conform to the same interface with
`sub` and `data`.

:::

## Using Consumer Data in Code

It's possible to write additional policies that run after the API Key
Authentication policy that perform further gating or authorization of the
request based on the data set in the consumer.

For example, you could gate access to a feature by checking for the `plan` value
stored in metadata (exposed via `request.user.data.plan`).

```ts
async function (request: ZuploRequest, context: ZuploContext) {
  if (request.user?.data.plan !== "gold") {
    return new Response("You need to upgrade your plan", {
      status: 403
    });
  }
  return new Response("you have the gold plan!");
}
```

The `metadata` could also be used to route requests to dedicated customer
services.

```ts
async function (request: ZuploRequest, context: ZuploContext) {
  const { customerId } = request.user.data;
  return fetch(`https://${customerId}.customers.example.com/`
}
```

The `request.user` object can be used in both
[handlers](../handlers/custom-handler.mdx) and
[policies](../policies/custom-code-inbound.mdx)

If you had a simple [function handler](../handlers/custom-handler.mdx) as
follows, it would return a `request.user` object to your route if the API Key is
successfully authenticated:

```ts
async function (request: ZuploRequest, context: ZuploContext) {
  // auto-serialize the user object and return it as JSON
  return request.user;
}
```

Would send the following response.

```json
{
  "sub": "my-consumer",
  "data": {
    "companyId": 12345,
    "plan": "gold"
  }
}
```

## Testing API Key Authentication

When running tests there are several ways to handle API Key authentication. The
following strategies cover testing with API Key authentication both locally and
in deployed environments.

### Testing locally

When running API Key Authentication locally, if you
[link the project](../cli/link.mdx) to a project, the same API Key bucket is
shared by both your development (working copy) environment and local
development.

### Setting the API Key bucket name

Either locally or in CI/CD you can specify any API Key bucket on the
[API Key Authentication](../policies/api-key-inbound.mdx) policy by setting the
`bucketName` property. This allows using a consistent API Key bucket that is set
up with consumers as required for testing. You can use the
[Zuplo Developer API](https://dev.zuplo.com) to
[create and manage buckets](./api-key-management.mdx), consumers, keys, and
more.

### Selectively disabling

:::danger

Be extremely careful using this strategy. If configured incorrectly this could
leave your API open to unauthorized access.

:::

Another option is to disable authentication on endpoints for testing purposes.
One way of doing this is to configure the
[API Key Authentication](../policies/api-key-inbound.mdx) policy to allow
unauthenticated requests through. This can be done by setting
`allowUnauthenticatedRequests` to true.

In order to enforce authentication with this setting disabled, you can create a
policy that comes after that selectively enforces auth based on some condition.

For example, an environment variable flag could be used to disable auth with the
following policy.

```ts
import {
  ZuploContext,
  ZuploRequest,
  environment,
  HttpProblems,
} from "@zuplo/runtime";

export default async function enforceAuth(
  request: ZuploRequest,
  context: ZuploContext,
) {
  if (environment.DISABLE_AUTH === "AUTH_DISABLED") {
    return request;
  }

  if (!request.user) {
    return HttpProblems.unauthorized(request, context);
  }

  return request;
}
```
