7.2. Synchronization

Beginning from version 3.11, privacyIDEA supports the synchronization of smartphones with the privacyIDEA server. This requires the privacyIDEA Authenticator App (v4.5.0 or higher) to be installed on the smartphone. It is currently only supported for the container type smartphone.

7.2.1. Use Cases

The synchronization enables the following use cases:

  • The user only needs to scan the QR code for the container and gets all tokens on his smartphone without the need to scan each token individually.

  • If new tokens are added to the container on the server, the user can synchronize the container with the privacyIDEA server to add the new tokens to the authenticator app.

  • If the user already has tokens on his smartphone, the first synchronization can add these tokens to the container on the server. This requires the container policy initially_add_tokens_to_container (see Container Policies).

  • Perform a rollover for all tokens in case the token secrets might be compromised.

  • Transfer all tokens to a new smartphone and invalidate the tokens on the old smartphone. This might be required if the smartphone is lost or stolen or if the user switches to a new smartphone.

Note that not all of these scenarios work for offline tokens. Offline tokens already existing on the smartphone can be synchronized with the server and, with the policy initially_add_tokens_to_container, also be added to the container on the server. However, a transfer to a new device or a rollover is not possible, as this would renew the token secret and hence invalidate the offline OTP values.

Additionally, SMS tokens will not be synchronized, because they are not stored on the smartphone.

With initially_add_tokens_to_container, tokens from the app are added to the container on the server once after the registration or a rollover, in the first synchronization in which the policy applies; tokens the app reports in later synchronizations are not added. Only tokens that already exist on the server are added: a token assigned to a user only if this user is the owner of the container, an unassigned token only if it is in one of the realms of the container, and no token that is already in another container.

7.2.2. Setup

To enable the synchronization, the smartphone container first needs to be registered on the smartphone. For the registration, a QR code is generated either during the container creation or on the container details page. The QR code contains the URL of the privacyIDEA server the smartphone can contact for synchronization.

To set up the smartphone synchronization properly, the following steps are required:

  1. Define container policies: In the policy scope Container at least the privacyIDEA_server_url needs to be set. This is the base URL of the privacyIDEA server the smartphone shall contact for registration and synchronization. Note, that the smartphone app may connect to a different privacyIDEA URL than the URL of the privacyIDEA Web UI.

  2. Define user or admin policies: In the scope user or admin, the action container_register needs to be allowed. This allows the user to create the registration QR code.

  3. Register Container: To register the container on the smartphone, the user needs to scan the QR code. The QR code can be generated during the container creation or on the container details page. Optionally, the user can secure the registration with a passphrase. Either the passphrase from the user store (beginning from version 3.12) or a manually defined passphrase can be used. The passphrase from the user store requires that the container is assigned to a user: the app asks for the password of this user, and the server checks it against the user store. For a container without owner the registration can not be completed. When using an individual passphrase, the passphrase prompt and response must be configured. The passphrase prompt will be displayed to the user in the app, e.g. “Enter the last four digits of your employee ID.”, and the passphrase response is the correct answer to the prompt, the actual passphrase. The registration is completed if the user scans the QR code and enters the correct passphrase.

  4. Synchronize Container: After a successful registration, the pi Authenticator app triggers a synchronization automatically. The user can also manually synchronize the container.

The following endpoints must be reachable for the smartphone:

7.2.3. Implementation Details

For the registration, the server creates the challenge together with the QR code, and the QR code contains it (see Registration). For every other action, the client first requests a challenge from the server at POST /container/challenge, which is only possible for a registered container. The client answers this challenge by signing a message containing the random nonce and the timestamp from the challenge and sends the response to the endpoint it wants to access. The server first verifies the response and then performs the requested action. For the signature, the Elliptic Curve Digital Signature Algorithm (ECDSA) with the curve secp384r1 is used.

Possible actions the client can perform are:

  • Register a container

  • Synchronize a container

  • Unregister a container

  • Perform a container rollover

7.2.3.1. Registration

The server initiates the registration by creating the QR code. The QR code contains a URI which uses the pia scheme. The following variables are included in the URI:

  • issuer: The issuer of the container, e.g. privacyIDEA

  • ttl: Time To Live of the registration challenge (Time the user has to scan the QR code)

  • nonce: A random nonce to prevent replay attacks

  • time: The time the registration challenge was created (ISO 8601 format)

  • url: URL of the privacyIDEA server

  • serial: Container serial

  • key_algorithm: The key algorithm to be used to generate the key pair

  • hash_algorithm: The hash algorithm the client uses for the ECDSA signatures, e.g. SHA256

  • ssl_verify: Whether the SSL certificate of the privacyIDEA server should be verified

  • passphrase: Optional passphrase prompt, displayed to the user to enter the corresponding passphrase

  • send_passphrase: Boolean value whether the passphrase should be sent to the server to finalize the registration. It is “True” if the user store passphrase is used and “False” if the individual passphrase or no passphrase at all is used. (Added in version 3.12)

Example of a URI:

pia://container/SMPH000588A4?issuer=privacyIDEA&ttl=10&nonce=97f94b36c199f4a0980720e18fcbcef99dbe871e
&time=2024-12-17T09%3A11%3A08.675629%2B00%3A00&url=https%3A//privacyidea.example.com&serial=SMPH000588A4
&key_algorithm=secp384r1&hash_algorithm=SHA256&ssl_verify=True
&passphrase=Enter%20the%20last%20four%20digits%20of%20your%20employee%20ID.&send_passphrase=False

The server creates an entry in the challenge database with the scope (URL of the API endpoint the client needs to contact to finalize the registration), the nonce, the time, and the correct passphrase response.

After scanning the QR code with the pi authenticator, the app creates an asymmetric elliptic key pair (k_priv, k_pub) with the curve secp384r1 and signs a message concatenating at least the nonce, time (ISO 8601 format), serial, and scope. Optionally, the passphrase response and device information are included in the signature:

sign(k_priv, nonce|time|serial|scope|device_brand|device_model|passphrase_response)

To complete the registration the endpoint POST /container/register/finalize is called with the following parameters:

container_serial: <serial>
signature: <signature>
public_client_key: <ecc public key of the client in PEM format (curve secp384r1)>
device_brand: <device brand>
device_model: <device model>
passphrase: <password of the container owner, only if send_passphrase=True>

passphrase is only sent and read with the passphrase from the user store (send_passphrase=True); the server checks it against the user store of the container owner. An individual passphrase is not sent; it is only part of the signed message, and the server uses the passphrase response stored in the challenge to verify the signature.

The server verifies the signature. If it is valid the registration is completed. See also privacyidea.lib.containers.smartphone.SmartphoneContainer.finalize_registration() for more details on the implementation details of the registration for smartphones.

It is highly recommended to always use SSL to verify the privacyIDEA server’s certificate. By default, SSL is activated but can be deactivated in the policies.

7.2.3.2. Synchronization

When synchronizing, the server response is additionally encrypted to secure the token secrets included in the response. For the encryption, the ECC Diffie-Hellman key exchange is used to create a session key. The response contains:

  • public_server_key: the X25519 public key of the server, 32 raw bytes, urlsafe base64 encoded

  • encryption_algorithm: AES

  • encryption_params: algorithm (AES), mode (GCM), init_vector (16 bytes) and tag, the last two urlsafe base64 encoded

  • container_dict_server: the JSON-encoded container dictionary of the server, encrypted, urlsafe base64 encoded

policies (the container policies for the client) and server_url are not encrypted. See also privacyidea.lib.containers.smartphone.SmartphoneContainer.encrypt_dict().

To synchronize the smartphone with the server, the authenticator app first requests a challenge at POST /container/challenge for the container and a scope (the synchronization endpoint). This endpoint returns a random nonce and a timestamp.

The authenticator app generates an ECC asymmetric key pair with the curve x25519 for the encryption. To synchronize with the server the app signs a message containing the parameters in the following order separated by “|”:

  • nonce (from the challenge)

  • timestamp (from the challenge)

  • serial of the container to synchronize

  • scope: The URL of the synchronization endpoint, e.g. https://privacyidea.example.com/container/synchronize

  • X25519 public key of the client for the encryption: its 32 raw bytes, urlsafe base64 encoded with padding (the same string as sent in public_enc_key_client)

  • container dictionary of the client

The container dictionary of the client contains the tokens that are already in the authenticator app in the following format:

{"tokens": [{"serial": "TOTP0001", "tokentype": "totp"},
            {"otp": ["123456", "234567"], "tokentype": "hotp"}]}

If the app does not know the serial of a token, it can also include a list of the next two OTP values. The server will then try to find the token by the OTP values.

Finally, the synchronization endpoint POST /container/synchronize is called with the following parameters:

container_serial: <serial>
signature: <signature>
public_enc_key_client: <X25519 public key of the client for the encryption, 32 raw bytes, urlsafe base64 encoded>
container_dict_client: <container dictionary containing the tokens of the client>

The server verifies the response of the challenge. See also privacyidea.lib.containers.smartphone.SmartphoneContainer.check_challenge_response() for more information on how the server verifies the response. If the challenge is valid, the server compares the client’s tokens with the tokens in the container on the server. For tokens that are not yet in the authenticator app, the server performs a rollover and includes the enrollment data in the response. For equal tokens, token details from the server are included in the response.

The pi authenticator adds the missing tokens, updates existing tokens, and removes tokens not available on the server.

7.2.3.3. Rollover

The rollover generates new secrets for all tokens and the client has to generate a new asymmetric key pair for the signatures. It can be initiated on the server, but also from the client. The client can use it to just renew all secrets in case they might be compromised or to transfer the container with all tokens to a new device.

Similar to the synchronization, the client first requests a challenge from the server at POST /container/challenge. The client has to pass the container serial and the scope (e.g. https://privacyidea.example.com/container/rollover) as parameters. This endpoint returns a random nonce and a timestamp.

The client signs a message containing the nonce, the timestamp, the serial of the container, and the scope and sends the signature and the container serial to the endpoint POST /container/rollover. This endpoint verifies the signature and if it is valid, the server initiates the rollover which is similar to the POST /container/register/initialize endpoint. The registration configurations are fetched from the policies such as privacyIDEA_server_url and a challenge is created. The endpoint returns a QR code containing the data as described for the Registration. This QR code is displayed in the authenticator app if the container shall be transferred to a new device. The new smartphone can scan the QR code and finalize the rollover at POST /container/register/finalize with the same parameters as a registration. The server recognizes the rollover from the registration state of the container; no extra parameter is needed. (A rollover started on the server uses POST /container/register/initialize with rollover=1, which requires the admin policy container_rollover or the user policy container_rollover.) The finalize endpoint generates new token secrets and stores the new public key of the client. Now the old smartphone will not be able to synchronize with the server anymore and the tokens on the old smartphone are invalidated. The new smartphone can now synchronize with the server to get all tokens. After this initial synchronization the rollover process is completed.

7.2.3.4. Terminate Registration

To unregister the container, the client first requests a challenge from the server at POST /container/challenge with the container serial and the scope (e.g. https://privacyidea.example.com/container/register/terminate/client) as parameters. Afterward, the client can sign a message containing the nonce, the timestamp, the serial of the container, and the scope. The signature and the container serial are sent to the endpoint POST /container/register/terminate/client. The server verifies the signature and if it is valid, the container is unregistered. The server deletes all data relevant for the synchronization such as the public client key and the registration state. Hence, a synchronization with the server is not possible anymore. The container is deleted in the authenticator app, but remains on the server.

If the user shall not be able to delete the container in the authenticator app and hence terminate the registration, the policy disable_client_container_unregister can be activated.