Contextual Authorization for Agentic Systems
Agentic job-seeking applications are getting good at real work—polishing resumes, sending follow-ups, syncing calendars, even nudging LinkedIn. That power cuts both ways: the blast radius is huge if you over‑scope access or keep long‑lived tokens lying around. In this post, I’ll outline a practical security baseline I use for these systems: just‑in‑time authorization, least‑privileged scopes, and contextual enforcement. Together, they keep users in control while letting your agent move fast.
Why this matters
Job‑seeking apps handle sensitive data: resumes, emails, calendars, files, and social profiles. Users will only trust your product if it’s obvious you’re minimizing exposure and asking for access only when it’s needed. On the engineering side, this posture reduces breach impact, simplifies audits, and plays nicely with modern compliance expectations.
Just‑in‑Time authorization: ask only when it’s relevant
Avoid “connect everything” upfront flows. Instead, request access at the moment of need:
- Scheduling an interview? Trigger Google Calendar OAuth when the user confirms “Schedule.”
- Exporting a resume? Prompt Drive access when the user picks “Upload from Drive.”
- Posting to LinkedIn? Ask for LinkedIn scope the first time they publish.
This pattern keeps permissions discoverable and intentional. Users learn exactly why you’re asking, and you don’t carry around credentials your app isn’t actively using.
Practical tips:
- Use short‑lived access tokens and refresh tokens with narrow scope.
- Cache consent state per integration and per user task, not globally.
- Defer OAuth until you have all task context (targets, time window, content) to request the minimal scope.
Least‑privileged access: minimize the blast radius
Every task should use the smallest scope set possible:
- Drive: prefer drive.readonly for picking a resume instead of full read/write.
- Gmail: restrict to send only (gmail.send) when composing follow‑ups; avoid read scopes unless you’re explicitly parsing inbox content.
- Calendar: request event creation for a single calendar instead of full list access to all calendars.
- LinkedIn: request only content creation scopes when posting, not profile write if you don’t need it.
Enforce this discipline in code. Treat scope expansion as a breaking change requiring review.
Practical tips:
- Maintain a mapping: Task → Required scopes (and nothing more).
- Use separate client IDs per integration area (email vs. calendar) to limit cross‑feature scope creep.
- Make migrations additive and revocable (revoke unused scopes on logout or feature disable).
Contextual enforcement: permission is a function of “who, what, when”
Even with tight scopes, you still need runtime guardrails. Contextual enforcement asks:
- Is this the right user? (user_id and session posture)
- Is this the right agent? (capabilities registered to the agent)
- Is this the right action right now? (task type, time window, resource owner)
- Is the data flow minimal? (only the fields required)
Examples:
- An agent sending a follow‑up email can call “compose and send” for that thread, but cannot list the inbox or read historical messages.
- Updating a LinkedIn headline is allowed only from the Profile agent with explicit user confirmation, not from a Job‑Apply agent.
- Calendar access is constrained to a specific calendar ID and event window.
Practical tips:
- Encode policies in a PDP (policy decision point) with inputs: user, agent, task, resource, scopes, and environment.
- In LangGraph Platform, retrieve the authenticated user via
config["configuration"]["langgraph_auth_user"]to drive policy checks and resource scoping. - Log all allow/deny decisions with correlation IDs for audits.
- Default‑deny unrecognized tasks and stale sessions.
User Authentication (Handled by LangGraph Platform)
User authentication is about users proving who they are to access your job‑seeking agent. LangGraph Platform provides a first‑class mechanism for this and makes the verified identity available throughout your agent.
# LangGraph Platform verifies user identity
@auth.authenticate
async def authenticate(headers: dict) -> Auth.types.MinimalUserDict:
api_key = headers.get("x-api-key")
if not is_valid_key(api_key):
raise Auth.exceptions.HTTPException(status_code=401)
return {
"identity": "user-123", # Who is this user?
"is_authenticated": True # Are they allowed in?
}
Once authenticated, users can create threads, run agents, and access agent resources. LangGraph Platform manages this and exposes the identity at runtime via config["configuration"]["langgraph_auth_user"]. Use this identity to:
- Bind tasks to the correct user (e.g., only schedule on their calendar).
- Enforce capability checks per user and agent.
- Constrain data access to user‑owned resources (files, email threads, LinkedIn account).
An implementation blueprint you can ship
-
Token strategy
- Use PKCE OAuth flows.
- Prefer provider‑issued short‑lived tokens; store refresh tokens encrypted, scoped, and per‑integration.
- Rotate and revoke on scope changes, org role change, or feature disable.
-
Capability registry
- Define a registry of agent capabilities (e.g., “calendar.create”, “email.send”, “linkedin.post”).
- Map UI actions → required capabilities → provider scopes.
- Validate at runtime that the active agent and user session have both capability and scope.
-
Data minimization
- Send only the minimal excerpt to your AI layer (e.g., the specific resume section or email body).
- Redact PII where possible; prefer structured prompts over raw document dumps.
-
Consent UX
- Inline, task‑gated prompts (“To schedule this interview, connect Calendar”).
- Explain why, what scope, and how to revoke.
- Provide a single “Connections” view to review and revoke integrations.
-
Observability and safety
- Structured audit logs: who/what/when/where/why/result.
- Rate limits and anomaly detection (e.g., burst sends).
- Dry‑run mode for risky tasks (preview before send/post).
Developer examples (scope sketches)
-
Gmail follow‑up sending
- Scopes: gmail.send
- Data: recipient, subject, composed body
- Policy: user owns thread; agent=“FollowUpAgent”; time ≤ 24h after interview
-
Google Drive resume import
- Scopes: drive.readonly + file picker
- Data: chosen file ID; single download URL
- Policy: single‑use token; discard after import
-
LinkedIn post
- Scopes: w_member_social
- Data: post body + optional media ID
- Policy: explicit user confirmation each time; no profile write
For users: what you get
- Control: the app asks when it needs access and explains why.
- Safety: permissions are narrow and expire.
- Transparency: clear logs and easy revocation.
Conclusion
Security for agentic job apps isn’t about saying “no”; it’s about making “yes” safe. Ask only when needed (JIT), ask for as little as possible (least privilege), and check context every time (policy at runtime). You’ll earn user trust, reduce risk, and keep the product moving fast without compromising what matters most: their data and their reputation.