Skip to content

Latest commit

 

History

History
449 lines (339 loc) · 12.6 KB

0378-package-registry-auth.md

File metadata and controls

449 lines (339 loc) · 12.6 KB

Package Registry Authentication

Introduction

A package registry may require authentication for some or all of its API in order to identify user performing the action and authorize the request accordingly.

Motivation

Common authentication methods used by web services include basic authentication, access token, and OAuth. SwiftPM supports only basic authentication today, which limits its abilities to interact with package registry services.

Proposed solution

We propose to modify the swift package-registry command and registry configuration to add token authentication support. The changes should also ensure there is flexibility to add other authentication methods in the future.

The design draws inspiration from docker login and npm login, in that there will be a single command for user to verify and persist registry credentials.

Detailed design

Changes to swift package-registry command

Instead of the swift package-registry set subcommand and the --login and --password options as proposed in SE-0292 originally, we propose the new login and logout subcommands for adding/removing registry credentials.

New login subcommand

Log in to a package registry. SwiftPM will verify the credentials using the registry service's login API. If it returns a successful response, credentials will be persisted to the operating system's credential store if supported, or the user-level netrc file otherwise. The user-level configuration file located at ~/.swiftpm/configuration/registries.json will also be updated.

SYNOPSIS
    swift package-registry login <url> [options]
OPTIONS:  
  --username     Username
  --password     Password
  
  --token        Access token

  --no-confirm   Allow writing to netrc file without confirmation
  --netrc-file   Specify the netrc file path
  --netrc        Use netrc file even in cases where other credential stores are preferred

url should be the registry's base URL (e.g., https://example-registry.com). In case the location of the login API is something other than /login (e.g., https://example-registry.com/api/v1/login), provide the full URL.

The URL must be HTTPS.

The table below shows the supported authentication types and their required option(s):

Authentication Method Required Option(s)
Basic --username, --password
Token --token

The tool will analyze the provided options to determine the authentication type and prompt (i.e., interactive mode) for the password/token if it is missing. For example, if only --username is present, the tool assumes basic authentication and prompts for the password.

For non-interactive mode, simply provide the --password or --token option as required or make sure the secret is present in credential storage.

If the operating system's credential store is not supported, the tool will prompt user for confirmation before writing credentials to the less secured netrc file. Use --no-confirm to disable this confirmation.

To force usage of netrc file instead of the operating system's credential store, pass the --netrc flag.

Example: basic authentication (macOS, interactive)
> swift package-registry login https://example-registry.com \
    --username jappleseed
Enter password for 'jappleseed':

Login successful.
Credentials have been saved to the operating system's secure credential store.

An entry for example-registry.com would be added to Keychain.

registries.json would be updated to indicate that example-registry.com requires basic authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "basic"
    },
    ...
  },
  ...
}
Example: basic authentication (operating system's credential store not supported, interactive)
> swift package-registry login https://example-registry.com \
    --username jappleseed
Enter password for 'jappleseed':

Login successful.

WARNING: Secure credential store is not supported on this platform.
Your credentials will be written out to netrc file.
Continue? (Yes/No): Yes

Credentials have been saved to netrc file.

An entry for example-registry.com would be added to the netrc file:

machine example-registry.com
login jappleseed
password alpine

registries.json would be updated to indicate that example-registry.com requires basic authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "basic"
    },
    ...
  },
  ...
}
Example: basic authentication (use netrc file instead of operating system's credential store, interactive)
> swift package-registry login https://example-registry.com \
    --username jappleseed
    --netrc
Enter password for 'jappleseed':

Login successful.

WARNING: You choose to use netrc file instead of the operating system's secure credential store. 
Your credentials will be written out to netrc file.
Continue? (Yes/No): Yes

Credentials have been saved to netrc file.

An entry for example-registry.com would be added to the netrc file:

machine example-registry.com
login jappleseed
password alpine

registries.json would be updated to indicate that example-registry.com requires basic authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "basic"
    },
    ...
  },
  ...
}
Example: basic authentication (operating system's credential store not supported, non-interactive)
> swift package-registry login https://example-registry.com \
    --username jappleseed \
    --password alpine \
    --no-confirm
    
Login successful.
Credentials have been saved to netrc file.

An entry for example-registry.com would be added to the netrc file:

machine example-registry.com
login jappleseed
password alpine

registries.json would be updated to indicate that example-registry.com requires basic authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "basic"
    },
    ...
  },
  ...
}

Example: basic authentication (operating system's credential store not supported, non-interactive, non-default login URL)
> swift package-registry login https://example-registry.com/api/v1/login \
    --username jappleseed \
    --password alpine \
    --no-confirm
    
Login successful.
Credentials have been saved to netrc file.

An entry for example-registry.com would be added to the netrc file:

machine example-registry.com
login jappleseed
password alpine

registries.json would be updated to indicate that example-registry.com requires basic authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "basic",
      "loginAPIPath": "/api/v1/login"
    },
    ...
  },
  ...
}
Example: token authentication
> swift package-registry login https://example-registry.com \
    --token jappleseedstoken

An entry for example-registry.com would be added to the operating system's credential store if supported, or the user-level netrc file otherwise:

machine example-registry.com
login token
password jappleseedstoken

registries.json would be updated to indicate that example-registry.com requires token authentication:

{
  "authentication": {
    "example-registry.com": {
      "type": "token"
    },
    ...
  },
  ...
}

New logout subcommand

Log out from a registry. Credentials are removed from the operating system's credential store if supported, and the user-level configuration file (registries.json).

To avoid accidental removal of sensitive data, netrc file needs to be updated manually by the user.

SYNOPSIS
    swift package-registry logout <url>

Changes to registry configuration

We will introduce a new authentication key to the user-level registries.json file, which by default is located at ~/.swiftpm/configuration/registries.json. Any package registry that requires authentication must have a corresponding entry in this dictionary.

{
  "registries": {
    "[default]": {
      "url": "https://example-registry.com"
    }
  },
  "authentication": {
    "example-registry.com": {
      "type": <AUTHENTICATION_TYPE>, // One of: "basic", "token"
      "loginAPIPath": <LOGIN_API_PATH> // Optional. Overrides the default API path (i.e., /login).
    }
  },
  "version": 1
}

type must be one of the following:

  • basic: username and password
  • token: access token

Credentials are to be specified in the native credential store of the operating system if supported, otherwise in the user-level netrc file. (Only macOS Keychain will be supported in the initial feature release; more might be added in the future.)

See credential storage for more details on configuring credentials for each authentication type.

Credential storage

SwiftPM will always use the most secure way to handle credentials on the platform. In general, this would mean using the operating system's credential store (e.g., Keychain on macOS). It falls back to netrc file only if there is no other solution available.

Basic Authentication

macOS Keychain

Registry credentials should be stored as "Internet password" items in the macOS Keychain. The "item name" should be the registry URL, including https:// (e.g., https://example-registry.com).

netrc file (if operating system's credential store is not supported)

A netrc entry for basic authentication looks as follows:

machine example-registry.com
login jappleseed
password alpine

By default, SwiftPM looks for netrc file in the user's home directory. A custom netrc file can be specified using the --netrc-file option.

Token Authentication

User can configure access token for a registry as similarly done for basic authentication, but with token as the login/username and the access token as the password.

For example, a netrc entry would look like:

machine example-registry.com
login token
password jappleseedstoken

Additional changes in SwiftPM

  1. Only the user-level netrc file will be used. Project-level netrc file will not be supported.
  2. SwiftPM will perform lookups in one credential store only. For macOS, it will be Keychain. For all other platforms, it will be the user-level netrc file.
  3. The --disable-keychain and --disable-netrc options will be removed.

New package registry service API

A package registry that requires authentication must implement the new API endpoint(s) covered in this section.

login API

SwiftPM will send a HTTP POST request to this API to validate user credentials provided by the login subcommand.

The default API path is /login, but this can be overridden by providing the full API URL to the login subcommand.

The API request will include an Authorization HTTP header constructed as follows:

  • Basic authentication: Authorization: Basic <base64 encoded username:password>
  • Token authentication: Authorization: Bearer <token>

The registry service must return HTTP status 200 in the response if login is successful, and 401 otherwise.

In case the registry service does not support an authentication method, it should return HTTP status 501.

SwiftPM will persist user credentials to local credential store if login is successful.

Security

This proposal moves SwiftPM to use operating system's native credential store (e.g., macOS Keychain) on supported platforms, which should yield better security.

We are also eliminating the use of project-level netrc file. This should prevent accidental checkin of netrc file and thus leakage of sensitive information.

Impact on existing packages

This proposal eliminates the project-level netrc file. There should be no other impact on existing packages.