AnsweredAssumed Answered

Kerberos authentication: OPTIONS method returns status 405

Question asked by rblock on Dec 15, 2016
Latest reply on Apr 3, 2017 by rxu

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);

  }

Outcomes