AWS Lambda middleware — Yealink desk phone DSS key unlocks front door via LenelS2 Elements API
Find a file
Adam Moussa 4265ad90fe Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32)
* feat: add gateway token authorizer to door-unlock API (INFRA-99)

All three routes (GET /unlock, /lockdown, /lockdown/status) were
AuthorizationType NONE — auth relied solely on each handler checking
the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer
(door-unlock-api-authorizer) that validates the SAME ?token= value the
Yealink XML Browser keys already send, against the existing
/seahaven/door-unlock/auth-token SSM SecureString, and attach it to all
three routes.

Transparent to the phones: identity source is $request.querystring.token
(exactly what the type-17 XML Browser keys send via GET), simple response
{isAuthorized}, fail-closed, 5-min results cache. Token is cached in
module scope so warm invocations skip SSM.

GET is kept (not switched to POST): the Yealink type-17 XML Browser keys
are GET-only and render the returned Yealink XML — they cannot issue a
POST body or custom headers. POST is therefore deferred to avoid bricking
the door keys.

Handlers retain their own token check as defense-in-depth. Purely
additive change set; no existing Lambda or integration is modified.

* chore: complete CI/CD migration to GitHub Actions (INFRA-2)

GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable
workflows) is the proven deploy path. Remove the now-orphaned
buildspec.yml and update the README CI/CD and architecture sections.

The legacy CodePipeline was already deleted (2026-06-05); the leftover
CodeBuild project seahaven-door-unlock-api-build and its IAM role
seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
.github chore(deps): remove blanket aws-cdk-lib dependabot ignore (#27) 2026-06-05 13:56:52 -04:00
bin Rename stack to kebab-case 2026-05-01 18:56:59 -04:00
lambda Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) 2026-06-08 18:03:30 -04:00
lib Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) 2026-06-08 18:03:30 -04:00
.gitignore Add dependency-review caller workflow (#25) 2026-06-05 12:34:08 -04:00
cdk.context.json Add lockdown mode and CI/CD pipeline (#3) 2026-05-01 18:52:37 -04:00
cdk.json Add door unlock API — Yealink DSS key triggers LenelS2 Elements TemporaryUnlock via API Gateway + Lambda 2026-04-22 14:36:55 -04:00
door-unlock-plan.md Add door unlock API — Yealink DSS key triggers LenelS2 Elements TemporaryUnlock via API Gateway + Lambda 2026-04-22 14:36:55 -04:00
package-lock.json Bump aws-cdk-lib in the minor-and-patch group across 1 directory (#28) 2026-06-05 14:47:13 -04:00
package.json Bump aws-cdk-lib in the minor-and-patch group across 1 directory (#28) 2026-06-05 14:47:13 -04:00
README.md Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) 2026-06-08 18:03:30 -04:00
tsconfig.json Add CI workflow (#17) 2026-05-08 16:03:06 -04:00
yealinkT54W-door-unlock-with-sp.ph.xml Apply display settings to SP template — both templates now match 2026-04-23 11:00:00 -04:00
yealinkT54W-door-unlock.ph.xml Add display settings to door-unlock template and update README 2026-04-22 20:01:09 -04:00
yealinkT58W-door-unlock.ph.xml Add lockdown mode and CI/CD pipeline (#3) 2026-05-01 18:52:37 -04:00

Sea Haven Door Unlock API

AWS Lambda middleware that allows Yealink desk phones to unlock the front door and manage lockdown profiles via LenelS2 Elements.

Yealink T54W/T58W → HTTPS GET (?token=) → API Gateway (token authorizer) → Lambda → LenelS2 Elements API

Architecture

  • API Gateway (HTTP API) — GET /unlock, GET /lockdown, GET /lockdown/status with throttling (5 burst / 2 sustained req/sec)
  • Token authorizer Lambda — a REQUEST-type Lambda authorizer validates the ?token= query-string value (the same shared secret the phones already send) against the /seahaven/door-unlock/auth-token SSM parameter, so unauthenticated callers are rejected at the gateway (401/403) before any handler runs. Identity source is $request.querystring.token; results are cached 5 minutes. Fail-closed. The handlers also re-validate the token as defense-in-depth.
  • Unlock Lambda — validates a shared auth token, calls the Elements TemporaryUnlock command
  • Lockdown Lambda — toggles lockdown profiles (start/stop) and checks status, returns Yealink XML TextScreen responses
  • Lockdown Poller Lambda — VPC-connected, polls Elements API every 15 seconds for lockdown status (runs 4x per 1-minute EventBridge schedule)
  • SSM Parameter Store — stores the Elements API key, auth token, door ID, and phone IPs
  • Secrets Manager — stores the Yealink phone admin password
  • Custom Domain — doorunlock.seahaven.com via Route 53 + ACM wildcard cert

Lockdown Profiles

Two lockdown profiles are configured:

Profile Elements ID Line Key
Bohemia - Whole Building 4b4a3e6b-c903-4cce-8cd6-288612bf0542 3
Ronkonkoma - Whole Building ff9876bc-c54f-472e-aef9-d2bffd4b7cf7 4

Pressing the line key toggles the lockdown on/off and displays the current status on the phone screen.

Known limitation: Line key LED color does not currently change to reflect lockdown status. The T58W's XML Browser key type (17) does not support persistent LED color changes via Push XML or Execute commands — LED commands are transient and immediately overridden by the phone's key type management.

SSM Parameters

Parameter Type Description
/seahaven/door-unlock/elements-api-key SecureString LenelS2 Elements API key
/seahaven/door-unlock/auth-token SecureString Shared secret embedded in the Yealink DSS key URL
/seahaven/door-unlock/door-id String Elements device ID for the front door reader
/seahaven/door-unlock/phone-ips String Comma-separated phone IPs for lockdown poller

Secrets Manager

Secret Description
door-unlock-api/phone-password Yealink phone admin password for Push XML

CI/CD

GitHub Actions, using the Sea Haven reusable workflows:

  • .github/workflows/ci.yaml — on pull requests to main, runs the ci-typescript-cdk reusable workflow (build, lint, synth).
  • .github/workflows/deploy.yaml — on push to main, runs the cd-cdk reusable workflow which assumes the githubdeploy-seahaven-door-unlock-api OIDC role (AWS_DEPLOY_ROLE_ARN repo secret) and runs cdk deploy.

The legacy CodePipeline/CodeBuild deploy path has been fully decommissioned.

Manual Deployment

npm install
npx cdk deploy

Phone Configuration

Configure DSS keys on the Yealink T54W/T58W (via phone web UI or 3CX):

  • Key 2 — Unlock Door

    • Type: URL
    • Value: https://doorunlock.seahaven.com/unlock?token=<auth-token>
  • Keys 3-4 — Lockdown Toggle

    • Type: XML Browser (17)
    • Value: https://doorunlock.seahaven.com/lockdown?token=<auth-token>&profile=bohemia|ronkonkoma

3CX Provisioning Templates

Custom 3CX templates are included with door unlock and lockdown URLs hardcoded.

Template Model Key 2 Keys 3-4 Display
yealinkT54W-door-unlock.ph.xml T54W Unlock Door Managed by 3CX BLF Dim after 5 min, never sleep
yealinkT54W-door-unlock-with-sp.ph.xml T54W Unlock Door Shared Parking SP1-3 Dim after 5 min, never sleep
yealinkT58W-door-unlock.ph.xml T58W Unlock Door Lockdown Toggle (Bohemia/Ronkonkoma) Default T58W display settings