You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pr@jena.apache.org by GitBox <gi...@apache.org> on 2022/06/12 22:00:41 UTC

[GitHub] [jena] kinow commented on a diff in pull request #1378: Auth general bearer

kinow commented on code in PR #1378:
URL: https://github.com/apache/jena/pull/1378#discussion_r895237017


##########
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java:
##########
@@ -36,51 +37,66 @@
 
 /**
  * Process an "Authorization: Bearer" header.
- * If present, extract as JWT
  * <p>
  * This has two modes:
  * <ul>
- * <li>{@code requireBearer=true} : Only accept requests with a bearer
- * authorization header. If missing, respond with a 401 challenge asking for a
- * bearer token.</li>
+ * <li>{@code requireBearer=true} : Only accept requests with a bearer authorization
+ * header. If missing, respond with a 401 challenge asking for a bearer token.</li>
  * <li>{@code requireBearer=false} : Verify any bearer token but otherwise pass
  * through the request as-is. This will pass through requests to an unsecured
  * ("public") dataset but will cause a 403 on a secured dataset, not a 401
- * challenge.
+ * challenge.</li>
  * </ul>
  * <p>
- * A more flexible approach for mixing authentication methods is to setup Fuseki
- * with multiple {@code AuthBearerFilter} filters installed in a Fuseki
- * server, with different path specs.</li>
+ * Handling the bearer token is delegated to a handler function, passing the token as
+ * seen in the HTTP request. Normally, this will be base64 encoded. It is the
+ * responsibility of the handler function to decode the token.
+ * <p>
+ * This class has some extension points for customizing the handling of bearer
+ * authentication for
+ * <ul>
+ * <li>getting the token from the HTTP request (e.g. from a different HTTP field).</li>
+ * <li>handling the challenge case (no authentication provided)

Review Comment:
   Missing closing `</li>`



##########
jena-arq/src/main/java/org/apache/jena/http/auth/AuthHeader.java:
##########
@@ -167,316 +193,407 @@ public String getBasicUserPassword() {
     /** The rest of the line for "unknown" */
     public String getUnknown() { return unknown; }
 
-    private static char EndOfStr = 0xFFFF;
+    private static class Builder {
 
-    private char nextChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        idx++;
-        return ch;
-    }
+        // The original header value.
+        private final String string;
+        private final int N;
+        private final boolean isChallenge;
 
-    private char peekChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        // No idx++
-        return ch;
-    }
+        private int idx;
+        private static char EndOfStr = 0xFFFF;
+        private char nextChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            idx++;
+            return ch;
+        }
 
-    private void parse$(String string) {
-        // The scheme.
-        int N = string.length();
-        skipWhitespace();
-        String token = token();
-        if ( token == null )
-            return;
-
-        this.authSchemeStr = token;
-        AuthScheme authScheme = AuthScheme.scheme(token);
-        if ( authScheme == null )
-            return;
-        this.authScheme = authScheme;
-        skipWhitespace();
-        if ( idx >= N )
-            return;
-        this.authSchemeArgs = string.substring(idx);
-
-        switch(authScheme) {
-            case BASIC :   parseBasic(); break;
-            case DIGEST :  parseDigest(); break;
-            case BEARER :  parseBearer(); break;
-            case UNKNOWN : parseUnknown(); break;
-            default :
-                break;
+        private char peekChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            // No idx++
+            return ch;
         }
-    }
 
-    /* RFC 7617 - was RFC 2617 - basic
-     * challenge   = "Basic" realm
-     * credentials = "Basic" basic-credentials
-     * basic-credentials = base64-user-pass
-     * base64-user-pass  = <base64 [4] encoding of user-pass,
-     *                      except not limited to 76 char/line>
-     * user-pass   = userid ":" password
-     * userid      = *<TEXT excluding ":">
-     * password    = *TEXT
-    */
-    private void parseBasic() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else
-            basicUserPassword = basicBase64();
-    }
+        // Scheme as written
+        private String authSchemeName = null;
+        // The rest of the value, stripped of leading and trailing whitespace.
+        private String authSchemeArgs = null;
+
+        private AuthScheme authScheme = null;
+        // Any key-value pairs.
+        private Map<String, String> authParams = null;
+
+        // Per schema fields.
+        private String basicUserPassword = null;
+        private String bearerToken = null;
+        private String unknown = null;
+
+        // Parsing setup.
+        Builder(String string, boolean isChallenge) {
+            this.isChallenge = isChallenge;
+            this.string = string;
+            this.N = string.length();
+        }
 
-    /* RFC 7616 - was RFC 2617 - digest
-     *
-     * challenge        =  "Digest" digest-challenge
-     *
-     * digest-challenge  = 1#( realm | [ domain ] | nonce |
-     *                     [ opaque ] |[ stale ] | [ algorithm ] |
-     *                     [ qop-options ] | [auth-param] )
-     *
-     * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
-     * URI               = absoluteURI | abs_path
-     * nonce             = "nonce" "=" nonce-value
-     * nonce-value       = quoted-string
-     * opaque            = "opaque" "=" quoted-string
-     * stale             = "stale" "=" ( "true" | "false" )
-     * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
-     * qop-options       = "qop" "=" <"> 1#qop-value <">
-     * qop-value         = "auth" | "auth-int" | token
-     */
-    /*
-     *  (",")? token "=" (token | quoted-string)
-     */
-    private void parseDigest() {
-        authParams = mapAuthParams();
-    }
+        // Non-parsing setup.
+        Builder() {
+            this.isChallenge = false;
+            this.string = null;
+            this.N = 0;
+        }
 
-    /* RFC 6750 - bearer
-     *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
-     *   credentials = "Bearer" 1*SP b64token
-     */
-    private void parseBearer() {
-        int startIdx = idx;
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else {
-            String token = b64token();
-            if ( token == null )
-                unknown = string.substring(startIdx).trim();
-            else
-                bearerToken = stripPadding(token);
+        public Builder setAuthSchemeName(String authSchemeName) {
+            this.authSchemeName = authSchemeName;
+            return this;
         }
-    }
 
-    // Remove trailing padding characters.
-    private String stripPadding(String token) {
-        int idx = token.length();
-        for (; idx > 0; idx--) {
-            if ( token.charAt(idx-1) != '=' )
-                break;
+        public Builder setAuthSchemeArgs(String authSchemeArgs) {
+            this.authSchemeArgs = authSchemeArgs;
+            return this;
         }
-        if ( idx == token.length() )
-            return token;
-        return token.substring(0,idx);
-    }
 
-    /*
-     * The rest of the line, trimmed.
-     */
-    private void parseUnknown() {
-        unknown = string.substring(idx).trim();
-    }
+        public Builder setAuthScheme(AuthScheme authScheme) {
+            this.authScheme = authScheme;
+            return this;
+        }
+
+        public Builder setAuthParams(Map<String, String> authParams) {
+            this.authParams = authParams;
+            return this;
+        }
+
+        public Builder setBasicUserPassword(String basicUserPassword) {
+            this.basicUserPassword = basicUserPassword;
+            return this;
+        }
+
+        public Builder setBearerToken(String bearerToken) {
+            this.bearerToken = bearerToken;
+            return this;
+        }
+
+        public Builder setUnknownToken(String unknownToken) {
+            this.unknown = unknownToken;
+            return this;
+        }
 
-    private Map<String, String> mapAuthParams() {
-        Map<String, String> map = new LinkedHashMap<>();
-        // auth-param = token BWS "=" BWS ( token / quoted-string )
-        skipWhitespaceComma();
-        while ( idx < N ) {
-            String key = token();
+        /** Set the fields by parsing */
+        private void parse$() {
+            // The scheme.
             skipWhitespace();
-            char ch = nextChar();
-            if ( idx == N || ch != '=' ) {
-                // Bad. Early end-of-string or no "="
-                return null;
-            }
+            String token = httpToken();
+            if ( token == null )
+                return;
+
+            setAuthSchemeName(token);
+            AuthScheme authScheme = AuthScheme.scheme(token);
+            if ( authScheme == null )
+                return;
+            setAuthScheme(authScheme);
             skipWhitespace();
-            String value = tokenOrQuotedString();
-            if ( value == null )
-                return null;
-            String lcKey = key.toLowerCase(Locale.ROOT);
-            map.put(lcKey, value);
-            skipWhitespaceComma();
+            if ( idx >= N )
+                return;
+            setAuthSchemeArgs(string.substring(idx));
+
+            switch(authScheme) {
+                case BASIC :   parseBasic(); break;
+                case DIGEST :  parseDigest(); break;
+                case BEARER :  parseBearer(); break;
+                case UNKNOWN : parseUnknown(); break;
+                default :
+                    break;
+            }
         }
-        return map;
-    }
 
-    // Basic and Bearer have a challenge form which is auth-params
-    // and a credentials form which is a base64 token.
-    // We can tell them apart by the use of "=".
-    // If the last equals "=" is followed by at least one non-whitespace character)
-    // it is an auth-param which still may fail parsing.
-    private boolean isChallenge() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        // Assumes the string has been stripped of trailing white space.
-        int idxEquals = string.lastIndexOf('=');
-        if ( idxEquals > 0 && idxEquals < N-1 )
-            // An "=" and not the last character, with non "=" after. Challenge.
-            return true;
-        return false;
-    }
+        private AuthHeader build() {
+            return new AuthHeader(string, authScheme, authSchemeName, authSchemeArgs, authParams, basicUserPassword, bearerToken, unknown);
+        }
 
-    private String tokenOrQuotedString() {
-        char ch = peekChar();
-        if ( ch == '"' )
-            return quotedString();
-        else
-            return token();
-    }
+        /* RFC 7617 - was RFC 2617 - basic
+         * challenge   = "Basic" realm
+         * credentials = "Basic" basic-credentials
+         * basic-credentials = base64-user-pass
+         * base64-user-pass  = <base64 [4] encoding of user-pass,
+         *                      except not limited to 76 char/line>
+         * user-pass   = userid ":" password
+         * userid      = *<TEXT excluding ":">
+         * password    = *TEXT
+         */
+        private void parseBasic() {
+            // Determine whether a challenge or credentials.
+            // challenge is "realm=" and at least one character.
+            // credentials is a URL base 64 encoded string, may end in "=".
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else
+                setBasicUserPassword(basicBase64());
+        }
 
-    private String token() {
-        int i = idx;
-        int j = whileTrue(string, idx, N, test_tchar);
-        if ( i == j )
-            return null;
-        this.idx = j;
-        if ( j > 0 )
-            return string.substring(i, j);
-        return null;
-    }
+        /* RFC 7616 - was RFC 2617 - digest
+         *
+         * challenge        =  "Digest" digest-challenge
+         *
+         * digest-challenge  = 1#( realm | [ domain ] | nonce |
+         *                     [ opaque ] |[ stale ] | [ algorithm ] |
+         *                     [ qop-options ] | [auth-param] )
+         *
+         * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
+         * URI               = absoluteURI | abs_path
+         * nonce             = "nonce" "=" nonce-value
+         * nonce-value       = quoted-string
+         * opaque            = "opaque" "=" quoted-string
+         * stale             = "stale" "=" ( "true" | "false" )
+         * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
+         * qop-options       = "qop" "=" <"> 1#qop-value <">
+         * qop-value         = "auth" | "auth-int" | token
+         */
+        /*
+         *  (",")? token "=" (token | quoted-string)
+         */
+        private void parseDigest() {
+            setAuthParams(mapAuthParams());
+        }
 
-    private String quotedString() {
-        if ( string == null )
-            return null;
-        if ( idx < 0 )
-            return null;
-        if ( idx >= N )
-            return null;
-        StringBuilder stringBuilder = new StringBuilder();
-        // If reuse a string builder. -- stringBuilder.setLength(0);
-        char ch0 = nextChar();
-        if ( ch0 != '"' ) {
-            System.out.println("Does not start with a dquote");
-            return null;
+        /* RFC 6750 - bearer
+         *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
+         *   credentials = "Bearer" 1*SP b64token
+         * But JWT occurs where each part is padded and so has internal "="
+         */
+        private void parseBearer() {
+            int startIdx = idx;
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else {
+                // Generalizes b64token to cover internal "=" as found in JWT.
+                String token = bearerToken();
+                if ( token == null )
+                    setUnknownToken(string.substring(startIdx).trim());
+                else {
+                    //setBearerToken(stripPadding(token));
+                    setBearerToken(token);
+                }
+            }
         }
 
-        for (;;) {
-            char ch = nextChar();
-            if ( ch == EndOfStr )
-                return null;
-            if ( ch == '"' )
-                // Terminating double quote
-                return stringBuilder.toString();
-            if ( ch == CH_RSLASH ) {
-                ch = nextChar();
-                if ( ch == EndOfStr )
+        // Remove trailing padding characters ('=') of base64.
+        private String stripPadding(String token) {
+            int idx = token.length();
+            for (; idx > 0; idx--) {
+                if ( token.charAt(idx-1) != '=' )
+                    break;
+            }
+            if ( idx == token.length() )
+                return token;
+            return token.substring(0,idx);
+        }
+
+        /*
+         * The rest of the line, trimmed.
+         */
+        private void parseUnknown() {
+            setUnknownToken(string.substring(idx).trim());
+        }
+
+        private Map<String, String> mapAuthParams() {
+            Map<String, String> map = new LinkedHashMap<>();
+            // auth-param = token BWS "=" BWS ( token / quoted-string )
+            skipWhitespaceComma();
+            while ( idx < N ) {
+                String key = httpToken();
+                skipWhitespace();
+                char ch = nextChar();
+                if ( idx == N || ch != '=' ) {
+                    // Bad. Early end-of-string or no "="
+                    return null;
+                }
+                skipWhitespace();
+                String value = tokenOrQuotedString();
+                if ( value == null )
                     return null;
+                String lcKey = key.toLowerCase(Locale.ROOT);
+                map.put(lcKey, value);
+                skipWhitespaceComma();
             }
-            stringBuilder.append(ch);
+            return map;
         }
-    }
 
-    // Base64 form defined in RFC4648, Section 4. with '+' and '/'
-    // An alternative is base64url uses '-' and '_'
-    private String basicBase64() {
-        int j1 = whileTrue(string, idx, N, test_base64);
-        int j2 = whileTrue(string, j1, N, test_base64_pad); // Strictly, one or two.
-        if ( j2 != N )
-            // Didn't reach the end of string.
+        private boolean isChallenge() { return isChallenge; }
+
+        private String tokenOrQuotedString() {
+            char ch = peekChar();
+            if ( ch == '"' )
+                return quotedString();
+            else
+                return httpToken();
+        }
+
+        // HTTP token.
+        private String httpToken() {
+            int i = idx;
+            int j = whileTrue(string, idx, N, test_http_tokenchar);
+            if ( i == j )
+                return null;
+            this.idx = j;
+            if ( j > 0 )
+                return string.substring(i, j);
             return null;
-        return string.substring(idx,  j2);
-    }
-    // base64url uses '-' and '_'
+        }
 
+        private String quotedString() {
+            if ( string == null )
+                return null;
+            if ( idx < 0 )
+                return null;
+            if ( idx >= N )
+                return null;
+            StringBuilder stringBuilder = new StringBuilder();
+            // If reuse a string builder. -- stringBuilder.setLength(0);
+            char ch0 = nextChar();
+            if ( ch0 != '"' ) {
+                //System.err.println("Does not start with a dquote");
+                return null;
+            }
 
+            for (;;) {
+                char ch = nextChar();
+                if ( ch == EndOfStr )
+                    return null;
+                if ( ch == '"' )
+                    // Terminating double quote
+                    return stringBuilder.toString();
+                if ( ch == CH_RSLASH ) {
+                    ch = nextChar();
+                    if ( ch == EndOfStr )
+                        return null;
+                }
+                stringBuilder.append(ch);
+            }
+        }
 
-    //token68        = 1*( ALPHA / DIGIT /
-    //                     "-" / "." / "_" / "~" / "+" / "/" ) *"="
-    /** Single token68 */
-    private String token68() {
-        int j1 = whileTrue(string, idx, N, test_tok68);
-        int j2 = whileTrue(string, j1, N, test_base64_pad);
-        if ( j2 != N )
-            // Didn't reach the end of string.
-            return null;
-        return string.substring(idx,  j2);
-    }
+        // Base64 form defined in RFC4648, Section 4. with '+' and '/'
+        // An alternative is base64url uses '-' and '_'
+        private String basicBase64() {
+            int j1 = whileTrue(string, idx, N, test_base64);
+            int j2 = whileTrue(string, j1, N, test_base64_pad); // Strictly, one or two.
+            if ( j2 != N )
+                // Didn't reach the end of string.
+                return null;
+            return string.substring(idx,  j2);
+        }
 
-    // Bearer "base 64" URL encoding is given as
-    // b64token    = 1*( ALPHA / DIGIT /
-    //                  "-" / "." / "_" / "~" / "+" / "/" ) *"="
-    // which is token68 with trailing "=".
+        //token68        = 1*( ALPHA / DIGIT /
+        //                     "-" / "." / "_" / "~" / "+" / "/" ) *"="
+        /** RFC 7235 : Single token68 */
+        private String token68() {
+            int j1 = whileTrue(string, idx, N, test_tok68);
+            int j2 = whileTrue(string, j1, N, test_base64_pad);
+            if ( j2 != N )
+                // Didn't reach the end of string.
+                return null;
+            String s = string.substring(idx,  j2);
+            idx = j2;
+            return s;
+        }
 
-    private String b64token() { return token68(); }
+        // RFC6750 OAuth
+        // Bearer "base 64" URL encoding is given as
+        // b64token    = 1*( ALPHA / DIGIT /
+        //                  "-" / "." / "_" / "~" / "+" / "/" ) *"="
+        // which is token68.
+
+        private String b64token() { return token68(); }

Review Comment:
   Not used?



##########
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java:
##########
@@ -36,51 +37,66 @@
 
 /**
  * Process an "Authorization: Bearer" header.
- * If present, extract as JWT
  * <p>
  * This has two modes:
  * <ul>
- * <li>{@code requireBearer=true} : Only accept requests with a bearer
- * authorization header. If missing, respond with a 401 challenge asking for a
- * bearer token.</li>
+ * <li>{@code requireBearer=true} : Only accept requests with a bearer authorization
+ * header. If missing, respond with a 401 challenge asking for a bearer token.</li>
  * <li>{@code requireBearer=false} : Verify any bearer token but otherwise pass
  * through the request as-is. This will pass through requests to an unsecured
  * ("public") dataset but will cause a 403 on a secured dataset, not a 401
- * challenge.
+ * challenge.</li>
  * </ul>
  * <p>
- * A more flexible approach for mixing authentication methods is to setup Fuseki
- * with multiple {@code AuthBearerFilter} filters installed in a Fuseki
- * server, with different path specs.</li>
+ * Handling the bearer token is delegated to a handler function, passing the token as
+ * seen in the HTTP request. Normally, this will be base64 encoded. It is the
+ * responsibility of the handler function to decode the token.
+ * <p>
+ * This class has some extension points for customizing the handling of bearer
+ * authentication for
+ * <ul>
+ * <li>getting the token from the HTTP request (e.g. from a different HTTP field).</li>
+ * <li>handling the challenge case (no authentication provided)
+ * <li>handling the case of authentication provided, but it is not "bearer" and bearer is required</li>
+ * <p>
+ * A more flexible approach for mixing authentication methods is to setup Fuseki with
+ * multiple {@code AuthBearerFilter} filters installed in a Fuseki server, with
+ * different path specs.
  */
 public class AuthBearerFilter implements Filter {
     private static Logger log = Fuseki.serverLog;
     private final Function<String, String> verifiedUser;
     private final boolean requireBearer;
 
-    /**
-     * Create a servlet filter that verifies a JWT as bearer authentication.
-     *
-     * @param verifiedUser Function to take the encoded bearer token and return the
-     *     user name of a verified user.
-     * @param requireBearer Mode
-     */
-    public AuthBearerFilter(Function<String, String> verifiedUser, boolean requireBearer) {
-        this.verifiedUser = verifiedUser;
-        this.requireBearer = requireBearer;
-    }
+    public enum BearerMode { REQUIRED, OPTIONAL/*, NONE*/ }
 
     /**
      * Create a servlet filter that verifies a JWT as bearer authentication. Only
      * requests with a verifiable bearer authorization header are accepted. If there
      * is no "Authentication" header, or it does not specify "Bearer", respond with a
-     * 401 challenge asking for a bearer token.
+     * 401 challenge asking for a bearer token (customisable behaviour via {@link #sendResponseNoAuthPresent(HttpServletResponse)}).
      * <p>
      * This is equivalent to calling the 2-argument constructor with
-     * "requireBearer=true".
+     * "{@code requireBearer=true}".
      */
     public AuthBearerFilter(Function<String, String> verifiedUser) {
-        this(verifiedUser, true);
+        this(verifiedUser, BearerMode.REQUIRED);
+    }
+
+    /**
+     * Create a servlet filter that verifies a JWT as bearer authentication.
+     *
+     * @param verifiedUser Function to take the encoded bearer token and return the
+     *     user name of a verified user.
+     * @param bearerMode Whether bearer required or not.
+     *     If set OPTIONAL, no auth, Basic and Digest requests will pass through.
+     *     If set REQUIRED, Bearer must be present, and no auth causes a challenge.
+     */
+    public AuthBearerFilter(Function<String, String> verifiedUser, BearerMode bearerMode) {
+        Objects.requireNonNull(bearerMode);
+        Objects.requireNonNull(verifiedUser);
+        this.verifiedUser = verifiedUser;

Review Comment:
   Can be used to assign the validated value too, I think
   
   ```
   this.verifiedUser = Objects.requireNonNull(verifiedUser);
   ```
   
   But it's probably easier to read leaving the validation at the beginning of the method.



##########
jena-arq/src/main/java/org/apache/jena/http/auth/AuthHeader.java:
##########
@@ -167,316 +193,407 @@ public String getBasicUserPassword() {
     /** The rest of the line for "unknown" */
     public String getUnknown() { return unknown; }
 
-    private static char EndOfStr = 0xFFFF;
+    private static class Builder {
 
-    private char nextChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        idx++;
-        return ch;
-    }
+        // The original header value.
+        private final String string;
+        private final int N;
+        private final boolean isChallenge;
 
-    private char peekChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        // No idx++
-        return ch;
-    }
+        private int idx;
+        private static char EndOfStr = 0xFFFF;
+        private char nextChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            idx++;
+            return ch;
+        }
 
-    private void parse$(String string) {
-        // The scheme.
-        int N = string.length();
-        skipWhitespace();
-        String token = token();
-        if ( token == null )
-            return;
-
-        this.authSchemeStr = token;
-        AuthScheme authScheme = AuthScheme.scheme(token);
-        if ( authScheme == null )
-            return;
-        this.authScheme = authScheme;
-        skipWhitespace();
-        if ( idx >= N )
-            return;
-        this.authSchemeArgs = string.substring(idx);
-
-        switch(authScheme) {
-            case BASIC :   parseBasic(); break;
-            case DIGEST :  parseDigest(); break;
-            case BEARER :  parseBearer(); break;
-            case UNKNOWN : parseUnknown(); break;
-            default :
-                break;
+        private char peekChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            // No idx++
+            return ch;
         }
-    }
 
-    /* RFC 7617 - was RFC 2617 - basic
-     * challenge   = "Basic" realm
-     * credentials = "Basic" basic-credentials
-     * basic-credentials = base64-user-pass
-     * base64-user-pass  = <base64 [4] encoding of user-pass,
-     *                      except not limited to 76 char/line>
-     * user-pass   = userid ":" password
-     * userid      = *<TEXT excluding ":">
-     * password    = *TEXT
-    */
-    private void parseBasic() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else
-            basicUserPassword = basicBase64();
-    }
+        // Scheme as written
+        private String authSchemeName = null;
+        // The rest of the value, stripped of leading and trailing whitespace.
+        private String authSchemeArgs = null;
+
+        private AuthScheme authScheme = null;
+        // Any key-value pairs.
+        private Map<String, String> authParams = null;
+
+        // Per schema fields.
+        private String basicUserPassword = null;
+        private String bearerToken = null;
+        private String unknown = null;
+
+        // Parsing setup.
+        Builder(String string, boolean isChallenge) {
+            this.isChallenge = isChallenge;
+            this.string = string;
+            this.N = string.length();
+        }
 
-    /* RFC 7616 - was RFC 2617 - digest
-     *
-     * challenge        =  "Digest" digest-challenge
-     *
-     * digest-challenge  = 1#( realm | [ domain ] | nonce |
-     *                     [ opaque ] |[ stale ] | [ algorithm ] |
-     *                     [ qop-options ] | [auth-param] )
-     *
-     * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
-     * URI               = absoluteURI | abs_path
-     * nonce             = "nonce" "=" nonce-value
-     * nonce-value       = quoted-string
-     * opaque            = "opaque" "=" quoted-string
-     * stale             = "stale" "=" ( "true" | "false" )
-     * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
-     * qop-options       = "qop" "=" <"> 1#qop-value <">
-     * qop-value         = "auth" | "auth-int" | token
-     */
-    /*
-     *  (",")? token "=" (token | quoted-string)
-     */
-    private void parseDigest() {
-        authParams = mapAuthParams();
-    }
+        // Non-parsing setup.
+        Builder() {
+            this.isChallenge = false;
+            this.string = null;
+            this.N = 0;
+        }
 
-    /* RFC 6750 - bearer
-     *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
-     *   credentials = "Bearer" 1*SP b64token
-     */
-    private void parseBearer() {
-        int startIdx = idx;
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else {
-            String token = b64token();
-            if ( token == null )
-                unknown = string.substring(startIdx).trim();
-            else
-                bearerToken = stripPadding(token);
+        public Builder setAuthSchemeName(String authSchemeName) {
+            this.authSchemeName = authSchemeName;
+            return this;
         }
-    }
 
-    // Remove trailing padding characters.
-    private String stripPadding(String token) {
-        int idx = token.length();
-        for (; idx > 0; idx--) {
-            if ( token.charAt(idx-1) != '=' )
-                break;
+        public Builder setAuthSchemeArgs(String authSchemeArgs) {
+            this.authSchemeArgs = authSchemeArgs;
+            return this;
         }
-        if ( idx == token.length() )
-            return token;
-        return token.substring(0,idx);
-    }
 
-    /*
-     * The rest of the line, trimmed.
-     */
-    private void parseUnknown() {
-        unknown = string.substring(idx).trim();
-    }
+        public Builder setAuthScheme(AuthScheme authScheme) {
+            this.authScheme = authScheme;
+            return this;
+        }
+
+        public Builder setAuthParams(Map<String, String> authParams) {
+            this.authParams = authParams;
+            return this;
+        }
+
+        public Builder setBasicUserPassword(String basicUserPassword) {
+            this.basicUserPassword = basicUserPassword;
+            return this;
+        }
+
+        public Builder setBearerToken(String bearerToken) {
+            this.bearerToken = bearerToken;
+            return this;
+        }
+
+        public Builder setUnknownToken(String unknownToken) {
+            this.unknown = unknownToken;
+            return this;
+        }
 
-    private Map<String, String> mapAuthParams() {
-        Map<String, String> map = new LinkedHashMap<>();
-        // auth-param = token BWS "=" BWS ( token / quoted-string )
-        skipWhitespaceComma();
-        while ( idx < N ) {
-            String key = token();
+        /** Set the fields by parsing */
+        private void parse$() {
+            // The scheme.
             skipWhitespace();
-            char ch = nextChar();
-            if ( idx == N || ch != '=' ) {
-                // Bad. Early end-of-string or no "="
-                return null;
-            }
+            String token = httpToken();
+            if ( token == null )
+                return;
+
+            setAuthSchemeName(token);
+            AuthScheme authScheme = AuthScheme.scheme(token);
+            if ( authScheme == null )
+                return;
+            setAuthScheme(authScheme);
             skipWhitespace();
-            String value = tokenOrQuotedString();
-            if ( value == null )
-                return null;
-            String lcKey = key.toLowerCase(Locale.ROOT);
-            map.put(lcKey, value);
-            skipWhitespaceComma();
+            if ( idx >= N )
+                return;
+            setAuthSchemeArgs(string.substring(idx));
+
+            switch(authScheme) {
+                case BASIC :   parseBasic(); break;
+                case DIGEST :  parseDigest(); break;
+                case BEARER :  parseBearer(); break;
+                case UNKNOWN : parseUnknown(); break;
+                default :
+                    break;
+            }
         }
-        return map;
-    }
 
-    // Basic and Bearer have a challenge form which is auth-params
-    // and a credentials form which is a base64 token.
-    // We can tell them apart by the use of "=".
-    // If the last equals "=" is followed by at least one non-whitespace character)
-    // it is an auth-param which still may fail parsing.
-    private boolean isChallenge() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        // Assumes the string has been stripped of trailing white space.
-        int idxEquals = string.lastIndexOf('=');
-        if ( idxEquals > 0 && idxEquals < N-1 )
-            // An "=" and not the last character, with non "=" after. Challenge.
-            return true;
-        return false;
-    }
+        private AuthHeader build() {
+            return new AuthHeader(string, authScheme, authSchemeName, authSchemeArgs, authParams, basicUserPassword, bearerToken, unknown);
+        }
 
-    private String tokenOrQuotedString() {
-        char ch = peekChar();
-        if ( ch == '"' )
-            return quotedString();
-        else
-            return token();
-    }
+        /* RFC 7617 - was RFC 2617 - basic
+         * challenge   = "Basic" realm
+         * credentials = "Basic" basic-credentials
+         * basic-credentials = base64-user-pass
+         * base64-user-pass  = <base64 [4] encoding of user-pass,
+         *                      except not limited to 76 char/line>
+         * user-pass   = userid ":" password
+         * userid      = *<TEXT excluding ":">
+         * password    = *TEXT
+         */
+        private void parseBasic() {
+            // Determine whether a challenge or credentials.
+            // challenge is "realm=" and at least one character.
+            // credentials is a URL base 64 encoded string, may end in "=".
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else
+                setBasicUserPassword(basicBase64());
+        }
 
-    private String token() {
-        int i = idx;
-        int j = whileTrue(string, idx, N, test_tchar);
-        if ( i == j )
-            return null;
-        this.idx = j;
-        if ( j > 0 )
-            return string.substring(i, j);
-        return null;
-    }
+        /* RFC 7616 - was RFC 2617 - digest
+         *
+         * challenge        =  "Digest" digest-challenge
+         *
+         * digest-challenge  = 1#( realm | [ domain ] | nonce |
+         *                     [ opaque ] |[ stale ] | [ algorithm ] |
+         *                     [ qop-options ] | [auth-param] )
+         *
+         * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
+         * URI               = absoluteURI | abs_path
+         * nonce             = "nonce" "=" nonce-value
+         * nonce-value       = quoted-string
+         * opaque            = "opaque" "=" quoted-string
+         * stale             = "stale" "=" ( "true" | "false" )
+         * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
+         * qop-options       = "qop" "=" <"> 1#qop-value <">
+         * qop-value         = "auth" | "auth-int" | token
+         */
+        /*
+         *  (",")? token "=" (token | quoted-string)
+         */
+        private void parseDigest() {
+            setAuthParams(mapAuthParams());
+        }
 
-    private String quotedString() {
-        if ( string == null )
-            return null;
-        if ( idx < 0 )
-            return null;
-        if ( idx >= N )
-            return null;
-        StringBuilder stringBuilder = new StringBuilder();
-        // If reuse a string builder. -- stringBuilder.setLength(0);
-        char ch0 = nextChar();
-        if ( ch0 != '"' ) {
-            System.out.println("Does not start with a dquote");
-            return null;
+        /* RFC 6750 - bearer
+         *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
+         *   credentials = "Bearer" 1*SP b64token
+         * But JWT occurs where each part is padded and so has internal "="
+         */
+        private void parseBearer() {
+            int startIdx = idx;
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else {
+                // Generalizes b64token to cover internal "=" as found in JWT.
+                String token = bearerToken();
+                if ( token == null )
+                    setUnknownToken(string.substring(startIdx).trim());
+                else {
+                    //setBearerToken(stripPadding(token));

Review Comment:
   This comment is the only reference to `stripPadding`.



##########
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/auth/AuthBearerFilter.java:
##########
@@ -154,33 +178,69 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
         }
     }
 
-    private void send401bearer(HttpServletResponse response) throws IOException {
+    @Override
+    public void destroy() {}
+
+    /**
+     * The HTTP field (header)
+     * Usually "Authenticate" ... although AWS Cognito is different.
+     */
+    protected String getHttpAuthField(HttpServletRequest request) {
+
+        return request.getHeader(HttpNames.hAuthorization);
+    }
+
+    /**
+     * Send the response when the authentication information is missing.
+     * Either 401 (Challenge, expecting the client to send the information)
+     * or 403 (no challenge step).
+     */
+    protected void sendResponseNoAuthPresent(HttpServletResponse response) throws IOException {
         response.setHeader(HttpNames.hWWWAuthenticate, "Bearer");   // No realm, no scope.
         response.sendError(HttpSC.UNAUTHORIZED_401);
     }
 
-    /** Add a value for "getUserPrincipal" */
+    /**
+     * Send the response when the authentication required is "Bearer"
+     * and it was something else ("Basic", "Digest").
+     * Either 401 (Challenge, expecting the client to send the right information)
+     * or 403 (no challenge, reject now).
+     * <p>
+     * Note: 403 is safer to avoid repeated attempts with the same non-bearer authentication
+     * when bearer authentication is being used for machine-to-machine services.
+     */
+    protected void sendResponseBearerRequired(HttpServletResponse response) throws IOException {
+        //sendResponseChallenge(response);
+        response.sendError(HttpSC.FORBIDDEN_403);
+    }
+
+    /**
+     * Create a AuthHeader
+     * Usually, this read the "Authenticate" and parse it (RFC 7230)

Review Comment:
   s/this read/this reads and s/parse/parses ?



##########
jena-arq/src/main/java/org/apache/jena/http/auth/AuthHeader.java:
##########
@@ -167,316 +193,407 @@ public String getBasicUserPassword() {
     /** The rest of the line for "unknown" */
     public String getUnknown() { return unknown; }
 
-    private static char EndOfStr = 0xFFFF;
+    private static class Builder {
 
-    private char nextChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        idx++;
-        return ch;
-    }
+        // The original header value.
+        private final String string;
+        private final int N;
+        private final boolean isChallenge;
 
-    private char peekChar() {
-        if ( idx < 0 || idx >= N )
-            return EndOfStr;
-        char ch = string.charAt(idx);
-        // No idx++
-        return ch;
-    }
+        private int idx;
+        private static char EndOfStr = 0xFFFF;
+        private char nextChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            idx++;
+            return ch;
+        }
 
-    private void parse$(String string) {
-        // The scheme.
-        int N = string.length();
-        skipWhitespace();
-        String token = token();
-        if ( token == null )
-            return;
-
-        this.authSchemeStr = token;
-        AuthScheme authScheme = AuthScheme.scheme(token);
-        if ( authScheme == null )
-            return;
-        this.authScheme = authScheme;
-        skipWhitespace();
-        if ( idx >= N )
-            return;
-        this.authSchemeArgs = string.substring(idx);
-
-        switch(authScheme) {
-            case BASIC :   parseBasic(); break;
-            case DIGEST :  parseDigest(); break;
-            case BEARER :  parseBearer(); break;
-            case UNKNOWN : parseUnknown(); break;
-            default :
-                break;
+        private char peekChar() {
+            if ( idx < 0 || idx >= N )
+                return EndOfStr;
+            char ch = string.charAt(idx);
+            // No idx++
+            return ch;
         }
-    }
 
-    /* RFC 7617 - was RFC 2617 - basic
-     * challenge   = "Basic" realm
-     * credentials = "Basic" basic-credentials
-     * basic-credentials = base64-user-pass
-     * base64-user-pass  = <base64 [4] encoding of user-pass,
-     *                      except not limited to 76 char/line>
-     * user-pass   = userid ":" password
-     * userid      = *<TEXT excluding ":">
-     * password    = *TEXT
-    */
-    private void parseBasic() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else
-            basicUserPassword = basicBase64();
-    }
+        // Scheme as written
+        private String authSchemeName = null;
+        // The rest of the value, stripped of leading and trailing whitespace.
+        private String authSchemeArgs = null;
+
+        private AuthScheme authScheme = null;
+        // Any key-value pairs.
+        private Map<String, String> authParams = null;
+
+        // Per schema fields.
+        private String basicUserPassword = null;
+        private String bearerToken = null;
+        private String unknown = null;
+
+        // Parsing setup.
+        Builder(String string, boolean isChallenge) {
+            this.isChallenge = isChallenge;
+            this.string = string;
+            this.N = string.length();
+        }
 
-    /* RFC 7616 - was RFC 2617 - digest
-     *
-     * challenge        =  "Digest" digest-challenge
-     *
-     * digest-challenge  = 1#( realm | [ domain ] | nonce |
-     *                     [ opaque ] |[ stale ] | [ algorithm ] |
-     *                     [ qop-options ] | [auth-param] )
-     *
-     * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
-     * URI               = absoluteURI | abs_path
-     * nonce             = "nonce" "=" nonce-value
-     * nonce-value       = quoted-string
-     * opaque            = "opaque" "=" quoted-string
-     * stale             = "stale" "=" ( "true" | "false" )
-     * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
-     * qop-options       = "qop" "=" <"> 1#qop-value <">
-     * qop-value         = "auth" | "auth-int" | token
-     */
-    /*
-     *  (",")? token "=" (token | quoted-string)
-     */
-    private void parseDigest() {
-        authParams = mapAuthParams();
-    }
+        // Non-parsing setup.
+        Builder() {
+            this.isChallenge = false;
+            this.string = null;
+            this.N = 0;
+        }
 
-    /* RFC 6750 - bearer
-     *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
-     *   credentials = "Bearer" 1*SP b64token
-     */
-    private void parseBearer() {
-        int startIdx = idx;
-        if ( isChallenge() )
-            authParams = mapAuthParams();
-        else {
-            String token = b64token();
-            if ( token == null )
-                unknown = string.substring(startIdx).trim();
-            else
-                bearerToken = stripPadding(token);
+        public Builder setAuthSchemeName(String authSchemeName) {
+            this.authSchemeName = authSchemeName;
+            return this;
         }
-    }
 
-    // Remove trailing padding characters.
-    private String stripPadding(String token) {
-        int idx = token.length();
-        for (; idx > 0; idx--) {
-            if ( token.charAt(idx-1) != '=' )
-                break;
+        public Builder setAuthSchemeArgs(String authSchemeArgs) {
+            this.authSchemeArgs = authSchemeArgs;
+            return this;
         }
-        if ( idx == token.length() )
-            return token;
-        return token.substring(0,idx);
-    }
 
-    /*
-     * The rest of the line, trimmed.
-     */
-    private void parseUnknown() {
-        unknown = string.substring(idx).trim();
-    }
+        public Builder setAuthScheme(AuthScheme authScheme) {
+            this.authScheme = authScheme;
+            return this;
+        }
+
+        public Builder setAuthParams(Map<String, String> authParams) {
+            this.authParams = authParams;
+            return this;
+        }
+
+        public Builder setBasicUserPassword(String basicUserPassword) {
+            this.basicUserPassword = basicUserPassword;
+            return this;
+        }
+
+        public Builder setBearerToken(String bearerToken) {
+            this.bearerToken = bearerToken;
+            return this;
+        }
+
+        public Builder setUnknownToken(String unknownToken) {
+            this.unknown = unknownToken;
+            return this;
+        }
 
-    private Map<String, String> mapAuthParams() {
-        Map<String, String> map = new LinkedHashMap<>();
-        // auth-param = token BWS "=" BWS ( token / quoted-string )
-        skipWhitespaceComma();
-        while ( idx < N ) {
-            String key = token();
+        /** Set the fields by parsing */
+        private void parse$() {
+            // The scheme.
             skipWhitespace();
-            char ch = nextChar();
-            if ( idx == N || ch != '=' ) {
-                // Bad. Early end-of-string or no "="
-                return null;
-            }
+            String token = httpToken();
+            if ( token == null )
+                return;
+
+            setAuthSchemeName(token);
+            AuthScheme authScheme = AuthScheme.scheme(token);
+            if ( authScheme == null )
+                return;
+            setAuthScheme(authScheme);
             skipWhitespace();
-            String value = tokenOrQuotedString();
-            if ( value == null )
-                return null;
-            String lcKey = key.toLowerCase(Locale.ROOT);
-            map.put(lcKey, value);
-            skipWhitespaceComma();
+            if ( idx >= N )
+                return;
+            setAuthSchemeArgs(string.substring(idx));
+
+            switch(authScheme) {
+                case BASIC :   parseBasic(); break;
+                case DIGEST :  parseDigest(); break;
+                case BEARER :  parseBearer(); break;
+                case UNKNOWN : parseUnknown(); break;
+                default :
+                    break;
+            }
         }
-        return map;
-    }
 
-    // Basic and Bearer have a challenge form which is auth-params
-    // and a credentials form which is a base64 token.
-    // We can tell them apart by the use of "=".
-    // If the last equals "=" is followed by at least one non-whitespace character)
-    // it is an auth-param which still may fail parsing.
-    private boolean isChallenge() {
-        // Determine whether a challenge or credentials.
-        // challenge is "realm=" and at least one character.
-        // credentials is a URL base 64 encoded string, may end in "=".
-        // Assumes the string has been stripped of trailing white space.
-        int idxEquals = string.lastIndexOf('=');
-        if ( idxEquals > 0 && idxEquals < N-1 )
-            // An "=" and not the last character, with non "=" after. Challenge.
-            return true;
-        return false;
-    }
+        private AuthHeader build() {
+            return new AuthHeader(string, authScheme, authSchemeName, authSchemeArgs, authParams, basicUserPassword, bearerToken, unknown);
+        }
 
-    private String tokenOrQuotedString() {
-        char ch = peekChar();
-        if ( ch == '"' )
-            return quotedString();
-        else
-            return token();
-    }
+        /* RFC 7617 - was RFC 2617 - basic
+         * challenge   = "Basic" realm
+         * credentials = "Basic" basic-credentials
+         * basic-credentials = base64-user-pass
+         * base64-user-pass  = <base64 [4] encoding of user-pass,
+         *                      except not limited to 76 char/line>
+         * user-pass   = userid ":" password
+         * userid      = *<TEXT excluding ":">
+         * password    = *TEXT
+         */
+        private void parseBasic() {
+            // Determine whether a challenge or credentials.
+            // challenge is "realm=" and at least one character.
+            // credentials is a URL base 64 encoded string, may end in "=".
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else
+                setBasicUserPassword(basicBase64());
+        }
 
-    private String token() {
-        int i = idx;
-        int j = whileTrue(string, idx, N, test_tchar);
-        if ( i == j )
-            return null;
-        this.idx = j;
-        if ( j > 0 )
-            return string.substring(i, j);
-        return null;
-    }
+        /* RFC 7616 - was RFC 2617 - digest
+         *
+         * challenge        =  "Digest" digest-challenge
+         *
+         * digest-challenge  = 1#( realm | [ domain ] | nonce |
+         *                     [ opaque ] |[ stale ] | [ algorithm ] |
+         *                     [ qop-options ] | [auth-param] )
+         *
+         * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
+         * URI               = absoluteURI | abs_path
+         * nonce             = "nonce" "=" nonce-value
+         * nonce-value       = quoted-string
+         * opaque            = "opaque" "=" quoted-string
+         * stale             = "stale" "=" ( "true" | "false" )
+         * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
+         * qop-options       = "qop" "=" <"> 1#qop-value <">
+         * qop-value         = "auth" | "auth-int" | token
+         */
+        /*
+         *  (",")? token "=" (token | quoted-string)
+         */
+        private void parseDigest() {
+            setAuthParams(mapAuthParams());
+        }
 
-    private String quotedString() {
-        if ( string == null )
-            return null;
-        if ( idx < 0 )
-            return null;
-        if ( idx >= N )
-            return null;
-        StringBuilder stringBuilder = new StringBuilder();
-        // If reuse a string builder. -- stringBuilder.setLength(0);
-        char ch0 = nextChar();
-        if ( ch0 != '"' ) {
-            System.out.println("Does not start with a dquote");
-            return null;
+        /* RFC 6750 - bearer
+         *   b64token    = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
+         *   credentials = "Bearer" 1*SP b64token
+         * But JWT occurs where each part is padded and so has internal "="
+         */
+        private void parseBearer() {
+            int startIdx = idx;
+            if ( isChallenge() )
+                setAuthParams(mapAuthParams());
+            else {
+                // Generalizes b64token to cover internal "=" as found in JWT.
+                String token = bearerToken();
+                if ( token == null )
+                    setUnknownToken(string.substring(startIdx).trim());
+                else {
+                    //setBearerToken(stripPadding(token));
+                    setBearerToken(token);
+                }
+            }
         }
 
-        for (;;) {
-            char ch = nextChar();
-            if ( ch == EndOfStr )
-                return null;
-            if ( ch == '"' )
-                // Terminating double quote
-                return stringBuilder.toString();
-            if ( ch == CH_RSLASH ) {
-                ch = nextChar();
-                if ( ch == EndOfStr )
+        // Remove trailing padding characters ('=') of base64.
+        private String stripPadding(String token) {

Review Comment:
   Not used? See comment above about a comment.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: pr-unsubscribe@jena.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@jena.apache.org
For additional commands, e-mail: pr-help@jena.apache.org