Authenticating with GV1

The GV1 protocol is used mostly by internal tools, such as the web interface and client tools, to establish authorized sessions on your behalf.]

Note: Using GV1 is more complex than the other methods, so if your use case supports one of the other methods, prefer it over GV1.

Getting Started

Generate a device key

First you must generate an ECDSA P-256 key pair. This is the device key which will uniquely identify your device to the API. Store this key securely on the device.

Generate a session key

Each time you connect, you’ll also need an ECDSA P-256 to use as a session key. You should not store this key to disk.

Fetch Configuration

Each account with has a unique configuration. You can determine your configuration by fetching /config from your user signin URL. For example:

$ curl
tenant: 5xyyocliasebyh

Constructing a request

Determine which headers will be signed

You don’t need to sign all the headers, but any HTTP headers you include that are not part of the signed headers will be ignored. Add your semicolon-delimited list of headers to the request as X-Grooveid-SignedHeaders:

X-Grooveid-SignedHeaders: Accept;Content-Type;User-Agent;X-Grooveid-Date;X-Grooveid-Tenant

Note: You must include one of X-Grooveid-Date or Date in signed headers. If you don’t your request will be rejected.

Every request must include X-Grooveid-Tenant in the signed headers.

Compute the string to sign

The string to sign consists of the following fields, separated by newline characters:

  • The server’s host name, e.g.
  • The tenant ID, e.g. 5xyyocliasebyh
  • The request method, e.g. POST
  • The path part of the URL, e.g. /users
  • The query string, e.g. start=10&limit=100
  • The hex-encoded SHA-256 hash of the canonical header string, which is computed by concatenating each of the signed headers, each terminated by \r\n, e.g. Accept: application/json\r\nContent-Type: application/json\r\n
  • The hex-encoded SHA-256 hash of the request body.

Compute the Authorization Header

The authorization header consists of the string gv1, a space, and a URL encoded string consisting of:

  • dev=DEVICE-PUBLIC-KEY - The device public key, base64 encoded using the URL-safe alphabet and with trailing equals-sign characters stripped.
  • sig=SIGNATURE - The ECDSA signature of the _string_tosign using the device key.
  • ses=SESSION-PUBLIC-KEY - The session public key, base64 encoded using the URL-safe alphabet and with trailing equals-sign characters stripped.
  • mac=MAC - the HMAC SHA-256 of signature using the session shared secret, if a session secret has been established. If the session secret has not yet been established, such as in the first request of a session, then omit this parameter.

Derive Session Secret

If you’ve just generated a session key, you will have to derive the session secret before you can make other requests.

You may also need to generate a session secret if your request is rejected with a response header containing X-Error-Code: Invalid Session. If this happens, discard the session key and shared secret, regenerate them, and retry the request.

To generate a shared secret, make a HEAD request to

X-Grooveid-Date: Mon, 02 Jan 2006 15:04:05 GMT
X-Grooveid-Tenant: 5xyyocliasebyh
X-Grooveid-Signed-Headers: X-Grooveid-Date;X-Grooveid-Tenant

The response will contain an X-Grooveid-Session-Init header which contains a URL-safe base64-encoded ECDSA public key on the P-256 curve, the server public key.

Use the _server_publickey and the _sessionkey to perform Elliptic-curve Diffie–Hellman (ECDH) to derive the shared secret. Use the shared secret to compute the mac parameter in the Authorization header.

Your request might look like:

Authorization: gv1 dev=BLhLAwTdTjN196LHydRmMqeZANw_-0pbQRhkxlvdvOTscX4jgPsspnC0lANeHkFUNV1Yq_izYBDzl0RRdmtJ4aE&sig=vp7darrJhKOqRW4J9WDy-bnvZeCe-sLhdov6O_kDkmLIqOHLzp0pRPzABY4gRTDWEVfABsYe2mvmKc5O01cjaA&ses=BKoLpJ1b22sHao4ODdIhRaKXU6G6eb6et90a4rpwD96OgE2RCbEVRkRPQgSAGOrPd43xjg2-5gSxf4orGHzEvx4
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
x-grooveid-date: Mon, 10 Dec 2018 21:07:23 GMT
x-grooveid-signedheaders: X-Grooveid-Date;X-Grooveid-Tenant
x-grooveid-tenant: 5xyyocliasebyh

And the response might look like:

HTTP/1.1 401 Unauthorized
date: Mon, 10 Dec 2018 21:07:23 GMT
expires: -1
x-content-type-options: nosniff
x-grooveid-session-init: BD6KGb387jywwcPjbaa0lTQSXZgnqRmmkNvmEy72nkbEqM44jrlWJDRr1nP5dgvSvI0Y_6ZzLs-SjHXoiN1GUvY

Use the session secret to compute the mac parameter in subsequent requests.

Putting it all together

You can now make authenticated requests to the API, which might look like:

GET /tenant HTTP/1.1
Authorization: gv1 dev=BLhLAwTdTjN196LHydRmMqeZANw_-0pbQRhkxlvdvOTscX4jgPsspnC0lANeHkFUNV1Yq_izYBDzl0RRdmtJ4aE&sig=4TDwIs0Ff8BZ0IXy63-X8vOc2EL0b1M4kFYsEJKXRmeQk6P6HXNhHT2eXpiYnZXuq1P0z3VO-7lCvtq59ZPSHQ&ses=BKoLpJ1b22sHao4ODdIhRaKXU6G6eb6et90a4rpwD96OgE2RCbEVRkRPQgSAGOrPd43xjg2-5gSxf4orGHzEvx4&mac=NxiGKLIC3bZs6OOWak6EC6HTyeQq0_VRsQ_C_dp_2Xc
x-grooveid-date: Mon, 10 Dec 2018 21:07:23 GMT
x-grooveid-signedheaders: X-Grooveid-Date;X-Grooveid-Tenant
x-grooveid-tenant: 5xyyocliasebyh

The server will respond with 200 Ok and a JSON-encoded tenant object.