Skip to content

Contributing

Sebastian F. Markdanner [MVP] edited this page May 11, 2026 · 2 revisions

This is the long-form contributor guide. The short version lives in CONTRIBUTING.md.

Project ground rules

These constraints are not negotiable in casual PRs — changing any of them needs a discussion in an issue first.

  • No backend. Every privileged call goes from the browser directly to Microsoft Graph or Azure Resource Manager.
  • No bundler, no framework. Portal/ is plain HTML / CSS / vanilla JavaScript so it can be served by any static host with no build step.
  • No new third-party origins without a corresponding update to the Content Security Policy in Portal/staticwebapp.config.json. The CSP is the security boundary — keep it minimal.
  • No inline scripts and no eval. The CSP forbids both.
  • Tokens stay in sessionStorage. Do not introduce code that persists access tokens, refresh tokens, or claims to localStorage, IndexedDB, cookies, or any other persistent store.
  • Delegated permissions only. No app-only permissions, no client secrets, no service principal credentials anywhere in the codebase or deployment artifacts.

Architecture orientation

Read How It Works before making non-trivial changes. The sections most likely to surprise contributors:

  • auth.js owns claims-threading. A new API code path that calls Microsoft Graph or ARM directly (instead of going through the existing clients) will skip claims threading and fail mid-operation when policies require auth contexts.
  • api/batch-client.js owns concurrency. Don't Promise.all an unbounded number of ARM calls — use the existing concurrency primitive.
  • api/graph-client.js owns batch chunking and 429 retry. New Graph calls should go through it.
  • policy-cache.js is in-memory and tenant-keyed. Adding a new fetch path should reuse it rather than re-fetching policies.
  • roles.js is the rendering brain. Changes that touch role state should respect the stable uid keying so selection survives refreshes.

Coding style

  • Modern JavaScript (ES2022+), no transpilation step.
  • Two-space indentation, single quotes, semicolons.
  • Small, well-named functions; comments where intent is non-obvious.
  • DOM updates batched where reasonable; no animation frame thrashing.
  • Public surface (anything attached to window) stays small and stable.
  • Prefer const; reach for let only when you reassign; avoid var.

Browser test matrix

For any user-facing change, test at minimum:

  • Latest Microsoft Edge (Chromium) on Windows
  • Latest Mozilla Firefox on Windows
  • Latest Safari on macOS or iOS

When testing across tenants, cover at least:

  • A single-tenant scenario (you are signed in to your home tenant only)
  • A multi-tenant scenario (you have eligibilities in your home tenant and as a guest in another)

CSP discipline

If your change involves a new third-party origin (a new CDN, a new API), update Portal/staticwebapp.config.json in the same PR and call it out at the top of the PR description. PRs that load resources from origins not listed in the CSP will fail in production with cryptic errors — saving reviewers the deciphering work is appreciated.

Pull-request checklist

Before opening a PR, confirm:

  • No backend, proxy, or third-party API was added.
  • No new third-party origin was introduced without a corresponding CSP update.
  • No inline scripts and no eval were introduced.
  • Tokens still live only in sessionStorage.
  • Delegated permissions only — no app permissions, secrets, or service-principal credentials added.
  • Tested in Chromium and Firefox (Safari for UI changes).
  • If UI changed, screenshots or a short GIF are attached.
  • If Bicep was changed, Portal/deploy/azuredeploy.json was rebuilt.
  • CHANGELOG.md updated under ## [Unreleased] for any user-visible change.

The PR template enforces most of this — you'll see the checklist auto-populated when you open the PR.

Commit messages

Conventional Commits style is encouraged but not strictly required:

feat(profiles): support tenant-scoped imports
fix(batch-client): honor Retry-After in seconds vs HTTP-date
docs(readme): clarify managed vs self-hosted authority

Working with the deployment template

The Bicep file Portal/deploy/bicep/portal-selfhosted.bicep is the source of truth. The corresponding ARM template Portal/deploy/azuredeploy.json is regenerated by the Sync Deployment Templates workflow. PRs that change Bicep without updating the ARM artifact are auto-flagged. To regenerate locally:

az bicep build --file Portal/deploy/bicep/portal-selfhosted.bicep --outfile Portal/deploy/azuredeploy.json

AI-assisted development

This project embraces AI-assisted development. If you used tools like GitHub Copilot, Claude, or ChatGPT for substantial portions of your contribution, please mention it in the PR description and which areas you used them on. All code must be understood, tested, and validated regardless of how it was produced.

Filing issues

By contributing, you agree your contributions will be licensed under the MIT License that covers this project.

Thank you for helping make the portal better.

Clone this wiki locally