Contents
OctoPrint offers a special API key type for apps to use, the so called App Session Key. These keys have a time based validity and are generated by OctoPrint for requesting apps.
Obtaining those keys is based on a handshake procedure backed by cryptographic signatures using RSA. OctoPrint needs to
be aware of apps and their associated public keys (this can be achieved either via entries in config.yaml
or by
installing app specific plugins which implement the AppPlugin
type).
Apps can be registered within OctoPrint via config.yaml
by adding them to the api
> apps
section, using the
application's id concatenated with its version as key, with the public key provided as item pubkey
(stripped of the
BEGIN RSA PUBLIC KEY
and END RSA PUBLIC KEY
separators and also newlines) and optionally also whether the app is
enabled or not (defaults to enabled, so can be left out if it's not to be set to disabled explicitly).
Example:
api:
apps:
"com.example.my_octoprint_app:0.9":
pubkey: MEgCQQDYkr5Fv/YXK5ZL1uwRN4A61IagZaYLGqJ5JJGFo8wDrmpAMRqE9kK4+5hIDblC5DzfEr5oP7OA3tRO48Rf5yInAgMBAAE=
enabled: false
"com.example.my_octoprint_app:1.0":
pubkey: MEgCQQCIWfi7Nc8bcnfZJJtA6a4RyMC+sKBlMOb25OVNNB4L2v0TiGO72jVKR4osvb4oztlbRW5GkdiY0T2LJcfDYvkJAgMBAAE=
In the example, the app com.example.my_octoprint_app
in version 0.9 has been disabled (e.g. due to the key having
leaked) whereas version 1.0 is fully registered with OctoPrint and may verify app session keys.
Apps perform the handshake by first requesting a temporary key with very limited validity, then sending a message back to OctoPrint containing their id, version, the temporary key and a signature created with their private key over these three pieces of data. OctoPrint then tries to verify the signature and if successful unlocks the key to be used as a fully recognized API key.
For performing the handshake a special API exists within OctoPrint for which no API key is needed which is described below.
.. http:get:: /apps/auth Retrieve a temporary session key with a minimum validity. It can only be used as a proper API key after having been :ref:`verified <sec-api-apps-sessionkey-verify>`. Returns the temporary session key and the timestamp until it's valid. **Example**: .. sourcecode:: http GET /apps/auth HTTP/1.1 Host: example.com .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "unverifiedKey": "F43A844750F74AD080FE9F438D47B33C", "validUntil": 1416220357.011 } :statuscode 200: No error
.. http:post:: /apps/auth Verify a formerly :ref:`retrieved <sec-api-apps-sessionkey-get>` temporary session key by providing credentials and a cryptographic signature over these credentials and the temporary key. Returns the now verified session key and the new validity. **Example**: .. sourcecode:: http POST /apps/auth HTTP/1.1 Host: example.com Content-Type: application/json { "appid": "com.example.my_octoprint_app", "appversion": "1.0", "key": "F43A844750F74AD080FE9F438D47B33C", "_sig": "LGVCiolQWDc4AVn1DOcWljY0cFQxWF4pldVveUjjmL9JhiL0LnCKBbGwZ/CwKBWswFAxPaxQ0kDusVdOmCUa/w==" } .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "key": "F43A844750F74AD080FE9F438D47B33C", "validUntil": 1416227497.011 }
The signature is created by concatenating the appid
, appversion
and key
fields, separated by a :
(colon),
signing the result with the app's private key using SHA-1 and then BASE64-encoding the result, stripping newlines.
Example for signature generation using Python and the Python RSA library:
import base64
import rsa
appid = "com.example.my_octoprint_app"
version = "1.0"
unverified_key = "F43A844750F74AD080FE9F438D47B33C"
message_to_sign = appid + ":" + version + ":" + unverified_key
// => "com.example.my_octoprint_app:1.0:F43A844750F74AD080FE9F438D47B33C"
private_key = rsa.PrivateKey.load_pkcs1("...")
signature = base64.encodestring(rsa.sign(message_to_sign, private_key, "SHA-1")).replace("\n", "")
// => "LGVCiolQWDc4AVn1DOcWljY0cFQxWF4pldVveUjjmL9JhiL0LnCKBbGwZ/CwKBWswFAxPaxQ0kDusVdOmCUa/w=="
If you want to use app session keys, here is the key pair with which the above examples were created, in order for you to verify your signature implementation:
-----BEGIN RSA PRIVATE KEY----- MIIBPQIBAAJBAIhZ+Ls1zxtyd9kkm0DprhHIwL6woGUw5vbk5U00Hgva/ROIY7va NUpHiiy9vijO2VtFbkaR2JjRPYslx8Ni+QkCAwEAAQJARK4lFo+FEcs3yR2iQjEy p+yaAbNQJ4hZXlVvltLAYICzOM3kyKx53/eKU59NjskLz9q6QxfleymYPWAgl4NW fQIjAJVH8MjwNcaAquTM9z2OiFi3OC8WgaKOi5W/T+r2+B70wG8CHwDp08dqOZ/u xcBiy4Wzpcme9bckqoVuS3gWMm+YqgcCIwCMFU07kkY0NyumtzxPdIA4F/7OGSWf IHqWFEfvasAddHlbAh8A5UgkB3Zf7Bt+7aFSBnlvve6FWm/XDPL12xYztYgrAiIa W3miN6FjIm+8TDowrk+nyYXG2GZefeY7QXOjYr6tlDn0 -----END RSA PRIVATE KEY----- -----BEGIN RSA PUBLIC KEY----- MEgCQQCIWfi7Nc8bcnfZJJtA6a4RyMC+sKBlMOb25OVNNB4L2v0TiGO72jVKR4os vb4oztlbRW5GkdiY0T2LJcfDYvkJAgMBAAE= -----END RSA PUBLIC KEY-----