

# Getting started with the Amazon DCV Web Client SDK
<a name="getting-started"></a>

 The Amazon DCV Web Client SDK comprises of a main `dcv.js` file and some auxiliary components. All the files are distributed inside a compressed archive that can be downloaded from the [Amazon DCV website](https://download.nice-dcv.com/webclientsdk.html) . 

**To get started with the Amazon DCV Web Client SDK**

1. The Amazon DCV Web Client SDK archive is digitally signed with a secure GPG signature. To verify the archive's signature, you must import the NICE GPG key. To do so, open a terminal window and import the NICE GPG key.

   ```
   $ wget https://d1uj6qtbmh3dt5.cloudfront.net/NICE-GPG-KEY
   ```

   ```
   $ gpg --import NICE-GPG-KEY
   ```

1.  Download the **Amazon DCV Web Client SDK archive** and the **Amazon DCV Web Client SDK archive signature** from the [Amazon DCV website](https://download.nice-dcv.com/webclientsdk.html) . 

1. Verify the signature of the Amazon DCV Web Client SDK archive using the signature.

   ```
   				$ gpg --verify
   				signature_filename.zip.sign
   				archive_filename.zip
   ```

   For example:

   ```
   $ gpg --verify nice-dcv-web-client-sdk-1.10.1-1011.zip.sign nice-dcv-web-client-sdk-1.10.1-1011.zip
   ```

1. If the signature verifies successfully, extract the contents of the Amazon DCV Web Client SDK archive and place the extracted directory on your web server. For example: 

   ```
   				$ unzip
   				archive_filename.zip
   				-d /
   				path_to
   				/
   				server_directory
   				/
   ```
**Important**  
You must retain the folder structure when deploying the Amazon DCV Web Client SDK on your web server.
When using Amazon DCV Web UI SDK, please beware that the `DCVViewer` React component expects the EULA.txt and third-party-licenses.txt files from this package to be present in the URL path for the embedded web server. The third-party-licenses.txt file should be modified to also include the content of the corresponding file from Amazon DCV Web Client SDK package and possibly any other license information from the libraries used by the consuming user application.

# Connect to a Amazon DCV server and get the first frame
<a name="establish-connection"></a>

The following tutorial shows you how to prepare your HTML page for your custom web client, how to authenticate and connect to a Amazon DCV server, and how to receive the first frame of streamed content from the Amazon DCV session.

**Topics**
+ [Step 1: Prepare your HTML page](#prep-html)
+ [Step 2: Authenticate, connect, and get the first frame](#auth-conn)
+ [Bonus: Automatically create an HTML login form](#get-token)

## Step 1: Prepare your HTML page
<a name="prep-html"></a>

 In your web page, you must load the needed JavaScript modules and you must add a `<div>` HTML element with a valid `id` where you want the Amazon DCV Web Client SDK to draw the content stream from the remote Amazon DCV server. 

For example:

```
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
  <head>
    <title>DCV first connection</title>
  </head>
  <body style="height: 100%;">
    <div id="root" style="height: 100%;"></div>
    <div id="dcv-display"></div>
    <script type="module" src="index.js"></script>
  </body>
</html>
```

## Step 2: Authenticate, connect, and get the first frame
<a name="auth-conn"></a>

This section shows how to complete the user authentication process, how to connect the Amazon DCV server, and how to receive the first frame of content from the Amazon DCV server.

 First, from the `index.js` file import the Amazon DCV Web Client SDK. It can be imported either as a Universal Module Definition (UMD) module, like so: 

```
import "./dcvjs/dcv.js"
```

 Otherwise, starting from version `1.1.0`, it can also be imported as a ECMAScript Module (ESM) from the corresponding package, like so: 

```
import dcv from "./dcvjs/dcv.js"
```

Define the variables to use to store the Authentication object, Connection object, and the Amazon DCV server URL.

```
let auth,
    connection,
    serverUrl;
```

 On script load, log the Amazon DCV Web Client SDK version, and on page load, call the `main` function. 

```
console.log("Using Amazon DCV Web Client SDK version " + dcv.version.versionStr);
document.addEventListener('DOMContentLoaded', main);
```

 The `main` function sets the log level and starts the authentication process. 

```
function main () {
  console.log("Setting log level to INFO");
  dcv.setLogLevel(dcv.LogLevel.INFO);

  serverUrl = "https://your-dcv-server-url:port/";

  console.log("Starting authentication with", serverUrl);

  auth = dcv.authenticate(
    serverUrl,
    {
      promptCredentials: onPromptCredentials,
      error: onError,
      success: onSuccess
    }
  );
}
```

 The `promptCredentials` , `error` , and `success` functions are mandatory callback functions that must be defined in the authentication process. 

 If the Amazon DCV server prompts for credentials, the `promptCredentials` callback function receives the requested credential challenge from the Amazon DCV server. If the Amazon DCV server is configured to use system authentication, then the sign-in credentials must be provided. The following code samples assume that the username is `my_dcv_user` and that the password is `my_password`. 

 If authentication fails, the `error` callback function receives an error object from the Amazon DCV server. 

 If the authentication succeeds, the `success` callback function receives an array of couples that includes the session id ( `sessionId` ) and authorization tokens ( `authToken` ) for each session that the `my_dcv_user` user is allowed to connect to on the Amazon DCV server. The following code sample calls the connect function and connects to the first session returned in the array. 

**Note**  
In the following code example, replace `MY_DCV_USER` with your own username and `MY_PASSWORD` with your own password.

```
function onPromptCredentials(auth, challenge) {
  // Let's check if in challege we have a username and password request
  if (challengeHasField(challenge, "username") && challengeHasField(challenge, "password")) {
    auth.sendCredentials({username: MY_DCV_USER, password: MY_PASSWORD})
  } else {
    // Challenge is requesting something else...
  }
}

function challengeHasField(challenge, field) {
  return challenge.requiredCredentials.some(credential => credential.name === field);
}

function onError(auth, error) {
  console.log("Error during the authentication: " + error.message);
}

// We connect to the first session returned
function onSuccess(auth, result) {
  let {sessionId, authToken} = {...result[0]};

  connect(sessionId, authToken);
}
```

 Connect to the Amazon DCV server. The `firstFrame` callback method is called when the first frame is received from the Amazon DCV server. 

```
function connect (sessionId, authToken) {
  console.log(sessionId, authToken);

  dcv.connect({
    url: serverUrl,
    sessionId: sessionId,
    authToken: authToken,
    divId: "dcv-display",
    callbacks: {
      firstFrame: () => console.log("First frame received")
    }
  }).then(function (conn) {
    console.log("Connection established!");
    connection= conn;
  }).catch(function (error) {
    console.log("Connection failed with error " + error.message);
  });
}
```

## Bonus: Automatically create an HTML login form
<a name="get-token"></a>

 The `challenge` object is returned when the `promptCredentials` callback function is called. It includes a property named `requiredCredentials` that is an array of objects - one object per credential that is requested by the Amazon DCV server. Each object includes the name and the type of the requested credential. You can use the `challenge` and `requiredCredentials` objects to automatically create an HTML login form. 

The following code sample shows you how to do this.

```
let form,
    fieldSet;

function submitCredentials (e) {
  var credentials = {};
  fieldSet.childNodes.forEach(input => credentials[input.id] = input.value);
  auth.sendCredentials(credentials);
  e.preventDefault();
}

function createLoginForm () {
  var submitButton = document.createElement("button");

  submitButton.type = "submit";
  submitButton.textContent = "Login";

  form = document.createElement("form");
  fieldSet = document.createElement("fieldset");

  form.onsubmit = submitCredentials;
  form.appendChild(fieldSet);
  form.appendChild(submitButton);

  document.body.appendChild(form);
}

function addInput (name) {
  var type = name === "password" ? "password" : "text";

  var inputField = document.createElement("input");
  inputField.name = name;
  inputField.id = name;
  inputField.placeholder = name;
  inputField.type = type;
  fieldSet.appendChild(inputField);
}

function onPromptCredentials (_, credentialsChallenge) {
  createLoginForm();
  credentialsChallenge.requiredCredentials.forEach(challenge => addInput(challenge.name));
}
```

# Work with Amazon DCV features
<a name="work-with-features"></a>

The availability of Amazon DCV features depends on the permissions configured for the Amazon DCV session and the capabilities of the client's web browser.

 The features that are available in a Amazon DCV session are managed by the permissions that have been specified for the session. This means that even if a feature is supported by the Amazon DCV Web Client SDK, access to that feature might be prevented based on the permissions defined by the session administrator. For more information, see [ Configuring Amazon DCV Authorization](https://docs.aws.amazon.com/dcv/latest/adminguide/security-authorization.html) in the *Amazon DCV Administrator Guide* . 

## Understanding the featuresUpdate callback function
<a name="understand"></a>

 When the availability of a feature in a Amazon DCV session changes, the Amazon DCV Web Client SDK notifies you using the `featuresUpdate` callback function that you specify at the time of establishing the connection. For example: 

```
featuresUpdate: function (connection, list) {
  ...
},
```

 The callback function notifies you only of the features for which the availability has changed. The `list` parameter is an array of strings, and it includes only the names of the updated features. For example, if the availability of the audio input feature changes for the session, the parameter includes only `["audio-in"]` . If at a later point, the availability of the clipboard copy and paste features change for the session, the parameter includes only `["clipboard-copy", "clipboard-paste"]` . 

## Handling feature updates
<a name="handle"></a>

 The `featuresUpdate` callback function only notifies you that the availability of one or more features has changed. To know which features were updated, you must query the feature using the `connection.queryFeature` method. This can be done at any time after the notification of change has been received. This method returns a `Promise` that resolves to the requested feature's updated status. The `status` value is always associated and it has a Boolean ( `true` \$1 `false` ) property called `enabled` . Some features might have additional properties in the `status` value. If the feature's availability has not been updated, it's rejected. 

The following example code shows how to do this.

```
// Connection callback called
function featuresUpdate (_, list) {
  if (list.length > 0) {
    list.forEach((feat) => {
      connection.queryFeature(feat).then(status => console.log(feat, "is", status.enabled)));
    });
  }
}
```

# Use Amazon DCV Web UI SDK
<a name="render-ui"></a>

 The following tutorial shows you how to authenticate against the Amazon DCV server, connect to it and render the `DCVViewer` React component from the Amazon DCV Web UI SDK. 

**Topics**
+ [Prerequisites](#prerequisites)
+ [Step 1: Prepare your HTML page](#prep-html-ui)
+ [Step 2: Authenticate, connect and render the `DCVViewer` React component.](#auth-conn-render)
+ [Updating from AWS-UI to Cloudscape Design System](#updateawsuitocloudscape)

## Prerequisites
<a name="prerequisites"></a>

 You need to install `React` , `ReactDOM` , `Cloudscape Design Components React` , `Cloudscape Design Global Styles` and `Cloudscape Design Design Tokens` . 

```
$ npm i react react-dom @cloudscape-design/components @cloudscape-design/global-styles @cloudscape-design/design-tokens
```

 You would also need to download `Amazon DCV Web Client SDK` . See [Getting started with the Amazon DCV Web Client SDK](getting-started.md) to read the step-by-step guide on how to do that. 

You must create an alias for importing the `dcv` module, since it is an external dependency for Amazon DCV Web UI SDK. For instance, if you are using webpack to bundle your web app, you can use the [ resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealias) option like so: 

```
const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      dcv: path.resolve('path', 'to', 'dcv.js'),
    },
  },
};
```

If you are using rollup for bundling, you can install [ @rollup/plugin-alias](https://www.npmjs.com/package/@rollup/plugin-alias), and use it like so: 

```
import alias from '@rollup/plugin-alias';
const path = require('path');

module.exports = {
  //...
  plugins: [
    alias({
      entries: [
        { find: 'dcv', replacement: path.resolve('path', 'to', 'dcv.js') },
      ]
    })
  ]
};
```

## Step 1: Prepare your HTML page
<a name="prep-html-ui"></a>

 In your web page, you must load the required JavaScript modules and you should have a `<div>` HTML element with a valid `id` where the entry component of your app will be rendered. 

For example:

```
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
  <head>
    <title>DCV first connection</title>
  </head>
  <body style="height: 100%;">
    <div id="root" style="height: 100%;"></div>
    <script type="module" src="index.js"></script>
  </body>
</html>
```

## Step 2: Authenticate, connect and render the `DCVViewer` React component.
<a name="auth-conn-render"></a>

 This section shows how to complete the user authentication process, how to connect the Amazon DCV server, and how to render the `DCVViewer` React component. 

 First, from the `index.js` file, import `React` , `ReactDOM` and your top level `App` component. 

```
import React from "react";
import ReactDOM from 'react-dom';
import App from './App';
```

Render the top level container node of your app.

```
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);
```

 In the `App.js` file, import the Amazon DCV Web Client SDK as a ESM module, the `DCVViewer` React component from the Amazon DCV Web UI SDK, `React` and the `Cloudscape Design Global Styles` package. 

```
import React from "react";
import dcv from "dcv";
import "@cloudscape-design/global-styles/index.css";
import {DCVViewer} from "./dcv-ui/dcv-ui.js";
```

 Following is an example showing how to authenticate against the Amazon DCV Server and render the `DCVViewer` React component from Amazon DCV Web UI SDK, provided the authentication was successful. 

```
const LOG_LEVEL = dcv.LogLevel.INFO;
const SERVER_URL = "https://your-dcv-server-url:port/";
const BASE_URL = "/static/js/dcvjs";

let auth;

function App() {
  const [authenticated, setAuthenticated] = React.useState(false);
  const [sessionId, setSessionId] = React.useState('');
  const [authToken, setAuthToken] = React.useState('');
  const [credentials, setCredentials] = React.useState({});

  const onSuccess = (_, result) => {
    var { sessionId, authToken } = { ...result[0] };

    console.log("Authentication successful.");

    setSessionId(sessionId);
    setAuthToken(authToken);
    setAuthenticated(true);
    setCredentials({});
  }

  const onPromptCredentials = (_, credentialsChallenge) => {
    let requestedCredentials = {};

    credentialsChallenge.requiredCredentials.forEach(challenge => requestedCredentials[challenge.name] = "");
    setCredentials(requestedCredentials);
  }

  const authenticate = () => {
    dcv.setLogLevel(LOG_LEVEL);

    auth = dcv.authenticate(
      SERVER_URL,
      {
        promptCredentials: onPromptCredentials,
        error: onError,
        success: onSuccess
      }
    );
  }

  const updateCredentials = (e) => {
    const { name, value } = e.target;
    setCredentials({
      ...credentials,
      [name]: value
    });
  }

  const submitCredentials = (e) => {
    auth.sendCredentials(credentials);
    e.preventDefault();
  }

  React.useEffect(() => {
    if (!authenticated) {
      authenticate();
    }
  }, [authenticated]);

  const handleDisconnect = (reason) => {
    console.log("Disconnected: " + reason.message + " (code: " + reason.code + ")");
    auth.retry();
    setAuthenticated(false);
  }

  return (
    authenticated ?
    <DCVViewer
      dcv={{
        sessionId: sessionId,
        authToken: authToken,
        serverUrl: SERVER_URL,
        baseUrl: BASE_URL,
        onDisconnect: handleDisconnect,
        logLevel: LOG_LEVEL
      }}
      uiConfig={{
        toolbar: {
          visible: true,
          fullscreenButton: true,
          multimonitorButton: true,
        },
      }}
    />
    :
    <div
      style={{
        height: window.innerHeight,
        backgroundColor: "#373737",
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      <form>
        <fieldset>
          {Object.keys(credentials).map((cred) => (
            <input
              key={cred}
              name={cred}
              placeholder={cred}
              type={cred === "password" ? "password" : "text"}
              onChange={updateCredentials}
              value={credentials[cred]}
            />
          ))}
        </fieldset>
        <button
          type="submit"
          onClick={submitCredentials}
        >
          Login
        </button>
      </form>
    </div>
  );
}

const onError = (_, error) => {
  console.log("Error during the authentication: " + error.message);
}

export default App;
```

 The `promptCredentials` , `error` , and `success` functions are mandatory callback functions that must be defined in the authentication process. 

 If the Amazon DCV server prompts for credentials, the `promptCredentials` callback function receives the requested credential challenge from the Amazon DCV server. If the Amazon DCV server is configured to use system authentication, then the credentials must be provided in the form of a user name and a password. 

 If authentication fails, the `error` callback function receives an error object from the Amazon DCV server. 

 If the authentication succeeds, the `success` callback function receives an array of couples that includes the session id ( `sessionId` ) and authorization tokens ( `authToken` ) for each session that the user is allowed to connect to on the Amazon DCV server. The code sample above updates the React state to render the `DCVViewer` component on successful authentication. 

 To know more about the properties accepted by this component, see the [ Amazon DCV Web UI SDK reference](https://docs.aws.amazon.com/dcv/latest/websdkguide/dcv-viewer.html#DCVViewer). 

 To know more about self-signed certificates, see the [ Redirection clarifications with self-signed certificates](https://docs.aws.amazon.com/dcv/latest/adminguide/redirection-clarifications-with-self-signed-certs.html). 

## Updating from AWS-UI to Cloudscape Design System
<a name="updateawsuitocloudscape"></a>

 Starting SDK version 1.3.0 we updated our `DCVViewer` component from AWS-UI to its evolution: [Cloudscape Design](https://cloudscape.design/). 

 Cloudscape uses a different visual theme than AWS-UI, but the underlying code base remains the same. Thus, migrating your application based on the `DCVViewer` should be easy. To migrate, replace the AWS-UI related NPM packages you've installed with the associated Cloudscape packages: 


| AWS-UI package name | Cloudscape package name | 
| --- | --- | 
| @awsui/components-react | @cloudscape-design/components | 
| @awsui/global-styles | @cloudscape-design/global-styles | 
| @awsui/collection-hooks | @cloudscape-design/collection-hooks | 
| @awsui/design-tokens | @cloudscape-design/design-tokens | 

 For further details on the migration please refear to the [AWS-UI GitHub documentation page](https://github.com/aws/awsui-documentation). 