New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@edirect/auth

Package Overview
Dependencies
Maintainers
26
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@edirect/auth

Auth Module

  • 9.3.2
  • npm
  • Socket score

Version published
Weekly downloads
155
increased by66.67%
Maintainers
26
Weekly downloads
 
Created
Source

@edirect/auth

The EDirectInsure Auth Module.

Installation

npm i @edirect/auth

Usage

The auth-module was designed to support AuthService and Keycloak access tokens. With this module, it is possible to configure the authentication and authorization of any endpoint of your microservice. Below is an example containing everything needed for this.

// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard, AuthMiddleware } from '@edirect/auth';

@Module({
  providers: [
    //...
    {
      provide: APP_GUARD,
      useFactory: (configService: ConfigService, reflector: Reflector) => new AuthGuard(configService, reflector),
      inject: [ConfigService, Reflector]
    },
    //...
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): void {
    consumer.apply(AuthMiddleware).forRoutes('cats');
  }
}

In this code snippet above, the APP_GUARD provider will make the authorization check available to any endpoint of your microservice, however, to enable it, you need to add AuthMiddleware to the route you intend to configure the authorization check (in this case, the cats route).

// cats.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Roles, Permissions } from '@edirect/auth';

@Controller('cats')
export class CatsController {
  @Get()
  @Resources('Default Resource')
  @Roles('ROLE_USER')
  @Permissions('CATS.READ')
  findAll(): Promise<Response> {
    return [];
  }
}

In this code snippet above, three notations used to verify the authorization of the GET /cats endpoint were configured, let's understand them:

  • @Resources - (Optional) Defines the name of the resource configured on the Resource Server associated with your microservice's client in Keycloak. It should only be set if you are using this feature.
  • @Roles - (Optional) Defines one or more roles that your microservice user or client must have to be able to access the endpoint. This will be used for a static check on the access token or dynamic by Keycloak if the @Resource annotation has been defined.
  • @Permissions - (Optional) Defines one or more permissions that your microservice user or client must have to be able to access the endpoint. This will be used for a static check on the access token or dynamic by Keycloak if the @Resource annotation has been defined.

Note: As you can see, all annotations are optional. If you don't define any of them, just a token integrity check via JWKS will be done, and in case of success, the endpoint will be authorized.

# .{NODE_ENV}.ENV
KEYCLOAK_BASE_URL=http://keycloak-base-url
KEYCLOAK_REALM=keycloak_realm
KEYCLOAK_CLIENT=your-app-client
AUTH_SERVICE_JWKS=https://auth-service-base-url/oidc/jwks
HTTP_TIMEOUT=30000 # optional

Use to generate internal access tokens for server-to-server communication with other microservices

In version 9.2.0 or higher, we made available a new feature that allows you to enable at runtime whether the microservice will generate and use access tokens from AuthService or Keycloak in server-to-server communication with another microservice.This feature is highly recommended as it is built to optimize the reuse of one or more access tokens through caching and expiration management.

  //...
  private keycloakEnable = this.configService.get('KEYCLOAK_ENABLE') === 'true';
  //...

  constructor(
    //...
    private serverAuthService: ServerAuthService,
    //...
  ) {}

  //...
    const ob = this.serverAuthService.getToken(
      {
        authService: {
          url: this.configService.get('AUTH_SERVICE_URL'),
          clientId: this.configService.get('AUTH_SERVICE_CLIENT_ID'),
          clientSecret: this.configService.get('AUTH_SERVICE_CLIENT_SECRET'),
          scope: this.configService.get('AUTH_SERVICE_SCOPE'),
        },
        keycloak: {
          enable: this.keycloakEnable,
          baseUrl: this.configService.get('KEYCLOAK_BASE_URL'),
          realm: this.configService.get('KEYCLOAK_REALM'),
          clientId: this.configService.get('KEYCLOAK_CLIENT'),
          clientSecret: this.configService.get('KEYCLOAK_CLIENT_SECRET'),
          scope: this.configService.get('KEYCLOAK_SCOPE'),
        },
      },
      'token',
    );
  //...

In this code snippet above, we optionally set keycloakEnable as a class property. It will be filled with the contents of the KEYCLOAK_ENABLE environment variable as the default value. We chose to do it this way to demonstrate that we can change it at runtime depending on your business rule. Then we inject an instance of ServerAuthService into the class constructor to use its getToken method in the most suitable place for your business rule. Finally, we highlight the second parameter of the getToken method that we defined with the string token. It is optional and should only be used if your microservice requires managing a unique access token for each microservice it communicates with. It represents the unique identifier of the access token cache for its reuse and renewal.

Sample Testing

  1. Start Keycloak: docker-compose -f .\keycloak\docker-compose.yml up -d;
  2. Start Sample project case 1 as in the documentation;
  3. Start Sample project case 2 as in the documentation;
  4. Run the command curl --location --request GET 'http://0.0.0.0:3000/case1/external-to-internal' \ --header 'Authorization: Bearer eyJhbG...Md2wDw' to simulate a request to your microservice and return access token information;
// Expected output for item 4
{
    "case": 1,
    "user": {
        "clientId": "5c6ea2b88b02943de7fab31a",
        "sub": "LWJmBVI0-OyqQuiMz7Y0w",
        "accountId": "LWJmBVI0-OyqQuiMz7Y0w",
        "username": "caminos#adailson.moreira@bolttech.io",
        "roles": [
            "ROLE_BROKER",
            "ROLE_USER"
        ],
        "permissions": [
            "QUOTE.ENTITY.READ",
            "QUOTE.ENTITY.CREATE",
            "QUOTE.ENTITY.UPDATE",
            "QUOTE.ENTITY.DELETE",
            "QUOTE.ENTITY.TRANSFER",
            "ENTITY.READ",
            "ENTITY.CREATE",
            "ENTITY.UPDATE",
            "ENTITY.DELETE",
            "QUOTE.ENTITY.SHOW_COMMISSION",
            "QUOTE.READ_ONLY_OWN",
            "QUOTE.CREATE",
            "STORAGE.READ",
            "STORAGE.CREATE",
            "STORAGE.UPDATE",
            "STORAGE.DELETE"
        ],
        "status": "active",
        "entityUser": {
            "_id": "6287815a20e555002044c1ed",
            "deletedAt": null,
            "bankCode": "",
            "branchCode": "",
            "sso": false,
            "accountId": "LWJmBVI0-OyqQuiMz7Y0w",
            "entityId": "caminos",
            "firstName": "Adailson",
            "lastName": "Moreiraa",
            "email": "adailson.moreira@bolttech.io",
            "referenceCode": 0,
            "entityUserId": "om11a-ox6GsrVCuMKAC0T",
            "createdAt": "2022-05-20T11:54:03.249Z",
            "updatedAt": "2022-06-23T09:53:55.207Z",
            "__v": 0,
            "vatNumber": "ES01234567A",
            "phone": "972367087",
            "hashUser": "62DEDE7FA9483FB24CFCA47D2686D151",
            "entities": [
                "caminosalmagro42",
                "caminosalmagro8",
                "caminosbcbarcelona",
                "caminosbcmadridalmagro42",
                "caminosbcmadridalmagro8",
                "far"
            ]
        },
        "jti": "ZX_5nXxPE_eSpd2HB3IRD",
        "iat": 1683900016,
        "exp": 1683986416,
        "scope": "openid accountId username roles permissions status entityUser",
        "iss": "http://0.0.0.0:9090",
        "aud": "5c6ea2b88b02943de7fab31a"
    },
    "rolesToCheck": [
        "ROLE_USER"
    ],
    "roleToCheck": "ROLE_USER",
    "permissionsToCheck": [
        "ENTITY.READ",
        "ENTITY.READ_ONLY_OWN"
    ],
    "permissionToCheck": "ENTITY.READ",
    "auth": {
        "getToken": "eyJ...xmA",
        "getUser": {
            "clientId": "5c6ea2b88b02943de7fab31a",
            "sub": "LWJmBVI0-OyqQuiMz7Y0w",
            "accountId": "LWJmBVI0-OyqQuiMz7Y0w",
            "username": "caminos#adailson.moreira@bolttech.io",
            "roles": [
                "ROLE_BROKER",
                "ROLE_USER"
            ],
            "permissions": [
                "QUOTE.ENTITY.READ",
                "QUOTE.ENTITY.CREATE",
                "QUOTE.ENTITY.UPDATE",
                "QUOTE.ENTITY.DELETE",
                "QUOTE.ENTITY.TRANSFER",
                "ENTITY.READ",
                "ENTITY.CREATE",
                "ENTITY.UPDATE",
                "ENTITY.DELETE",
                "QUOTE.ENTITY.SHOW_COMMISSION",
                "QUOTE.READ_ONLY_OWN",
                "QUOTE.CREATE",
                "STORAGE.READ",
                "STORAGE.CREATE",
                "STORAGE.UPDATE",
                "STORAGE.DELETE"
            ],
            "status": "active",
            "entityUser": {
                "_id": "6287815a20e555002044c1ed",
                "deletedAt": null,
                "bankCode": "",
                "branchCode": "",
                "sso": false,
                "accountId": "LWJmBVI0-OyqQuiMz7Y0w",
                "entityId": "caminos",
                "firstName": "Adailson",
                "lastName": "Moreiraa",
                "email": "adailson.moreira@bolttech.io",
                "referenceCode": 0,
                "entityUserId": "om11a-ox6GsrVCuMKAC0T",
                "createdAt": "2022-05-20T11:54:03.249Z",
                "updatedAt": "2022-06-23T09:53:55.207Z",
                "__v": 0,
                "vatNumber": "ES01234567A",
                "phone": "972367087",
                "hashUser": "62DEDE7FA9483FB24CFCA47D2686D151",
                "entities": [
                    "caminosalmagro42",
                    "caminosalmagro8",
                    "caminosbcbarcelona",
                    "caminosbcmadridalmagro42",
                    "caminosbcmadridalmagro8",
                    "far"
                ]
            },
            "jti": "ZX_5nXxPE_eSpd2HB3IRD",
            "iat": 1683900016,
            "exp": 1683986416,
            "scope": "openid accountId username roles permissions status entityUser",
            "iss": "http://0.0.0.0:9090",
            "aud": "5c6ea2b88b02943de7fab31a"
        },
        "getEntityUser": {
            "_id": "6287815a20e555002044c1ed",
            "deletedAt": null,
            "bankCode": "",
            "branchCode": "",
            "sso": false,
            "accountId": "LWJmBVI0-OyqQuiMz7Y0w",
            "entityId": "caminos",
            "firstName": "Adailson",
            "lastName": "Moreiraa",
            "email": "adailson.moreira@bolttech.io",
            "referenceCode": 0,
            "entityUserId": "om11a-ox6GsrVCuMKAC0T",
            "createdAt": "2022-05-20T11:54:03.249Z",
            "updatedAt": "2022-06-23T09:53:55.207Z",
            "__v": 0,
            "vatNumber": "ES01234567A",
            "phone": "972367087",
            "hashUser": "62DEDE7FA9483FB24CFCA47D2686D151",
            "entities": [
                "caminosalmagro42",
                "caminosalmagro8",
                "caminosbcbarcelona",
                "caminosbcmadridalmagro42",
                "caminosbcmadridalmagro8",
                "far"
            ]
        },
        "hasRoles": true,
        "hasRole": true,
        "hasPermissions": false,
        "hasPermission": true,
        "isAdmin": false,
        "isUser": true,
        "isService": false,
        "isCrm": false,
        "isBroker": true,
        "isAgentLicensed": false,
        "isAgentUnlicensed": false,
        "getTokenProvider": "authservice"
    }
}
  1. Run the command curl --location --request GET 'http://0.0.0.0:3000/case1/external-to-internal' \ --header 'Authorization: Bearer eyJhbG...Md2wDw' to simulate a request to your microservice, where internally there is communication with another microservice and return the internal access token information.
// Expected output for item 4
{
    "case": 2,
    "user": {
        "clientId": "5c6ea2b88b02943de7fab31a",
        "accountId": "aYoqi4OK5Gb72Nxqg8QD6",
        "username": "edirect#admin@edirectinsure.com",
        "roles": [
            "ROLE_SERVICE"
        ],
        "permissions": [
            "QUOTE.READ",
            "QUOTE.CREATE",
            "QUOTE.UPDATE",
            "QUOTE.DELETE",
            "ENTITY.READ",
            "ENTITY.CREATE",
            "ENTITY.UPDATE",
            "ENTITY.DELETE",
            "STORAGE.READ",
            "STORAGE.CREATE",
            "STORAGE.UPDATE",
            "STORAGE.DELETE"
        ],
        "status": "active",
        "jti": "31jETjs3N9MoF1-PJ6ugA",
        "iat": 1683900332,
        "exp": 1683900932,
        "scope": "openid accountId username roles permissions status entityUser",
        "iss": "http://0.0.0.0:9090",
        "aud": "5c6ea2b88b02943de7fab31a"
    },
    "rolesToCheck": [
        "ROLE_USER"
    ],
    "roleToCheck": "ROLE_USER",
    "permissionsToCheck": [
        "ENTITY.READ",
        "ENTITY.READ_ONLY_OWN"
    ],
    "permissionToCheck": "ENTITY.READ",
    "auth": {
        "getToken": "eyJ...19w",
        "getUser": {
            "clientId": "5c6ea2b88b02943de7fab31a",
            "accountId": "aYoqi4OK5Gb72Nxqg8QD6",
            "username": "edirect#admin@edirectinsure.com",
            "roles": [
                "ROLE_SERVICE"
            ],
            "permissions": [
                "QUOTE.READ",
                "QUOTE.CREATE",
                "QUOTE.UPDATE",
                "QUOTE.DELETE",
                "ENTITY.READ",
                "ENTITY.CREATE",
                "ENTITY.UPDATE",
                "ENTITY.DELETE",
                "STORAGE.READ",
                "STORAGE.CREATE",
                "STORAGE.UPDATE",
                "STORAGE.DELETE"
            ],
            "status": "active",
            "jti": "31jETjs3N9MoF1-PJ6ugA",
            "iat": 1683900332,
            "exp": 1683900932,
            "scope": "openid accountId username roles permissions status entityUser",
            "iss": "http://0.0.0.0:9090",
            "aud": "5c6ea2b88b02943de7fab31a"
        },
        "hasRoles": false,
        "hasRole": false,
        "hasPermissions": false,
        "hasPermission": true,
        "isAdmin": false,
        "isUser": false,
        "isService": true,
        "isCrm": false,
        "isBroker": false,
        "isAgentLicensed": false,
        "isAgentUnlicensed": false,
        "getTokenProvider": "authservice"
    }
}

Note: For this to work, your application must start its bootstrap as nest-app.

Use of providers

Providers are modules that create an abstraction of the complexity of communicating with supported IAMs through a standard interface. Currently, are supported:

  • AuthService
// app.module.ts
import { Module } from '@nestjs/common';
import { AuthServiceProviderModule } from '@edirect/auth';

@Module({
  imports: [AuthServiceProviderModule.register({
    baseUrl: `https://auth-service-stage1-eir.stag.bolttechbroker.net`,
    wellKnownPath: `/oidc/.well-known/openid-configuration`,
    grantType: 'client_credentials',
    clientId: 'LPGfwxm5ocSPArisz1HaJ',
    clientSecret: '70e53a2c0e62c82d8c21c2d171430745',
    scope: 'openid',
    adminPath: '',
  })]
})
export class AppModule { }
// auth.service.ts
import { AuthServiceUserProvider, AuthServiceUser } from '@edirect/auth';
import { Injectable } from '@nestjs/common';

@Injectable()
export class AuthService {
  constructor(private readonly userProvider: AuthServiceUserProvider) { }

  createUser(user: AuthServiceUser): Observable<AuthServiceUser> {
    return this.userProvider.create(user);
  }
}
  • Keycloak
// app.module.ts
import { Module } from '@nestjs/common';
import { KeycloakProviderModule } from '@edirect/auth';

@Module({
  imports: [KeycloakProviderModule.register({
    baseUrl: `http://localhost:49149`,
    wellKnownPath: `/realms/main/.well-known/openid-configuration`,
    grantType: 'client_credentials',
    clientId: 'fEBOJ4cqtKO59NGqinGlB', //sample-client
    clientSecret: 'rmtjq0sjcaNzOPYT2Vp3KThGpxFyase2',
    scope: 'openid',
    adminPath: '/admin/realms/main',
  })]
})
export class AppModule { }
// auth.service.ts
import { KeycloakUserProvider, KeycloakUser } from '@edirect/auth';
import { Injectable } from '@nestjs/common';

@Injectable()
export class AuthService {
  constructor(private readonly userProvider: KeycloakUserProvider) { }

  createUser(user: KeycloakUser): Observable<KeycloakUser> {
    return this.userProvider.create(user);
  }
}

Problems

The cache-manager version

This library underwent a change from version 4 to 5 that will significantly change its behavior (more details in this link). We, therefore, recommend that it be updated to version 5 in your project.

FAQs

Package last updated on 17 Jul 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc