Package Registry Authentication
01 何が問題だったのか
パッケージレジストリによっては、一部または全部のAPIを利用するために認証を要求したい場合があります。認証によって、操作を行うユーザを識別し、そのリクエストを適切に認可できるようになります。
しかし、SwiftPMがこれまでサポートしていたのはBasic認証(ユーザ名とパスワード)のみで、多くのWebサービスで一般的なアクセストークンやOAuthといった方式には対応していませんでした。これでは、近年のパッケージレジストリサービスと連携するにあたって認証方式の選択肢が不足しています。
加えて、SE-0292 で用意されていた認証情報の登録手段は swift package-registry set サブコマンドに --login と --password を渡すというもので、認証情報が正しいかを事前に検証する仕組みもなく、格納先もプロジェクトレベルのnetrcファイルを含む複数の場所に分散しており、意図せず認証情報をコミットしてしまうリスクもありました。認証方式の拡張と、より安全で一貫した認証情報の管理方法の両方が求められていました。
02 どのように解決されるのか
swift package-registry コマンドに、docker login や npm login からの着想を得た login / logout サブコマンドを追加し、Basic認証に加えてトークン認証をサポートします。将来の認証方式の追加にも対応できる設計になっています。認証情報は可能な限りOSのネイティブな認証情報ストア(macOSではKeychain)に保存し、それが利用できない環境でのみユーザレベルのnetrcファイルにフォールバックします。
login サブコマンド
指定したレジストリに対して認証情報を登録します。SwiftPMはレジストリ側の login APIに対して認証情報を検証するリクエストを送り、成功したら認証情報を永続化します。
swift package-registry login <url> [options]
OPTIONS:
--username ユーザ名
--password パスワード
--token アクセストークン
--no-confirm netrcファイルへの書き出しを確認なしで許可
--netrc-file netrcファイルのパスを指定
--netrc OS側の認証情報ストアが使える場合でもnetrcファイルを使う
<url> はレジストリのベースURL(例: https://example-registry.com)を指定します。HTTPSが必須です。login APIのパスがデフォルトの /login ではない場合は、APIのフルURL(例: https://example-registry.com/api/v1/login)を渡します。
オプションの組み合わせから認証方式が決まります。
| 認証方式 | 必要なオプション |
|---|---|
| Basic | --username, --password |
| Token | --token |
対話モードでは、不足している --password や --token の入力を促されます。たとえば --username のみが指定されていればBasic認証とみなしてパスワードを入力するプロンプトを出します。非対話モードで使う場合は、必要な値を明示的に渡すか、事前に認証情報ストアに登録しておきます。
例: Basic認証(macOS、対話モード)
> 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.
この例では example-registry.com に対するエントリがKeychainに追加されます。
例: トークン認証
> swift package-registry login https://example-registry.com \
--token jappleseedstoken
トークン認証の場合、ログイン名を token、パスワードをアクセストークン本体として認証情報ストア(またはnetrcファイル)に保存されます。
OS側の認証情報ストアが使えない環境では、netrcファイルへ書き出してよいかを確認するプロンプトが出ます。--no-confirm を付けるとこの確認を省略できます。--netrc を付けると、OS側のストアが使える環境でも強制的にnetrcファイルを使わせることができます。
logout サブコマンド
レジストリからログアウトします。OS側の認証情報ストアとユーザレベルの registries.json からエントリを取り除きます。機密情報を誤って消さないよう、netrcファイルは自動更新せず、ユーザ自身で編集する必要があります。
swift package-registry logout <url>
registries.json の拡張
ユーザレベルの ~/.swiftpm/configuration/registries.json に新しく authentication キーを追加し、どのレジストリがどの認証方式を必要とするかを記述します。
{
"registries": {
"[default]": {
"url": "https://example-registry.com"
}
},
"authentication": {
"example-registry.com": {
"type": "basic",
"loginAPIPath": "/api/v1/login"
}
},
"version": 1
}
type は basic または token のいずれかです。loginAPIPath は任意で、デフォルトの /login を上書きしたいときに指定します。login サブコマンドは成功時にこのファイルを自動で更新します。
認証情報の保存先
認証情報は、プラットフォームごとに最も安全な方法で扱われます。
- Basic認証・macOS Keychain: “Internet password” として保存し、Keychainの “item name” は
https://example-registry.comのようにスキーム付きのレジストリURLになります。 -
Basic認証・netrcファイル(OS側のストアが使えない場合のフォールバック):
machine example-registry.com login jappleseed password alpine -
トークン認証・netrcファイル: ログイン名を
token固定にして、トークン本体をパスワード欄に書きます。machine example-registry.com login token password jappleseedstoken
SwiftPMはデフォルトではユーザのホームディレクトリのnetrcファイルを参照し、--netrc-file で別のパスを指定できます。初期リリースでKeychainが使えるのはmacOSのみで、他のプラットフォームではユーザレベルのnetrcファイルにフォールバックします。
SwiftPM側の動作変更
- プロジェクトレベルのnetrcファイルは非対応になります。 netrcファイルの誤コミットによる情報漏えいを防ぐため、ユーザレベルのnetrcファイルのみを参照します。
- 認証情報の探索先はひとつだけになります。 macOSではKeychain、それ以外のプラットフォームではユーザレベルのnetrcファイルです。
--disable-keychainと--disable-netrcオプションは削除されます。
レジストリサービス側のAPI
認証が必要なレジストリは、login APIを実装する必要があります。SwiftPMは認証情報を検証するためにこのAPIへHTTP POST リクエストを送り、その際に次のような Authorization ヘッダを付けます。
- Basic認証:
Authorization: Basic <base64 encoded username:password> - トークン認証:
Authorization: Bearer <token>
レジストリ側は認証成功時に 200、失敗時に 401 を返します。対応していない認証方式が指定された場合は 501 を返します。SwiftPMは 200 が返ってきたときだけ認証情報をローカルに永続化します。