def get_auth_context(hostname, username, password, auth_mech):
    """
    Returns an AuthContext used in the CredSSP authentication process and to
    wrap/unwrap tokens sent to and from the client. This step get's the context
    based on the auth_mech configured and what is available on the server. It
    tries to favour system libraries like SSPI (Windows) or GSSAPI (Unix) if
    possible but falls back to a Python implementation of NTLM that works on
    all platforms.

    While in some cases the system libraries are used and they may not require
    a password to authenticate, CredSSP requires the password as it is sent
    to the remote host and so we won't rely on the user's logon tokens.

    :param hostname: The hostname of the server, this should be the FQDN when
        kerberos is desired
    :param username: The username to authenticate with
    :param password: The password of username
    :param auth_mech: The authentication mechanism to use;
        auto: Uses the SPNEGO/Negotiate mechanism which tries Kerberos if
            possible and then falls back to NTLM
        kerberos: Only allow authentication with Kerberos
        ntlm: Only allow authentication with NTLM
    :return: tuple
        AuthContext: The authentication context chosen that has been init
        generator step: The Python generator that takes further input tokens
            and produces output tokens to send to the server
        bytes first token: The first output token to send to the server
    """
    if auth_mech not in ["auto", "ntlm", "kerberos"]:
        raise InvalidConfigurationException("Invalid auth_mech supplied "
                                            "%s, must be auto, ntlm, or "
                                            "kerberos" % auth_mech)
    context_init = False
    out_token = None
    context_gen = None

    if HAS_SSPI:
        # always use SSPI when it is available
        log.debug("SSPI is available and will be used as auth backend")
        context = SSPIContext(hostname, username, password, auth_mech)
    elif HAS_GSSAPI:
        mechs_available = ["kerberos"]
        # to save on computing costs we only check the mechs that are available
        # when auth_mech is auto or ntlm as it doesn't matter when kerberos
        # is set (Kerberos is always available when python-gssapi is installed
        if auth_mech != "kerberos":
            log.debug("GSSAPI is available, determine what mechanism to use "
                      "as auth backend")
            mechs_available = GSSAPIContext.get_mechs_available()

        log.debug("GSSAPI mechs available: %s" % ", ".join(mechs_available))
        if auth_mech in mechs_available or auth_mech == "kerberos":
            log.debug("GSSAPI with mech %s is being used as auth backend"
                      % auth_mech)
            context = GSSAPIContext(hostname, username, password, auth_mech)
        elif auth_mech == "ntlm":
            log.debug("GSSAPI is available but does not support NTLM, using "
                      "ntlm-auth as auth backend instead")
            context = NTLMContext(username, password)
        else:
            # make sure we can actually initialise a GSSAPI context in auto,
            # otherwise fallback to NTLMContext if that fails
            # we need to explicitly set auth_mech as kerberos if the GSSAPI
            # implementation does not support NTLM so we know to use NTLM if
            # GSSAPI fails
            try:
                log.debug("Attempting to use GSSAPI Kerberos as auth backend")
                context = GSSAPIContext(hostname, username, password,
                                        "kerberos")
                context.init_context()
                context_gen = context.step()
                out_token = next(context_gen)
                context_init = True
                log.info("GSSAPI with mech kerberos is being used as auth "
                         "backend")
            except gssapi.exceptions.GSSError as err:
                log.warning("Failed to initialise GSSAPI context, falling "
                            "back to NTLM: %s" % str(err))
                context = NTLMContext(username, password)
    else:
        log.debug("SSPI or GSSAPI is not available, using ntlm-auth as auth "
                  "backend")
        if auth_mech == "kerberos":
            raise InvalidConfigurationException("The auth_mechanism is set "
                                                "to kerberos but SSPI or "
                                                "GSSAPI is not available")
        context = NTLMContext(username, password)

    # we only init the context when HAS_GSSAPI and it doesn't natively offer
    # SPNEGO that works with Windows, so let's init it here
    if not context_init:
        context.init_context()
        context_gen = context.step()
        out_token = next(context_gen)

    return context, context_gen, out_token