public UserData fromCredentials(Credentials credentials) {

        String json;
        byte[] correctSignature;

        // Abort if the request itself is not allowed
        if (!requestService.isAuthenticationAllowed(credentials))
            return null;

        // Pull base64-encoded, encrypted JSON data from HTTP request, if any
        // such data is present
        String base64 = credentials.getParameter(ENCRYPTED_DATA_PARAMETER);
        if (base64 == null)
            return null;

        // Decrypt base64-encoded parameter
        try {

            // Decrypt using defined encryption key
            byte[] decrypted = cryptoService.decrypt(
                cryptoService.createEncryptionKey(confService.getSecretKey()),
                BaseEncoding.base64().decode(base64)
            );

            // Abort if decrypted value cannot possibly have a signature AND data
            if (decrypted.length <= CryptoService.SIGNATURE_LENGTH) {
                logger.warn("Submitted data is too small to contain both a signature and JSON.");
                return null;
            }

            // Split data into signature and JSON portions
            byte[] receivedSignature = Arrays.copyOf(decrypted, CryptoService.SIGNATURE_LENGTH);
            byte[] receivedJSON = Arrays.copyOfRange(decrypted, CryptoService.SIGNATURE_LENGTH, decrypted.length);

            // Produce signature for decrypted data
            correctSignature = cryptoService.sign(
                cryptoService.createSignatureKey(confService.getSecretKey()),
                receivedJSON
            );

            // Verify signatures
            if (!Arrays.equals(receivedSignature, correctSignature)) {
                logger.warn("Signature of submitted data is incorrect.");
                return null;
            }

            // Convert from UTF-8
            json = new String(receivedJSON, "UTF-8");

        }

        // Fail if base64 data is not valid
        catch (IllegalArgumentException e) {
            logger.warn("Submitted data is not proper base64.");
            logger.debug("Invalid base64 data.", e);
            return null;
        }

        // Handle lack of standard UTF-8 support (should never happen)
        catch (UnsupportedEncodingException e) {
            logger.error("Unexpected lack of support for UTF-8: {}", e.getMessage());
            logger.debug("Unable to decode base64 data as UTF-8.", e);
            return null;
        }

        // Fail if decryption or key retrieval fails for any reason
        catch (GuacamoleException e) {
            logger.error("Decryption of received data failed: {}", e.getMessage());
            logger.debug("Unable to decrypt received data.", e);
            return null;
        }

        // Deserialize UserData from submitted JSON data
        try {

            // Deserialize UserData, but reject if expired
            UserData userData = mapper.readValue(json, UserData.class);
            if (userData.isExpired())
                return null;

            // Reject if data is single-use and already present in the denylist
            if (userData.isSingleUse() && !denylist.add(userData, correctSignature))
                return null;

            return userData;

        }

        // Fail UserData creation if JSON is invalid/unreadable
        catch (IOException e) {
            logger.error("Received JSON is invalid: {}", e.getMessage());
            logger.debug("Error parsing UserData JSON.", e);
            return null;
        }

    }
Derives a new UserData object from the data contained within the given
Credentials. If no such data is present, or the data present is invalid,
null is returned.

@param credentials
    The Credentials from which the new UserData object should be
    derived.

@return
    A new UserData object derived from the data contained within the
    given Credentials, or null if no such data is present or if the data
    present is invalid.
extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/UserDataService.java