# Link new device This document describes the architecture of the feature allowing users to link their account to a new device, referred to as the `Link new device` process. ```{contents} :local: :depth: 3 ``` ## Terminology To understand this document, here are some key terms: - **`import side`**: The device importing the account. - **`export side`**: The device exporting the account. - **`token`**: A URI that identifies a device on the Distributed Hash Table (DHT). ## State machine The `daemon` manages this functionality using a [state machine](https://en.wikipedia.org/wiki/Finite-state_machine). The state evolution is communicated to clients, enabling the appropriate interface display. Currently, the state machine is symmetrical for both `import side` and `export side`, though certain states are inaccessible depending on the side. ### State overview | State | Name | Usage (Side) | Description | | ----- | --------------- | ------------- | ---------------------------------------------------------------------------------------------------------------- | | 0 | Init | None | Initial state. | | 1 | Token available | Import only | The `token` is available. This is the URI identifying the new device on the DHT, displayed as text or a QR code. | | 2 | Connecting | Export/Import | A peer-to-peer connection is being established. | | 3 | Authenticating | Export/Import | The identity of the account and device address are being confirmed. | | 4 | In progress | Export/Import | State transition, the account archive is being transferred. | | 5 | Done | Export/Import | Final state. Represents success or failure. | ### Details The state machine can include supplementary information for display purposes, passed as a `map<String, String>` called `details`. #### Details for `import side` | State | Name | Details | | ----- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Init | Not applicable. | | 1 | Token available | `token`: A 59-character URI with the prefix `jami-auth://`. | | 2 | Connecting | No details. | | 3 | Authenticating | `peer_id`: Jami ID of the imported account.<br>`auth_scheme`: `{"", "none", "password"}` (empty if unprotected).<br>`auth_error`: `{"bad_password"}`. | | 4 | In progress | No details. | | 5 | Done | `error`: `{"", "none", "network", "authentication"}` (empty if no error). | #### Details for `export side` | State | Name | Details | | ----- |---------------- | ------------------------------------------------------------------------- | | 0 | Init | Not applicable. | | 1 | Token available | Not applicable. | | 2 | Connecting | No details. | | 3 | Authenticating | `peer_address`: IP address of the exporting device. | | 4 | In progress | No details. | | 5 | Done | `error`: `{"", "none", "network", "authentication"}` (empty if no error). | ## API between daemon and client ### API for `import side` | Signal Name | Direction | Purpose | | ------------------------------ | --------- | ------------------------------------------------------------------------------------------------- | | `addAccount` | Outbound | Announces the intent to import an account. Must include the key `Account.archiveURL="jami-auth"`. | | `provideAccountAuthentication` | Outbound | Provides a password if needed and confirms the identity of the imported account. | | `removeAccount` | Outbound | Cancels the operation. | | `deviceAuthStateChanged` | Inbound | Indicates the new state and provides details. | ### API for `export side` | Signal Name | Direction | Purpose | | ----------------------- | ------------- | --------------------------------------------- | | `addDevice` | Outbound | Announces the intent to export an account. | | `confirmAddDevice` | Outbound | Confirms the address of the exporting device. | | `cancelAddDevice` | Outbound | Cancels the operation. | | `addDeviceStateChanged` | Inbound | Indicates the new state and provides details. | ## Daemon state machine ```mermaid stateDiagram-v2 state "Import Side" as Import { [*] --> Import_Init Import_Init --> Import_TokenAvailable: Generate token Import_TokenAvailable --> Import_Connecting: Peer detected Import_Connecting --> Import_Authenticating: Connection established Import_Authenticating --> Import_InProgress: Auth success Import_InProgress --> Import_Done: Transfer complete note right of Import_TokenAvailable Provides: - Authentication code - QR data end note note right of Import_Authenticating May require password auth_scheme: "", "none", "password" end note note right of Import_Done error: "", "none", "network", "authentication" end note } state "Export Side" as Export { [*] --> Export_Init Export_Init --> Export_Connecting: Token validated Export_Connecting --> Export_Authenticating: Connection established Export_Authenticating --> Export_InProgress: Auth success Export_InProgress --> Export_Done: Transfer complete note right of Export_Init Accepts: - Authentication code - QR data end note note right of Export_Authenticating Confirms peer address end note } ``` ## Client state machine ```mermaid stateDiagram-v2 [*] --> Initial Initial --> ImportDevice: ImportFromDevice selected Initial --> ExportDevice: ExportToDevice selected state "Import Device" as ImportDevice { [*] --> Import_Init Import_Init --> Import_TokenAvailable: Token received Import_TokenAvailable --> Import_Connecting: Peer detected Import_Connecting --> Import_Authenticating: Connection established Import_Authenticating --> Import_InProgress: Auth success Import_InProgress --> Import_Done: Transfer complete Import_Authenticating --> Import_Error: Bad password Import_Connecting --> Import_Error: Connection failed Import_InProgress --> Import_Error: Transfer failed Import_Error --> [*]: Reset Import_Done --> [*]: Account ready note right of Import_TokenAvailable Display: - QR code - Authentication code - Copy button end note note right of Import_Authenticating Show password input if needed end note } state "Export Device" as ExportDevice { [*] --> Export_Init state Export_Init { [*] --> ShowInputOptions ShowInputOptions --> ScanQR: Camera selected ShowInputOptions --> ManualEntry: Manual selected ScanQR --> QRScanning: Start camera QRScanning --> TokenObtained: QR detected QRScanning --> ShowInputOptions: Cancel scan ManualEntry --> TokenObtained: Valid code entered ManualEntry --> ShowInputOptions: Cancel entry } Export_Init --> Export_Connecting: Token validated Export_Connecting --> Export_Authenticating: Connection established Export_Authenticating --> Export_InProgress: Auth provided Export_InProgress --> Export_Done: Transfer complete Export_Connecting --> Export_Error: Invalid token Export_Authenticating --> Export_Error: Auth failed Export_InProgress --> Export_Error: Transfer failed Export_Error --> [*]: Reset Export_Done --> [*]: Device added note right of Export_Init Input options: - QR scanner - Manual code entry end note note right of Export_Authenticating Confirm peer device end note } ImportDevice --> Initial: Back/Cancel ExportDevice --> Initial: Back/Cancel ``` ## Full sequence diagram (import and export) ```mermaid sequenceDiagram box white Import Side participant IC as New Client participant ID as New Daemon end box white Export Side participant ED as Old Daemon participant EC as Old Client end %% Initial Setup IC->>ID: addAccount(archiveURL="jami-auth") activate ID ID-->>IC: deviceAuthStateChanged(state=TOKEN_AVAILABLE) Note over IC: Display QR code<br/>and auth token %% Export Side Initiation EC->>EC: User chooses to export EC->>EC: Scan QR/Enter token EC->>ED: addDevice(token) activate ED %% Connection Establishment ED->>ID: DHT connection request ID-->>IC: deviceAuthStateChanged(state=CONNECTING) ED-->>EC: addDeviceStateChanged(state=CONNECTING) %% Authentication Phase ID-->>IC: deviceAuthStateChanged(state=AUTHENTICATING,<br/>peer_id, auth_scheme) ED-->>EC: addDeviceStateChanged(state=AUTHENTICATING,<br/>peer_address) alt Account is password protected IC->>IC: Show password prompt IC->>ID: provideAccountAuthentication(password) end EC->>ED: confirmAddDevice() %% Transfer Phase ID-->>IC: deviceAuthStateChanged(state=IN_PROGRESS) ED-->>EC: addDeviceStateChanged(state=IN_PROGRESS) ED->>ID: Transfer account archive %% Completion ID-->>IC: deviceAuthStateChanged(state=DONE, error="") ED-->>EC: addDeviceStateChanged(state=DONE, error="") deactivate ID deactivate ED Note over IC,EC: Account successfully linked alt Error Scenarios ID-->>IC: deviceAuthStateChanged(state=DONE, error="network") ED-->>EC: addDeviceStateChanged(state=DONE, error="network") Note over IC,EC: Network error during transfer ID-->>IC: deviceAuthStateChanged(state=DONE, error="authentication") ED-->>EC: addDeviceStateChanged(state=DONE, error="authentication") Note over IC,EC: Authentication failed end %% Cancellation Scenarios rect rgb(240, 240, 240) Note over IC,EC: Optional Cancellation Flows IC->>ID: removeAccount() EC->>ED: cancelAddDevice() end ```