跳到主要内容

WebRTC Viewer Temporary Credentials PoC (P6)

Status

  • P6-0: Feasibility report — PROCEED verdict
  • P6-1: AWS IAM test resources created, STS AssumeRole + inline session policy verified
  • P6-2: Local code PoC — this document

Architecture Decision

Existing Auth.js + Postgres permission architecture remains unchanged

The permission chain is:

  1. Auth.js (Google / Apple / Email Magic Link) or App Bearer Token → identity
  2. DeviceOwnership + roleAllowsLegacyCapabilitylive_view capability gate
  3. assertDeviceServiceAccess → device existence, ownership, role, provisioning check
  4. STS AssumeRole + inline session policy → temporary, single-channel-scoped AWS credentials

Why not Cognito User Pool?

  • Auth.js already handles web authentication (Google / Apple / Magic Link)
  • App Bearer tokens handle mobile API auth
  • Introducing Cognito User Pool would:
    • Duplicate the identity layer
    • Require migrating existing users
    • Add complexity for no benefit (the backend proxies STS, not the browser)

Why STS AssumeRole + inline session policy?

  • No new identity system required — works with existing IAM
  • Single-channel scoping via inline session policy (verified in P6-1)
  • Short TTL (15 minutes default) limits exposure
  • Server-side assume — credentials never touch long-term AWS keys
  • No Cognito Identity Pool needed — the browser calls the Next.js API, which proxies STS

AWS Resources Created (P6-1)

See P6-1 Report for full details. Summary:

ResourceARNPurpose
IAM Rolearn:aws:iam::288669178338:role/aovis-webrtc-viewer-dev-roleTrusts aovis-backend-service user; base policy allows KVS viewer actions on test channel
Managed Policyarn:aws:iam::288669178338:policy/aovis-webrtc-viewer-dev-base-policykinesisvideo:GetSignalingChannelEndpoint/IceServerConfig/ConnectAsViewer on test channel
Backend policy v6aovis-backend-service-policy + StsAssumeWebrtcViewerRolests:AssumeRole on the viewer role only

IAM Permissions Boundary

The inline session policy used on every AssumeRole call restricts to exactly:

{
"Effect": "Allow",
"Action": [
"kinesisvideo:GetSignalingChannelEndpoint",
"kinesisvideo:GetIceServerConfig",
"kinesisvideo:ConnectAsViewer"
],
"Resource": "${channelArn}"
}
  • No kinesisvideo:*
  • No s3:*, bedrock:*, iam:*, ec2:*
  • No kinesisvideo:ConnectAsMaster
  • No kinesisvideo:PutMedia
  • No wildcard * in Resource

Environment Variables

VariableDefaultMaxPurpose
WEBRTC_VIEWER_ROLE_ARN(empty)STS role ARN to assume. When empty, API returns credential_not_configured (503)
WEBRTC_VIEWER_SESSION_SECONDS9003600STS session duration. Clamped at 3600

Region is reused from AWS_REGION / awsConfig.region.


API Response Schema

GET /api/devices/[id]/webrtc

200 OK (Cache-Control: no-store, Cross-Origin-Resource-Policy: same-origin)
{
"channel_arn": "arn:aws:kinesisvideo:us-east-1:...",
"region": "us-east-1",
"endpoints": {
"wss": "wss://...kinesisvideo.us-east-1.amazonaws.com",
"https": "https://...kinesisvideo.us-east-1.amazonaws.com"
},
"ice_servers": [
{
"uris": ["turn:...:443?transport=udp"],
"username": "...",
"credential": "..."
}
],
"credentials": {
"accessKeyId": "ASIA...",
"secretAccessKey": "...",
"sessionToken": "...",
"expiration": "2026-05-18T13:53:04.000Z"
}
}

Error responses

StatuserrorWhen
401unauthorizedNo valid session/token
403forbidden_device_roleRole lacks live_view
404device_not_foundNo device or no active ownership
409device_not_provisionedkvsChannelArn is null
503credential_not_configuredWEBRTC_VIEWER_ROLE_ARN not set
502signaling_errorSTS / KVS API failure

Browser Credential Security Rules

  • Credentials are never stored in localStorage, sessionStorage, or window global
  • Credentials exist only in React component state / refs
  • console.log of credentials is prohibited (lint enforcement recommended)
  • CSP headers restrict what scripts can execute
  • STS credentials expire in 15 minutes (configurable)
  • Each session is scoped to exactly one signaling channel ARN
  • On component unmount, all references are cleared

What This PoC Can Verify

  • ✅ Backend auth chain (Auth.js + App token + DeviceOwnership + role check)
  • ✅ STS AssumeRole with inline single-channel policy works
  • ✅ Temporary credentials have ASIA prefix (not long-term AKIA)
  • ✅ Cross-channel access is denied
  • ✅ S3 / Bedrock / KVS media / list operations are denied
  • ✅ API returns Cache-Control: no-store and Cross-Origin-Resource-Policy: same-origin
  • ✅ Frontend initializes SignalingClient from amazon-kinesis-video-streams-webrtc
  • ✅ Frontend handles credential_not_configured, no_master, signaling_error states
  • ✅ Frontend does not leak credentials to persistent storage

What Cannot Be Verified Yet

  • Live video playback — requires a hardware IPC master connected to the signaling channel (no NEXA Prime 4K unit available yet)
  • Mobile app viewer — this PoC only covers browser-based viewer via Next.js
  • End-to-end latency — no master stream to measure
  • Multi-device production role design — base policy currently scoped to one test channel

Files Changed

FileChange
lib/aws/sts-webrtc.tsNew — STS helper: buildWebrtcViewerSessionPolicy, assumeWebrtcViewerRole, WebRtcRoleNotConfiguredError
lib/env.tsAdded webrtcViewerRoleArn, webrtcViewerSessionSeconds (clamped to 3600)
app/api/devices/[id]/webrtc/route.tsIntegrated assumeWebrtcViewerRole; added region, credentials to response; added Cache-Control: no-store and Cross-Origin-Resource-Policy: same-origin headers; returns credential_not_configured (503) when role unconfigured
components/devices/webrtc-live-viewer.tsxAdded region, credentials to config type; replaced debug-only view with SignalingClient integration; added connecting, no_master, credential_not_configured states; removed debug cards
lib/__tests__/sts-webrtc.test.tsNew — 25 tests covering policy structure, action constraints, env defaults, route source analysis, component behavior
package.jsonAdded amazon-kinesis-video-streams-webrtc dependency
package-lock.jsonUpdated
docs/aws-webrtc-viewer-temporary-credentials-poc.mdNew — this document

Dependencies Added


Cleanup Plan

After production rollout:

  1. Create production IAM role aovis-webrtc-viewer-role (naming without -dev- suffix)
  2. Update aovis-backend-service-policy — remove aovis-webrtc-viewer-dev-role reference, add production role
  3. Delete aovis-webrtc-viewer-dev-role and aovis-webrtc-viewer-dev-base-policy
  4. Clean up aovis-backend-service-policy stale versions

Production TODO

  • Production role name: decide on aovis-webrtc-viewer-role vs keep dev naming
  • Multi-device base policy: role base policy should cover aovis-webrtc-* channels; inline session policy narrows to single device
  • EC2 instance profile: migrate from IAM user key to instance profile for production backend
  • CSP hardening: add script-src and connect-src restrictions for WebRTC endpoints
  • App bearer token: already supported by assertDeviceServiceAccess — mobile app can reuse the same API
  • Monitoring: STS throttling (AssumeRole has 60 req/s default limit per account)

Document version: 1.0 (2026-05-18) Related: P6-0 Feasibility Report | P6-1 Verification Report