Allow addition of tokens for insecure localhost repositories (#3777)

Thanks for the contribution!
diff --git a/lib/src/command/token_add.dart b/lib/src/command/token_add.dart
index 86f032f..90805cb 100644
--- a/lib/src/command/token_add.dart
+++ b/lib/src/command/token_add.dart
@@ -48,7 +48,9 @@
 
     try {
       var hostedUrl = validateAndNormalizeHostedUrl(rawHostedUrl);
-      if (!hostedUrl.isScheme('HTTPS')) {
+      var isLocalhost =
+          ['localhost', '127.0.0.1', '::1'].contains(hostedUrl.host);
+      if (!hostedUrl.isScheme('HTTPS') && !isLocalhost) {
         throw FormatException('url must be https://, '
             'insecure repositories cannot use authentication.');
       }
diff --git a/test/token/add_token_test.dart b/test/token/add_token_test.dart
index 4321d90..bb61bf3 100644
--- a/test/token/add_token_test.dart
+++ b/test/token/add_token_test.dart
@@ -153,6 +153,24 @@
     await d.dir(configPath, [d.nothing('pub-tokens.json')]).validate();
   });
 
+  test(
+      'with non-secure localhost url creates pub-tokens.json that contains token',
+      () async {
+    await d.dir(configPath).create();
+
+    await runPub(
+      args: ['token', 'add', 'http://localhost/'],
+      input: ['auth-token'],
+    );
+
+    await d.tokensFile({
+      'version': 1,
+      'hosted': [
+        {'url': 'http://localhost', 'token': 'auth-token'}
+      ]
+    }).validate();
+  });
+
   test('with empty environment gives error message', () async {
     await runPub(
       args: ['token', 'add', 'https://mypub.com'],