Authentication

Usage

Ts.ED use middleware to protect your route with your own strategy. To handle correctly a request and protect your endpoints, we have to use the @@UseAuth@@ 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 a authentication strategy. Just keep in mind, to works properly, the middleware must use @@EndpointInfo@@ decorator to retrieve the endpoint context execution.

Here an example of the a CustomAuth middleware with use the Passport.js method to check authentication:

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

@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, UseAuth} from "@tsed/common";
import {Responses, 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")
@Responses(401, {description: "Unauthorized"})
@Responses(403, {description: "Forbidden"})
class DashboardCtrl {
  @Get("/")
  @UseAuth(CustomAuthMiddleware, {role: "admin"}) // or for specific endpoints
  @Security("oauth2", "email", "firstname")
  @Responses(401, {description: "Unauthorized"})
  @Responses(403, {description: "Forbidden"})
  public getResource() {
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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

import {IUseAuthOptions, UseAuth} from "@tsed/common";
import {applyDecorators} from "@tsed/core";
import {Responses, Security} from "@tsed/swagger";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

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

export function CustomAuth(options: ICustomAuthOptions = {}): Function {
  return applyDecorators(
    UseAuth(CustomAuthMiddleware, options),
    Security("oauth", ...(options.scopes || [])),
    Responses(401, {description: "Unauthorized"}),
    Responses(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 Operation decorator to add automatically in the swagger spec, the Authorization header field:

import {IUseAuthOptions, UseAuth} from "@tsed/common";
import {applyDecorators} from "@tsed/core";
import {Operation, Responses, Security} from "@tsed/swagger";
import {CustomAuthMiddleware} from "../guards/CustomAuthMiddleware";

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

export function CustomAuth(options: ICustomAuthOptions = {}): Function {
  return applyDecorators(
    UseAuth(CustomAuthMiddleware, options),
    Security("oauth", ...(options.scopes || [])),
    Operation({
      "parameters": [
        {
          "in": "header",
          "name": "Authorization",
          "type": "string",
          "required": true
        }
      ]
    }),
    Responses(401, {description: "Unauthorized"}),
    Responses(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