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 with a nonce.
  3. Client requests the same protected resource but this time providing credentials including the nonce.
  4. The request is successful and access to the resource is granted.

Efficient Authentication with Nonce Reuse (Firmware 2.0.0+)

Starting with firmware 2.0.0, clients can reuse the same nonce for multiple requests by incrementing the nc (nonce count) parameter:

sequenceDiagram autonumber Client->>Device: Request without credentials Device->>Client: 401 + nonce Client->>Device: Request with auth (nc=00000001) Device->>Client: Success Client->>Device: Request with auth (nc=00000002) Device->>Client: Success Client->>Device: Request with auth (nc=00000003) Device->>Client: Success Note over Client,Device: ...continue up to 30,000 requests or 1 hour... Client->>Device: Request with auth (expired/exhausted nonce) Device->>Client: 401 + stale=true + new nonce Client->>Device: Request with auth (nc=00000001, new nonce) Device->>Client: Success

This allows clients to avoid the 401 challenge-response overhead for every request, significantly improving communication efficiency.

Nonce Handling (Firmware 2.0.0+)

Starting with firmware 2.0.0, the device implements RFC 7616-compliant nonce management:

  • Nonce validity: Each nonce is valid for 1 hour from the time it was issued.
  • Nonce reuse: A single nonce can be reused for up to 30,000 requests by incrementing the nc (nonce count) parameter with each request.
  • Replay protection: The device tracks the nonce count and rejects requests with an nc value that is not strictly greater than the previously seen value for that nonce.
  • Stale nonces: When a nonce expires or exceeds its usage limit, the device returns a 401 response with stale=true, indicating the client should obtain a new nonce without re-prompting the user for credentials.

This allows for efficient communication over both HTTP and WebSocket:

  • HTTP: You can reuse the same nonce across multiple requests by incrementing nc each time, avoiding the overhead of obtaining a new nonce for every request.
  • WebSocket: Construct the auth object once and reuse it for consecutive requests, incrementing nc for each call.

Nonce Tracking and Eviction (Firmware 2.0.0+)

The device maintains a circular buffer of up to 32 nonce entries to track active sessions:

Nonce StateDescription
PendingNonce issued in a 401 challenge, client hasn't responded yet (nc not yet received)
ActiveClient has authenticated at least once with this nonce

Eviction policy when the buffer is full:

  1. Empty slots are used first.
  2. Least-used active sessions (sessions used only once) are evicted next.
  3. Pending challenges are protected - unanswered 401 challenges are not evicted in the first pass.
  4. If no suitable slot is found, a 2-second throttle window begins. During this time, requests for new nonces return HTTP 429 (Too Many Requests).
  5. After the throttle window expires, the slot with the minimum usage count is force-evicted (including pending challenges if necessary).

This design ensures that:

  • Active sessions with ongoing communication are preserved longer.
  • Pending authentication challenges have time to complete.
  • The device gracefully handles high connection loads with temporary throttling rather than immediate eviction.

Brute-Force Protection (Firmware 2.0.0+)

The device implements brute-force protection to prevent password guessing attacks:

  • Failed authentication attempts are tracked using a 10-minute sliding window.
  • After multiple failed attempts, the device enforces progressive backoff delays:
    • 1-10 failures: No delay
    • 11-20 failures: 10 seconds between attempts
    • 21-30 failures: 30 seconds between attempts
    • 31-40 failures: 60 seconds between attempts
    • 40+ failures: 5 minutes between attempts
  • When throttled, the device returns HTTP status 429 (Too Many Requests) instead of 401.
  • Successful authentication with a fresh nonce resets the failure counters.
  • Brute-force counters are reset on the device reboot/reset.

Error 401 and the Authentication Challenge

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

  • auth_type: string, digest
  • nonce: string, base64-encoded cryptographic token (firmware 2.0.0+) or numeric value (prior versions)
  • nc: number, nonce counter (returned only through websocket channel). (removed in firmware 2.0.0+)
  • realm: string, device_id of the Shelly device
  • algorithm: string, SHA-256
  • stale: boolean (optional), when true indicates the nonce has expired but credentials are valid - client should retry with a new nonce (since firmware 2.0.0)

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="AAAAAABn...(base64)...", 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\": \"AAAAAABn...(base64)...\", \"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: string, the nonce value from the authentication challenge. Required
  • cnonce: number, client nonce, random number generated by the client. Required
  • nc: string, nonce count - a client-side monotonically increasing counter represented as 8 hexadecimal digits padded with leading zeros (e.g., "00000001", "0000000a"). Usually starts from "00000001" for a fresh nonce and increment with each request. Required
  • response: string, the digest response calculated as described below. Required
  • algorithm: string, SHA-256. Required
Calculating the Response

The response field is calculated as: SHA256(<ha1>:<nonce>:<nc>:<cnonce>:auth:<ha2>)

Where:

  • ha1: SHA256(<user>:<realm>:<password>) - can be pre-computed and stored
  • ha2: SHA256(<method>:<uri>) - depends on the transport type:
TransportMethodURI
HTTPThe actual HTTP method (e.g., "GET", "POST")The actual request URI (e.g., "/rpc/Shelly.GetStatus")
WebSocket RPC"dummy_method""dummy_uri"
Other RPC transports"dummy_method""dummy_uri"

Example for WebSocket/RPC:

Response calculation for WebSocket
let ha1 = "..."; // pre-calculated SHA256(admin:realm:password)
let ha2 = crypto.createHash("sha256").update("dummy_method:dummy_uri").digest("hex");
let response = crypto.createHash("sha256")
.update(`${ha1}:${nonce}:${nc}:${cnonce}:auth:${ha2}`)
.digest("hex");

Example for HTTP:

Response calculation for HTTP
let ha1 = "..."; // pre-calculated SHA256(admin:realm:password)
let method = "POST";
let uri = "/rpc";
let ha2 = crypto.createHash("sha256").update(`${method}:${uri}`).digest("hex");
let response = crypto.createHash("sha256")
.update(`${ha1}:${nonce}:${nc}:${cnonce}:auth:${ha2}`)
.digest("hex");

Example request:

Request1
curl -X POST -d '{"id":1, "method":"Shelly.DetectLocation", "auth":
{"realm": "shellypro4pm-f008d1d8b8b8", "username": "admin", "nonce": "AAAAAABn...(base64)...",
"cnonce": 313273957, "nc": "00000001", "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


Prior to Firmware 2.0.0

Legacy Behavior

The following section describes the authentication behavior in firmware versions prior to 2.0.0. If you are developing new integrations, please refer to the sections above for the current implementation.

Nonce Handling (Legacy)

In firmware versions prior to 2.0.0:

  • Nonces were simple numeric values (e.g., 1625053638)
  • There was no server-side tracking of nonce counts
  • Nonces did not have a built-in expiration mechanism
  • Each HTTP request required going through the full 401 challenge-response cycle

Authentication Details (Legacy)

The auth object parameters:

  • realm: string, device_id of the Shelly device. Required
  • username: string, must be set to admin. Required
  • nonce: number, the numeric nonce from the error message. Required
  • cnonce: number, client nonce, random number generated by the client. Required
  • nc: string, nonce count - a client-side counter, usually has value of "00000001".
  • response: string, calculated using ha1, ha2, nonce, cnonce and nc. Required
    • ha1: string, <user>:<realm>:<password> encoded in SHA256
    • ha2: string, "dummy_method:dummy_uri" encoded in SHA256
  • algorithm: string, SHA-256. Required

Note: In the legacy implementation, nc was not tracked server-side, so clients typically used "00000001" for all requests.

Authentication Challenge Example (Legacy)

Legacy HTTP Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest qop="auth", realm="shellypro4pm-f008d1d8b8b8", nonce="60dc59c6", algorithm=SHA-256
Legacy WebSocket Response
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"error": {
"code": 401,
"message": "{\"auth_type\": \"digest\", \"nonce\": 1625053638, \"nc\": 1, \"realm\": \"shellypro4pm-f008d1d8b8b8\", \"algorithm\": \"SHA-256\"}"
}
}

Legacy Request Example

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

Note: In the legacy implementation, the nonce was a number rather than a base64-encoded string, and there was no brute-force protection mechanism.


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.