# Adding Authentication

OpenAPI provides the ability to specify authentication in a few forms, which can be handled by creating Authentication Controllers.

## Defining Authenticators

Authenticator controllers are created with the `@Authenticator` decorator on a class that is passed as a controller along with your other controllers. This decorator takes the name of your security scheme, as well as the OpenAPI specification of the scheme.

This class should implement the `AuthenticationController` interface, providing an `authenticate` function to process the request for authentication requirements. This function will get called with the value of the authentication request, the scopes the endpoint requires, and a context providing the request, response, and detailed information about the endpoint being called. It can be synchronous, or asynchronous, and have the following behavior:

* If authentication succeeds, it should return 'true', or an object containing details of the authentication which may be passed to the request method handler as input.
* If authentication fails, the function may:
  * return false
  * throw an error matching the HttpError interface of the `http-errors` library.

```typescript
import {
  Authenticator,
  AuthenticationController,
  OperationRequestContext,
  HTTPBearerAuthenticationCredentials,
} from "@simply-openapi/controllers";
import { Unauthorized } from "http-errors";

@Authenticator("myAuth", {
  type: "http",
  scheme: "bearer",
})
class MyAuthenticator implements AuthenticationController {
  async authenticate(
    value: HTTPBearerAuthenticationCredentials,
    scopes: string[],
    ctx: OperationRequestContext,
  ) {
    const user = await decodeBearerToken(value);
    if (!user) {
      // Returning false is the equivalent of throwing
      // an Unauthorized http-error with the default message.
      return false;
    }

    // It is your responsibility to check the scopes
    // in the authentication request.
    if (!scopes.every((cope) => user.scopes.includes(scope))) {
      // Thrown errors will be recorded.
      // If all authentication options reject the request,
      // the last thrown http-error will be used.
      throw new Unauthorized("Insufficient permissions");
    }

    // If authentication succeeds, you can return a value that will
    // be passed to handler method arguments decorated to recieve
    // the authenticated user.
    // Any non-falsy value will be interpreted as a successful auth.
    return user;
  }
}

// When creating the spec, pass the authenticator to the list of controllers.
const openApiSpec = createOpenAPIFromControllers(
  { title: "My App", version: "1.0.0" },
  [MyAuthenticator, ...controllers],
);
```

### HTTP Basic authentication

The [HTTP Basic](https://swagger.io/docs/specification/authentication/basic-authentication/) security scheme takes an encoded (not encrypted) username and password from the `Authorization` header. @simply-openapi/controllers will decode the credentials and pass your authenticator an object containing the `username` and `password` properties.

```typescript
import {
  Authenticator,
  AuthenticationController,
  HttpBasicAuthenticationCredentials,
  OperationRequestContext,
} from "@simply-openapi/controllers";

@Authenticator("myAuth", {
  type: "http",
  scheme: "basic",
})
class MyAuthenticator implements AuthenticationController {
  async authenticate(
    value: HttpBasicAuthenticationCredentials,
    scopes: string[],
    ctx: OperationRequestContext,
  ) {
    const user = await getUserByUsername(value.username);
    if (!checkPassword(value.password, user.hashedPassword)) {
      return false;
    }

    if (!scopes.every((scope) => user.scopes.includes(scope))) {
      return false;
    }

    return user;
  }
}
```

### HTTP Bearer authentication

The [HTTP Bearer](https://swagger.io/docs/specification/authentication/bearer-authentication/) security scheme expects authentication in an `Authorization` header, prefixed with `Bearer` . @simply-openapi/controllers will validate the presense of this header and the Bearer prefix, before extracting the payload (everything after `Bearer`) and passing it as the value to your authentication method.

Note that the `bearerFormat` OpenAPI property is descriptive only; the value indicates no special processing instructions for OpenAPI and is not interpreted by this library.

```typescript
import {
  Authenticator,
  AuthenticationController,
  HttpBearerAuthenticationCredentials,
  OperationRequestContext,
} from "@simply-openapi/controllers";

@Authenticator("myAuth", {
  type: "http",
  scheme: "bearer",
  bearerFormat: "JWT",
})
class MyAuthenticator implements AuthenticationController {
  async authenticate(
    value: HttpBearerAuthenticationCredentials,
    scopes: string[],
    ctx: OperationRequestContext,
  ) {
    const user = await decodeJwt(value);

    if (!scopes.every((scope) => user.scopes.includes(scope))) {
      return false;
    }

    return user;
  }
}
```

## Requiring Authentication

Authentication can be specified in 3 locations:

* At the top level of your OpenAPI specification in the `security` property, to get applied to every endpoint
* Per controller with the `@RequireAuthentication` decorator, to be applied only to methods in that controller
* Per method with the `@RequireAuthentication` decorator, to be applied only to that method.

Per the OpenAPI spec, any authentication specified on a method or controller will take precedence over any authentication specified at the root level of your OpenAPI specification.

### Requiring authentication for all endpoints

The OpenAPI specification provides the `security` top level property, which specifies default security settings for all methods that do not themselves specify a security property.

This property is obeyed by the library, so setting it in your specification will appropriately gate all methods behind it, excepting methods that override it.

To pass in this property, use the `addendOpenAPIFromControllers` method to generate the final OpenAPI schema given your starting fragment.

```typescript
import { OpenAPI } from "openapi3-ts/oai31";
import {
  Authenticator,
  AuthenticationController,
  HttpBearerAuthenticationCredentials,
  OperationRequestContext,
  addendOpenAPIFromControllers,
  createRouterFromSpec
} from "@simply-openapi/controllers";

// Source the rest of your controllers that contain your endpoints.
import controllers from "./controllers";

// Define and implement a security strategy.
// This security scheme will be injected into the produced OpenAPI.
@Authenticator("myAuth", {
  type: "http",
  scheme: "bearer",
})
class MyAuthenticator implements AuthenticationController {
  async authenticate(
    value: HttpBearerAuthenticationCredentials,
    scopes: string[],
    ctx: OperationRequestContext
  ) {
    ...
  }
}

const mySpecFragment: OpenAPI = {
  info: {
    title: "My App",
    version: "1.0.0"
  },
  security: [
    {
      myAuth: ["myScope"]
    }
  ]
}

const openApiSpec = addendOpenAPIFromControllers(
  mySpecFragment,
  [MyAuthenticator, ...controllers]
);
const router = createRouterFromSpec(openApiSpec);
```

### Requiring authentication on all methods of a controller

You can use the `@RequireAuthentication` decorator on a controller to annotate that all methods in that controller are gated behind the specified security scheme.

The first argument to `@RequireAuthentication` takes either a string name of the security scheme, or it can take a constructor for an AuthenticationController decorated with `@Authenticator`.

The second argument to `@RequireAuthentication` takes an array of scopes, all of which will be required to access the methods. Note that this is not handled automatically; your AuthenticationController must implement its own logic to check the given scopes against the decoded credentials.

Since OpenAPI has no concept of a controller to group the methods by, the security specification will be repeated on every method in the controller.

```typescript
import { Controller, RequireAuthentication } from "@simply-openapi/controllers";

@Controller("/widgets")
@RequireAuthentication("myAuth", ["widgets.read"])
class WidgetsController {
  ...
}
```

For convienence and to reduce duplication, `@RequireAuthentication` can be given the class constructor of an existing `@Authenticator`-marked authentication controller. This is the equivalent of passing the scheme name defined in that decorator.

You must still pass the `@Authenticator`-decorated authentication controller to the list of controllers in `createOpenAPIFromControllers` or `addendOpenAPIFromControllers` in this case.

```typescript
import {
  Authenticator,
  AuthenticationController,
  HttpBearerAuthenticationCredentials,
  OperationRequestContext,
  Controller,
  RequireAuthentication
} from "@simply-openapi/controllers";

@Authenticator("myAuth", {
  type: "http",
  scheme: "bearer",
})
class MyAuthenticator implements AuthenticationController {
  async authenticate(
    value: HttpBearerAuthenticationCredentials,
    scopes: string[],
    ctx: OperationRequestContext
  ) {
    ...
  }
}

@Controller("/widgets")
// This is the equivalent of `@RequireAuthentication("myAuth", ["widgets.read"])`
@RequireAuthentication(MyAuthenticator, ["widgets.read"])
class WidgetsController {
  ...
}
```

### Requiring authentication on a specific method

You can use the `@RequireAuthentication` decorator on a single method to annotate that the method is gated behind the specified security scheme.

The first argument to the decorator can either be a string specifying the name of the authenticator, or for convienence it may be the constructor of an authenticator class. In the case of the latter, the authenticator class must still be passed as a controller to `createOpenAPIFromControllers`, or an error will be thrown when building the spec.

```typescript
import {
  Controller,
  RequireAuthentication,
  Get,
} from "@simply-openapi/controllers";

@Controller()
class WidgetController {
  @Get("/authenticated")
  @RequireAuthentication("myAuth", ["my-scope"])
  getAuthenticated() {
    return "OK";
  }
}
```

The OpenAPI specification indicates that multiple items in the `security` array act as an `any-of` fashion; if any entry in the list matches, the request is allowed. In support of this, you may specify `@RequireAuthentication` multiple times.

* If multiple `@RequireAuthentication` decorators specify the same security scheme, the scopes will be concatenated
* If multiple `@RequireAuthentication` decorators specify different security schemes, then **any** passing security scheme will allow access. Security schemes will be checked in-order, and the first passing one will be used.

### Retrieving the authentication result

`@RequireAuthentication` can be placed directly on method arguments. This will both ensure the user is authenticated, and provide the result of the authenticator to the decorated argument.

```typescript
import {
  Controller,
  RequireAuthentication,
  Get,
} from "@simply-openapi/controllers";

@Controller()
class WidgetController {
  @Get("/authenticated")
  getAuthenticated(
    @RequireAuthentication("myAuth", ["my-scope"])
    user: MyAuthUserType,
  ) {
    console.log("Got request from", user.username);
    return "OK";
  }
}
```

In the case of multiple security schemes, multiple parameters can be decorated.

Keep in mind that security schemes are tried in order of definition, and the library will stop at the first one to return a value. Due to this, you should only ever expect exactly one argument to be provided. All authentication decorators that were not used will have a value of `undefined`.

```typescript
import {
  Controller,
  RequireAuthentication,
  Get,
} from "@simply-openapi/controllers";
import { Unauthorized } from "http-errors";

@Controller()
class WidgetController {
  @Get("/authenticated")
  getAuthenticated(
    @RequireAuthentication("myFirstAuth", [])
    firstAuthUser: FirstAuthUserType | undefined,
    @RequireAuthentication("mySecondAuth", [])
    secondAuthUser: SecondAuthUserType | undefined,
  ) {
    if (firstAuthUser) {
      console.log(
        "Got a request covered by the first authentication:",
        firstAuthUser.id,
      );
    } else if (secondAuthUser) {
      console.log(
        "Got a request covered by the second authentication:",
        secondAuthUser.id,
      );
    } else {
      // This branch will theoretically never be hit,
      // as your method will not be called if none of
      // the provided authentication requirements are met.
      // However, it is best practice to include this anyway.
      throw new Unauthorized();
    }

    return "OK";
  }
}
```

Alternatively, you can use the `@BindSecurity` decorator to retrieve the resolved security scheme value. This is useful in a few cases:

* Security schemes defined in OpenAPI for bound methods
* Security schemes defined in OpenAPI at the root level
* Security schemes defined by `@RequireAuthentication` at the controller level

It is important to remember that `@BindSecurity` **does not authenticate the method**. It only retrieves the value of the authenticator with the given name. If the authenticator was not called for the method in question, the argument will be undefined. In order for a security scheme authenticator to be applied to a method, it must be specified in the OpenAPI specification or by a decorator that adds such specification.

Like `@RequireAuthentication`, you may pass a `@Authenticator`-decorated class constructor in place of the scheme name.

```typescript
import {
  Controller,
  RequireAuthentication,
  Get,
  BindSecurity
} from "@simply-openapi/controllers";

@Controller("/widgets")
@RequireAuthentication("myAuth", ["widgets.read"])
class WidgetsController {
  @Get("/{widget_id}")
  getWidget(
    @BindSecurity("myAuth")
    user: MyAuthUser
  ) {
    console.log("Request from user", user.username);
    ...
  }
}
```


---

# 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://simply-openapi.gitbook.io/simply-openapi-controllers/dev/adding-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.
