Pulling and pushing over HTTP using a Personal Access Token

The problem

Using your main password to push and pull from your repositories over HTTP has several drawbacks:

  • You may not even have a main password!

    This is the case on heptapod.host, the commercial service managed by Octobus and Clever Cloud.

    This is more common than it sounds, with the Single Sign-Ons (SSOs) that effectively allow to authenticate and register without any password. Some instances, such as notably heptapod.host, don't even support user passwords: they require users to authenticate with SSOs only.

  • You'll have either to type the password each time you push or to store it, perhaps as clear text in your personal HGRC file (very bad, don't do that!) or using an extension such as mercurial_keyring.

    The problem is that this password controls potentially much more than repository content.

  • It doesn't work if you are using two-factor authentication (2FA).

The solution

Create a Personal Access Token for repository access and use it as if it were a regular password.

This is a standard GitLab feature, that Heptapod has always supported.

Tutorial

The example uses foss.heptapod.net but it works the same on heptapod.host or any other instance running Heptapod ≥ 0.12.

1. Go to your Settings area

Menu access to User Settings

2. Go to the Access Tokens page

Menu access to Access Tokens

3. Fill the token creation form

As you can see in the example below, there are interesting options: one can schedule an automatic expiration, or grant other kinds of access as repository content.

The name will be important for subsequent token management. Without it, you wouldn't recognize it among other tokens.

Personal Access Token Create Form

4. Keep note of the freshly created token immediately

Let us repeat here what's already on the web page: this is the only occasion where the token value is displayed.

Personal Access Token created, only occasion to save it

5. Use the token as if it were your password in repository operations

Here's a demonstration with a freshly created private project.

Let's first check that the project is indeed private:

/tmp $ hg clone https://foss.heptapod.net/testgr/access_token_demo
http authorization required
realm: GitLab
url: https://foss.heptapod.net/testgr/access_token_demo
user: interrupted!

Let's clone using the token:

/tmp $ hg clone https://gracinet:USxSaxBjAu99StGuJUzy@foss.heptapod.net/demos/access_token
destination directory: access_token
no changes found
updating to branch default
0 files updated, 0 files merged, 0 files removed, 0 files unresolved

Mercurial does not store such passwords in the repository-local HGRC file:

/tmp $ cd access_token
/tmp/access_token $ hg paths
default = https://gracinet@foss.heptapod.net/demos/access_token

so we'll repeat it for clarity in this tutorial.

Let's create and push a changeset:

/tmp/access_token $ hg push https://gracinet:USxSaxBjAu99StGuJUzy@foss.heptapod.net/demos/access_token
pushing to https://gracinet:***@foss.heptapod.net/demos/access_token
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files

It worked!

Project after push, displaying the changeset

Conclusion

Personal Access Tokens are the only way to pull and push over HTTP if you don't have a password or are using two-factor authentication.

Of course you still need to store them securely.

They open up interesting ways to manage your access. For instance, you could use different tokens for each of your computers and enforce a personal rotation policy with the automatic expiration date. In case of doubt that your laptop may have been compromised, you can just invalidate its token, without impairing operation from your desktop workstation. In the mean time, nobody could use the compromised machine to delete your repositories or worse if you have instance-wide administration permissions.

The token used for this tutorial was immediately revoked:

/tmp/access_token $ hg pull https://gracinet:USxSaxBjAu99StGuJUzy@foss.heptapod.net/demos/access_token
pulling from https://gracinet:***@foss.heptapod.net/demos/access_token
abort: authorization failed