Custom dashboards
Your own dashboard for a project, hosted by us: a static site you build and upload, served on an address of its own, signing your team in with GreenMind and calling the platform API with whatever each person may do.
Use it for what the console does not do for your game: a view of your own data, a tool your designers want, a page your support staff live in. Everything the platform keeps (players, sessions, config, flags and the rest) stays in the console, and your dashboard can read it through the API.
What we host
- Static files only. A ZIP of a built site: HTML, scripts, styles, JSON, pictures, fonts and WebAssembly. Nothing you upload ever runs on our servers. A file of any other type is refused, and so is the upload it is in.
- On an origin of its own. Each project's dashboard is served at
https://<project key>.<dashboards domain>, a domain that is not ours, so your dashboard shares no cookies and no origin with the console, with GreenMind's sites, or with another studio's dashboard. The console's Custom dashboard page shows the address. - Under a strict policy. Every answer carries a Content-Security-Policy that lets a page load scripts, styles, pictures and fonts from its own origin, and call the platform API, and nothing else: no inline scripts, no
eval, no scripts or calls to other sites, no frames in either direction, no forms that leave the dashboard. Inlinestyleattributes are allowed. Build your site to keep its scripts in files (any modern bundler does), and bring any fonts and pictures in the upload rather than from another site. - As a single-page app. An address that names no file and has no extension (
/players/42) is answered withindex.html, so your app's own router can take it from there. An address with an extension that names no file is404. - Cached briefly. Pages are checked on every load, so a publish shows at once; other files may be kept for up to five minutes. Give your built files content-hashed names (a bundler's default) and a new version is never mixed with the old.
Making a dashboard
On your project's page in the console, choose Custom dashboard, then Make the dashboard. Only somebody who may administer the project can: what you upload runs in the browser of everybody who opens it.
Making it gives the dashboard its address and registers its sign-in client with GreenMind. The page lists what your site needs to sign people in:
| Setting | What it is |
|---|---|
| Address | Where the dashboard is served |
| Sign-in client id | The dashboard's own client, for the authorization request |
| Redirect URI | https://<address>/auth/callback, the one address the sign-in returns to |
| Authorization endpoint | Where your page sends the browser to sign in |
| Platform API | The API's base address, which your pages call |
| App keys | Each environment's, which your page names when it signs in |
Uploading, publishing and rolling back
- Build your site so that
index.htmlis at the root of its output (dist/, say), and ZIP it. A ZIP of the folder itself is fine: one folder that holds everything is treated as the root. - Upload it on the Custom dashboard page. It is checked at once: its size, its files' types and paths, and that
index.htmlis there. A refused upload is listed with every reason it was refused, and serves nothing. - Publish a ready version. Everybody who opens the dashboard gets it from then on.
| Limit | Most |
|---|---|
| An upload, zipped | 20 MB |
| Unpacked | 50 MB, and 1,000 files |
| One file | 10 MB |
| A path | 200 characters; no hidden files or folders |
| Versions kept | 50 |
Every version stays listed, newest first. Publishing an older one rolls back to it; Take it down stops serving anything until a version is published again. The page's history says who put what live, and when.
Signing people in
A dashboard signs people in with GreenMind using the authorization code flow with PKCE, the way any single-page app signs in with OpenID Connect, except for its last step: the page hands the code to the platform API, which exchanges it, checks who signed in, and answers with a dashboard token. GreenMind's own tokens never reach your page.
- Make a PKCE verifier, its S256 challenge, a
stateand anonce, and keep them in the page's session storage. - Send the browser to the authorization endpoint with
client_id(your sign-in client id),response_type=code,scope=openid,redirect_uri(your redirect URI),state,nonce,code_challengeandcode_challenge_method=S256. People are asked once whether your dashboard may know who they are. - GreenMind sends the browser back to
/auth/callback?code=...&state=.... Checkstateagainst the one you kept, then callPOST /auth/dashboardwith the code, the verifier, the nonce and the environment to sign in to. - Use the dashboard token as the bearer credential on every call. It lasts an hour; when it runs out, or a call answers
401, sign in again. Somebody still signed in to GreenMind comes straight back.
const API = 'https://api.greenmindmedia.com/api/platform/v1'
/** After the browser comes back to /auth/callback. */
async function finishSignIn(appKey: string) {
const query = new URLSearchParams(location.search)
if (query.get('state') !== sessionStorage.getItem('state')) throw new Error('Sign in again')
const response = await fetch(`${API}/auth/dashboard`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appKey,
code: query.get('code'),
codeVerifier: sessionStorage.getItem('verifier'),
nonce: sessionStorage.getItem('nonce'),
}),
})
if (!response.ok) throw new Error('Sign in again')
// { accessToken, expiresIn, appKey, permissions }
return response.json()
}
Keep the dashboard token in memory, or in session storage at most: it is the person's access to your project's environment, so treat it as you would a password.
A token names one environment. To work in another (test and production, say), sign in to each, with its own app key; somebody who may open a dashboard in only one of them gets a token for that one only. Signing in is refused with forbidden for somebody who may not open a dashboard in the environment at all.
What a dashboard may call
A dashboard token names a person, not a game: it grants nothing by itself. Every route a dashboard may call names the permission it needs in the environment, and the permission engine is asked on every call, so a role given or taken away in the console counts on the next request.
| Route | Needs |
|---|---|
GET /dashboard/session |
open_dashboard |
GET /players |
view_players |
GET /config/values |
open_dashboard |
GET /config/tables |
open_dashboard |
GET /config/tables/:key |
open_dashboard |
GET /flags |
open_dashboard |
GET /dashboard/session answers who the token is for and what they may do in its environment now: use it to decide what your pages show. Any other route answers a dashboard token with forbidden, as does one of these for somebody without its permission. The API reference describes each route; the OpenAPI document marks every route a dashboard may call with x-gm-dashboard-permission.
Who may sign in is whoever may open the environment in the console (open_dashboard), and what they reach beyond that is what their roles there allow. Both are set on the people pages of your project and its environments.
Checking a token on your own server
A dashboard token is a JWT signed with the same keys as players' tokens, with type dashboard. A check for a player's token, which requires type access (see Authentication), refuses it, so a dashboard token can never pass as a player's.