Skip to content

Commit 3d4c58d

Browse files
feat(enrolledDevice): add the authentication flow in the framework
- Init the retrieval of the keys in the environement - Parse the base64 string - Add a RegEx parser based on the expected format - Update the authentication to support EnrolledDevice type - Inject the Nitro secret in the payload when targeting NodeWS Proxy
1 parent 01b6a20 commit 3d4c58d

File tree

8 files changed

+132
-4
lines changed

8 files changed

+132
-4
lines changed

src/errors.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,24 @@ export class CouldNotFindTeamCredentialsError extends Error {
44
}
55
}
66

7+
export class CouldNotFindEnrolledTeamDeviceCredentialsError extends Error {
8+
constructor() {
9+
super('Could not find enrolled team device credentials');
10+
}
11+
}
12+
713
export class TeamCredentialsWrongFormatError extends Error {
814
constructor() {
915
super('Team credentials has a wrong format');
1016
}
1117
}
1218

19+
export class EnrolledTeamDeviceCredentialsWrongFormatError extends Error {
20+
constructor() {
21+
super('Enrolled team device credentials has a wrong format');
22+
}
23+
}
24+
1325
export class DeviceCredentialsWrongFormatError extends Error {
1426
constructor() {
1527
super('Device credentials has a wrong format');

src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import process from 'process';
44

55
import { cliVersionToString, CLI_VERSION } from './cliVersion.js';
66
import { rootCommands } from './commands/index.js';
7-
import { initDeviceCredentials, initStagingCheck, initTeamDeviceCredentials } from './utils/index.js';
7+
import {
8+
initDeviceCredentials,
9+
initEnrolledTeamDeviceCredentials,
10+
initStagingCheck,
11+
initTeamDeviceCredentials,
12+
} from './utils/index.js';
813
import { errorColor, initLogger } from './logger.js';
914

1015
process.removeAllListeners('warning');
@@ -16,6 +21,7 @@ initLogger({ debugLevel });
1621
initStagingCheck();
1722
initTeamDeviceCredentials();
1823
initDeviceCredentials();
24+
initEnrolledTeamDeviceCredentials();
1925

2026
const program = new Command();
2127

src/modules/tunnel-api-connect/steps/sendSecureContent.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { SecureContentRequest, SecureContentResponse, SendSecureContentPara
44
import { SecureTunnelNotInitialized, SendSecureContentDataDecryptionError } from '../errors.js';
55
import type { ApiConnectInternalParams, ApiData, ApiRequestsDefault } from '../types.js';
66
import { TypeCheck } from '../../typecheck/index.js';
7-
import { requestAppApi, requestTeamApi, requestUserApi } from '../../../requestApi.js';
7+
import { requestAppApi, requestEnrolledDeviceApi, requestTeamApi, requestUserApi } from '../../../requestApi.js';
88

99
const verifySendSecureBodySchemaValidator = new TypeCheck<SecureContentResponse>(secureContentBodyDataSchema);
1010

@@ -27,7 +27,18 @@ export const sendSecureContent = async <R extends ApiRequestsDefault>(
2727
const { path, clientStateIn, clientStateOut, payload: rawPayload, authentication = { type: 'app' } } = params;
2828
const { tunnelUuid } = apiData.clientHello;
2929

30-
const encryptedData = encryptData(clientStateOut, rawPayload);
30+
// We assume that this section of the code will only be calling NodeWS Proxy with this authentication
31+
// Inject the enrolledDeviceSecretKey when targeting NodeWS Enclave Proxy authentification
32+
// to provide Nitro specific key
33+
let injectedPayload = rawPayload;
34+
if (authentication.type === 'enrolledDevice') {
35+
injectedPayload = {
36+
...(typeof rawPayload === 'object' ? rawPayload : {}),
37+
enrolledDeviceSecretKey: authentication.enrolledTeamDeviceKeys.nitroDeviceSecretKey,
38+
};
39+
}
40+
41+
const encryptedData = encryptData(clientStateOut, injectedPayload);
3142

3243
const payload = {
3344
encryptedData: sodium.to_hex(encryptedData),
@@ -55,6 +66,14 @@ export const sendSecureContent = async <R extends ApiRequestsDefault>(
5566
teamUuid: authentication.teamUuid,
5667
});
5768
break;
69+
case 'enrolledDevice':
70+
response = await requestEnrolledDeviceApi<SecureContentResponse>({
71+
path,
72+
payload,
73+
isNitroEncryptionService: true,
74+
enrolledTeamDeviceKeys: authentication.enrolledTeamDeviceKeys,
75+
});
76+
break;
5877
case 'app':
5978
response = await requestAppApi<SecureContentResponse>({
6079
path,

src/modules/tunnel-api-connect/steps/types.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,21 @@ interface TeamDeviceAuthenticationParams {
4242
teamDeviceKeys: { accessKey: string; secretKey: string };
4343
}
4444

45+
interface EnrolledDeviceAuthenticationParams {
46+
type: 'enrolledDevice';
47+
enrolledTeamDeviceKeys: {
48+
nodeWSAccessKey: string;
49+
nitroDeviceAccessKey: string;
50+
nitroDeviceSecretKey: string;
51+
secretKey: string;
52+
};
53+
}
54+
4555
export type AuthenticationParams =
4656
| AppAuthenticationParams
4757
| UserDeviceAuthenticationParams
48-
| TeamDeviceAuthenticationParams;
58+
| TeamDeviceAuthenticationParams
59+
| EnrolledDeviceAuthenticationParams;
4960

5061
export interface SendSecureContentParams<R extends ApiRequestsDefault> {
5162
path: R['path'];

src/requestApi.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,26 @@ export const requestTeamApi = async <T>(params: RequestTeamApi): Promise<T> => {
133133
},
134134
});
135135
};
136+
137+
export interface RequestEnrolledDeviceApi {
138+
payload: Record<string, unknown>;
139+
path: string;
140+
isNitroEncryptionService?: boolean;
141+
enrolledTeamDeviceKeys: {
142+
nodeWSAccessKey: string;
143+
nitroDeviceAccessKey: string;
144+
secretKey: string;
145+
};
146+
}
147+
148+
export const requestEnrolledDeviceApi = async <T>(params: RequestEnrolledDeviceApi): Promise<T> => {
149+
const { enrolledTeamDeviceKeys, ...otherParams } = params;
150+
return requestApi({
151+
...otherParams,
152+
authentication: {
153+
type: 'enrolledDevice',
154+
...dashlaneApiKeys,
155+
...enrolledTeamDeviceKeys,
156+
},
157+
});
158+
};

src/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ export interface TeamDeviceCredentials {
2525
secretKey: string;
2626
}
2727

28+
export interface EnrolledTeamDeviceCredentials {
29+
// Node WS Access Key
30+
nodeWSAccessKey: string;
31+
// Node WS Secret Key
32+
secretKey: string;
33+
// Nitro Access Key
34+
nitroDeviceAccessKey: string;
35+
// Nitro Secret Key
36+
nitroDeviceSecretKey: string;
37+
}
38+
2839
export interface DeviceCredentials {
2940
login: string;
3041
accessKey: string;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {
2+
CouldNotFindEnrolledTeamDeviceCredentialsError,
3+
EnrolledTeamDeviceCredentialsWrongFormatError,
4+
} from '../errors.js';
5+
import { EnrolledTeamDeviceCredentials } from '../types.js';
6+
7+
let enrolledTeamDeviceCredentials: EnrolledTeamDeviceCredentials | null = null;
8+
9+
const validateEnrolledDeviceKeys = (keys: string) =>
10+
new RegExp(
11+
/(DASH_EDWSA_[a-zA-Z0-9_-]{64})-(DASH_EDWSS_[a-zA-Z0-9_-]{64})-(DASH_EDNTA_[a-zA-Z0-9_-]{64})-(DASH_EDNTS_[a-zA-Z0-9_-]{64})/
12+
).exec(keys);
13+
14+
export const initEnrolledTeamDeviceCredentials = (): EnrolledTeamDeviceCredentials | null => {
15+
const { DASHLANE_ENROLLED_TEAM_DEVICE_KEYS } = process.env;
16+
17+
if (DASHLANE_ENROLLED_TEAM_DEVICE_KEYS) {
18+
// Parsing the base64 string
19+
const parsedString = Buffer.from(DASHLANE_ENROLLED_TEAM_DEVICE_KEYS, 'base64').toString('utf-8');
20+
// Validating the keys
21+
const parsedKeys = validateEnrolledDeviceKeys(parsedString);
22+
23+
if (!parsedKeys) {
24+
throw new EnrolledTeamDeviceCredentialsWrongFormatError();
25+
}
26+
27+
const [nodeWSAccessKey, secretKey, nitroDeviceAccessKey, nitroDeviceSecretKey] = parsedKeys.slice(1);
28+
enrolledTeamDeviceCredentials = {
29+
nodeWSAccessKey,
30+
secretKey,
31+
nitroDeviceAccessKey,
32+
nitroDeviceSecretKey,
33+
};
34+
}
35+
36+
return enrolledTeamDeviceCredentials;
37+
};
38+
39+
export const getEnrolledTeamDeviceCredentials = (): EnrolledTeamDeviceCredentials => {
40+
if (!enrolledTeamDeviceCredentials) {
41+
throw new CouldNotFindEnrolledTeamDeviceCredentialsError();
42+
}
43+
44+
return enrolledTeamDeviceCredentials;
45+
};

src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export * from './secretTransformation.js';
88
export * from './stagingCheck.js';
99
export * from './strings.js';
1010
export * from './teamDeviceCredentials.js';
11+
export * from './enrolledTeamDeviceCredentials.js';

0 commit comments

Comments
 (0)