cr
diff --git a/lib/src/authentication_challenge.dart b/lib/src/authentication_challenge.dart
index 22dc2a6..773ee3b 100644
--- a/lib/src/authentication_challenge.dart
+++ b/lib/src/authentication_challenge.dart
@@ -40,20 +40,17 @@
       var scanner = new StringScanner(header);
       scanner.scan(whitespace);
       var challenges = parseList(scanner, () {
-        scanner.expect(token, name: "a token");
-        var scheme = scanner.lastMatch[0].toLowerCase();
-
-        scanner.scan(whitespace);
-
-        // The spec specifically requires a space between the scheme and its
-        // params.
-        if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) {
-          scanner.expect(" ", name: '" " or "="');
-        }
+        var scheme = _scanScheme(scanner, whitespaceName: '" " or "="');
 
         // Manually parse the inner list. We need to do some lookahead to
         // disambiguate between an auth param and another challenge.
         var params = {};
+
+        // Consume initial empty values.
+        while (scanner.scan(",")) {
+          scanner.scan(whitespace);
+        }
+
         _scanAuthParam(scanner, params);
 
         var beforeComma = scanner.position;
@@ -61,7 +58,7 @@
           scanner.scan(whitespace);
 
           // Empty elements are allowed, but excluded from the results.
-          if (scanner.matches(",")) continue;
+          if (scanner.matches(",") || scanner.isDone) continue;
 
           scanner.expect(token, name: "a token");
           var name = scanner.lastMatch[0];
@@ -102,16 +99,7 @@
     return wrapFormatException("authentication challenge", challenge, () {
       var scanner = new StringScanner(challenge);
       scanner.scan(whitespace);
-      scanner.expect(token, name: "a token");
-      var scheme = scanner.lastMatch[0].toLowerCase();
-
-      scanner.scan(whitespace);
-
-      // The spec specifically requires a space between the scheme and its
-      // params.
-      if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) {
-        scanner.expect(" ");
-      }
+      var scheme = _scanScheme(scanner);
 
       var params = {};
       parseList(scanner, () => _scanAuthParam(scanner, params));
@@ -121,6 +109,25 @@
     });
   }
 
+  /// Scans a single scheme name and asserts that it's followed by a space.
+  ///
+  /// If [whitespaceName] is passed, it's used as the name for exceptions thrown
+  /// due to invalid trailing whitespace.
+  static String _scanScheme(StringScanner scanner, {String whitespaceName}) {
+    scanner.expect(token, name: "a token");
+    var scheme = scanner.lastMatch[0].toLowerCase();
+
+    scanner.scan(whitespace);
+
+    // The spec specifically requires a space between the scheme and its
+    // params.
+    if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) {
+      scanner.expect(" ", name: whitespaceName);
+    }
+
+    return scheme;
+  }
+
   /// Scans a single authentication parameter and stores its result in [params].
   static void _scanAuthParam(StringScanner scanner, Map params) {
     scanner.expect(token, name: "a token");
@@ -139,9 +146,7 @@
     scanner.scan(whitespace);
   }
 
-  /// Creates a new challenge value with [scheme] and, optionally, [parameters].
-  ///
-  /// If [parameters] isn't passed, it defaults to an empty map.
+  /// Creates a new challenge value with [scheme] and [parameters].
   AuthenticationChallenge(this.scheme, Map<String, String> parameters)
       : parameters = new UnmodifiableMapView(
             new CaseInsensitiveMap.from(parameters));
diff --git a/lib/src/scan.dart b/lib/src/scan.dart
index 1036383..d675b8b 100644
--- a/lib/src/scan.dart
+++ b/lib/src/scan.dart
@@ -41,6 +41,12 @@
 /// the string, or the end of the string.
 List parseList(StringScanner scanner, parseElement()) {
   var result = [];
+
+  // Consume initial empty values.
+  while (scanner.scan(",")) {
+    scanner.scan(whitespace);
+  }
+
   result.add(parseElement());
   scanner.scan(whitespace);
 
@@ -48,7 +54,7 @@
     scanner.scan(whitespace);
 
     // Empty elements are allowed, but excluded from the results.
-    if (scanner.matches(",")) continue;
+    if (scanner.matches(",") || scanner.isDone) continue;
 
     result.add(parseElement());
     scanner.scan(whitespace);
diff --git a/test/authentication_challenge_test.dart b/test/authentication_challenge_test.dart
index ccb8d32..4be4444 100644
--- a/test/authentication_challenge_test.dart
+++ b/test/authentication_challenge_test.dart
@@ -94,6 +94,13 @@
     expect(challenge.parameters, containsPair("realm", "fblthp"));
   });
 
+  test("doesn't normalize the case of the parameter value", () {
+    var challenge = parseChallenge("scheme realm=FbLtHp");
+    expect(challenge.scheme, equals("scheme"));
+    expect(challenge.parameters, containsPair("realm", "FbLtHp"));
+    expect(challenge.parameters, isNot(containsPair("realm", "fblthp")));
+  });
+
   test("allows extra whitespace", () {
     var challenge = parseChallenge(
         "  scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t");
@@ -114,6 +121,26 @@
     }));
   });
 
+  test("allows a leading comma", () {
+    var challenge = parseChallenge(
+        "scheme , realm=fblthp, foo=bar,");
+    expect(challenge.scheme, equals("scheme"));
+    expect(challenge.parameters, equals({
+      "realm": "fblthp",
+      "foo": "bar"
+    }));
+  });
+
+  test("allows a trailing comma", () {
+    var challenge = parseChallenge(
+        "scheme realm=fblthp, foo=bar, ,");
+    expect(challenge.scheme, equals("scheme"));
+    expect(challenge.parameters, equals({
+      "realm": "fblthp",
+      "foo": "bar"
+    }));
+  });
+
   test("disallows only a scheme", () {
     expect(() => parseChallenge("scheme"),
         throwsFormatException);