# Buggy code
def authorize(self, session, uri, action, options):
    """
    Authorize a session connected under this role to perform the given
    action on the given URI.

    :param session: The WAMP session that requests the action.
    :type session: Instance of :class:`autobahn.wamp.protocol.ApplicationSession`
    :param uri: The URI on which to perform the action.
    :type uri: str
    :param action: The action to be performed.
    :type action: str

    :return: bool -- Flag indicating whether session is authorized or not.
    """
    session_details = getattr(session, "_session_details", None)
    if session_details is None:
        # this happens for "embedded" sessions -- perhaps we
        # should have a better way to detect this -- also
        # session._transport should be a RouterApplicationSession
        session_details = {
            "session": session._session_id,
            "authid": session._authid,
            "authrole": session._authrole,
            "authmethod": session._authmethod,
            "authprovider": session._authprovider,
            "authextra": session._authextra,
            "transport": {
                "type": "stdio",  # or maybe "embedded"?
            },
        }

    self.log.debug(
        "CrossbarRouterRoleDynamicAuth.authorize {uri} {action} {details}",
        uri=uri,
        action=action,
        details=session_details,
    )

    d = self._session.call(self._authorizer, session_details, uri, action, options)

    # we could do backwards-compatibility for clients that didn't
    # yet add the 5th "options" argument to their authorizers like
    # so:
    def maybe_call_old_way(result):
        if isinstance(result, Failure):
            if isinstance(result.value, ApplicationError):
                if "takes exactly 4 arguments" in str(result.value):
                    self.log.warn(
                        "legacy authorizer '{auth}'; should take 5 arguments. Calling with 4.",
                        auth=self._authorizer,
                    )
                    return self._session.call(
                        self._authorizer, session_details, uri, action
                    )
        return result

    d.addBoth(maybe_call_old_way)

    def sanity_check(authorization):
        """
        Ensure the return-value we got from the user-supplied method makes sense
        """
        if isinstance(authorization, dict):
            for key in authorization.keys():
                if key not in ["allow", "cache", "disclose"]:
                    return Failure(
                        ValueError(
                            "Authorizer returned unknown key '{key}'".format(
                                key=key,
                            )
                        )
                    )
            # must have "allow"
            if "allow" not in authorization:
                return Failure(
                    ValueError("Authorizer must have 'allow' in returned dict")
                )
            # all values must be bools
            for key, value in authorization.items():
                if not isinstance(value, bool):
                    return Failure(
                        ValueError("Authorizer must have bool for '{}'".format(key))
                    )
            return authorization

        elif isinstance(authorization, bool):
            return authorization

        return Failure(
            ValueError(
                "Authorizer returned unknown type '{name}'".format(
                    name=type(authorization).__name__,
                )
            )
        )

    d.addCallback(sanity_check)
    return d


# Traceback (autobahn.wamp.exception.SerializationError)
2018-11-11T20:19:23+0100 [Router       9015] Unhandled error in Deferred:
2018-11-11T20:19:23+0100 [Router       9015]
Traceback (most recent call last):
File ".../lib/python3.7/site-packages/crossbar/router/router.py", line 278, in process
self._dealer.processCall(session, msg)
File ".../lib/python3.7/site-packages/crossbar/router/dealer.py", line 742, in processCall
txaio.add_callbacks(d, on_authorize_success, on_authorize_error)
File ".../lib/python3.7/site-packages/txaio/tx.py", line 506, in add_callbacks
future.addCallbacks(callback, errback)
File ".../lib/python3.7/site-packages/twisted/internet/defer.py", line 311, in addCallbacks
self._runCallbacks()
--- <exception caught here> ---
File ".../lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File ".../lib/python3.7/site-packages/crossbar/router/dealer.py", line 721, in on_authorize_success
self._call(session, call, registration, authorization)
File ".../lib/python3.7/site-packages/crossbar/router/dealer.py", line 923, in _call
self._router.send(callee, invocation)
File ".../lib/python3.7/site-packages/crossbar/router/router.py", line 240, in send
session._transport.send(msg)
File ".../lib/python3.7/site-packages/autobahn/twisted/rawsocket.py", line 140, in send
raise SerializationError("WampRawSocketProtocol: unable to serialize WAMP application payload ({0})".format(e))
autobahn.wamp.exception.SerializationError: WampRawSocketProtocol: unable to serialize WAMP application payload (cannot serialize unknown object: <autobahn.wamp.types.SessionDetails object at 0x7f2e5fe018b8>)

# Fixed code
def authorize(self, session, uri, action, options):
    """
    Authorize a session connected under this role to perform the given
    action on the given URI.

    :param session: The WAMP session that requests the action.
    :type session: Instance of :class:`autobahn.wamp.protocol.ApplicationSession`
    :param uri: The URI on which to perform the action.
    :type uri: str
    :param action: The action to be performed.
    :type action: str

    :return: bool -- Flag indicating whether session is authorized or not.
    """
    session_details = getattr(session, "_session_details", None)
    if session_details is None:
        # this happens for "embedded" sessions -- perhaps we
        # should have a better way to detect this -- also
        # session._transport should be a RouterApplicationSession
        details = {
            "session": session._session_id,
            "authid": session._authid,
            "authrole": session._authrole,
            "authmethod": session._authmethod,
            "authprovider": session._authprovider,
            "authextra": session._authextra,
            "transport": {
                "type": "stdio",  # or maybe "embedded"?
            },
        }
    else:
        details = {
            "session": session_details.session,
            "authid": session_details.authid,
            "authrole": session_details.authrole,
            "authmethod": session_details.authmethod,
            "authprovider": session_details.authprovider,
            "authextra": session_details.authextra,
            "transport": session._transport._transport_info,
        }

    self.log.debug(
        "CrossbarRouterRoleDynamicAuth.authorize {uri} {action} {details}",
        uri=uri,
        action=action,
        details=details,
    )

    d = self._session.call(self._authorizer, details, uri, action, options)

    # we could do backwards-compatibility for clients that didn't
    # yet add the 5th "options" argument to their authorizers like
    # so:
    def maybe_call_old_way(result):
        if isinstance(result, Failure):
            if isinstance(result.value, ApplicationError):
                if "takes exactly 4 arguments" in str(result.value):
                    self.log.warn(
                        "legacy authorizer '{auth}'; should take 5 arguments. Calling with 4.",
                        auth=self._authorizer,
                    )
                    return self._session.call(
                        self._authorizer, session_details, uri, action
                    )
        return result

    d.addBoth(maybe_call_old_way)

    def sanity_check(authorization):
        """
        Ensure the return-value we got from the user-supplied method makes sense
        """
        if isinstance(authorization, dict):
            for key in authorization.keys():
                if key not in ["allow", "cache", "disclose"]:
                    return Failure(
                        ValueError(
                            "Authorizer returned unknown key '{key}'".format(
                                key=key,
                            )
                        )
                    )
            # must have "allow"
            if "allow" not in authorization:
                return Failure(
                    ValueError("Authorizer must have 'allow' in returned dict")
                )
            # all values must be bools
            for key, value in authorization.items():
                if not isinstance(value, bool):
                    return Failure(
                        ValueError("Authorizer must have bool for '{}'".format(key))
                    )
            return authorization

        elif isinstance(authorization, bool):
            return authorization

        return Failure(
            ValueError(
                "Authorizer returned unknown type '{name}'".format(
                    name=type(authorization).__name__,
                )
            )
        )

    d.addCallback(sanity_check)
    return d