worqcloud
← Back to the journal

Stop storing cloud access keys in GitHub Actions

Stop storing cloud access keys in GitHub Actions

A long-form migration guide to short-lived deployment credentials, with trust boundaries, negative tests, failure diagnosis, and a plan to retire the old key.

A reusable key sits beside a temporary pass with a clock cutout.
Original worqcloud illustration. Replace a reusable credential with a bounded identity exchange. View full size ↗

A deployment key is a copy of trust

A deployment pipeline can begin with an innocent shortcut. Someone creates a cloud access key, stores it as a repository secret, and gives the workflow permission to update a service. The application ships. Months later, the team can name the secret but cannot easily explain which repositories still use its value, who can replace it, or which older workflow can obtain it. The key has become shared infrastructure without a clear owner.

This guide follows a hypothetical team moving one production deployment from a stored key to federated identity. The service is an orders API, the repository is privately maintained, and the deployment changes an existing application. The team wants to remove a reusable credential while preserving a reliable rollback path. The names and decisions are illustrative. They describe a review process, not an account of measured customer results.

GitHub documents how Actions jobs can request identity tokens and exchange them with a supporting cloud provider. That is the technical foundation. The migration sequence, exercises, acceptance criteria, and review questions here are worqcloud editorial recommendations. Apply them to a test environment first and use the linked provider documentation for the exact configuration supported by your account.

The goal is a deployment whose permission has an understandable boundary. A reviewer should be able to identify the repository, workflow context, cloud role, target resources, and expiration behavior. Successful login is one checkpoint. The more important question is whether the right job can do the required work while a different job is denied.

Inventory what the old credential can actually do

Begin with the current deployment rather than a new identity-provider screen. Identify the secret name, where its underlying credential was created, which workflow references it, and the target environment. Search reusable workflows and organization-level secrets as well as the visible repository configuration. A local copy in a release tool can survive long after the main workflow has changed.

Write down the cloud operations performed during a normal deployment. The orders API might require uploading an artifact, updating an application configuration, and checking rollout state. A database migration may run under a separate identity. Treat those as separate activities until you have evidence that combining them is necessary. A role called deployment administrator does not tell you what the workflow really needs.

Gather recent successful run identifiers and cloud audit records. Use them to connect a job to specific target resources and actions. Logs are evidence of what occurred, not proof that unused permission can safely be removed immediately. The workflow may have an infrequent rollback, cleanup, or scheduled release path that did not run during your sample.

Finish the inventory with an owner and a retirement plan for the old key. Record who can revoke it, where dependent jobs are listed, and how you will know the new path has taken over. If you cannot locate the owner of the credential, resolve that gap before copying its authority into a new federated role. Otherwise, the migration can preserve an access problem under a newer name.

Understand the two identities in the exchange

Separate the job's claim about itself from the credential the cloud eventually issues. In a federated deployment, the workflow obtains a signed identity token from GitHub. A cloud trust configuration decides whether that token is acceptable for a particular role or identity. If the exchange succeeds, the workflow receives cloud credentials with the permissions and lifetime of that session.

The identity token is therefore not a replacement password to paste into every tool. It is evidence presented during a specific exchange. The resulting cloud session is what subsequent deployment commands use. Keeping those stages distinct helps diagnose errors. A job can obtain an identity token and still fail the cloud exchange. It can also exchange successfully and then lack permission to update the service.

For the hypothetical orders API, draw three boundaries on paper. The first boundary is the repository and job allowed to request identity. The second is the cloud trust policy that accepts a constrained identity context. The third is the permission policy that authorizes operations on the deployment target. Each boundary needs a separate review question.

Ask who can change the code that runs before and after authentication. Short credential lifetime reduces the value of a stolen credential, but an authorized job can still issue harmful commands during its session. Protecting workflow definitions, dependencies, and approvals remains part of the design. Federation changes how a job obtains authority. Your review must also cover how that authority is used.

Describe the trusted deployment in plain language

Before editing policy, write a sentence describing the intended trust relationship. For example, only the production deployment job in the orders repository may assume the production deployment role after the production environment's checks have passed. That sentence gives reviewers something to compare against configuration, rather than forcing them to infer the business boundary from token syntax.

Now identify which parts of that sentence are enforced where. Repository ownership belongs to GitHub access controls. Environment requirements belong to the environment configuration. Token claim matching belongs to the provider trust relationship. Resource permissions belong to the cloud role. A single broad allow rule cannot replace all four controls.

Be precise about repository names, environments, and branches. A repository can contain both trusted release workflows and experimental automation. A branch label alone does not explain whether code review is required before it changes. An environment named production is useful only when the team has configured the rules it intends to rely on.

Record exceptions in the same document. Perhaps a disaster-recovery workflow needs a separate role, or a scheduled maintenance job requires a different environment. Make each exception explicit instead of broadening the main relationship until every scenario passes. A narrow primary path and a named exception are usually easier to understand than a policy whose accepted subjects have accumulated without explanation.

Match the audience and subject deliberately

The linked GitHub guidance for AWS emphasizes restricting the trust relationship using supported claims, including the audience and subject. The audience indicates the intended recipient of the token. The subject represents a particular workflow context. Review both values against the actual token configuration and provider integration you are using.

Do not infer a subject solely from the branch visible in the browser. Environment-based jobs can have a different subject shape from jobs that reference a branch directly. A team can correctly name its repository and still configure the wrong condition because it copied an example for another execution context. Compare a sanitized description of the actual claims with the intended trust sentence.

Use the provider's documented claim support. AWS and other providers do not offer identical policy features or accept every GitHub claim in the same way. Avoid treating a customization that works in one platform as a portable guarantee. For a first migration, prefer the smallest documented condition set that expresses the required boundary clearly.

Have a second reviewer read accepted and rejected examples. Show a production environment job, an ordinary feature branch job, and a different repository. Ask which should succeed and why. If the reviewer needs to guess how a wildcard expands, simplify the policy or document the exact accepted scope before proceeding.

JSON example
{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::<ACCOUNT_ID>: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:<ORG>/<REPO>:environment:production"
    }
  }
}

Illustrative AWS trust-policy statement. Replace the placeholders, use your supported audience, and configure the production environment protections separately. This statement does not grant permissions on application resources.

Three narrowing gates show job context, cloud trust, and resource permissions.
Each gate answers a different access-control question. Verify the supported claims for your provider. View full size ↗

Give the cloud session only deployment permissions

A successful identity exchange does not establish that the role is well scoped. The trust relationship answers who may obtain the role. The permission policy answers what the role may do. Review those independently, especially if the old key belonged to a broad account-level administrator.

For the orders API, divide the work into artifact publishing, service updates, and read-only rollout checks. Identify whether each action can be restricted to a resource, path, environment, or other supported condition. Use the relevant service documentation to confirm the actual authorization model. Do not invent a resource restriction for an operation that requires a different policy shape.

Consider separating the build from the deployment. A build that compiles and tests code may need repository access and package downloads without production authority. A deployment job can consume the reviewed artifact and obtain cloud credentials later. That reduces how much unrelated code runs while production access is available.

Review failure and recovery operations too. A deployment role that can update the service but cannot inspect its rollout can produce a confusing failure even when the update succeeded. Conversely, a role that can delete unrelated services because someone wanted an easy cleanup path carries unnecessary authority. Test the operations the runbook actually needs rather than copying a broad managed policy and stopping at the first green run.

Request identity at the job boundary

GitHub Actions exposes an identity-token permission that enables a workflow to request an OIDC token. Scope that permission to the job that needs cloud authentication where the workflow design permits. Avoid making every job capable of requesting identity simply because the deployment job needs it.

The surrounding workflow permissions also deserve attention. Identity-token access and repository-token access solve different problems. A job that only reads source code should not gain repository write permission through an unrelated setting copied from a release example. Review the permissions block as a complete set and document why each capability is present.

Place authentication close to the commands that need it. If the job performs long-running packaging after it obtains cloud credentials, the session may expire before deployment. More importantly, unrelated steps gain an opportunity to use those credentials. A clearer sequence is to prepare the artifact, complete the required checks, authenticate, and execute the bounded deployment actions.

Use maintained authentication actions or documented provider tools, and pin dependencies according to your team's review policy. Federation does not remove the need to trust the code handling the exchange. A compromised dependency in the deployment job may still exercise legitimate authority while the job is running. Review dependency updates with the same care as changes to the deployment commands.

A sequence separates building and checks, identity exchange, and a temporary deployment session.
Conceptual sequence. Complete preparation before obtaining the session that performs the deployment. View full size ↗

Keep untrusted contributions away from deployment authority

Think through what happens when someone proposes a change from a fork or an unreviewed branch. The safe behavior is a test run without production deployment authority. The exact event model and available credentials depend on the workflow, so inspect your configured triggers rather than relying on a general statement about pull requests.

Pay special attention to any design that combines a privileged workflow context with code from an untrusted change. A workflow can have trusted configuration while executing scripts supplied by the proposed revision. Ask which revision is checked out, which dependencies are installed, and whether a build script can run arbitrary commands before cloud authentication.

For the orders repository, create an explicit negative test. A contribution branch should run tests and produce its expected outputs, but an attempted production identity exchange from that context should be denied. Keep the test harmless and use a sandbox role or a documented validation process. You are testing a boundary, not trying to obtain authority outside it.

Review third-party actions invoked by privileged jobs. A job's authority is available to the code running within that job, subject to how credentials are exposed. Do not assume that a friendly action name creates an isolation boundary. Where practical, move untrusted processing into a separate job and pass only the required artifact through a reviewed interface.

Treat environment approval as an operational control

An approval step is useful only when the approver can understand what will be deployed. Present the artifact identifier, target environment, source revision, and expected effect. A generic approval request that says production deployment without an identifiable release makes it difficult for the reviewer to make a meaningful decision.

Use the environment's supported protection rules to match your release process. Verify who can approve, which branches or tags may target the environment, and what happens when a required reviewer is unavailable. These settings should agree with the subject conditions accepted by the cloud provider. Otherwise, the workflow may be protected in one layer and unexpectedly open or unusable in another.

Walk through an urgent fix before the first outage. Decide who can authorize an emergency release and whether that path uses a separate role or environment. Record what evidence must be attached afterward. An emergency process that depends on someone improvising a broad trust policy during an incident is hard to audit and easy to leave in place.

Make changes to environment protections visible in review. A deployment can retain the same YAML while its approval boundary changes through repository settings. Include those settings in periodic access review, along with role trust and resource permissions. The complete deployment boundary spans more than the workflow file.

Build a migration that can fail safely

Choose a low-risk application environment for the first exchange test. The first milestone is obtaining the intended role and reading a harmless resource under that role. This separates identity configuration from the complexity of a real service rollout. If the read fails, you can diagnose trust or permission without wondering whether a deployment partially completed.

Next, run the ordinary deployment in that environment using the federated path. Keep the previous configuration available through a controlled rollback change. Avoid a workflow that silently tries OIDC and then falls back to the stored key on any error. That can hide a broken migration and make audit records hard to interpret.

For production, name a release window and an observer who can inspect both workflow and cloud logs. Record the session identity, deployment revision, and target service. If authentication succeeds but the update fails, determine whether any operation completed before choosing a retry. The identity migration does not make application deployment atomic.

Set a deadline for retiring the old key after a successful observation period. A permanent overlap leaves the reusable credential available and weakens the reason for migrating. The observation period should cover relevant release paths, including a rollback or equivalent rehearsal, rather than an arbitrary number of calendar days with no deployments.

Diagnose a token request failure

A failure before token exchange belongs to the GitHub side of the flow. Check that the job has the required identity-token permission, that the expected workflow is actually running, and that the event context is the one the team designed for. A copied configuration can be correct in isolation but applied to a different job.

Read the error without printing tokens. The job name, event, revision, environment, and provider-action version usually give a useful starting point. Keep full identity tokens and cloud credentials out of logs, issue comments, and screenshots. A debugging transcript should identify the failed stage and sanitized context rather than expose the proof used for authentication.

Check whether a reusable workflow changes where the relevant configuration lives. A repository-level caller and a centrally managed workflow can each contribute to the execution context. Follow the documented behavior for that workflow arrangement, and compare it with a simple sandbox job if the larger pipeline obscures the source of failure.

After a fix, rerun the negative tests as well as the successful job. It is easy to solve a permission error by granting identity-token access more broadly than intended. A narrow correction should restore the expected path while keeping unrelated jobs denied. Capture that result in the migration record so a future reviewer can understand why the permission was added.

Diagnose a cloud exchange failure

When the workflow obtains an identity token but the cloud exchange fails, compare the intended trust relationship with the actual execution context. The audience may differ from the provider configuration, the subject may refer to an environment instead of a branch, or the role identifier may point at a different account.

Start with exact values rather than broadening conditions. Check spelling, repository ownership, environment naming, and the configured provider endpoint. A mismatch is information about the relationship you expressed. Replacing a precise subject with a wide wildcard may make the job pass while accepting identities the team never intended to trust.

Use cloud audit information to confirm which role and identity provider were involved. If multiple accounts have similarly named roles, include the account identifier in the deployment record. A developer can spend time reviewing the correct policy in the wrong account while the job continues to fail against another target.

Retest with one configuration change at a time. Keep a short record of the hypothesis, the changed value, and the observed result. This is especially useful during a migration where provider setup, workflow changes, and environment settings are all new. A reproducible explanation is more valuable than a final configuration that works for reasons nobody can describe.

Diagnose authorization after a successful exchange

A deployment can authenticate correctly and still receive access denied when updating a service. At that point, the trust policy may be working exactly as intended. Inspect the resource operation, requested target, role permissions, and any additional policy layers that apply in the account.

Compare the failed command with the inventory of required actions. Perhaps the tool reads a configuration before writing it, publishes an artifact to a separate location, or requests a rollout status from another API. Add only the supported operation and scope needed for that behavior. Avoid treating every access-denied response as a reason to replace the role with administrator access.

Consider partial completion. An artifact upload might have succeeded before a service update failed. A retry should either reuse the same immutable artifact or clearly identify a new release. Ask whether the command can safely run twice and whether its output identifies the resulting state. Permission debugging should not accidentally create a second operational problem.

After the deployment succeeds, examine whether temporary diagnostic permissions remain. Remove them through review and repeat the normal deployment test. The final role should describe the real release process, including its recovery operations, rather than the sequence of permissions people tried while investigating the first failure.

Plan for credential expiry during a long release

Temporary credentials have a lifetime. A workflow that waits for a large upload, a long migration, or a manual pause after authentication can reach an expiry boundary. Design the sequence so the expected release duration fits the credential model, and confirm the provider's supported duration and refresh behavior for your chosen tooling.

Avoid hiding expiry behind unlimited retries. If a command failed halfway through a release, first determine which operation completed and whether the tool can resume safely. A fresh credential does not tell you whether an application update is still in progress. State checks and idempotent deployment steps remain essential.

For the orders API, place approvals and expensive artifact preparation before authentication. Use a distinct migration job if that activity has different access and runtime requirements. This makes it easier to understand which session performs which operation and reduces the chance that a delayed build consumes the deployment session before it is used.

Test expiry behavior in a sandbox with a bounded operation. The exercise should answer what the operator sees, which state can be inspected without the expired session, and how the job recovers. Document the result in the runbook. During an incident, a recognizable expiry error with a safe recovery procedure is far easier to handle than an unexplained cascade of failed commands.

Make the audit trail useful to another person

A useful deployment record links a source revision, workflow run, artifact, environment, and cloud session. Another engineer should be able to follow those links without asking the original author which terminal command they ran. This is a design goal for the release process, not a reason to log credential material.

Use meaningful session labels where the provider and tooling support them. Include the run identifier in the deployment output or release record. Keep the exact supported fields in provider documentation rather than assuming every cloud exposes the same session naming or claim display. The goal is correlation, not a cosmetic naming convention.

Ask an operator to investigate a harmless test deployment using only the retained records. Can they identify who approved it, which artifact was applied, and which resources changed? If the answer requires access to a private chat thread, improve the release record before treating the migration as complete.

Set retention and access according to the sensitivity of those records. Deployment logs can contain internal resource names, paths, and operational details even when they contain no secrets. Give the people responsible for incident response enough access to investigate while avoiding unnecessary distribution of detailed infrastructure information.

Rotate trust when repositories and teams change

Federation removes a stored cloud key from the workflow, but trust relationships still need maintenance. A repository rename, organization transfer, environment rename, or release-process change can affect the context accepted by the provider. Include identity review in those changes instead of discovering the mismatch during the next production release.

When a repository is archived, identify and remove its unused deployment relationships. When a team splits one service into several repositories, decide whether each repository needs an independent role. Copying the original role into every new project can expand production access faster than the team notices.

Assign ownership to both sides of the relationship. The repository maintainer understands workflow changes. The cloud platform owner understands account policies and role boundaries. A short shared record containing the trusted context, role identifier, purpose, and review date helps them coordinate without relying on memory.

Treat a change in accepted subject as a permission change. Review its effect on positive and negative test cases, update the runbook, and retain the previous configuration for a controlled rollback. Trust configuration is executable access control. Giving it the same review discipline as application code makes the deployment easier to operate over time.

Handle a suspected deployment compromise

If a deployment job behaves unexpectedly, preserve the run identifier and relevant cloud audit records. Determine which identity obtained which session and what actions it performed. Suspending a workflow, disabling a trust relationship, or restricting a role may help contain future use, but the exact response belongs to your organization's incident process.

Do not assume that waiting for credentials to expire resolves every consequence. A session may already have changed a service, created another resource, or exposed data. Review resulting state as well as the authentication event. The practical benefit of short-lived access is a smaller credential reuse window, not automatic reversal of actions.

For a rehearsal, choose a harmless simulated indicator such as an unexpected deployment revision. Ask the incident team to identify the run, suspend further releases through the approved process, inspect the target, and restore a known version. Measure whether the necessary people and records are available, rather than staging a real attack on production.

Feed the findings back into the normal workflow. Perhaps the artifact identifier was missing, a role could modify more services than expected, or emergency ownership was unclear. Improve the boundary and evidence before the next release. A federation migration is a useful opportunity to make the whole deployment path more understandable.

Review multi-repository and reusable-workflow designs

A central deployment workflow can reduce duplication, but it introduces another trust question. Who may call it, which caller context is accepted, and which revision of the shared workflow runs? A familiar central repository does not automatically make every caller or input trustworthy.

List the inputs that select the environment, role, artifact, and target service. Validate that callers cannot freely combine a production role with an arbitrary artifact or target. If a shared workflow accepts broad parameters, document the checks that constrain their combinations. Simplicity at the caller should not conceal an unrestricted deployment interface underneath.

Review how changes to the shared workflow reach production repositories. Pinning a reviewed revision can provide predictable behavior, while an update process is still needed to receive fixes. Choose a policy the team can maintain. An unchanging dependency with no owner and an automatically advancing dependency with no review each create different operational problems.

Test one authorized caller and one unauthorized caller in a sandbox arrangement. Also test a permitted repository using an unexpected environment or artifact source. The important boundary is the complete request to deploy, including its context and inputs. A review that checks only the shared workflow file leaves important assumptions untested.

Run a small tabletop before production

Bring the repository maintainer, cloud role owner, release operator, and incident responder into a short rehearsal. Present four cards: normal release, feature-branch test, failed exchange, and emergency rollback. Ask each person to describe the expected identity, permission, evidence, and next action.

For the normal release, identify the approved artifact and environment. For the feature branch, explain why production access is denied. For the failed exchange, locate the sanitized claims and provider error. For rollback, identify the known artifact and the authority required to apply it. Keep the discussion grounded in the actual workflow and account.

Write down every answer that depends on a person remembering an undocumented detail. Turn those gaps into small changes with owners. An emergency phone number, a missing audit link, or an unclear environment approval rule can matter as much as a token condition during a stressful release.

End the exercise with a short decision. Either the team has enough evidence for a bounded production migration, or specific gaps remain. Avoid replacing the decision with a vague confidence statement. A useful record names the tests completed, the failures understood, the old key retirement owner, and the date when the new relationship will be reviewed.

Work through a permission review example

Consider three proposed permissions for the orders deployment. The first uploads a versioned artifact to the application's release location. The second updates the orders service to use that artifact. The third allows deletion of every service in the account. The first two fit the described release, subject to their exact resource scopes. The third requires a separate explanation.

Ask the author to demonstrate the release step that needs deletion. If cleanup only removes an old artifact, the broad service permission does not match that need. If an emergency procedure really removes a service, review that as a distinct operation with its own approval and recovery consequences. Naming the task makes it possible to choose an appropriate control.

Now consider a read permission that appears unnecessary because the deployment command only says update. The underlying tool might read current configuration before it makes a change. Verify that behavior in a sandbox rather than deleting the permission blindly. Least privilege is an evidence-based boundary. It is not a competition to produce the shortest policy at the expense of an operable release.

Capture the decision in plain language next to the reviewed policy. For example, the job reads rollout state so the pipeline can distinguish an in-progress update from a failed one. That explanation helps the next maintainer understand why an action is present. It also makes future removal easier when the deployment tool or service behavior changes.

Avoid turning federation into a one-person specialty

A migration can be technically sound and still fragile if only its author understands it. Ask a colleague to make a small, safe change in the sandbox environment using the written procedure. Observe where they hesitate, which account screens they need, and whether the expected role is easy to identify.

Have that colleague diagnose a deliberately incorrect environment name in the sandbox workflow. The exercise should lead them to the context mismatch without granting broader production access. If they immediately reach for an administrator role, improve the diagnostic guidance. The runbook should lead people toward a narrow explanation and a controlled test.

Include identity configuration in normal service ownership. When an on-call engineer changes teams, their successor needs the repository, role, provider relationship, review history, and escalation path. None of those should depend on a private collection of screenshots. A small maintained document is more durable than an elaborate guide that nobody updates.

Finally, reserve time to remove the temporary migration machinery. Delete unused test roles through the team's normal process, retire obsolete secrets, and close the tracked migration tasks. Federation earns its operational value when the resulting system is easier for several people to understand and safely change.

Questions to answer before the next repository

What can be reused from the first migration? The inventory checklist, review questions, and test sequence probably are. Exact role policies, accepted subjects, and recovery requirements may differ. Carry forward the reasoning while reviewing each new workload's boundary on its own merits.

A batch migration should still have a stop condition. If a repository uses an undocumented release tool, depends on an ownerless credential, or cannot demonstrate rollback, mark that work for investigation. Counting repositories migrated can hide unresolved access paths. A useful progress report separates verified retirement from configurations that were merely added.

Publish a short record for every completed migration. Include the retired credential reference, replacement relationship, tested release paths, and owner. That gives security and platform teams a reliable basis for later reviews without asking every application team to reconstruct its history.

The practical next step is modest: select the next repository and write its trust sentence before editing configuration. If the team can agree on who should deploy what, from which context, the policy review becomes a translation exercise with testable outcomes.

Preserve the useful evidence without copying secrets

Build a small migration evidence pack that contains the trust sentence, reviewed role policy, workflow revision, environment configuration reference, and successful test runs. Include the negative cases and the old credential retirement record. Keep tokens and credential values outside that pack.

The pack should help another engineer answer a practical question: why does this workflow have this authority? If the answer requires reading hundreds of lines of debug output, add a short explanation of the intended relationship and its tested boundaries.

Use the same pack when reviewing a later change. A new repository caller, deployment target, or environment rule can be compared with the original intent. This gives the team a reason to update the design consciously instead of allowing configuration to accumulate around old exceptions.

The final habit is to treat identity as part of the service's release interface. A team that can describe and test that interface can migrate away from reusable keys with less guesswork, diagnose failures more narrowly, and leave a clearer operating model for the next maintainer.

Decide when the migration is complete

The final acceptance test should cover more than a successful login. Confirm a normal deployment, a harmless denied action outside the role, an unauthorized workflow context, and the operational recovery path. Retain the run identifiers and the reviewed configuration that produced those results.

Confirm that the old key has been revoked through the responsible process and that dependent jobs have been accounted for. Check a later release so the team can see that ordinary work still succeeds after retirement. Remove obsolete instructions that tell operators to recreate the old secret, because an outdated runbook can quietly reverse a security improvement.

Review the reader-facing outcome of the migration as well. Developers should know where deployment failures are reported, who owns identity configuration, and how to request an environment or permission change. A deployment system that is secure but incomprehensible encourages workarounds during the next urgent release.

Start with one repository, one target, and one clearly described trust relationship. Build the evidence that the authorized path works and the denied path stays denied. Then use what you learned to migrate the next workload with fewer assumptions. A good first rollout leaves behind a repeatable review process and a smaller inventory of reusable credentials.

References & further reading

Explore more insights ↗