# Authentication

# Usage

Ts.ED uses middlewares to protect your route with your own strategy. To handle correctly a request and protect your endpoints, we have to use the decorator.

import {Controller, Get, UseAuth} from "@tsed/common";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

@Controller("/dashboard")
@UseAuth(CustomAuthMiddleware, {role: "admin"}) // on class level for all endpoints
class DashboardCtrl {
  @Get("/")
  @UseAuth(CustomAuthMiddleware, {role: "admin"}) // or for specific endpoints
  public getResource() {
  }
}
1
2
3
4
5
6
7
8
9
10
11

TIP

If you planed to use Passport.js, it's recommended to follow the Passport.js guide here.

Any middleware can be used as an authentication strategy. Just keep in mind, to work properly, the middleware must use decorator to retrieve the endpoint context execution.

Here is an example of the CustomAuth middleware using the Passport.js method to check authentication:

import {EndpointInfo, IMiddleware, Middleware, Req} from "@tsed/common";
import {Forbidden, Unauthorized} from "@tsed/exceptions";

@Middleware()
export class CustomAuthMiddleware implements IMiddleware {
  public use(@Req() request: Express.Request, @EndpointInfo() endpoint: EndpointInfo) {
    // retrieve options given to the @UseAuth decorator
    const options = endpoint.get(CustomAuthMiddleware) || {};

    if (!request.isAuthenticated()) { // passport.js method to check auth
      throw new Unauthorized("Unauthorized");
    }

    if (request.user.role !== options.role) {
      throw new Forbidden("Forbidden");
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Create your Auth decorator

It could be practical to create you own Authentication decorator to reduce the amount of code. For example, if we use swagger, we have to configure some extra security and responses information and it can quickly become heavy.

Example:

import {Controller, Get, Returns, UseAuth} from "@tsed/common";
import {Security} from "@tsed/swagger";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

@Controller("/dashboard")
@UseAuth(CustomAuthMiddleware, {role: "admin"}) // on class level for all endpoints
@Security("oauth2", "email", "firstname")
class DashboardCtrl {
  @Get("/")
  @UseAuth(CustomAuthMiddleware, {role: "admin"}) // or for specific endpoints
  @Security("oauth2", "email", "firstname")
  @Returns(401, {description: "Unauthorized"})
  @Returns(403, {description: "Forbidden"})
  public getResource() {
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

To avoid that, we can create a decorator which apply all of these instructions automatically, like this:

import {IAuthOptions, Returns, UseAuth} from "@tsed/common";
import {useDecorators} from "@tsed/core";
import {Security} from "@tsed/swagger";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

export interface ICustomAuthOptions extends IAuthOptions {
  role?: string;
  scopes?: string[];
}

export function CustomAuth(options: ICustomAuthOptions = {}): Function {
  return useDecorators(
    UseAuth(CustomAuthMiddleware, options),
    Security("oauth", ...(options.scopes || [])),
    Returns(401, {description: "Unauthorized"}),
    Returns(403, {description: "Forbidden"})
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

TIP

Additionally, you can use the Operation decorator to add automatically the Authorization header field in the swagger spec:

import {IAuthOptions, Returns, UseAuth} from "@tsed/common";
import {useDecorators} from "@tsed/core";
import {Operation, Security} from "@tsed/swagger";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

export interface ICustomAuthOptions extends IAuthOptions {
  role?: string;
  scopes?: string[];
}

export function CustomAuth(options: ICustomAuthOptions = {}): Function {
  return useDecorators(
    UseAuth(CustomAuthMiddleware, options),
    Security("oauth", ...(options.scopes || [])),
    Operation({ // will be remove in v6. Example will be updated
      "parameters": [
        {
          "in": "header",
          "name": "Authorization",
          "type": "string",
          "required": true
        }
      ]
    }),
    Returns(401, {description: "Unauthorized"}),
    Returns(403, {description: "Forbidden"})
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

And use it on our controller and endpoints:

import {Controller, Get} from "@tsed/common";
import {CustomAuth} from "../decorators/CustomAuth";

@Controller("/dashboard")
@CustomAuth({role: "admin", scopes: ["email", "firstname"]})
class DashboardCtrl {
  @Get("/")
  @CustomAuth({role: "admin", scopes: ["email", "firstname"]})
  public getResource() {
  }
}
1
2
3
4
5
6
7
8
9
10
11