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
The following steps should be performed once per process.
-
Generate a device key, on first use. If there is no existing device key, generate it. A device key is an ECDSA P-256 key pair. This is the 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 the tenant configuration. Each tenant with Groove.id has a unique configuration. You can determine your configuration by fetching
https://SIGNIN_URL/config
, where SIGNIN_URL is a virtual host configured for your tenant.$ curl https://SIGNIN_URL/config 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
. You must includeX-Grooveid-Tenant
and eitherX-Grooveid-Date
orDate
.X-Grooveid-SignedHeaders: Accept;Content-Type;User-Agent;X-Grooveid-Date;X-Grooveid-Tenant
Note
You must include one ofX-Grooveid-Date
orDate
in signed headers. If you don’t your request will be rejected. -
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.
api.groove.id
- 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.
The canonical header string is computed by concatenating:
- each of the signed headers, each terminated by
\r\n
- The hex-encoded SHA-256 hash of the request body.
For example:
Accept: application/json\r\nContent-Type: application/json\r\nb5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
- The server’s host name, e.g.
-
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 to sign_ 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 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 shared secret
If you’ve just generated a new session key, you will have to derive the session secret before you can make other requests.
You may also need to generate a session shared 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
https://api.groove.id
HEAD / HTTP/1.0 Host: api.groove.id X-Grooveid-Date: Mon, 02 Jan 2006 15:04:05 GMT X-Grooveid-Tenant: TENANT X-Grooveid-Signed-Headers: X-Grooveid-Date;X-Grooveid-Tenant Authorization: gv1 dev=DEVICE_PUBLIC_KEY&sig=SIGNATURE&ses=SESSION_PUBLIC_KEY
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 public key and the session key 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 init request might look like:
HEAD / HTTP/1.1 Authorization: gv1 dev=BLhLAwTdTjN196LHydRmMqeZANw_-0pbQRhkxlvdvOTscX4jgPsspnC0lANeHkFUNV1Yq_izYBDzl0RRdmtJ4aE&sig=vp7darrJhKOqRW4J9WDy-bnvZeCe-sLhdov6O_kDkmLIqOHLzp0pRPzABY4gRTDWEVfABsYe2mvmKc5O01cjaA&ses=BKoLpJ1b22sHao4ODdIhRaKXU6G6eb6et90a4rpwD96OgE2RCbEVRkRPQgSAGOrPd43xjg2-5gSxf4orGHzEvx4 Origin: https://signin.initech.biz Referer: https://signin.initech.biz/ 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.