How the actions/checkout Action Manages Authentication

The actions/checkout action supports two authentication methods—HTTPS via token and SSH via private key—injecting credentials into Git's configuration before cloning and cleaning them up after the job completes.

The authentication layer of actions/checkout is critical for securely accessing private repositories in GitHub Actions workflows. According to the source code in the actions/checkout repository, the action handles credential injection through a dedicated authentication helper that operates before any Git commands execute. This architecture ensures that sensitive tokens and SSH keys remain masked in logs while providing seamless access to protected resources.

Authentication Architecture Overview

The authentication flow begins in src/input-helper.ts, which parses workflow inputs like token, ssh-key, ssh-known-hosts, and persist-credentials into a GitSourceSettings object. These settings are passed to src/git-auth-helper.ts, where the createAuthHelper(git, settings) factory function instantiates a GitAuthHelper class. This helper coordinates two distinct authentication strategies: HTTPS token-based auth and SSH key-based auth.

How HTTPS Token Authentication Works

When you provide a token input (defaulting to GITHUB_TOKEN), the action configures Git to send an authorization header with every HTTPS request.

Token Header Generation and Masking

In src/git-auth-helper.ts, the configureToken() method constructs a basic authentication header from the supplied token. The action immediately registers the raw token as a secret using core.setSecret() to prevent exposure in workflow logs. To avoid leaking the credential through process audit logs, the implementation writes a placeholder string (AUTHORIZATION: basic ***) to a temporary credentials file, then replaces the placeholder with the actual base64-encoded token value.

Repository Configuration Injection

The credentials are injected into Git's configuration through conditional includes. The helper writes an includeIf.gitdir: entry pointing to the temporary credentials file directly into the repository's local Git config. If persist-credentials is set to true, the action instead uses a global include.path directive, allowing subsequent Git commands in the same job to reuse the authentication without re-entering the token.

How SSH Key Authentication Works

For repositories requiring SSH access, the action accepts an ssh-key input containing a base64-encoded PEM private key.

Temporary Key Storage and Environment Setup

The configureSsh() method in src/git-auth-helper.ts decodes the key and writes it to a temporary file within RUNNER_TEMP. It also constructs a combined known_hosts file, incorporating any user-provided hosts from the ssh-known-hosts input alongside default GitHub hosts. The helper then builds a custom GIT_SSH_COMMAND that explicitly points to the temporary private key and known-hosts file.

Persistent SSH Configuration

When persist-credentials is enabled, the action stores the SSH command in the repository's Git configuration under the core.sshCommand key. For repositories with submodules, configureSubmoduleAuth() applies the same core.sshCommand setting to each submodule's local config, ensuring recursive clones authenticate correctly.

Global vs. Local Credential Storage

The action distinguishes between job-scoped and repository-scoped authentication through two separate configuration paths.

Local Repository Configuration: By default, credentials are scoped only to the specific repository being checked out. The configureAuth() method sets up token or SSH authentication specifically for the target working directory.

Global Job Configuration: When workflows require authentication for multiple repositories or subsequent Git commands, configureGlobalAuth() creates a temporary HOME directory, copies the existing global .gitconfig, and applies authentication settings globally. This approach ensures that tools spawning separate Git processes inherit the necessary credentials without modifying the runner's permanent user configuration.

Post-Job Credential Cleanup

Security cleanup occurs in the action's post-run phase, registered in src/main.ts. The removeAuth() and removeGlobalConfig() methods in src/git-auth-helper.ts delete temporary SSH keys, remove the known-hosts files, and unset all Git configuration keys added during setup. This cleanup ensures that temporary credentials do not persist on the runner for subsequent jobs, even if the workflow fails or is cancelled.

Practical Usage Examples

Configure HTTPS authentication with automatic cleanup:

steps:
  - uses: actions/checkout@v4
    with:
      token: ${{ secrets.GITHUB_TOKEN }}
      persist-credentials: false

Configure SSH authentication with persistent credentials for subsequent Git commands:

steps:
  - uses: actions/checkout@v4
    with:
      ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
      ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
      persist-credentials: true
      submodules: true

Summary

  • actions/checkout supports HTTPS token and SSH key authentication methods, handling both through the GitAuthHelper class in src/git-auth-helper.ts.
  • Token authentication injects an authorization header via Git's includeIf configuration, with values masked to prevent log exposure.
  • SSH authentication writes temporary keys to RUNNER_TEMP and configures GIT_SSH_COMMAND for secure transport.
  • The persist-credentials input controls whether authentication persists in repository config (true) or is removed immediately after checkout (false).
  • Automatic cleanup in the post-run phase removes all temporary files and configuration entries to prevent credential leakage.

Frequently Asked Questions

How does actions/checkout handle the GitHub token securely?

The action registers the token as a secret using core.setSecret() immediately upon receipt, and writes credentials to temporary files using placeholder replacement to avoid exposing the value in process logs. According to the implementation in src/git-auth-helper.ts, the actual token value is never written to disk in plaintext during the initial configuration phase; instead, a placeholder is replaced atomically.

What is the difference between persist-credentials true and false?

When persist-credentials is set to true, the action stores authentication configuration in the repository's local Git config (or global config for configureGlobalAuth()), allowing subsequent steps to run Git commands without re-authenticating. When set to false (recommended for security), the action removes all credential configurations and temporary files immediately after the checkout completes, limiting the exposure window.

Can I use SSH authentication for submodules?

Yes. When you provide an ssh-key and set submodules: true, the configureSubmoduleAuth() function in src/git-auth-helper.ts iterates through all submodules and sets the core.sshCommand configuration in each submodule's local config to use the same temporary SSH key and known-hosts file used for the parent repository.

Where does actions/checkout store temporary SSH keys?

The action stores temporary SSH keys and known-hosts files in the directory specified by the RUNNER_TEMP environment variable. These files are created by the configureSsh() method and are automatically deleted during the post-job cleanup phase by removeAuth(), ensuring no private key material persists on the self-hosted or GitHub-hosted runner after the workflow completes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →