mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
@@ -87,6 +87,7 @@
|
|||||||
"object-hash": "^2.0.3",
|
"object-hash": "^2.0.3",
|
||||||
"objection": "^3.1.5",
|
"objection": "^3.1.5",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
|
"passport-headerapikey": "^1.2.2",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"plaid": "^10.3.0",
|
"plaid": "^10.3.0",
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ export class AccountsController {
|
|||||||
items: { $ref: getSchemaPath(AccountResponseDto) },
|
items: { $ref: getSchemaPath(AccountResponseDto) },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ApiResponse({})
|
|
||||||
async getAccounts(@Query() filter: Partial<IAccountsFilter>) {
|
async getAccounts(@Query() filter: Partial<IAccountsFilter>) {
|
||||||
return this.accountsApplication.getAccounts(filter);
|
return this.accountsApplication.getAccounts(filter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,22 @@ import { AuthedController } from './Authed.controller';
|
|||||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||||
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
||||||
import { EnsureUserVerifiedGuard } from './guards/EnsureUserVerified.guard';
|
import { EnsureUserVerifiedGuard } from './guards/EnsureUserVerified.guard';
|
||||||
|
import { ApiKeyAuthGuard } from './api-key/AuthApiKey.guard';
|
||||||
|
import { MixedAuthGuard } from './api-key/MixedAuth.guard';
|
||||||
|
import { ApiKeyStrategy } from './api-key/AuthApiKey.strategy';
|
||||||
|
import { ApiKeyModel } from './models/ApiKey.model';
|
||||||
|
import { AuthApiKeysController } from './AuthApiKeys.controllers';
|
||||||
|
import { AuthApiKeyAuthorizeService } from './commands/AuthApiKeyAuthorization.service';
|
||||||
|
import { GenerateApiKey } from './commands/GenerateApiKey.service';
|
||||||
|
import { GetApiKeysService } from './queries/GetApiKeys.service';
|
||||||
|
|
||||||
const models = [InjectSystemModel(PasswordReset)];
|
const models = [
|
||||||
|
InjectSystemModel(PasswordReset),
|
||||||
|
InjectSystemModel(ApiKeyModel),
|
||||||
|
];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [AuthController, AuthedController],
|
controllers: [AuthController, AuthedController, AuthApiKeysController],
|
||||||
imports: [
|
imports: [
|
||||||
MailModule,
|
MailModule,
|
||||||
PassportModule.register({ defaultStrategy: 'jwt' }),
|
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||||
@@ -70,9 +81,15 @@ const models = [InjectSystemModel(PasswordReset)];
|
|||||||
SendSignupVerificationMailProcessor,
|
SendSignupVerificationMailProcessor,
|
||||||
GetAuthMetaService,
|
GetAuthMetaService,
|
||||||
GetAuthenticatedAccount,
|
GetAuthenticatedAccount,
|
||||||
|
ApiKeyAuthGuard,
|
||||||
|
ApiKeyStrategy,
|
||||||
|
AuthApiKeyAuthorizeService,
|
||||||
|
GenerateApiKey,
|
||||||
|
GetApiKeysService,
|
||||||
|
JwtAuthGuard,
|
||||||
{
|
{
|
||||||
provide: APP_GUARD,
|
provide: APP_GUARD,
|
||||||
useClass: JwtAuthGuard,
|
useClass: MixedAuthGuard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: APP_GUARD,
|
provide: APP_GUARD,
|
||||||
|
|||||||
26
packages/server/src/modules/Auth/AuthApiKeys.controllers.ts
Normal file
26
packages/server/src/modules/Auth/AuthApiKeys.controllers.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Controller, Post, Param, Get, Put } from '@nestjs/common';
|
||||||
|
import { GenerateApiKey } from './commands/GenerateApiKey.service';
|
||||||
|
import { GetApiKeysService } from './queries/GetApiKeys.service';
|
||||||
|
|
||||||
|
@Controller('api-keys')
|
||||||
|
export class AuthApiKeysController {
|
||||||
|
constructor(
|
||||||
|
private readonly getApiKeysService: GetApiKeysService,
|
||||||
|
private readonly generateApiKeyService: GenerateApiKey,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Post('generate')
|
||||||
|
async generate() {
|
||||||
|
return this.generateApiKeyService.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put(':id/revoke')
|
||||||
|
async revoke(@Param('id') id: number) {
|
||||||
|
return this.generateApiKeyService.revoke(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async getApiKeys() {
|
||||||
|
return this.getApiKeysService.getApiKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
packages/server/src/modules/Auth/api-key/AuthApiKey.guard.ts
Normal file
13
packages/server/src/modules/Auth/api-key/AuthApiKey.guard.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApiKeyAuthGuard extends AuthGuard('apiKey') {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext) {
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { AuthApiKeyAuthorizeService } from '../commands/AuthApiKeyAuthorization.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApiKeyStrategy extends PassportStrategy(
|
||||||
|
HeaderAPIKeyStrategy,
|
||||||
|
'apiKey',
|
||||||
|
) {
|
||||||
|
constructor(
|
||||||
|
private readonly authApiKeyAuthorizeService: AuthApiKeyAuthorizeService,
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
header: 'x-api-key',
|
||||||
|
prefix: '',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(apiKey: string): unknown {
|
||||||
|
return this.authApiKeyAuthorizeService.authorize(apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/server/src/modules/Auth/api-key/MixedAuth.guard.ts
Normal file
23
packages/server/src/modules/Auth/api-key/MixedAuth.guard.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { JwtAuthGuard } from '../guards/jwt.guard';
|
||||||
|
import { ApiKeyAuthGuard } from './AuthApiKey.guard';
|
||||||
|
|
||||||
|
// mixed-auth.guard.ts
|
||||||
|
@Injectable()
|
||||||
|
export class MixedAuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private jwtGuard: JwtAuthGuard,
|
||||||
|
private apiKeyGuard: ApiKeyAuthGuard,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext) {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const apiKey = request.headers['x-api-key'];
|
||||||
|
|
||||||
|
if (apiKey) {
|
||||||
|
return this.apiKeyGuard.canActivate(context);
|
||||||
|
} else {
|
||||||
|
return this.jwtGuard.canActivate(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ApiKeyModel } from '../models/ApiKey.model';
|
||||||
|
import { ClsService } from 'nestjs-cls';
|
||||||
|
import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthApiKeyAuthorizeService {
|
||||||
|
constructor(
|
||||||
|
private readonly clsService: ClsService,
|
||||||
|
|
||||||
|
@Inject(ApiKeyModel.name)
|
||||||
|
private readonly apikeyModel: typeof ApiKeyModel,
|
||||||
|
|
||||||
|
@Inject(TenantModel.name)
|
||||||
|
private readonly tenantModel: typeof TenantModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate using the given api key.
|
||||||
|
*/
|
||||||
|
async authorize(apiKey: string): Promise<boolean> {
|
||||||
|
const apiKeyRecord = await this.apikeyModel
|
||||||
|
.query()
|
||||||
|
.findOne({ key: apiKey });
|
||||||
|
|
||||||
|
if (!apiKeyRecord) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (apiKeyRecord.revoked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
apiKeyRecord.expiresAt &&
|
||||||
|
new Date(apiKeyRecord.expiresAt) < new Date()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tenant = await this.tenantModel
|
||||||
|
.query()
|
||||||
|
.findById(apiKeyRecord.tenantId);
|
||||||
|
|
||||||
|
if (!tenant) return false;
|
||||||
|
|
||||||
|
this.clsService.set('tenantId', tenant.id);
|
||||||
|
this.clsService.set('organizationId', tenant.organizationId);
|
||||||
|
this.clsService.set('userId', apiKeyRecord.userId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import { ApiKeyModel } from '../models/ApiKey.model';
|
||||||
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GenerateApiKey {
|
||||||
|
constructor(
|
||||||
|
private readonly tenancyContext: TenancyContext,
|
||||||
|
@Inject(ApiKeyModel.name)
|
||||||
|
private readonly apiKeyModel: typeof ApiKeyModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new secure API key for the current tenant and system user.
|
||||||
|
* The key is saved in the database and returned (only the key and id for security).
|
||||||
|
* @returns {Promise<{ key: string; id: number }>} The generated API key and its database id.
|
||||||
|
*/
|
||||||
|
async generate() {
|
||||||
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
const user = await this.tenancyContext.getSystemUser();
|
||||||
|
|
||||||
|
// Generate a secure random API key
|
||||||
|
const key = crypto.randomBytes(48).toString('hex');
|
||||||
|
// Save the API key to the database
|
||||||
|
const apiKeyRecord = await this.apiKeyModel.query().insert({
|
||||||
|
key,
|
||||||
|
tenantId: tenant.id,
|
||||||
|
userId: user.id,
|
||||||
|
createdAt: new Date(),
|
||||||
|
revokedAt: null,
|
||||||
|
});
|
||||||
|
// Return the created API key (not the full record for security)
|
||||||
|
return { key: apiKeyRecord.key, id: apiKeyRecord.id };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revokes an API key by setting its revokedAt timestamp.
|
||||||
|
* @param {number} apiKeyId - The id of the API key to revoke.
|
||||||
|
* @returns {Promise<{ id: number; revoked: boolean }>} The id of the revoked API key and a revoked flag.
|
||||||
|
*/
|
||||||
|
async revoke(apiKeyId: number) {
|
||||||
|
// Set the revoked flag to true for the given API key
|
||||||
|
await ApiKeyModel.query()
|
||||||
|
.findById(apiKeyId)
|
||||||
|
.patch({ revokedAt: new Date() });
|
||||||
|
|
||||||
|
return { id: apiKeyId, revoked: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ export class JwtAuthGuard extends AuthGuard('jwt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canActivate(context: ExecutionContext) {
|
canActivate(context: ExecutionContext) {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||||
IS_PUBLIC_ROUTE,
|
IS_PUBLIC_ROUTE,
|
||||||
[context.getHandler(), context.getClass()],
|
[context.getHandler(), context.getClass()],
|
||||||
|
|||||||
72
packages/server/src/modules/Auth/models/ApiKey.model.ts
Normal file
72
packages/server/src/modules/Auth/models/ApiKey.model.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { SystemModel } from '@/modules/System/models/SystemModel';
|
||||||
|
import { Model } from 'objection';
|
||||||
|
|
||||||
|
export class ApiKeyModel extends SystemModel {
|
||||||
|
readonly key: string;
|
||||||
|
readonly name?: string;
|
||||||
|
readonly createdAt: Date;
|
||||||
|
readonly expiresAt?: Date;
|
||||||
|
readonly revokedAt?: Date;
|
||||||
|
readonly userId: number;
|
||||||
|
readonly tenantId: number;
|
||||||
|
|
||||||
|
get revoked() {
|
||||||
|
return !!this.revokedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get virtualAttributes() {
|
||||||
|
return ['revoked'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'api_keys';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamps columns.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['createdAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relation mappings for Objection.js
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { SystemUser } = require('../../System/models/SystemUser');
|
||||||
|
const { TenantModel } = require('../../System/models/TenantModel');
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: SystemUser,
|
||||||
|
join: {
|
||||||
|
from: 'api_keys.userId',
|
||||||
|
to: 'users.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tenant: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: TenantModel,
|
||||||
|
join: {
|
||||||
|
from: 'api_keys.tenantId',
|
||||||
|
to: 'tenants.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model modifiers.
|
||||||
|
*/
|
||||||
|
static get modifiers() {
|
||||||
|
return {
|
||||||
|
notRevoked(query) {
|
||||||
|
query.whereNull('revokedAt');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ApiKeyModel } from '../models/ApiKey.model';
|
||||||
|
import { GetApiKeysTransformer } from './GetApiKeys.transformer';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetApiKeysService {
|
||||||
|
constructor(
|
||||||
|
private readonly injectableTransformer: TransformerInjectable,
|
||||||
|
private readonly tenancyContext: TenancyContext,
|
||||||
|
|
||||||
|
@Inject(ApiKeyModel.name)
|
||||||
|
private readonly apiKeyModel: typeof ApiKeyModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getApiKeys() {
|
||||||
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
const apiKeys = await this.apiKeyModel
|
||||||
|
.query()
|
||||||
|
.modify('notRevoked')
|
||||||
|
.where({ tenantId: tenant.id });
|
||||||
|
|
||||||
|
return this.injectableTransformer.transform(
|
||||||
|
apiKeys,
|
||||||
|
new GetApiKeysTransformer(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||||
|
|
||||||
|
export class GetApiKeysTransformer extends Transformer {
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['tenantId'];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,8 +11,6 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { TenantController } from '../Tenancy/Tenant.controller';
|
import { TenantController } from '../Tenancy/Tenant.controller';
|
||||||
import { SubscriptionGuard } from '../Subscription/interceptors/Subscription.guard';
|
|
||||||
import { JwtAuthGuard } from '../Auth/guards/jwt.guard';
|
|
||||||
import { ItemsApplicationService } from './ItemsApplication.service';
|
import { ItemsApplicationService } from './ItemsApplication.service';
|
||||||
import {
|
import {
|
||||||
ApiExtraModels,
|
ApiExtraModels,
|
||||||
@@ -34,7 +32,6 @@ import { ItemReceiptsResponseDto } from './dtos/ItemReceiptsResponse.dto';
|
|||||||
|
|
||||||
@Controller('/items')
|
@Controller('/items')
|
||||||
@ApiTags('Items')
|
@ApiTags('Items')
|
||||||
@UseGuards(SubscriptionGuard)
|
|
||||||
@ApiExtraModels(ItemResponseDto)
|
@ApiExtraModels(ItemResponseDto)
|
||||||
@ApiExtraModels(PaginatedResponseDto)
|
@ApiExtraModels(PaginatedResponseDto)
|
||||||
@ApiExtraModels(ItemInvoiceResponseDto)
|
@ApiExtraModels(ItemInvoiceResponseDto)
|
||||||
@@ -138,7 +135,6 @@ export class ItemsController extends TenantController {
|
|||||||
* @returns The updated item id.
|
* @returns The updated item id.
|
||||||
*/
|
*/
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@ApiOperation({ summary: 'Edit the given item (product or service).' })
|
@ApiOperation({ summary: 'Edit the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ export class SaleInvoiceGLEntries {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Rewrites the given invoice GL entries.
|
* Rewrites the given invoice GL entries.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {number} saleInvoiceId
|
* @param {number} saleInvoiceId
|
||||||
* @param {Knex.Transaction} trx
|
* @param {Knex.Transaction} trx
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
Injectable,
|
Injectable,
|
||||||
CanActivate,
|
CanActivate,
|
||||||
@@ -24,11 +25,12 @@ export class TenancyGlobalGuard implements CanActivate {
|
|||||||
canActivate(context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
const organizationId = request.headers['organization-id'];
|
const organizationId = request.headers['organization-id'];
|
||||||
|
const authorization = request.headers['authorization']?.trim();
|
||||||
|
|
||||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||||
IS_PUBLIC_ROUTE,
|
IS_PUBLIC_ROUTE,
|
||||||
[context.getHandler(), context.getClass()],
|
[context.getHandler(), context.getClass()],
|
||||||
)
|
);
|
||||||
const isTenantAgnostic = this.reflector.getAllAndOverride<boolean>(
|
const isTenantAgnostic = this.reflector.getAllAndOverride<boolean>(
|
||||||
IS_TENANT_AGNOSTIC,
|
IS_TENANT_AGNOSTIC,
|
||||||
[context.getHandler(), context.getClass()],
|
[context.getHandler(), context.getClass()],
|
||||||
@@ -36,7 +38,7 @@ export class TenancyGlobalGuard implements CanActivate {
|
|||||||
if (isPublic || isTenantAgnostic) {
|
if (isPublic || isTenantAgnostic) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!organizationId) {
|
if (!isEmpty(authorization) && !organizationId) {
|
||||||
throw new UnauthorizedException('Organization ID is required.');
|
throw new UnauthorizedException('Organization ID is required.');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -240,6 +240,9 @@ importers:
|
|||||||
passport:
|
passport:
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
version: 0.7.0
|
version: 0.7.0
|
||||||
|
passport-headerapikey:
|
||||||
|
specifier: ^1.2.2
|
||||||
|
version: 1.2.2
|
||||||
passport-jwt:
|
passport-jwt:
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
@@ -23358,6 +23361,13 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/passport-headerapikey@1.2.2:
|
||||||
|
resolution: {integrity: sha512-4BvVJRrWsNJPrd3UoZfcnnl4zvUWYKEtfYkoDsaOKBsrWHYmzTApCjs7qUbncOLexE9ul0IRiYBFfBG0y9IVQA==}
|
||||||
|
dependencies:
|
||||||
|
lodash: 4.17.21
|
||||||
|
passport-strategy: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/passport-jwt@4.0.1:
|
/passport-jwt@4.0.1:
|
||||||
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
|
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user