private byte[] parseToken(byte[] rawToken) {
    byte[] token = rawToken;
    DerValue tmpToken = new DerValue(rawToken);
    if (debug.messageEnabled()) {
      debug.message("token tag:" + DerValue.printByte(tmpToken.getTag()));
    }
    if (tmpToken.getTag() != (byte) 0x60) {
      return null;
    }

    ByteArrayInputStream tmpInput = new ByteArrayInputStream(tmpToken.getData());

    // check for SPNEGO OID
    byte[] oidArray = new byte[spnegoOID.length];
    tmpInput.read(oidArray, 0, oidArray.length);
    if (Arrays.equals(oidArray, spnegoOID)) {
      debug.message("SPNEGO OID found in the Auth Token");
      tmpToken = new DerValue(tmpInput);

      // 0xa0 indicates an init token(NegTokenInit); 0xa1 indicates an
      // response arg token(NegTokenTarg). no arg token is needed for us.

      if (tmpToken.getTag() == (byte) 0xa0) {
        debug.message("DerValue: found init token");
        tmpToken = new DerValue(tmpToken.getData());
        if (tmpToken.getTag() == (byte) 0x30) {
          debug.message("DerValue: 0x30 constructed token found");
          tmpInput = new ByteArrayInputStream(tmpToken.getData());
          tmpToken = new DerValue(tmpInput);

          // In an init token, it can contain 4 optional arguments:
          // a0: mechTypes
          // a1: contextFlags
          // a2: octect string(with leading char 0x04) for the token
          // a3: message integrity value

          while (tmpToken.getTag() != (byte) -1 && tmpToken.getTag() != (byte) 0xa2) {
            // look for next mech token DER
            tmpToken = new DerValue(tmpInput);
          }
          if (tmpToken.getTag() != (byte) -1) {
            // retrieve octet string
            tmpToken = new DerValue(tmpToken.getData());
            token = tmpToken.getData();
          }
        }
      }
    } else {
      debug.message("SPNEGO OID not found in the Auth Token");
      byte[] krb5Oid = new byte[KERBEROS_V5_OID.length];
      int i = 0;
      for (; i < oidArray.length; i++) {
        krb5Oid[i] = oidArray[i];
      }
      tmpInput.read(krb5Oid, i, krb5Oid.length - i);
      if (!Arrays.equals(krb5Oid, KERBEROS_V5_OID)) {
        debug.message("Kerberos V5 OID not found in the Auth Token");
        token = null;
      } else {
        debug.message("Kerberos V5 OID found in the Auth Token");
      }
    }
    return token;
  }
  /**
   * Processes the authentication request.
   *
   * @param callbacks
   * @param state
   * @return -1 as succeeded; 0 as failed.
   * @exception AuthLoginException upon any failure.
   */
  public int process(Callback[] callbacks, int state) throws AuthLoginException {
    int result = ISAuthConstants.LOGIN_IGNORE;

    if (!getConfigParams()) {
      initWindowsDesktopSSOAuth(options);
    }

    // retrieve the spnego token
    byte[] spnegoToken = getSPNEGOTokenFromHTTPRequest(getHttpServletRequest());
    if (spnegoToken == null) {
      spnegoToken = getSPNEGOTokenFromCallback(callbacks);
    }

    if (spnegoToken == null) {
      debug.message("spnego token is not valid.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "token", null);
    }

    if (debug.messageEnabled()) {
      debug.message(
          "SPNEGO token: \n" + DerValue.printByteArray(spnegoToken, 0, spnegoToken.length));
    }
    // parse the spnego token and extract the kerberos mech token from it
    final byte[] kerberosToken = parseToken(spnegoToken);
    if (kerberosToken == null) {
      debug.message("kerberos token is not valid.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "token", null);
    }
    if (debug.messageEnabled()) {
      debug.message(
          "Kerberos token retrieved from SPNEGO token: \n"
              + DerValue.printByteArray(kerberosToken, 0, kerberosToken.length));
    }

    // authenticate the user with the kerberos token
    try {
      authenticateToken(kerberosToken);
      debug.message("WindowsDesktopSSO authentication succeeded.");
      result = ISAuthConstants.LOGIN_SUCCEED;
    } catch (PrivilegedActionException pe) {
      Exception e = extractException(pe);
      if (e instanceof GSSException) {
        int major = ((GSSException) e).getMajor();
        if (major == GSSException.CREDENTIALS_EXPIRED) {
          debug.message("Credential expired. Re-establish credential...");
          serviceLogin();
          try {
            authenticateToken(kerberosToken);
            debug.message("Authentication succeeded with new cred.");
            result = ISAuthConstants.LOGIN_SUCCEED;
          } catch (Exception ee) {
            debug.message("Authentication failed with new cred.");
            throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, ee);
          }
        } else {
          debug.message("Authentication failed with GSSException.");
          throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
        }
      }
    } catch (GSSException e) {
      int major = e.getMajor();
      if (major == GSSException.CREDENTIALS_EXPIRED) {
        debug.message("Credential expired. Re-establish credential...");
        serviceLogin();
        try {
          authenticateToken(kerberosToken);
          debug.message("Authentication succeeded with new cred.");
          result = ISAuthConstants.LOGIN_SUCCEED;
        } catch (Exception ee) {
          debug.message("Authentication failed with new cred.");
          throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, ee);
        }
      } else {
        debug.message("Authentication failed with GSSException.");
        throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
      }
    } catch (AuthLoginException e) {
      throw e;
    } catch (Exception e) {
      debug.message("Authentication failed with generic exception.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
    }
    return result;
  }