diff --git a/ibis/ibisclient.h b/ibis/ibisclient.h
--- a/ibis/ibisclient.h
+++ b/ibis/ibisclient.h
@@ -94,6 +94,32 @@
 void ibis_client_disconnect(IbisClient *client, GError *error, const char *message);
 
 /**
+ * ibis_client_get_alt_nick:
+ * @client: The instance.
+ *
+ * Gets the alternative nick to use in the event that [property@Client:nick] is
+ * already in use.
+ *
+ * Returns: (nullable): The alternative nick.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+const char *ibis_client_get_alt_nick(IbisClient *client);
+
+/**
+ * ibis_client_set_alt_nick:
+ * @client: The instance.
+ * @alt_nick: (nullable): The new alternative nick.
+ *
+ * Sets the alternative nick of @client to @alt_nick.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+void ibis_client_set_alt_nick(IbisClient *client, const char *alt_nick);
+
+/**
  * ibis_client_get_cancellable:
  * @client: The instance.
  *
@@ -147,6 +173,81 @@
 GError *ibis_client_get_error(IbisClient *client);
 
 /**
+ * ibis_client_get_nick:
+ * @client: The instance.
+ *
+ * Gets the nick from @client.
+ *
+ * Returns: The nick.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+const char *ibis_client_get_nick(IbisClient *client);
+
+/**
+ * ibis_client_set_nick:
+ * @client: The instance.
+ * @nick: The new nick.
+ *
+ * Sets the nick of @client to @nick.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+void ibis_client_set_nick(IbisClient *client, const char *nick);
+
+/**
+ * ibis_client_get_realname:
+ * @client: The instance.
+ *
+ * Gets the realname that is being used when sending the `USER` command.
+ *
+ * Returns: The realname.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+const char *ibis_client_get_realname(IbisClient *client);
+
+/**
+ * ibis_client_set_realname:
+ * @client: The instance.
+ * @realname: (nullable): The new realname.
+ *
+ * Sets the realname to use when sending the `USER` command.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+void ibis_client_set_realname(IbisClient *client, const char *realname);
+
+/**
+ * ibis_client_get_username:
+ * @client: The instance.
+ *
+ * Gets the username that is being used when sending the `USER` command.
+ *
+ * Returns: The username.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+const char *ibis_client_get_username(IbisClient *client);
+
+/**
+ * ibis_client_set_username:
+ * @client: The instance.
+ * @username: (nullable): The new username.
+ *
+ * Sets the username to use when sending the `USER` command.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+void ibis_client_set_username(IbisClient *client, const char *username);
+
+/**
  * ibis_client_new:
  *
  * Creates a new client.
# HG changeset patch
# Node ID 110951ca5498
# Parent  fa2153da2469
diff --git a/ibis/ibisclient.c b/ibis/ibisclient.c
--- a/ibis/ibisclient.c
+++ b/ibis/ibisclient.c
@@ -30,10 +30,14 @@
 
 enum {
 	PROP_0,
+	PROP_ALT_NICK,
 	PROP_CANCELLABLE,
 	PROP_CAPABILITIES,
 	PROP_CONNECTED,
 	PROP_ERROR,
+	PROP_NICK,
+	PROP_REALNAME,
+	PROP_USERNAME,
 	N_PROPERTIES,
 };
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -55,6 +59,11 @@
 	GIOStream *stream;
 	GDataInputStream *input;
 	GOutputStream *output;
+
+	char *nick;
+	char *alt_nick;
+	char *realname;
+	char *username;
 };
 
 /******************************************************************************
@@ -225,6 +234,11 @@
 	g_clear_object(&client->capabilities);
 	g_clear_error(&client->error);
 
+	g_clear_pointer(&client->alt_nick, g_free);
+	g_clear_pointer(&client->nick, g_free);
+	g_clear_pointer(&client->realname, g_free);
+	g_clear_pointer(&client->username, g_free);
+
 	G_OBJECT_CLASS(ibis_client_parent_class)->finalize(obj);
 }
 
@@ -235,6 +249,9 @@
 	IbisClient *client = IBIS_CLIENT(obj);
 
 	switch(param_id) {
+	case PROP_ALT_NICK:
+		g_value_set_string(value, ibis_client_get_alt_nick(client));
+		break;
 	case PROP_CANCELLABLE:
 		g_value_set_object(value, ibis_client_get_cancellable(client));
 		break;
@@ -247,6 +264,40 @@
 	case PROP_ERROR:
 		g_value_set_boxed(value, ibis_client_get_error(client));
 		break;
+	case PROP_NICK:
+		g_value_set_string(value, ibis_client_get_nick(client));
+		break;
+	case PROP_REALNAME:
+		g_value_set_string(value, ibis_client_get_realname(client));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string(value, ibis_client_get_username(client));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+		break;
+	}
+}
+
+static void
+ibis_client_set_property(GObject *obj, guint param_id, const GValue *value,
+                         GParamSpec *pspec)
+{
+	IbisClient *client = IBIS_CLIENT(obj);
+
+	switch(param_id) {
+	case PROP_ALT_NICK:
+		ibis_client_set_alt_nick(client, g_value_get_string(value));
+		break;
+	case PROP_NICK:
+		ibis_client_set_nick(client, g_value_get_string(value));
+		break;
+	case PROP_REALNAME:
+		ibis_client_set_realname(client, g_value_get_string(value));
+		break;
+	case PROP_USERNAME:
+		ibis_client_set_username(client, g_value_get_string(value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 		break;
@@ -264,9 +315,23 @@
 
 	obj_class->finalize = ibis_client_finalize;
 	obj_class->get_property = ibis_client_get_property;
+	obj_class->set_property = ibis_client_set_property;
 
 	/**
-	 * IbisClient: cancellable:
+	 * IbisClient:alt-nick:
+	 *
+	 * The alternative nick to use if [property@Client:nick] is already in use.
+	 *
+	 * Since: 0.1
+	 */
+	properties[PROP_ALT_NICK] = g_param_spec_string(
+		"alt-nick", "alt-nick",
+		"The alternative nick to use.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * IbisClient:cancellable:
 	 *
 	 * The [class@Glib.Cancellable] for the client.
 	 *
@@ -317,6 +382,52 @@
 		G_TYPE_ERROR,
 		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+	/**
+	 * IbisClient:nick:
+	 *
+	 * The primary nick to use.
+	 *
+	 * If the server tells us this nick is in use, an attempt to use
+	 * [property@Client:alt-nick] will be made.
+	 *
+	 * Since: 0.1
+	 */
+	properties[PROP_NICK] = g_param_spec_string(
+		"nick", "nick",
+		"The primary nick to use.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * IbisClient:realname:
+	 *
+	 * The realname to use when sending the USER command.
+	 *
+	 * If this is %NULL, the value of [property@Client:nick] will be returned.
+	 *
+	 * Since: 0.1
+	 */
+	properties[PROP_REALNAME] = g_param_spec_string(
+		"realname", "realname",
+		"The realname to use.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * IbisClient:username:
+	 *
+	 * The username to use when sending the USER command.
+	 *
+	 * If this is %NULL, the value of [property@Client:nick] will be returned.
+	 *
+	 * Since: 0.1
+	 */
+	properties[PROP_USERNAME] = g_param_spec_string(
+		"username", "username",
+		"The username to use.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
 
 	/**
@@ -425,6 +536,23 @@
 	g_clear_object(&client->cancellable);
 }
 
+const char *
+ibis_client_get_alt_nick(IbisClient *client) {
+	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
+
+	return client->alt_nick;
+}
+
+void
+ibis_client_set_alt_nick(IbisClient *client, const char *alt_nick) {
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+	g_return_if_fail(alt_nick == NULL || alt_nick[0] != '\0');
+
+	if(g_set_str(&client->alt_nick, alt_nick)) {
+		g_object_notify_by_pspec(G_OBJECT(client), properties[PROP_ALT_NICK]);
+	}
+}
+
 GCancellable *
 ibis_client_get_cancellable(IbisClient *client) {
 	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
@@ -453,6 +581,65 @@
 	return client->error;
 }
 
+const char *
+ibis_client_get_nick(IbisClient *client) {
+	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
+
+	return client->nick;
+}
+
+void
+ibis_client_set_nick(IbisClient *client, const char *nick) {
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+	g_return_if_fail(!ibis_str_is_empty(nick));
+
+	if(g_set_str(&client->nick, nick)) {
+		g_object_notify_by_pspec(G_OBJECT(client), properties[PROP_NICK]);
+	}
+}
+
+const char *
+ibis_client_get_realname(IbisClient *client) {
+	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
+
+	if(client->realname != NULL) {
+		return client->realname;
+	}
+
+	return client->nick;
+}
+
+void
+ibis_client_set_realname(IbisClient *client, const char *realname) {
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+	g_return_if_fail(realname == NULL || realname[0] != '\0');
+
+	if(g_set_str(&client->realname, realname)) {
+		g_object_notify_by_pspec(G_OBJECT(client), properties[PROP_REALNAME]);
+	}
+}
+
+const char *
+ibis_client_get_username(IbisClient *client) {
+	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
+
+	if(client->username != NULL) {
+		return client->username;
+	}
+
+	return client->nick;
+}
+
+void
+ibis_client_set_username(IbisClient *client, const char *username) {
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+	g_return_if_fail(username == NULL || username[0] != '\0');
+
+	if(g_set_str(&client->username, username)) {
+		g_object_notify_by_pspec(G_OBJECT(client), properties[PROP_USERNAME]);
+	}
+}
+
 IbisClient *
 ibis_client_new(void) {
 	return g_object_new(IBIS_TYPE_CLIENT, NULL);
diff --git a/ibis/tests/meson.build b/ibis/tests/meson.build
--- a/ibis/tests/meson.build
+++ b/ibis/tests/meson.build
@@ -1,4 +1,5 @@
 test_programs = [
+  'client',
   'formatting',
   'message',
   'parser',
diff --git a/ibis/tests/test_client.c b/ibis/tests/test_client.c
new file mode 100644
--- /dev/null
+++ b/ibis/tests/test_client.c
@@ -0,0 +1,211 @@
+/*
+ * Ibis - IRCv3 Library
+ * Copyright (C) 2022-2024 Ibis Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <ibis.h>
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_ibis_client_properties(void) {
+	IbisCapabilities *capabilities = NULL;
+	IbisClient *client = NULL;
+	GCancellable *cancellable = NULL;
+	GError *error = NULL;
+	char *alt_nick = NULL;
+	char *nick = NULL;
+	char *realname = NULL;
+	char *username = NULL;
+	gboolean connected = FALSE;
+
+	client = g_object_new(
+		IBIS_TYPE_CLIENT,
+		"alt-nick", "nick1",
+		"nick", "nick",
+		"realname", "realname",
+		"username", "username",
+		NULL);
+
+	g_object_get(
+		G_OBJECT(client),
+		"alt-nick", &alt_nick,
+		"cancellable", &cancellable,
+		"capabilities", &capabilities,
+		"connected", &connected,
+		"error", &error,
+		"nick", &nick,
+		"realname", &realname,
+		"username", &username,
+		NULL);
+
+	g_assert_cmpstr(alt_nick, ==, "nick1");
+	g_clear_pointer(&alt_nick, g_free);
+
+	g_assert_null(cancellable);
+
+	g_assert_true(IBIS_IS_CAPABILITIES(capabilities));
+	g_clear_object(&capabilities);
+
+	g_assert_false(connected);
+
+	g_assert_no_error(error);
+
+	g_assert_cmpstr(nick, ==, "nick");
+	g_clear_pointer(&nick, g_free);
+
+	g_assert_cmpstr(realname, ==, "realname");
+	g_clear_pointer(&realname, g_free);
+
+	g_assert_cmpstr(username, ==, "username");
+	g_clear_pointer(&username, g_free);
+
+	/* We can't use g_assert_finalize_object because there is a circular
+	 * reference between the client and it's capabilities object.
+	 */
+	g_clear_object(&client);
+}
+
+static void
+test_ibis_client_alt_nick_null(void) {
+	IbisClient *client = NULL;
+	const char *alt_nick = NULL;
+
+	client = ibis_client_new();
+
+	ibis_client_set_alt_nick(client, "alt");
+	alt_nick = ibis_client_get_alt_nick(client);
+	g_assert_cmpstr(alt_nick, ==, "alt");
+
+	ibis_client_set_alt_nick(client, NULL);
+	alt_nick = ibis_client_get_alt_nick(client);
+	g_assert_null(alt_nick);
+
+	g_clear_object(&client);
+}
+
+static void
+test_ibis_client_alt_nick_empty(void) {
+	if(g_test_subprocess()) {
+		IbisClient *client = NULL;
+
+		client = ibis_client_new();
+		ibis_client_set_alt_nick(client, "");
+
+		g_clear_object(&client);
+	}
+
+	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
+	g_test_trap_assert_stderr("*alt_nick[0] != '\\0'*");
+}
+
+static void
+test_ibis_client_realname_null(void) {
+	IbisClient *client = NULL;
+	const char *realname = NULL;
+
+	client = ibis_client_new();
+
+	ibis_client_set_realname(client, "realname");
+	realname = ibis_client_get_realname(client);
+	g_assert_cmpstr(realname, ==, "realname");
+
+	ibis_client_set_realname(client, NULL);
+	realname = ibis_client_get_realname(client);
+	g_assert_null(realname);
+
+	g_clear_object(&client);
+}
+
+static void
+test_ibis_client_realname_empty(void) {
+	if(g_test_subprocess()) {
+		IbisClient *client = NULL;
+
+		client = ibis_client_new();
+		ibis_client_set_realname(client, "");
+
+		g_clear_object(&client);
+	}
+
+	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
+	g_test_trap_assert_stderr("*realname[0] != '\\0'*");
+}
+
+static void
+test_ibis_client_username_null(void) {
+	IbisClient *client = NULL;
+	const char *username = NULL;
+
+	client = ibis_client_new();
+
+	ibis_client_set_username(client, "username");
+	username = ibis_client_get_username(client);
+	g_assert_cmpstr(username, ==, "username");
+
+	ibis_client_set_username(client, NULL);
+	username = ibis_client_get_username(client);
+	g_assert_null(username);
+
+	g_clear_object(&client);
+}
+
+static void
+test_ibis_client_username_empty(void) {
+	if(g_test_subprocess()) {
+		IbisClient *client = NULL;
+
+		client = ibis_client_new();
+		ibis_client_set_username(client, "");
+
+		g_clear_object(&client);
+	}
+
+	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
+	g_test_trap_assert_stderr("*username[0] != '\\0'*");
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+int
+main(int argc, char *argv[]) {
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_set_nonfatal_assertions();
+
+	g_test_add_func("/ibis/client/properties", test_ibis_client_properties);
+
+	g_test_add_func("/ibis/client/alt-nick/null",
+	                test_ibis_client_alt_nick_null);
+	g_test_add_func("/ibis/client/alt-nick/empty",
+	                test_ibis_client_alt_nick_empty);
+
+	g_test_add_func("/ibis/client/realname/null",
+	                test_ibis_client_realname_null);
+	g_test_add_func("/ibis/client/realname/empty",
+	                test_ibis_client_realname_empty);
+
+	g_test_add_func("/ibis/client/username/null",
+	                test_ibis_client_username_null);
+	g_test_add_func("/ibis/client/username/empty",
+	                test_ibis_client_username_empty);
+
+	return g_test_run();
+}
