AWS labs · Lesson 2 · IAM and OIDC
Who are you, and what may you do?
IAM, OIDC, OAuth, SAML, STS, JWT, SSO, IRSA… it sounds like a pile of acronyms, but they all fit one story.
Something wants to use AWS. First it has to prove who it is with a proof AWS trusts. Then AWS swaps that proof for short-lived keys.
Then every request is checked against policies. Learn that story once and every name below has an obvious place in it.
The whole of AWS access in four sentences
- Every request to AWS answers two questions: who are you? (authentication) and what may you do? (authorization).
- "Who" is proven by someone AWS already trusts: GitHub or an EKS cluster with an OIDC token, your company login with SAML, or AWS itself for an EC2 machine.
- STS swaps that proof for temporary keys of a role. The role's trust policy decides who may borrow it.
- The role's permission policies decide what it can do. An explicit Deny anywhere wins, and nothing is allowed unless something allows it.
01 Every name on one map
Each name answers one of three questions. Read left to right: that's also the order things happen in.
Prove who you are
authentication
OIDC (OpenID Connect)A standard way for a trusted party to say "this is who this is" with a signed token. Built on OAuth 2.0.GitHub Actions, EKS pods, "Log in with Google"
JWT (JSON Web Token)The token OIDC hands out: three parts, readable by anyone, signed so nobody can change it.the "ID card" itself
SAMLThe older single sign-on standard. Same job as OIDC, but an XML document instead of a JWT.company login into AWS, Salesforce…
OAuth 2.0About access, not identity: lets an app act for you on another service without your password."allow this app to read your repos"
Identity provider (IdP)Whoever vouches for you and signs the proof.GitHub, an EKS cluster, Entra ID, Okta, Google
SCIMKeeps user lists in sync from the identity provider to apps, so leaving the company removes access everywhere.Entra ID → IAM Identity Center
MFAA second proof (phone, key) on top of the password, checked by the identity provider.
Swap the proof for keys
AWS takes over
IAM identity providerYour account's record that says "I trust tokens from this OIDC issuer" or "from this SAML provider".token.actions.githubusercontent.com
STS (Security Token Service)The desk that checks the proof and hands out temporary keys for a role.AssumeRole · AssumeRoleWithWebIdentity · AssumeRoleWithSAML
RoleA set of permissions that someone or something borrows for a while. No password, no permanent keys.arn:aws:iam::111122223333:role/deploy-web
Trust policyAttached to a role: who may borrow it, and on what conditions (which repo, which service account).
Temporary credentialsAccess key (starts ASIA), secret and session token, valid for minutes to hours.long-lived IAM user keys start AKIA
IAM Identity CenterAWS's single sign-on for people: log in with the company account, pick an AWS account and a permission set.
Instance profile, IRSA, Pod IdentityHow machines inside AWS get a role: an EC2 instance, or a Kubernetes pod.
Decide what you may do
authorization
Permission policyJSON that allows or denies actions on resources. Attached to a role, user or group (identity policy).Allow s3:PutObject on acme-web-site/*
Resource policyA policy attached to the thing itself, like an S3 bucket policy. It names who may use it.
SCP (service control policy)A ceiling for a whole account, set by AWS Organizations. Can only take permissions away.
Permission boundaryA ceiling for one role or user. Also only takes away.
Session policyAn extra ceiling passed when a role is assumed, for that one session.
ARN and conditionsHow policies name things (arn:aws:s3:::bucket) and add rules like "only from this repo" or "only with MFA".
identity provider signs a proof→STS checks it + the role's trust policy→temporary keys→each request checked against policies→allowed or denied
02 The token journey, in 3D
Four everyday situations, one machinery. Step through each and watch what travels between whom. Notice that STS, the role and the bucket never change: only the proof of identity does.
Try it. Pick a scenario, then use Next (or the arrow keys) to move the token one hop at a time. Drag to turn the scene; open it on its own to zoom. Colours: proof of identity, keys from STS, a check by IAM, the real call you wanted.
| Situation | Who vouches | Proof | STS call | Trust policy trusts |
| GitHub Actions deploys | GitHub | OIDC token (JWT) | AssumeRoleWithWebIdentity | the GitHub OIDC provider, for one repo and branch |
| EC2 machine reads S3 | AWS itself | being that instance | done for you via the metadata service | the service ec2.amazonaws.com |
| Pod in EKS reads S3 | the EKS cluster | OIDC token (JWT) | AssumeRoleWithWebIdentity | the cluster's OIDC provider, for one service account |
| You log in to the console | Company login (Entra, Okta) | SAML assertion | AssumeRoleWithSAML (by Identity Center) | Identity Center's SAML provider |
03 Inside a token
This is the kind of token GitHub gives a CI job. A JWT is three parts separated by dots. The first two are just base64: anyone can read them. The third is a signature only GitHub can make, so nobody can change a single character without being caught.
1 · Header
which key and algorithm signed it
2 · Payload (claims)
who this is, for whom, until when
3 · Signature
made with GitHub's private key; checked with its public key
| Claim | Means | Why AWS cares |
| iss | Issuer: who signed it | Must match an OIDC identity provider registered in your account |
| aud | Audience: who it's meant for | Should be sts.amazonaws.com, so a token meant for another service can't be replayed at AWS |
| sub | Subject: exactly who this is | The trust policy pins it, e.g. repo:acme/web:ref:refs/heads/main |
| exp, iat, nbf | Expiry, issued at, not before | Old tokens are refused; GitHub's live for minutes |
| repository, ref, environment, job_workflow_ref | Extra GitHub details | Can be used in conditions too, for finer rules |
Readable is not secretBase64 is an encoding, not encryption. Never put secrets in a JWT, and treat a valid token like a key while it lasts: whoever holds it can use it until it expires.
04 OAuth, OIDC and SAML, side by side
Three standards that people mix up. OAuth is a valet key: it lets something do a job for you. OIDC and SAML are ID badges: they say who you are.
| OAuth 2.0 | OIDC | SAML 2.0 |
| Answers | What may this app do for you? | Who is this? | Who is this? |
| Gives you | an access token, to call an API | an ID token (always a JWT), plus OAuth's access token | an assertion (signed XML) |
| Since | 2012 | 2014, built on top of OAuth 2.0 | 2005 |
| Typical use | "Allow this app to read your calendar" | "Log in with Google", machine-to-cloud (GitHub → AWS, EKS → AWS) | company single sign-on into business apps |
| In AWS | aws sso login uses an OAuth device flow | IAM OIDC identity providers, AssumeRoleWithWebIdentity | IAM SAML providers, Identity Center, AssumeRoleWithSAML |
| How trust is checked | the API checks the access token | signature checked against the issuer's public keys (JWKS), found via /.well-known/openid-configuration | signature checked against the provider's certificate from its metadata file |
Rule of thumbMachines talking to AWS today: OIDC. People logging in through the company: SAML (often hidden behind Identity Center). An app acting on your behalf somewhere: OAuth.
05 IAM building blocks
Four nouns, and one shape of policy. This is roadmap Day 40.
UserA long-lived identity with a password and/or access keys. Fine for a quick lab; avoid for real people (use Identity Center) and for machines (use roles).
GroupA bag of users that share policies. Policies attach to the group, people join and leave.
RolePermissions with no owner, borrowed for a session. What machines, CI jobs, other accounts and SSO users actually use.
PolicyA JSON document of Effect (Allow or Deny), Action (what), Resource (on what) and optional Condition (only when…). Resource policies also name a Principal (who).
The confusion everyone has: a role has two policies
Trust policy: who may borrow this role?
The Principal is the GitHub OIDC provider; the conditions pin the audience and exactly one repo and branch.
Permission policy: what can the role do?
Only uploads into one bucket. Least privilege: no s3:*, no "Resource": "*".
Identity policy vs resource policyAn identity policy is attached to a user, group or role and says what they can do. A resource policy is attached to a bucket, queue or key and says who may use it. In one account, either one allowing is usually enough (unless something denies; KMS keys are the main exception). Across accounts, both sides must allow.
06 Trust-policy checker
The trust policy above only lets the main branch of acme/web deploy. Change what the token says and see which condition stops it.
Why the strict line mattersGitHub signs tokens for every repo on GitHub. If the sub condition is * or missing, anyone who can run a workflow anywhere can assume your role. Pin the repo, and pin the branch or environment for anything that deploys.
07 Is this request allowed?
Once a role has keys, every single request is evaluated against every policy that applies. Set what each layer says and watch the decision. The rule: an explicit Deny anywhere wins; otherwise every ceiling must allow, and at least one grant must allow.
This is the same-account logic. Across two accounts, the caller's own policies and the resource's policy must both allow.
08 Hear a problem, pick a pattern
The same few patterns cover almost every request you'll hear. The first question is always: is it a person, a machine inside AWS, or something outside AWS?
09 Traps
| Trap | Why it hurts | Do this instead |
| Access keys (AKIA…) in code, a laptop file or a CI secret | They never expire and leak through logs, screenshots and old commits | Roles everywhere: OIDC for CI, instance profiles and IRSA for apps, aws sso login for people |
A trust policy with sub as * or a whole org | Any repo (or anyone on GitHub) can assume the role | Pin repo:org/name plus the branch or environment |
Forgetting the aud condition | A token meant for another service could be replayed at AWS | Require aud = sts.amazonaws.com |
| Using the root user | It can't be limited by any policy | MFA on root, lock it away, use it only for the few root-only tasks (Day 37) |
"Action": "*" and "Resource": "*" | One leaked role can do anything | Name the actions and resources; start small and add what's missing |
| Fixing an AccessDenied by attaching AdministratorAccess | Hides the real cause and leaves a hole | Read the error: it names the action and resource; check SCPs, boundaries and denies (Day 43) |
| Leaving IMDSv1 on EC2 | A web bug can trick the app into fetching the instance's keys | Require IMDSv2 (session tokens) on every instance |
10 Self-check
Ten questions. The reason appears after you choose.
Glossary
- Authentication
- Proving who you are.
- Authorization
- Deciding what you may do.
- Principal
- Whoever is making the request: a user, a role session, an AWS service, a federated identity.
- Federation
- Trusting identities from outside AWS (GitHub, Entra ID, Okta) instead of creating IAM users.
- JWKS
- JSON Web Key Set: the public keys an OIDC issuer publishes so anyone can check its signatures.
- Claim
- One field inside a token, like
sub or aud.
- Assume a role
- Exchange a proof for temporary credentials of that role.
- Permission set
- In Identity Center, a reusable bundle of policies that becomes a role in each account it's assigned to.
- External ID
- A shared secret a third-party service must send when assuming a role in your account, so other customers of that service can't.
- Least privilege
- Give only the permissions a job needs, nothing more.
Use this in the roadmap