Wiki
Clone wikiac-koa-hipchat / Multi-tenancy
One add-on can be installed with multiple HipChat OAuth2 clients, referred to here as 'tenants'. In practice, a tenant is either a HipChat room or group, depending on the installation scope of the add-on.
Tenant registration and information
Tenant installation and uninstallation is handled automatically by the library, which configures the necessary routes and handlers for each mounted add-on. Each installation results in the registration information for that tenant to be verified and stored for later use, including the tenant's shared secret, used for bi-directional authentication.
Add-on implementations are given access to the tenant
information object in every Koa context for routes and webhook listeners in which this library is involved.
ctx.tenant
Field | Description |
---|---|
id |
This tenant's id. |
group |
This tenant's group id. |
secret |
This tenant's shared secret. |
room |
This tenant's room id, if installed in a single room. |
webhookToken |
A secret token added to all dynamically generated webhooks, as an extra measure of security. |
links |
A collection of this tenant's relevant URLs. |
links.capabilities |
This tenant's capabilities descriptor URL. |
links.base |
This tenant's base URL. |
links.api |
This tenant's base API URL. |
links.token |
This tenant's OAuth2 token generation URL. |
Tenant authentication
This library handles bi-direction authentication between tenants and add-ons. It provides the following facilities:
Inbound JWT signature verification
For add-on routes that provide web UI to the tenant, such as the one defined in an add-on's configurable
capabilitity, use the addon
object's authenticate()
middleware to protect your route. This middleware will then verify that requests have a valid JWT signature provided either as the signed_request
query parameter or in a standard HTTP Authorization
header with the format Authorization: JWT token=<jwt-token-value>
.
For example, one would secure an addon's /configure
route with this middleware as follows:
addon.get('/configure', addon.authenticate(), function *() { // Normal Koa route handling here... } );
Requests successfully passing through this middleware will have the following object available on the Koa context:
ctx.authentication
Field | Description |
---|---|
issuer |
The authenticated tenant's id. |
issued |
The timestamp at which the token was generated. |
userId |
The id of the user making the request. |
expiry |
The time at which the token should be expired. |
context |
An additional request context object sent by the tenant as part of the signed data. This may contain the current user's timezone in a field named tz . |
token |
A refreshed version of the JWT token, suitable for use in subsequent requests over Ajax or as form or link parameters. See the Hearsay example add-on for a demonstration of this technique. We prefer this approach to maintaining state for iframed add-on UI over cookies due to some anti-click-jacking browser security models that prevent cookies from being set in cross-domain iframes. |
Outbound request token handling
Outbound requests to the tenant's REST APIs require an current OAuth2 bearer token, which must be refreshed via the tenant's token API when it expires. As long as the add-on uses the ctx.tenantClient
or ctx.roomClient
APIs, this token management is handled automatically. See below for information about the these services.
Tenant services
For convenient add-on implementation, several service objects are attached to the Koa context that provide tenant-aware operations.
Tenant data storage
In order to support multiple tenants concurrently, a tenant-aware data storage abstraction is used throughout the library to partition tenant data, and a derivative of a tenant's unique storage object is provided with each Koa context, allowing add-ons access to a simple, partioned location to store basic key/value information. Storage of more structured data in alternative data stores is left as an exercise for each add-on, though every Koa context contains the full tenant data model, making such manual data partioning straightforward.
ctx.tenantStore
Method | Description |
---|---|
get(key) |
Gets a value for a given key. Returns a promise. |
set(key, value) |
Sets a value for a given key. Returns a promise. |
del(key) |
Deletes a value for a given key. Returns a promise. |
all() |
Gets all values in the current storage scope. Returns a promise. |
narrow(scope) |
Creates and returns a substore narrowed by the given scope. |
Updated