From d71249b85e29fa9bac0b2a2863cf61446ffd0cf6 Mon Sep 17 00:00:00 2001 From: Corentin Mors Date: Fri, 15 Mar 2024 17:06:36 +0100 Subject: [PATCH] Migrate to service device keys format (#220) Following an internal ADR (Architecture Decision Record), I'm updating the non-interactive device registration and usage to a new format. --- documentation/pages/personal/devices.mdx | 13 +++++++----- src/command-handlers/devices.ts | 22 ++++++++++++-------- src/errors.ts | 6 ++++++ src/utils/deviceCredentials.ts | 26 +++++++++++++++++------- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/documentation/pages/personal/devices.mdx b/documentation/pages/personal/devices.mdx index 310e6362..eb295fc6 100644 --- a/documentation/pages/personal/devices.mdx +++ b/documentation/pages/personal/devices.mdx @@ -64,14 +64,17 @@ This will create a new device named `my_server` and will print the device creden Save them in a safe place (like in a secure note), as you won't be able to retrieve them later. Run the suggested commands on your target device (your server or CI) to set the device credentials as environment variables. +```sh +export DASHLANE_SERVICE_DEVICE_KEYS=dls_[deviceAccessKey]_[payload] +``` + +On Windows, you can use the `set` command instead of `export`. + ```sh -export DASHLANE_DEVICE_ACCESS_KEY=bdd5[..redacted..]6eb -export DASHLANE_DEVICE_SECRET_KEY=99f7d9bd547c0[..redacted..]c93fa2118cdf7e3d0 -export DASHLANE_LOGIN=email@domain.com -export DASHLANE_MASTER_PASSWORD= +set DASHLANE_SERVICE_DEVICE_KEYS=dls_[deviceAccessKey]_[payload] ``` -Please, replace `` with your actual master password. +The token you'll get is starting by `dls` in order to be easily identified by scanning tools. OTP at each login and SSO are not supported for non-interactive devices. We recommend creating a dedicated Dashlane diff --git a/src/command-handlers/devices.ts b/src/command-handlers/devices.ts index 8b4c6200..d90fdcbf 100644 --- a/src/command-handlers/devices.ts +++ b/src/command-handlers/devices.ts @@ -97,7 +97,7 @@ export async function removeAllDevices(devices: string[] | null, options: { all: export const registerNonInteractiveDevice = async (deviceName: string, options: { json: boolean }) => { const { - localConfiguration: { login }, + localConfiguration: { login, masterPassword }, db, } = await connectAndPrepare({ autoSync: false }); @@ -116,19 +116,25 @@ export const registerNonInteractiveDevice = async (deviceName: string, options: deviceName: `Non-Interactive - ${deviceName}`, }); + const serviceDeviceKeysPayload = { + login, + deviceSecretKey, + masterPassword, + }; + + const serviceDeviceKeysPayloadB64 = Buffer.from(JSON.stringify(serviceDeviceKeysPayload)).toString('base64'); + + const serviceDeviceKeys = `dls_${deviceAccessKey}_${serviceDeviceKeysPayloadB64}`; + if (options.json) { console.log( JSON.stringify({ - DASHLANE_DEVICE_ACCESS_KEY: deviceAccessKey, - DASHLANE_DEVICE_SECRET_KEY: deviceSecretKey, + DASHLANE_SERVICE_DEVICE_KEYS: serviceDeviceKeys, }) ); } else { - winston.info('The device credentials have been generated, save and run the following commands to export them:'); - console.log(`export DASHLANE_DEVICE_ACCESS_KEY=${deviceAccessKey}`); - console.log(`export DASHLANE_DEVICE_SECRET_KEY=${deviceSecretKey}`); - console.log(`export DASHLANE_LOGIN=${login}`); - console.log(`export DASHLANE_MASTER_PASSWORD=`); + winston.info('The device credentials have been generated, save and run the following command to export them:'); + console.log(`export DASHLANE_SERVICE_DEVICE_KEYS=${serviceDeviceKeys}`); } db.close(); diff --git a/src/errors.ts b/src/errors.ts index b593275c..f3b62b7f 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -10,6 +10,12 @@ export class TeamCredentialsWrongFormatError extends Error { } } +export class DeviceCredentialsWrongFormatError extends Error { + constructor() { + super('Device credentials has a wrong format'); + } +} + export class InvalidDashlanePathError extends Error { constructor() { super('Invalid Dashlane path'); diff --git a/src/utils/deviceCredentials.ts b/src/utils/deviceCredentials.ts index b8823697..c34649c4 100644 --- a/src/utils/deviceCredentials.ts +++ b/src/utils/deviceCredentials.ts @@ -1,16 +1,28 @@ +import { DeviceCredentialsWrongFormatError } from '../errors'; import { DeviceCredentials } from '../types'; let deviceCredentials: DeviceCredentials | null = null; export const initDeviceCredentials = (): DeviceCredentials | null => { - const { DASHLANE_DEVICE_ACCESS_KEY, DASHLANE_DEVICE_SECRET_KEY, DASHLANE_LOGIN, DASHLANE_MASTER_PASSWORD } = - process.env; - if (DASHLANE_DEVICE_ACCESS_KEY && DASHLANE_DEVICE_SECRET_KEY && DASHLANE_LOGIN && DASHLANE_MASTER_PASSWORD) { + const { DASHLANE_SERVICE_DEVICE_KEYS } = process.env; + if (DASHLANE_SERVICE_DEVICE_KEYS) { + if (!DASHLANE_SERVICE_DEVICE_KEYS.startsWith('dls_')) { + throw new DeviceCredentialsWrongFormatError(); + } + + const [accessKey, payloadB64] = DASHLANE_SERVICE_DEVICE_KEYS.split('_').slice(1); + + const payload = JSON.parse(Buffer.from(payloadB64, 'base64').toString('utf-8')) as { + login: string; + deviceSecretKey: string; + masterPassword: string; + }; + deviceCredentials = { - login: DASHLANE_LOGIN, - accessKey: DASHLANE_DEVICE_ACCESS_KEY, - secretKey: DASHLANE_DEVICE_SECRET_KEY, - masterPassword: DASHLANE_MASTER_PASSWORD, + login: payload.login, + accessKey, + secretKey: payload.deviceSecretKey, + masterPassword: payload.masterPassword, }; } return deviceCredentials;