egit Authentication and Credentials Guide

View Source

This guide covers how to use egit to access private Git repositories using various authentication methods.

Table of Contents

Quick Start

Clone a private repository with SSH key authentication:

1> Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"/home/user/.ssh/id_rsa">>,
  pubkey => <<"/home/user/.ssh/id_rsa.pub">>
}.

2> Repo = git:clone(
  <<"git@github.com:user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => Creds}
).

3> git:fetch(Repo, <<"origin">>, #{credentials => Creds}).

4> git:push(Repo, <<"origin">>, [<<"main">>], #{credentials => Creds}).

Supported Authentication Methods

egit supports four credential types:

TypeURL SchemeUse Case
ssh_keygit@...SSH key-based authentication
userpasshttps://...HTTP basic authentication
tokenhttps://...Personal access tokens (GitHub, GitLab, etc.)
ssh_agentgit@...Delegate to system SSH agent

SSH Key Authentication

SSH Key from Files

Use SSH keys from file paths on the filesystem:

Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"/home/user/.ssh/id_rsa">>,
  pubkey => <<"/home/user/.ssh/id_rsa.pub">>,
  passphrase => <<"optional_passphrase">>
}.

Repo = git:clone(
  <<"git@github.com:user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => Creds}
).

Fields:

  • type (required): ssh_key
  • username (required): Git username (typically "git" for GitHub/GitLab)
  • privkey (required): Path to private key file or PEM content
  • pubkey (optional): Path to public key file or SSH public key content
  • passphrase (optional): Passphrase if key is encrypted

SSH Key from Memory

Use SSH key material directly in memory (useful for Docker/Kubernetes):

PrivateKeyPEM = <<"-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1234567890...
... (PEM content)
-----END RSA PRIVATE KEY-----">>,

PublicKeySSH = <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...">>,

Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => PrivateKeyPEM,
  pubkey => PublicKeySSH
}.

Repo = git:clone(Url, Path, #{credentials => Creds}).

Auto-Detection

egit automatically detects whether the key material is a file path or PEM content:

%% libgit2 will first try ssh_key_memory_new (for PEM content)
%% If that fails, it will try ssh_key_new (for file paths)

With Encrypted Keys

For passphrase-protected SSH keys:

Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"/home/user/.ssh/id_rsa">>,
  pubkey => <<"/home/user/.ssh/id_rsa.pub">>,
  passphrase => <<"my_passphrase">>  %% Provide passphrase here
}.

Repo = git:clone(Url, Path, #{credentials => Creds}).

Username/Password Authentication

For HTTP(S) repositories with basic authentication:

Creds = #{
  type => userpass,
  username => <<"github_user">>,
  password => <<"personal_access_token_or_password">>
}.

Repo = git:clone(
  <<"https://github.com/user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => Creds}
).

git:fetch(Repo, <<"origin">>, #{credentials => Creds}).

git:push(Repo, <<"origin">>, [<<"main">>], #{credentials => Creds}).

Fields:

  • type (required): userpass
  • username (required): GitHub/GitLab username
  • password (required): Password or personal access token

Note: For security, use personal access tokens instead of passwords.

Personal Access Tokens

GitHub Personal Access Token

%% Create at: https://github.com/settings/tokens
%% Select scopes: repo (full control of private repositories)

TokenCreds = #{
  type => token,
  username => <<"oauth2">>,  %% or any username
  token => <<"ghp_1234567890abcdefghijklmnopqrstuvwxyz">>
}.

%% HTTPS URL required for token-based auth
Repo = git:clone(
  <<"https://github.com/user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => TokenCreds}
).

GitLab Personal Access Token

%% Create at: https://gitlab.com/-/profile/personal_access_tokens
%% Select scopes: api, read_repository, write_repository

TokenCreds = #{
  type => token,
  username => <<"gitlab-ci-token">>,
  token => <<"glpat-1234567890abcdefghij">>
}.

Repo = git:clone(
  <<"https://gitlab.com/user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => TokenCreds}
).

Other Git Hosting Services

For any service supporting token-based HTTPS auth:

TokenCreds = #{
  type => token,
  username => <<"username_or_oauth2">>,
  token => <<"your_token_here">>
}.

SSH Agent

Delegate authentication to the system SSH agent:

%% Requires ssh-agent running with your keys loaded:
%% $ ssh-agent
%% $ ssh-add ~/.ssh/id_rsa

AgentCreds = #{
  type => ssh_agent,
  username => <<"git">>
}.

Repo = git:clone(
  <<"git@github.com:user/private-repo.git">>,
  <<"/tmp/private-repo">>,
  #{credentials => AgentCreds}
).

Note: On macOS, SSH keys are automatically managed by the system and added to ssh-agent.

API Reference

Credentials Data Structure

Map Format (Recommended)

%% SSH Key
#{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"path_or_content">>,
  pubkey => <<"path_or_content">>,  % Optional
  passphrase => <<"pass">>           % Optional
}

%% Username/Password
#{
  type => userpass,
  username => <<"user">>,
  password => <<"pass">>
}

%% Token
#{
  type => token,
  username => <<"user">>,
  token => <<"token_value">>
}

%% SSH Agent
#{
  type => ssh_agent,
  username => <<"git">>
}

Proplist Format

All formats can also be specified as proplists:

[
  {type, ssh_key},
  {username, <<"git">>},
  {privkey, <<"path">>},
  {pubkey, <<"path">>}
]

Functions

clone/3

Clone a repository with credentials:

-spec clone(binary()|string(), binary()|string(), Options :: map()) ->
  repository() | {error, term()}.

git:clone(Url, Path, #{credentials => Creds}).

fetch/3

Fetch from a remote with credentials:

-spec fetch(repository(), binary()|string(), Options :: map()) ->
  ok | {error, term()}.

git:fetch(Repo, <<"origin">>, #{credentials => Creds}).

pull/3

Pull from a remote with credentials:

-spec pull(repository(), binary()|string(), Options :: map()) ->
  ok | {error, term()}.

git:pull(Repo, <<"origin">>, #{credentials => Creds}).

push/4

Push to a remote with credentials:

-spec push(repository(), binary()|string(), [binary()|string()], Options :: map()) ->
  ok | {error, term()}.

git:push(Repo, <<"origin">>, [<<"refs/heads/main">>], #{credentials => Creds}).

Best Practices

1. Use SSH Keys Over Passwords

SSH keys are more secure than passwords:

%% Good: SSH key
Creds = #{type => ssh_key, username => <<"git">>, privkey => Path}.

%% Avoid: Plain password
% Creds = #{type => userpass, username => <<"user">>, password => <<"pass">>}.

2. Use Personal Access Tokens Over Passwords

For HTTPS, use PATs instead of passwords:

%% Good: Personal access token
TokenCreds = #{type => token, username => <<"user">>, token => Token}.

%% Avoid: Password
% Creds = #{type => userpass, username => <<"user">>, password => Password}.

3. Protect Sensitive Data

Never hardcode credentials in your source:

%% Good: Load from environment
PrivateKey = os:getenv("GITHUB_SSH_KEY"),
Creds = #{type => ssh_key, username => <<"git">>, privkey => PrivateKey}.

%% Good: Load from file
{ok, KeyContent} = file:read_file("/etc/secrets/github_key"),
Creds = #{type => ssh_key, username => <<"git">>, privkey => KeyContent}.

%% Avoid: Hardcoded credentials
% Creds = #{type => token, token => <<"ghp_1234567890...">>}.

4. Use SSH Agent in CI/CD

In CI/CD pipelines, use SSH agent:

%% Docker/Kubernetes with SSH agent socket
AgentCreds = #{type => ssh_agent, username => <<"git">>},
Repo = git:clone(Url, Path, #{credentials => AgentCreds}).

5. Default Authentication

When no credentials are provided, egit falls back to system-level auth:

%% Uses SSH agent, ~/.ssh/config, git credential helper
Repo = git:clone(Url, Path).
git:fetch(Repo, <<"origin">>).
git:push(Repo, <<"origin">>, [<<"main">>]).

6. Scope Tokens Appropriately

When creating PATs, use the minimum required scopes:

GitHub:

  • Clone private repos: repo scope (full control)
  • Push to private repos: repo scope
  • Deploy key for read-only: Create deploy key instead

GitLab:

  • Read-only: read_repository
  • Write access: write_repository
  • Full access: api

Troubleshooting

"authentication required but no callback set"

The library needs credentials for the repository. Provide credentials in the options map:

%% Wrong: No credentials
Repo = git:clone(<<"git@github.com:user/private-repo.git">>, Path).

%% Correct: Provide credentials
Creds = #{type => ssh_key, username => <<"git">>, privkey => KeyPath},
Repo = git:clone(
  <<"git@github.com:user/private-repo.git">>,
  Path,
  #{credentials => Creds}
).

SSH Key Not Found

Verify the key path is correct:

%% Check if key file exists
file:read_file("/home/user/.ssh/id_rsa").

%% Use absolute path
Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"/home/user/.ssh/id_rsa">>  %% Absolute path
}.

"Permission denied" with SSH Keys

Verify the key has the correct permissions:

# SSH keys should be readable only by owner
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub

Token Expired or Invalid

Check if your personal access token is still valid:

GitHub:

GitLab:

HTTP(S) with SSH URL

If using HTTPS credentials with an SSH URL, switch the URL format:

%% Wrong: Token credentials with SSH URL
TokenCreds = #{type => token, username => <<"user">>, token => Token},
% Repo = git:clone(<<"git@github.com:user/repo.git">>, Path, #{credentials => TokenCreds}).

%% Correct: Use HTTPS URL
Repo = git:clone(
  <<"https://github.com/user/repo.git">>,
  Path,
  #{credentials => TokenCreds}
).

SSH Keys with Passphrase

Ensure passphrase is provided for encrypted keys:

%% Encrypted key without passphrase will fail
Creds = #{
  type => ssh_key,
  username => <<"git">>,
  privkey => <<"/home/user/.ssh/id_rsa">>,
  passphrase => <<"correct_passphrase">>  %% Must provide passphrase
}.

SSH Agent Not Found

Ensure SSH agent is running:

# Start SSH agent
eval "$(ssh-agent -s)"

# Add your key
ssh-add ~/.ssh/id_rsa

# Verify key is loaded
ssh-add -l

Then use SSH agent credentials:

AgentCreds = #{type => ssh_agent, username => <<"git">>},
Repo = git:clone(Url, Path, #{credentials => AgentCreds}).

Examples

Complete Workflow: Private Mono-Repository

-module(repo_manager).

setup_and_sync(Url, LocalPath, PrivateKeyPath) ->
  %% Create credentials
  Creds = #{
    type => ssh_key,
    username => <<"git">>,
    privkey => PrivateKeyPath,
    pubkey => PrivateKeyPath ++ ".pub"
  },

  %% Clone repository
  Repo = git:clone(Url, LocalPath, #{credentials => Creds}),

  %% Create feature branch
  ok = git:branch_create(Repo, "feature/new-work"),
  ok = git:checkout(Repo, "feature/new-work"),

  %% Make changes
  ok = file:write_file(
    LocalPath ++ "/new_file.txt",
    "Important work\n"
  ),

  %% Stage and commit
  #{files := Files} = git:add(Repo, "."),
  io:format("Staged: ~w~n", [Files]),

  {ok, CommitOID} = git:commit(Repo, "Add new feature"),
  io:format("Committed: ~s~n", [CommitOID]),

  %% Push with credentials
  ok = git:push(
    Repo,
    <<"origin">>,
    [<<"refs/heads/feature/new-work:refs/heads/feature/new-work">>],
    #{credentials => Creds}
  ),

  io:format("Pushed to remote~n"),
  Repo.

Docker Environment

%% In Docker/Kubernetes with SSH agent socket
docker_clone(Url, Path) ->
  %% SSH agent socket is typically at /run/user/1000/ssh_agent
  AgentCreds = #{type => ssh_agent, username => <<"git">>},
  git:clone(Url, Path, #{credentials => AgentCreds}).

GitHub Actions CI/CD

%% In GitHub Actions with SSH key in secret
ci_clone(Url, Path) ->
  %% Secrets are available as environment variables
  PrivateKey = os:getenv("GITHUB_SSH_KEY"),
  Creds = #{
    type => ssh_key,
    username => <<"git">>,
    privkey => PrivateKey
  },
  git:clone(Url, Path, #{credentials => Creds}).

See Also