Skip to main content
Version: 1.0

Authentication

Communication through HTTP and Websocket channels is secured by a digest authentication mechanism using the SHA256 hmac algorithm as defined in RFC7616.

When enabled, all communication is protected except:

  • Communication over MQTT
  • Communication over UART
  • Communication over Outbound Websocket
  • the RPC method Shelly.GetDeviceInfo
  • the HTTP endpoint /shelly

Setting Authentication Credentials for a Device

Authentication can be enabled by setting authentication details through the RPC method Shelly.SetAuth. A pre-calculated ha1 parameter is expected - this is the result of SHA256(<user>:<realm>:<password>). Please refer to RFC7616 for further details on the mechanism.

Example:

HA1 calculation with JavaScript
   let crypto = require('crypto');
let username = 'admin'; // always
let password = 'mysecretpassword';
let realm = 'shellymodel-XXXXXXXXXXXX';
let auth_parts = [username, realm, password];
let ha1 = crypto.createHash("sha256").update(auth_parts.join(':')).digest("hex");
console.log(ha1);

Authentication process

sequenceDiagram autonumber Client->>Device: Requesting protected resource. Device->>Client: Error 401 with authentication challenge. Client->>Device: Requesting protected resource with credentials. Device->>Client: Result of the successful request.

Steps in the process:

  1. Client requests a protected resource without providing credentials.
  2. Server response containing error 401 (unauthorized) is received.
  3. Client requests the same protected resource but this time providing credentials.
  4. The request is successful and access to the resource is granted.

When communicating over HTTP, this process must be repeated for each request you send to the device. However, for communication over websocket there is no need to pass through steps 1 and 2 more than once: you need to construct the auth object only once(see more about that below) and then use it for all consecutive requests to that device.

Error 401 and the Authentication Challenge

The error 401 response mentioned in step 2 contains information about:

  • auth_type: string, digest
  • nonce: number, random or pseudo-random number to prevent replay attacks
  • nc: number, nonce counter (returned only through websocket channel). It has value of 1 if it is not available in the response
  • realm: string, device_id of the Shelly device
  • algorithm: string, SHA-256

Authentication over HTTP

When communicating through a HTTP channel this information can be found in the WWW-Authenticate header of the response.

Example:

Request
curl -X POST -i -d '{"id":1, "method":"Shelly.GetStatus"}' http://${SHELLY}/rpc
Response
HTTP/1.1 401 Unauthorized
Server: ShellyHTTP/1.0.0
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Content-Length: 0
Connection: close
WWW-Authenticate: Digest qop="auth", realm="shellypro4pm-f008d1d8b8b8", nonce="60dc59c6", algorithm=SHA-256

Authentication over Websocket

When communicating through a websocket channel, the response contains error frame where the above attributes can be found.

Example:

Request
curl -X POST -d '{"id":1, "method":"Shelly.DetectLocation"}' http://${SHELLY}/rpc
Response
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"error": {
"code": 401,
"message": "{\"auth_type\": \"digest\", \"nonce\": 1625038762, \"nc\": 1, \"realm\": \"shellypro4pm-f008d1d8b8b8\", \"algorithm\": \"SHA-256\"}"
}
}

Successful Request with Authentication Details

In order to make the request work properly, an additional auth JSON object should be added to the request frame. It should contain:

  • realm: string, device_id of the Shelly device. Required
  • username: string, must be set to admin. Required
  • nonce: number, random or pseudo-random number to prevent replay attacks, taken from the error message. Required
  • cnonce: number, client nonce, random number generated by the client. Required
  • response: string, encoding of the string <ha1> + ":" + <nonce> + ":" + <nc> + ":" + <cnonce> + ":" + "auth" + ":" + <ha2> in SHA256. Required
    • ha1: string, <user>:<realm>:<password> encoded in SHA256
    • ha2: string, "dummy_method:dummy_uri" encoded in SHA256
  • algorithm: string, SHA-256. Required

Example:

Request1
curl -X POST -d '{"id":1, "method":"Shelly.DetectLocation", "auth":
{"realm": "shellypro4pm-f008d1d8b8b8", "username": "admin", "nonce": 1625038762,
"cnonce": 313273957, "response": "eab75cbbd7acdb7082164cb52148cfbe351f28bf80856f93a23387c6157dbb69",
"algorithm": "SHA-256"}}' \
http://${SHELLY}/rpc

Or:

Request2
curl --anyauth -u admin:mypass http://${SHELLY}/rpc/Shelly.DetectLocation

Or:

Request3
curl --anyauth -u admin:mypass -X POST -d '{"id":1, "method":"Shelly.DetectLocation"}' http://${SHELLY}/rpc
Response
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"result": {
"tz": "Europe/Sofia",
"lat": 42.67236,
"lon": 23.38738
}
}

Client implementations

SSL support for outbound connections

Gen2+ provides secure socket support for:

  • MQTT with support for CA bundle selection
  • Webhooks (only built in CA bundle supported, support for CA selection will be added in 0.8.0)
  • The RPC method HTTP.GET

Internally, TLS is provided by the industry-standard mbedtls library. Certificate validation is supported, and users can choose between no validation, a built-in CA bundle and a user-provided CA bundle.