System 05 — privileged access
JIT Access
Privileged-access management is one of the most expensive vendor categories in security, and most of the products are portals nobody opens. The actual requirement is small — a way to ask, a way to decide, a clock to revoke — and all of it can live where the team already is. JIT Access does that, in Slack, with no standing elevated permissions left behind.
The problem
Vendor PAM (the well-known six-figure category) bundles a session vault, secret manager, an agent, a recording engine, and a portal — and charges for all of it whether you use the parts or not. Most teams need a much smaller thing: an engineer asks for elevated access, an owner approves it, and the access disappears on its own. Standing elevated permissions, even short-lived ones, are a category of liability that compliance frameworks (CIS, ISO 27001 A.9, OJK least-privilege) put pressure on directly, and the cheapest control is to never grant them in the first place.
What I built
A Slack-native just-in-time access portal that lives entirely inside the workspace. Identity stays in Okta and AWS IAM Identity Center; this system only mediates elevation.
- Request in Slack — an engineer fires a slash command, picks the target account/role, gives a reason and a duration.
- Approve in Slack — the request lands in the account owner's channel as a Block Kit message with Approve / Deny actions, the requester and reason inline. The decision happens in the thread, fully searchable, with the approver's identity already established by Okta SSO into Slack.
- Grant + clock — on approval, the elevated role is granted to the requesting principal (via IAM Identity Center / role assumption) and the grant is written to DynamoDB with a TTL set to the requested expiry. The requester is DM'd: activated, expires at X.
- Auto-revoke — Dynamo's native TTL marks the row dead; a sweeper enforces the revoke at the IAM boundary as a belt to the Dynamo braces. The audit row persists.
- Slack Socket Mode — the integration is an outbound WebSocket from ECS to Slack. There is no public HTTP receiver for the bot; nothing has to be exposed to the internet for any of this to work.
┌────────────────┐ ┌─────────────────────────────────────────────┐
│ engineer asks │ ──▶ │ ECS service · DynamoDB state │
│ for elevation │ │ write request → post Approve / Deny │
│ via /jit slash │ │ Block-Kit message in owner channel │
└────────────────┘ └─────────────────────────────────────────────┘
owner clicks ▼ in Slack
┌────────────────────────────────────────────┐
│ grant elevated role (IAM Identity Center / │
│ STS AssumeRole) · write grant with TTL · │
│ notify requester │
└────────────────────────────────────────────┘
TTL expires → sweeper revokes
audit row persists for review
Slack Socket Mode · no public endpoint · auto-expire
Design decisions
Slack Socket Mode, not a public webhook
The integration is an outbound WebSocket from ECS to Slack. No public HTTP endpoint, no ingress to defend, no rotating webhook secret. The whole approve/deny surface lives inside the workspace's existing auth boundary.
Trade-off single Slack-workspace dependency. If Slack is down, new requests and approvals stall; existing grants continue to expire cleanly because the clock lives in Dynamo.
DynamoDB with TTL as the source of truth
The state model is small — request, grant, decision — and Dynamo's native TTL gives you "auto-revoke when the row dies" without an external cron. The table scales to zero between requests; the bill is effectively rounding error.
Trade-off Dynamo TTL is best-effort within minutes, not exact at the second. A sweeper enforces revocation at the IAM boundary so the revoke is always at least as tight as Dynamo's clock, never looser.
Approve in-channel, not in a portal
Decisions happen where the approver already lives. The request, the context, and the decision are one Slack thread, searchable forever, with the approver's identity already trusted by the Slack/Okta SSO chain. No second login, no portal anyone forgets to open.
Trade-off harder to enforce step-up MFA at the click than a dedicated portal would. Mitigated by short approval windows and a hardened Slack/Okta posture; for the very highest-blast-radius roles a WebAuthn challenge at approval time is the right next step (see below).
Bounded by design — no silent renewals
Sessions expire at the granted duration and require a fresh request to extend. Elevation does not slide forward by accident, and there is no "leave it open" mode.
Trade-off a small amount of friction for engineers who would rather keep a session warm. That friction is the feature: standing elevation is exactly what this system exists to remove.
Replaces vendor PAM, not vendor SSO
Identity remains in Okta and AWS IAM Identity Center. This system mediates elevation only, which keeps the scope and the attack surface small.
Trade-off not a full PAM — no secrets vault, no session recording, no agent. Chosen scope: the goal was the JIT lifecycle, not a Swiss-army-knife product.
Operating profile
Engineers request elevated access in Slack, account owners approve in Slack, and the access disappears on its own. The audit trail is the Slack thread plus the Dynamo grant record — no separate ticket queue, no portal anyone forgets to log into. Compliance auditors get a clean trail keyed to requester, approver, account, role, and duration, satisfying ISO 27001 A.9 / CIS / OJK least-privilege controls with a concrete, lived workflow.
What I would change
A WebAuthn step-up at the approval click for break-glass-tier roles — today the approver's Okta/Slack session is trusted, which is correct for most decisions but soft for the highest-blast-radius accounts. Pair that with a per-grant EventBridge scheduled revoke for those same tiers, so high-risk grants drop at the exact second rather than at Dynamo's best-effort. Both are fine to add incrementally; neither was needed to ship the system.