How IAM Identity Center Powers Secure Access for Claude AI Agents in Node.js, Explained Simply
Human users and AI agents both need the right permissions, but mixing SSO with programmatic access is tricky. In just a few minutes you’ll see how to let a Claude‑powered Lambda speak to Bedrock without ever hard‑coding static keys. The pattern works for any LLM‑backed tool you build today. Why IAM

Human users and AI agents both need the right permissions, but mixing SSO with programmatic access is tricky. In just a few minutes you’ll see how to let a Claude‑powered Lambda speak to Bedrock without ever hard‑coding static keys. The pattern works for any LLM‑backed tool you build today.
When a human logs into the AWS console we usually think about IAM Identity Center (formerly AWS SSO) as a single‑sign‑on (SSO) portal. It lets people click a link, type a password, and get a web console with all the permissions they need.
An AI agent —for example a Lambda function that talks to Claude on Bedrock—needs the same “right‑things‑can‑do” guarantee, but it can’t click a button. If you give the function long‑lived static credentials, you expose a secret that never rotates.
In plain English: IAM Identity Center can hand out short‑lived, automatically‑rotated credentials for programs, just like it hands a temporary badge to a visitor.
Think of IAM Identity Center as a hotel front desk. A human guest gets a room key that works for a few hours. An AI agent is a robot concierge that needs a key to fetch a specific amenity (the Claude model). The front desk can issue a temporary key for the robot, and the key expires automatically, keeping the hotel secure.
A permission set groups one or more IAM policies and is assigned to a user or group in IAM Identity Center. For a machine, you create a dedicated permission set that includes only the actions needed to call Bedrock and read any supporting resources (like a Secrets Manager secret that holds the SAML assertion).
Tip: Keep the permission set tiny. If the agent only needs bedrock:InvokeModel, don’t also grant s3:*.
Instead of hand‑editing JSON in the console, you can write the policy in TypeScript and let the compiler verify its shape. The satisfies operator ensures the object conforms to the IAM policy schema at compile time.
// src/policy.ts import type { PolicyDocument } from " aws-sdk/clients/iam "; /** * Inline policy granting the minimal rights for a Claude‑powered Lambda. * The `satisfies` keyword makes TypeScript check that every required field exists. */ export const claudeAgentPolicy = { Version: " 2012-10-17 ", Statement: [ { Effect: " Allow ", Action: [ " bedrock:InvokeModel ", // call Claude on Bedrock " sts:AssumeRoleWithSAML ", // exchange SAML for temporary creds " secretsmanager:GetSecretValue ", // read the SAML assertion stored securely ], Resource: " * ", // you could lock this down to specific ARNs for tighter security }, ], } satisfies PolicyDocument; Enter fullscreen mode Exit fullscreen mode Key takeaway: Using satisfies catches typos (like "bedrock:InvokModel" ) before you ever deploy, turning a runtime surprise into a compile‑time warning.
You can copy the resulting JSON into the console’s Inline Policy editor when you create the permission set.
Now that the permission set exists, the Lambda must assume the role represented by that set. The standard way is to call the AWS Security Token Service (STS) operation AssumeRoleWithSAML. The flow looks like this:
Below is a minimal, fully‑commented Lambda handler written in TypeScript for Node.js 22. It uses the AWS SDK v3 packages you asked for: @aws-sdk/client-sts, @aws-sdk/client-secrets-manager, and @aws-sdk/client-bedrock-runtime. (The client-iam and client-lambda packages are not needed directly in the handler but are listed in the service context for completeness.)
Now that we have a Bedrock client backed by temporary credentials, the actual model call is straightforward. The heavy lifting is in the request payload format, which differs per provider. Claude expects a JSON body with prompt, max_tokens_to_sample, and optional parameters like temperature.
In plain English: Think of the Bedrock client as a mailbox you hand a sealed letter to (the request). The envelope (HTTP headers) tells Bedrock what kind of letter it is ( application/json ). The temporary credentials are the postage stamp that proves you’re allowed to send mail to that address.
// Inside the Lambda handler – after creating `bedrock` client const requestPayload = { body: JSON. stringify ({ prompt: " Write a haiku about sunrise over the mountains. ", max_tokens_to_sample: 64, temperature: 0.5, }), modelId: CLAUDE_MODEL_ID, accept: " application/json ", contentType: " application/json ", }; const result = await bedrock. send ( new InvokeModelCommand ( requestPayload )); const modelOutput = Buffer. from ( result. body! ). toString ( " utf-8 " ); console. log ( " Claude says: ", modelOutput ); Enter fullscreen mode Exit fullscreen mode Why we don’t use static keys: The bedrock client receives credentials that expire after an hour. If the Lambda runs longer (e.g., a long‑running batch job), the next iteration will hit ExpiredTokenException.
The catch block from the previous section restarts the assume flow, guaranteeing fresh credentials without any human touch.
Earlier we stored the policy in policy.ts. Let’s expand that pattern to a policy generation script that could be run in CI/CD pipelines. The idea is to keep IAM definitions alongside your application code, ensuring they evolve together.
// scripts/build-policies.ts import { writeFileSync } from " fs "; import { claudeAgentPolicy } from "../src/policy "; // The `as const` assertion preserves literal types for maximum safety. const policyJson = JSON. stringify ( claudeAgentPolicy, null, 2 ) as const; // Write the policy to a file that can be fed to `aws iam create-policy` writeFileSync ( "./dist/claude-agent-policy.json ", policyJson ); console. log ( " Policy written to./dist/claude-agent-policy.json " ); Enter fullscreen mode Exit fullscreen mode Running npm run build-policies would generate a JSON file ready for the AWS CLI:
aws iam create-policy \ --policy-name ClaudeAgentInlinePolicy \ --policy-document file://dist/claude-agent-policy.json Enter fullscreen mode Exit fullscreen mode Key takeaway: By generating policies from TypeScript, you eliminate drift between code and permissions. The satisfies keyword guarantees the shape matches IAM’s expectations before you ever call the CLI.
By following this pattern, you give your Claude‑powered agents the same security rigor you apply to human users, while staying free of hard‑coded keys and long‑lived secrets. The approach works for any LLM you hook up to Bedrock, making your AI tooling both safe and maintainable.
This article was written with the help of an AI system — Groq (GPT OSS 120B).
Published: 2026-10-19 · Primary focus: IAM
All code blocks are intended to be correct and runnable, but please verify them against the official docs for the tools mentioned before using in production.
Find an error? Drop a comment — corrections are always welcome.