What is an IAM Identity Provider?

An IAM Identity Provider is a configuration in AWS that tells IAM “trust this external identity system.” It lets users authenticated by external systems (Google, Okta, GitHub Actions, etc.) get temporary AWS credentials without creating IAM users.

Key Concepts

ConceptWhat it is
Identity Provider (IdP)External system that authenticates users (Google, Okta, Azure AD, etc.)
Service Provider (SP)System that trusts the IdP and provides resources (AWS in this case)
FederationLinking identities across different systems—user logs in once, accesses multiple systems
Trust RelationshipAWS saying “I believe what this IdP tells me about users”
OIDC ProviderIAM entity for trusting OpenID Connect-based IdPs
SAML ProviderIAM entity for trusting SAML 2.0-based IdPs

OIDC vs SAML

  • OIDC (OpenID Connect): Modern, JSON/REST-based protocol—used by web apps, mobile apps, and programmatic access (GitHub Actions, EKS pods, Cognito)
  • SAML 2.0: XML-based enterprise protocol—used for browser-based SSO to AWS Console (Okta, Azure AD, corporate SSO)

Trust Anchor

When you create an IAM Identity Provider, you get:

  • An ARN like arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
  • A trust anchor that IAM roles can reference in their trust policies

What is a Trust Anchor?

Every IAM role has two parts:

  1. Permission policy: What the role CAN DO (S3 access, EC2 actions, etc.)
  2. Trust policy: WHO CAN ASSUME this role

A trust anchor is the entity you specify in the trust policy as “trusted to assume this role”—the starting point of trust that AWS checks first.

Examples of Trust Anchors

Trust AnchorWho can assume the role
arn:aws:iam::123456789012:user/aliceIAM user Alice
arn:aws:iam::123456789012:role/OtherRoleAnother IAM role
ec2.amazonaws.comEC2 service (for instance profiles)
arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.comGitHub Actions (via OIDC)

Creating Identity Providers

OIDC Provider Configuration

SettingWhat to specifyDetails
Provider URLThe IdP’s OIDC discovery endpointMust be HTTPS. AWS fetches /.well-known/openid-configuration from here
AudienceWho the token is intended forThe aud claim in the JWT. Often sts.amazonaws.com for AWS
ThumbprintSHA-1 fingerprint of IdP’s TLS certificateAWS uses this to verify it’s talking to the real IdP
# Example: GitHub Actions OIDC Provider
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com

SAML Provider Configuration

SettingWhat to specifyDetails
Provider NameA name you choosee.g., Okta, AzureAD, CorporateIdP
Metadata DocumentXML file from your IdPContains IdP’s public certificate, SSO URLs, entity ID

The SAML Metadata XML contains:

  • EntityID: Unique identifier for the IdP
  • SSO URL: Where AWS redirects users to log in
  • X.509 Certificate: Public key to verify SAML assertions

Trust Policy Differences

OIDC Trust Policy

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
    }
  }
}

SAML Trust Policy

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::123456789012:saml-provider/Okta"
  },
  "Action": "sts:AssumeRoleWithSAML",
  "Condition": {
    "StringEquals": {
      "SAML:aud": "https://signin.aws.amazon.com/saml"
    }
  }
}

Key Differences

AspectOIDCSAML
STS ActionAssumeRoleWithWebIdentityAssumeRoleWithSAML
Token FormatJWT (JSON Web Token)SAML Assertion (XML)
Condition Prefix<provider-url>:claimSAML:attribute
Audience Valuests.amazonaws.comhttps://signin.aws.amazon.com/saml

AssumeRoleWithWebIdentity: Internal Flow

What happens inside AWS when a JWT is sent:

Step-by-Step Process

  1. Receive request: STS receives JWT + RoleArn
  2. Parse JWT: Extract issuer (iss) from token payload (without verifying yet)
  3. Lookup OIDC provider: Find IAM OIDC provider matching the issuer in the account
  4. Fetch JWKS: HTTP GET to <issuer>/.well-known/jwks.json to get public keys
  5. Verify signature: Use public key (matched by kid) to verify JWT signature
  6. Validate claims: Check exp (not expired), iat (issued in past), aud (matches configured audience)
  7. Evaluate trust policy: Check role’s trust policy conditions against JWT claims
  8. Generate credentials: Create temporary AccessKeyId, SecretAccessKey, SessionToken
  9. Return response: Send credentials back to caller

Failure Modes

StepFailure
3“No OpenIDConnect provider found”
5“Token signature invalid”
6“Token expired” / “Invalid audience”
7“Not authorized to perform sts:AssumeRoleWithWebIdentity”

Security Points

  • Signature verification proves authenticity: Only the real IdP has the private key
  • JWKS fetched live: AWS doesn’t store IdP keys—fetches them each time (with caching)
  • kid (Key ID): Allows IdP to rotate keys without breaking existing tokens

AssumeRoleWithSAML: Flow

User clicks AWS app in Okta
        ↓
Okta authenticates (password, MFA)
        ↓
Okta returns SAML Response (XML in HTML form that auto-submits)
        ↓
Browser POSTs to https://signin.aws.amazon.com/saml
        ↓
AWS validates:
  - Verify XML signature using certificate from SAML metadata
  - Extract role ARN from SAML attribute
  - Call AssumeRoleWithSAML internally
        ↓
Redirect to AWS Console with session

What SAML Assertion Contains

<saml:Assertion>
  <saml:Subject>
    <saml:NameID>user@company.com</saml:NameID>
  </saml:Subject>
  <saml:AttributeStatement>
    <saml:Attribute Name="https://aws.amazon.com/SAML/Attributes/Role">
      <saml:AttributeValue>
        arn:aws:iam::123456789012:role/OktaAdminRole,
        arn:aws:iam::123456789012:saml-provider/Okta
      </saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="https://aws.amazon.com/SAML/Attributes/SessionDuration">
      <saml:AttributeValue>3600</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
  <ds:Signature>...</ds:Signature>
</saml:Assertion>

GetFederationToken API

sts:GetFederationToken creates temporary credentials for a federated user you define—without using an IAM role or external IdP.

Comparison with Other STS APIs

APIWho calls itTrust relationship
AssumeRoleAnyone allowed by role’s trust policyRole trusts the caller
AssumeRoleWithWebIdentityExternal IdP token holderRole trusts the OIDC provider
AssumeRoleWithSAMLSAML assertion holderRole trusts the SAML provider
GetFederationTokenIAM userNo role involved—caller’s permissions are the ceiling

How It Works

aws sts get-federation-token \
  --name "customer-123" \
  --duration-seconds 3600 \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/customers/123/*"
    }]
  }'

Permission Scoping

Federated user’s permissions = intersection of:

  1. The calling IAM user’s permissions
  2. The Policy parameter you pass (optional)

The policy parameter can only restrict, not expand permissions.

When to Use

Use CaseBetter Choice
External IdP (Google, Okta, GitHub)AssumeRoleWithWebIdentity / AssumeRoleWithSAML
Cross-account accessAssumeRole
Custom federation broker (your backend creates tokens for users)GetFederationToken
Dynamic per-user policies (each user gets different resource access)GetFederationToken

Limitations

  • Caller must be IAM user (not a role, not federated user)
  • Cannot call certain STS APIs with resulting credentials
  • Max duration: 12 hours

Configuring Audience (aud) Claim

The aud claim is set by the IdP when it issues the token.

GitHub Actions

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
      aws-region: us-east-1
      audience: sts.amazonaws.com  # Sets the aud claim

Cognito

The aud is automatically set to the App Client ID that was used to authenticate:

{
  "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXX",
  "aud": "abc123clientid",
  "sub": "12345678-1234-1234-1234-123456789012"
}

Control it by which App Client the user authenticates with.

AWS Side

When creating IAM OIDC Provider, specify accepted aud values:

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com  # Accepted aud values

Both sides must match, or you get: "Invalid audience in token"


When to Use Which Protocol

Use CaseProtocol
CI/CD (GitHub Actions, GitLab)OIDC
Kubernetes pods (EKS)OIDC
Mobile/Web apps via CognitoOIDC
Enterprise SSO to ConsoleSAML
Okta/Azure AD/OneLoginSAML
Custom backend token brokerGetFederationToken