2 Replies Latest reply on Apr 3, 2017 7:41 PM by rxu

    Kerberos authentication: OPTIONS method returns status 405

    rblock

      I am writing a java client to send data to PI using the Web API, and having trouble getting Kerberos authentication to work.

      The KerberosAuthentication java library that works with HDFS and Kafka does a HTTP handshake sequence with the REST endpoint to establish the secure session, and part of that handshake involves sending Http requests using the OPTIONS Http method (as opposed to GET or POST).  This does not seem to be recognized by the WebAPI server, as it returns status code 405 (Bad Method).

       

      The handshake sequence goes roughly like this.

              while (!established) {

                    1. Call gssContext.initSecContext() to generate a new token

                    2. Send request to endpoint URL with HttpMethod = OPTIONS and header "Authentication: Negotiate token"

                    3. Read token in the response header "WWW-Authenticate"

                    4. if the context state is established, then break out

              }

       

      Step 2 fails with status 405.

      I have checked other client code for Kerberos authentication, and using the OPTIONS method seems fairly commonplace.  Is there a way to tell PI WebAPI (or underlying IIS) to accept it?  I did bounce the WebAPI service after setting up Kerberos.

       

      (Code is from hadoop-auth-2.7.3.jar/org.apache.hadoop.security.authentication.client.KerberosAuthenticator.java)

       

        public static final String WWW_AUTHENTICATE = "WWW-Authenticate";

      private static final String AUTH_HTTP_METHOD = "OPTIONS";

       

          void doSpnegoSequence()

             // ...

                  byte[] inToken = new byte[0];

                  byte[] outToken;

                  boolean established = false;

       

                  // Loop while the context is still not established

                  while (!established) {

                    outToken = gssContext.initSecContext(inToken, 0, inToken.length);

                    if (outToken != null) {

                      sendToken(outToken);

                    }

                    if (!gssContext.isEstablished()) {

                      inToken = readToken();

                    } else {

                      established = true;

                    }

                  }

       

         private void sendToken(byte[] outToken) throws IOException {

              String token = base64.encodeToString(outToken);

              conn = (HttpURLConnection) url.openConnection();

              if (connConfigurator != null) {

                conn = connConfigurator.configure(conn);

              }

              conn.setRequestMethod(AUTH_HTTP_METHOD);

              conn.setRequestProperty(AUTHORIZATION, NEGOTIATE + " " + token);

              conn.connect();

            }

       

        private byte[] readToken() throws IOException, AuthenticationException {

          int status = conn.getResponseCode();

          if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_UNAUTHORIZED) {

            String authHeader = conn.getHeaderField(WWW_AUTHENTICATE);

            if (authHeader == null || !authHeader.trim().startsWith(NEGOTIATE)) {

              throw new AuthenticationException("Invalid SPNEGO sequence, '" + WWW_AUTHENTICATE +

                                                "' header incorrect: " + authHeader);

            }

            String negotiation = authHeader.trim().substring((NEGOTIATE + " ").length()).trim();

            return base64.decode(negotiation);

          }

          throw new AuthenticationException("Invalid SPNEGO sequence, status code: " + status);

        }

        • Re: Kerberos authentication: OPTIONS method returns status 405
          gregor

          Hello Robert,

           

          The only place where the PI Web API 2016 R2 User Guide mentions http method OPTIONS is within the chapter about Cross Origin Resource Sharing (CORS) and I believe it is intended as example HTTP method. Also to my best knowledge, we suggest using an authentication header when sending GET or POST requests. Can you give that a try?

          • Re: Kerberos authentication: OPTIONS method returns status 405
            rxu

            Hi Robert,

            Sorry for this late reply!

            I am not familiar with the java code (hadoop-auth-2.7.3.jar/org.apache.hadoop.security.authentication.client.KerberosAuthenticator.java) in your post, but your Kerberos challenge workflow seems strange to me. Here is the normal Kerberos/Negotiate challenge workflow in PI Web API:

             

            1. Request from the client to PI Web API

                 GET /piwebapi/ HTTP/1.1

            2. Response from PI Web API to the client

                 HTTP/1.1 401 Unauthorized

                 WWW-Authenticate: Negotiate [token]

            3. Request from the client to PI Web API

                GET /piwebapi/ HTTP/1.1

                 Authorization: Negotiate [token]

            4. One of the following two possible results

                 4.1 Authentication failure

                      GOTO Step 2

                 4.2 Authentication success

                      HTTP/1.1 200 OK

                      WWW-Authenticate: [token]

             

            As you can see OPTION is not used in the challenge or authentication workflow. In PI Web API, OPTION is used in pre-flight requests for CORS. So every OPTION request is expected with headers "Access-Control-Request-Headers", "Access-Control-Request-Methods", "Origin" and other CORS control headers. You may or may not know that PI Web API is built on top of Microsoft ASP.NET Web API, and the CORS protection is handled by the ASP.NET framework. That explains that you got 405 (Method Not Allowed) status code, because the pre-flight request lacks the correct headers.

             

            I hope this clears that misunderstanding for you. Please let me know if you have further questions.