Appearance
ServiceAccounts Module
Service accounts are a specialized type of user within an application, designed specifically for enabling secure and authorized service-to-service communication or API access. They are represented as User
entities with a type set to SA
(service account) and have their own roles and permissions, just like regular users.
The Service Account API provides a dedicated layer for managing service accounts, simplifying their creation, modification, and management. By using service accounts instead of regular user accounts, applications can maintain a clear separation between human users and automated processes, ensuring proper access control and reducing the risk of unauthorized actions.
Key Components
The module provides a set of reusable components that can be easily integrated into a NestJS project:
Service Accounts Service
The ServiceAccountsService is a specialized module within an application that handles the creation, modification, and management of service accounts.
Service Accounts Resolver
The ServiceAccountsResolver
is a crucial component within a NestJS application that handles the GraphQL queries and mutations related to service accounts
Entity Inheritance
NestKit provide base classes that can be inherited and customized with your needs. For entities we use TypeORM TableInheritance
.
How to install ServiceAccountsModule to my app
To create a ServiceAccountsModule
in NestJS, you can follow these steps:
Install the required dependencies for
ServiceAccountsModule
:Create a
service-accounts
folder in your project'ssrc/modules
directory.Inside the
service-accounts
folder, create aservice-accounts.service.ts
file and extendsBaseServiceAccountsService
from NestKit with the following code:ts// src/modules/service-accounts/service-accounts.service.ts import { BaseServiceAccountsService } from '@deeepvision/nest-kit/dist/modules/service-accounts'; import { Injectable } from '@nestjs/common'; import { UserToRole } from '../user-to-roles/user-to-role.entity'; import { User } from '../users/user.entity'; import { Organization } from '@/modules/organizations/organization.entity'; @Injectable() export class ServiceAccountsService extends BaseServiceAccountsService<User, UserToRole, Organization> {}
Inside the
service-accounts
folder, create aservice-accounts.resolver.ts
file and extendsBaseServiceAccountsResolver
with the following code:ts// src/modules/serviceaccounts/service-accounts.resolver.ts import { Resolver } from '@nestjs/graphql'; import { User } from '@/modules/users/user.entity'; import { BaseServiceAccountsResolver } from '@deeepvision/nest-kit/dist/modules/service-accounts'; import { UserToRole } from '../user-to-roles/user-to-role.entity'; import { Organization } from '../organizations/organization.entity'; @Resolver() export class ServiceAccountsResolver extends BaseServiceAccountsResolver<User, UserToRole, Organization>(User) {}
Inside the
service-accounts/service-tokens
folder, createservice-token.entity.ts
file and extends BaseServiceToken with the following code:ts// src/modules/serviceaccounts/service-tokens/service-token.entity.ts import { BaseServiceToken } from '@deeepvision/nest-kit/dist/modules/service-accounts/service-tokens/service-token.entity'; import { ObjectType } from '@nestjs/graphql'; import { ChildEntity } from 'typeorm'; import { User } from '@/modules/users/user.entity'; @ObjectType() @ChildEntity() export class ServiceToken extends BaseServiceToken { user!: Promise<User>; }
Inside the
service-accounts/service-tokens
folder, createservice-tokens.service.ts
file and extends BaseServiceTokensService with the following code:ts// src/modules/serviceaccounts/service-tokens/service-tokens.service.ts import { User } from '@/modules/users/user.entity'; import { Injectable } from '@nestjs/common'; import { ServiceToken } from './service-token.entity'; import { BaseServiceTokensService } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Injectable() export class ServiceTokensService extends BaseServiceTokensService<User, ServiceToken> {}
Inside the
service-accounts/service-tokens
folder, createservice-accounts.resolver.ts
file and extends BaseServiceTokensResolver with the following code:ts// src/modules/serviceaccounts/service-tokens/service-accounts.resolver.ts import { Resolver } from '@nestjs/graphql'; import { ServiceToken } from './service-token.entity'; import { User } from '@/modules/users/user.entity'; import { BaseServiceTokensResolver } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Resolver() export class ServiceTokensResolver extends BaseServiceTokensResolver<User, ServiceToken>(ServiceToken) {}
Inside the
service-accounts
folder, create aservice-accounts.module.ts
file with the following code:ts// src/modules/service-accounts/service-accounts.module.ts import { Module } from '@nestjs/common'; import { ServiceAccountsService } from './service-accounts.service'; import { ServiceAccountsResolver } from './service-accounts.resolver'; import { UsersModule } from '../users/users.module'; import { UserToRolesModule } from '../user-to-roles/user-to-roles.module'; import { OrganizationsModule } from '../organizations/organizations.module'; import { ServiceTokensService } from './service-tokens/service-tokens.service'; import { ServiceTokensResolver } from './service-tokens/service-tokens.resolver'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ServiceToken } from './service-tokens/service-token.entity'; import { SERVICE_ACCOUNTS_SERVICE_TOKEN, SERVICE_TOKENS_SERVICE_TOKEN } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Module({ imports: [ TypeOrmModule.forFeature([ ServiceToken, ]), UsersModule, UserToRolesModule, OrganizationsModule, ], providers: [ ServiceAccountsService, { provide: SERVICE_ACCOUNTS_SERVICE_TOKEN, useExisting: ServiceAccountsService, }, ServiceAccountsResolver, ServiceTokensService, { provide: SERVICE_TOKENS_SERVICE_TOKEN, useExisting: ServiceTokensService, }, ServiceTokensResolver, ], exports: [ ServiceAccountsService, { provide: SERVICE_ACCOUNTS_SERVICE_TOKEN, useExisting: ServiceAccountsService, }, ], }) export class ServiceAccountsModule {}
Finally, import the
ServiceAccountsModule
into your main AppModule or another module:ts// src/modules/app.module.ts import { Module } from '@nestjs/common'; import { ServiceAccountsModule } from './modules/service-accounts/service-accounts.module.ts'; @Module({ imports: [ServiceAccountsModule], }) export class AppModule {}
In this code, we are importing the
ServiceAccountsModule
and adding it to theimports
array of ourAppModule
.
How to create service account with full access to service
Here's an example of how to call ServiceAccountService.create
with an CreateServiceAccountOptions
:
ts
const boServiceAccountWithFullAccess = await this.serviceAccountService.create({
alias: 'backoffice',
name: 'Backoffice App',
permissions: [
'bo:*:*:*',
],
});
// boServiceAccountWithFullAccess => User with type "SA"
Each service account is created with a unique email in the format {alias}@{saEmailDomain}
. For example, if the alias is backoffice
, the email for the service account would be [email protected]
. This ensures that each service account has a distinct email address within the system.
Create new service-account with GraphQL (full access)
Here's an example GraphQL mutation to create a new service-account (full access).
How to create service account with specific role in organization
Here's an example of how to call ServiceAccountService.create
with an CreateServiceAccountOptions
and create service account with role in organization:
ts
const boServiceAccountWithRole = await this.serviceAccountService.create({
alias: 'backoffice',
name: 'Backoffice App',
organizationId: 'hcorg:xjg239iomjs',
roleId: 'hcrol:2j3g2jgg2jt',
});
// boServiceAccountWithRole => User with type "SA"
Create new service account with GraphQL (with role)
Here's an example GraphQL mutation to create a new service-account (with role).
How to check if service account is exists by alias
To check if a service account with a specific alias exists, you can call the isExists
method of the ServiceAccountServices
class. Here's an example:
ts
const alias = 'backoffice';
const isExists = await this.serviceAccountService.isExists(alias);
if (isExists) {
console.log(`A service account with the alias "${alias}" already exists.`);
}
In this example, we check whether a service account with the alias backoffice
exists. The isExists
method returns a boolean value indicating the existence of the service account.
How to create service token for a service account
To create a long-lived service token for a service account, you can call the createServiceToken
method of the ServiceTokensService
class. Here's an example:
ts
const input = {
serviceAccountId: 'hcsa:xxxxxxxxxxx',
name: 'Token01',
}
const accessToken = await this.serviceTokensService.create(input, ctx);
console.log(`Createed service token for service account ID "${serviceAccountId}": ${accessToken}`);
In this example, we create an serive token for a service account with a given ID. The createServiceToken
method returns a string containing the access token. You can then use this token for making authorized requests on behalf of the service account.
Create service token for service account with GraphQL
Here's an example GraphQL mutation to create service token for service account
How to update service account
Here's an example of how to call ServiceAccountService.update
with an UpdateServiceAccountOptions
and update service account:
ts
const updatedServiceAccount = await this.serviceAccountService.update({
id: 'hcsa:xxxxxxxxxxx',
name: 'Updated Backoffice App',
});
How to delete service account
Call the delete
method of the ServiceAccountsService
and pass in the id
of the service account you want to delete:
ts
await this.serviceAccountsService.delete('hcsa:xxxxxxxxxxx');
In this example, we are deleting a service account with an id
of hcsa:xxxxxxxxxxx
.
Delete service account with GraphQL
Here's an example GraphQL mutation to delete service account.
ServiceAccountsService uses Soft-Delete feature
Soft delete is a feature in NestKit that allows you to mark a record as "deleted" instead of physically deleting it from the database. When you use soft delete, the record is not actually removed from the database, but a status sets to DELETED
to indicate that the record has been "soft-deleted". This allows you to retain the data and history associated with the record, while still hiding it from normal queries.