Skip to main content

How to Send Authentication Information in HTTP Request Headers

The authentication information (API key) for the WebSocket interface can be set not only within the s command in the form authorization={API_KEY}, but can also be written in the HTTP request header when establishing the WebSocket connection. This section explains how to do this.

Method 1: Using the Authorization Header

In the HTTP request header at the start of the WebSocket connection, write a custom authentication header as follows.

Authorization: Bearer {API_KEY}

How to specify a custom header differs depending on the client library and execution environment you use.

For example, to rewrite the Python sample code introduced in the WebSocket Interface into this format, add the header argument as follows. Also, remove the description related to authentication information within the s command.

ws = websocket.WebSocketApp('wss://acp-api.amivoice.com/v1/',
header=["Authorization: Bearer {API_KEY}"],
on_open=on_open,
on_message=on_message,
on_close=on_close)

Method 2: Using the Sec-WebSocket-Protocol Header

Even when the Authorization header of Method 1 is not available, the AmiVoice API allows you to use the subprotocol negotiation header to send authentication information.

In this case, write the following in the HTTP request header.

Sec-WebSocket-Protocol: wrp, {API_KEY}

For example, to rewrite the Python sample code introduced in the WebSocket Interface into this format, add the argument that specifies the subprotocol as follows. Also, remove the description related to authentication information within the s command.

ws = websocket.WebSocketApp('wss://acp-api.amivoice.com/v1/',
subprotocols=["wrp", "{API_KEY}"],
on_open=on_open,
on_message=on_message,
on_close=on_close)
warning

The method using the Sec-WebSocket-Protocol header can also be used in environments where the Authorization header cannot be specified directly, such as browser JavaScript. However, note that when you connect directly from the browser using this method, the API key becomes accessible from the client-side code or the execution environment. To avoid distributing the API key to end users, connect to the AmiVoice API on the server side, such as through a proxy server.

Priority of Authentication Information

When authentication information is written using multiple methods, the following priority order is used for the actual authentication.

  1. authorization={API_KEY} within the s command
  2. Authorization: Bearer {API_KEY} header (Method 1)
  3. Sec-WebSocket-Protocol: wrp, {API_KEY} header (Method 2)

If you want to use the authentication information written in the HTTP request header, do not write the authentication information (authorization) within the s command.

Managing the API Key on a Proxy Server

If you do not want to distribute the API key to client devices for security reasons, one possible configuration is to manage the API key on a server-side component such as a proxy server, connect to the AmiVoice API from that server-side component, and have the client devices connect to the server. However, in the case of the WebSocket interface, if you send the API key with the s command after establishing the WebSocket session, then in a configuration where the proxy server merely relays the WebSocket, the API key cannot be added along the way, and the client device ends up having to hold the API key.

This is where the method of sending authentication information explained here is useful. When the proxy server establishes the WebSocket connection to the AmiVoice API, by setting the API key in the HTTP request header at the time of connection, you can achieve a configuration that does not distribute the API key to the end users' client devices.

Note that even when the application is in an environment where the Authorization header cannot be specified directly, such as browser JavaScript, Method 1 can be used if the proxy server makes the WebSocket connection to the AmiVoice API.

Sample Code

Below is the Sample Code for the WebSocket interface rewritten to use Method 1. The rewritten parts are highlighted.

websocket-sample-2.py
import time
import websocket
import json
import threading
import logging


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(threadName)s %(message)s")

server = 'wss://acp-api.amivoice.com/v1/'
header = ["Authorization: Bearer {API_KEY}"]
filename = 'test.wav'
codec = "16K"
audio_block_size = 16000
grammar_file_names = "-a-general"
options = {
"profileId" : "",
"profileWords" : "",
"keepFillerToken": "",
"resultUpdatedInterval" : "1000",
}


def on_open(ws):
logger.info("open")
def start(*args):
command = "s {} {}".format(codec, grammar_file_names)
for k, v in options.items():
if v != "":
if k == 'profileWords':
v = '"' + v.replace('"', '""') + '"'
command += f" {k}={v}"
logger.info(f"send> {command}")
ws.send(command)
threading.Thread(target=start).start()


def on_message(ws, message):
event = message[0]
content = message[2:].rstrip()
logger.info(f"message: {event} {content}")

if event == 's':
if content == "can't connect to recognizer server":
logger.error(content)
return

def send_audio(*args):
with open(filename, mode='rb') as file:
buf = file.read(audio_block_size)
while buf:
logger.debug("send> p [..({} bytes)..]".format(len(buf)))
ws.send(b'p' + buf,
opcode=websocket.ABNF.OPCODE_BINARY)
buf = file.read(audio_block_size)
time.sleep(0.5)
logger.info("send> e")
ws.send('e')
threading.Thread(target=send_audio).start()

elif event == 'G':
pass
elif event == 'S':
starttime = int(content)
elif event == 'E':
endtime = int(content)
elif event == 'C':
pass
elif event == 'U':
raw = json.loads(content) if content else ''
elif event == 'A' or event == 'R':
raw = json.loads(content) if content else ''
elif event == 'e':
logger.info("close>")
ws.close()


def on_close(ws):
logger.info("close")


logger.info("open> {}".format(server))
ws = websocket.WebSocketApp(server,
header=header,
on_open=on_open,
on_message=on_message,
on_close=on_close)
ws.run_forever()