Blob Blame History Raw
From 0ccab22ee39bd98b63d31c7457d91778b98080bf Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 22 Oct 2014 11:06:19 -0400
Subject: [PATCH 1/3] kerberos: zero initialize operation structure

We need zero initialize the structure so we don't end up
freeing unused members when cleaning up the operation.
---
 src/goaidentity/goakerberosidentitymanager.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index 7cc90b9..c4da420 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -102,61 +102,61 @@ typedef struct
   GoaKerberosIdentityManager *manager;
   GoaIdentity *identity;
 } IdentitySignalWork;
 
 static GoaIdentityManager *goa_kerberos_identity_manager_singleton;
 
 static void identity_manager_interface_init (GoaIdentityManagerInterface *
                                              interface);
 static void initable_interface_init (GInitableIface *interface);
 
 static void on_identity_expired (GoaIdentity                *identity,
                                  GoaKerberosIdentityManager *self);
 
 G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
                          goa_kerberos_identity_manager,
                          G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER,
                                                 identity_manager_interface_init)
                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                 initable_interface_init));
 #define FALLBACK_POLLING_INTERVAL 5
 
 static Operation *
 operation_new (GoaKerberosIdentityManager *self,
                GCancellable               *cancellable,
                OperationType               type,
                GSimpleAsyncResult         *result)
 {
   Operation *operation;
 
-  operation = g_slice_new (Operation);
+  operation = g_slice_new0 (Operation);
 
   operation->manager = self;
   operation->type = type;
 
   if (cancellable == NULL)
     cancellable = g_cancellable_new ();
   else
     g_object_ref (cancellable);
   operation->cancellable = cancellable;
 
   if (result != NULL)
     g_object_ref (result);
   operation->result = result;
 
   operation->identity = NULL;
 
   return operation;
 }
 
 static void
 operation_free (Operation *operation)
 {
   g_clear_object (&operation->cancellable);
 
   if (operation->type != OPERATION_TYPE_SIGN_IN &&
       operation->type != OPERATION_TYPE_GET_IDENTITY)
     g_clear_object (&operation->identity);
   else
     g_clear_pointer (&operation->identifier, g_free);
 
-- 
1.8.3.1


From 8e36b5743bf1e20636a67f406f4937ccc132e50b Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:38:17 -0400
Subject: [PATCH 2/3] ticketing: add new details property

This commit adds a new "details" variant for attaching metadata
about tickets getting requested via the ticketing interface.

This will give the kerberos account provider a place to tuck away
kerberos-specific preauthentication configuration for the tickets
associated with smartcard backed kerberos accounts.
---
 data/dbus-interfaces.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/data/dbus-interfaces.xml b/data/dbus-interfaces.xml
index fa4101f..e4d39c4 100644
--- a/data/dbus-interfaces.xml
+++ b/data/dbus-interfaces.xml
@@ -604,40 +604,42 @@
     <!-- AcceptSslErrors:
          @since: 3.8.0
          Accept SSL/TLS errors caused by invalid certificates.
       -->
     <property name="AcceptSslErrors" type="b" access="read"/>
 
     <!-- Host:
          The Exchange server to use. This is always a domain name.
 
          Use this to determine the <ulink
          url="http://msdn.microsoft.com/en-us/library/gg591268&percnt;28v&period;exchg.140&percnt;29.aspx">
          Autodiscover</ulink> service endpoints. eg. if
          #org.gnome.OnlineAccounts.Exchange:Host is
          <literal>bar.com</literal>, then the possible endpoints are
          <literal>https://bar.com/autodiscover/autodiscover.xml
          </literal> and <literal>
          https://autodiscover.bar.com/autodiscover/autodiscover.xml
          </literal>.
       -->
     <property name="Host" type="s" access="read"/>
   </interface>
 
   <!--
       org.gnome.OnlineAccounts.Ticketing:
       @since: 3.6.0
 
       An account object implements this interface if it provides
       ticketing capabilities.
   -->
   <interface name="org.gnome.OnlineAccounts.Ticketing">
+    <property name="details" type="a{ss}" access="read"/>
+
     <!--
       GetTicket:
 
       Use this method to obtain an ticket that can be used to
       access resources for the account.
     -->
     <method name="GetTicket"/>
   </interface>
 
 </node>
-- 
1.8.3.1


From 2cf3c36818fa995791d99c6512b258099cec2879 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:46:50 -0400
Subject: [PATCH 3/3] kerberos: support refreshing smartcard authenticated
 kerberos tickets

Right now gnome-online-accounts doesn't manager smartcard based
kerberos credentials very well.  The sign-in button just fails.

This commit adds support for tickets granted through smartcards.

Note, at the moment we don't provide a way to add new smartcard
based accounts, merely, manage existing ones that are added implicitly
by logging in with a smartcard, or by explicit kinit.
---
 src/goabackend/goakerberosprovider.c         | 43 +++++++++++++++++
 src/goaidentity/goaidentitymanager.c         |  2 +
 src/goaidentity/goaidentitymanager.h         |  2 +
 src/goaidentity/goaidentityservice.c         | 19 +++++++-
 src/goaidentity/goakerberosidentity.c        | 71 +++++++++++++++++++++++++++-
 src/goaidentity/goakerberosidentity.h        |  2 +
 src/goaidentity/goakerberosidentitymanager.c | 14 ++++--
 7 files changed, 146 insertions(+), 7 deletions(-)

diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c
index 7fe77f6..b125914 100644
--- a/src/goabackend/goakerberosprovider.c
+++ b/src/goabackend/goakerberosprovider.c
@@ -345,93 +345,100 @@ ensure_object_manager (GoaKerberosProvider *self,
                      "cancellable",
                      cancellable);
 
   if (self->object_manager != NULL)
     {
       g_simple_async_result_set_op_res_gpointer (operation_result,
                                                  g_object_ref (self->object_manager),
                                                  (GDestroyNotify)
                                                  g_object_unref);
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
       return;
     }
   goa_identity_service_object_manager_client_new_for_bus (G_BUS_TYPE_SESSION,
                                                           G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
                                                           "org.gnome.Identity",
                                                           "/org/gnome/Identity",
                                                           cancellable,
                                                           (GAsyncReadyCallback)
                                                           on_object_manager_ensured,
                                                           operation_result);
 }
 
 static void
 on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
                                       GAsyncResult        *result,
                                       GSimpleAsyncResult  *operation_result)
 {
   const char       *identifier;
   const char       *password;
+  const char       *preauth_source;
   GCancellable     *cancellable;
   GError           *error;
   GVariantBuilder   details;
 
   error = NULL;
 
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
                                              &error))
     {
       g_simple_async_result_take_error (operation_result, error);
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
       return;
     }
 
   cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
   password = g_object_get_data (G_OBJECT (operation_result), "password");
+  preauth_source = g_object_get_data (G_OBJECT (operation_result), "preauthentication-source");
   identifier = g_simple_async_result_get_source_tag (operation_result);
 
   g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
 
   if (password != NULL)
     {
       GcrSecretExchange *secret_exchange;
       char *secret;
 
       secret_exchange = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
 
       secret = gcr_secret_exchange_send (secret_exchange, password, -1);
       g_variant_builder_add (&details, "{ss}", "initial-password", secret);
       g_free (secret);
     }
 
+  if (preauth_source != NULL)
+    {
+      g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauth_source);
+    }
+
   goa_identity_service_manager_call_sign_in (self->identity_manager,
                                              identifier,
                                              g_variant_builder_end (&details),
                                              cancellable,
                                              (GAsyncReadyCallback)
                                              on_identity_signed_in,
                                              operation_result);
 }
 
 static void
 on_secret_keys_exchanged (GoaIdentityServiceManager *manager,
                           GAsyncResult              *result,
                           GSimpleAsyncResult        *operation_result)
 {
   GcrSecretExchange *secret_exchange;
   char              *return_key;
   GError            *error;
 
   secret_exchange = g_simple_async_result_get_source_tag (operation_result);
 
   error = NULL;
   if (!goa_identity_service_manager_call_exchange_secret_keys_finish (manager,
                                                                       &return_key,
                                                                       result,
                                                                       &error))
     {
       g_object_unref (secret_exchange);
 
       g_simple_async_result_take_error (operation_result, error);
       g_simple_async_result_complete_in_idle (operation_result);
@@ -513,82 +520,88 @@ on_identity_manager_ensured_for_sign_in (GoaKerberosProvider *self,
 
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
                                              &error))
     {
       g_simple_async_result_take_error (operation_result, error);
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
       return;
     }
 
   manager = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
 
   if (self->identity_manager == NULL)
     self->identity_manager = g_object_ref (manager);
 
   cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
   password = g_object_get_data (G_OBJECT (operation_result), "password");
 
   exchange_secret_keys (self,
                         password,
                         cancellable,
                         (GAsyncReadyCallback)
                         on_secret_keys_exchanged_for_sign_in,
                         operation_result);
 }
 
 static void
 sign_in_identity (GoaKerberosProvider  *self,
                   const char           *identifier,
                   const char           *password,
+                  const char           *preauth_source,
                   GCancellable         *cancellable,
                   GAsyncReadyCallback   callback,
                   gpointer              user_data)
 {
   GSimpleAsyncResult *operation_result;
 
   operation_result = g_simple_async_result_new (G_OBJECT (self),
                                                 callback,
                                                 user_data,
                                                 (gpointer)
                                                 identifier);
 
   g_simple_async_result_set_check_cancellable (operation_result, cancellable);
 
   g_object_set_data (G_OBJECT (operation_result),
                      "cancellable",
                      cancellable);
   g_object_set_data (G_OBJECT (operation_result),
                      "password",
                      (gpointer)
                      password);
 
+  g_object_set_data_full (G_OBJECT (operation_result),
+                          "preauthentication-source",
+                          g_strdup (preauth_source),
+                          g_free);
+
   ensure_identity_manager (self,
                            cancellable,
                            (GAsyncReadyCallback)
                            on_identity_manager_ensured_for_sign_in,
                            operation_result);
 }
 
 static void
 on_object_manager_ensured_for_look_up (GoaKerberosProvider *self,
                                        GAsyncResult        *result,
                                        GSimpleAsyncResult  *operation_result)
 {
   GDBusObjectManager *manager;
   const char         *identifier;
   GList              *objects, *node;
   GError             *error;
   gboolean            found;
 
   error = NULL;
   found = FALSE;
 
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
                                              &error))
     {
 
       g_simple_async_result_take_error (operation_result, error);
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
       return;
     }
@@ -666,128 +679,141 @@ look_up_identity (GoaKerberosProvider  *self,
                          (GAsyncReadyCallback)
                          on_object_manager_ensured_for_look_up,
                          operation_result);
 }
 
 static void
 on_account_signed_in (GoaProvider   *provider,
                       GAsyncResult  *result,
                       SignInRequest *request)
 {
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
                                              &request->error))
     {
       g_main_loop_quit (request->loop);
       return;
     }
 
   g_main_loop_quit (request->loop);
 }
 
 static gboolean
 get_ticket_sync (GoaKerberosProvider *self,
                  GoaObject           *object,
                  gboolean             is_interactive,
                  GCancellable        *cancellable,
                  GError             **error)
 {
   GVariant            *credentials;
   GError              *lookup_error;
   GoaAccount          *account;
+  GoaTicketing        *ticketing;
+  GVariant            *details;
   const char          *identifier;
   const char          *password;
+  const char          *preauth_source;
   SignInRequest        request;
   gboolean             ret;
 
   ret = FALSE;
 
   account = goa_object_peek_account (object);
   identifier = goa_account_get_identity (account);
+
+  ticketing = goa_object_get_ticketing (GOA_OBJECT (object));
+  details = goa_ticketing_get_details (ticketing);
+
+  preauth_source = NULL;
+  g_variant_lookup (details, "preauthentication-source", "&s", &preauth_source);
+
   password = NULL;
 
   lookup_error = NULL;
   credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (self),
                                                    object,
                                                    cancellable,
                                                    &lookup_error);
 
   if (credentials == NULL && !is_interactive)
     {
       if (lookup_error != NULL)
           g_propagate_error (error, lookup_error);
       else
           g_set_error (error,
                        GOA_ERROR,
                        GOA_ERROR_NOT_AUTHORIZED,
                        _("Could not find saved credentials for principal `%s' in keyring"), identifier);
       goto out;
     }
   else if (credentials != NULL)
     {
       gboolean has_password;
 
       has_password = g_variant_lookup (credentials, "password", "&s", &password);
 
       if (!has_password && !is_interactive)
         {
           g_set_error (error,
                        GOA_ERROR,
                        GOA_ERROR_NOT_AUTHORIZED,
                        _("Did not find password for principal `%s' in credentials"),
                        identifier);
           goto out;
         }
     }
 
   memset (&request, 0, sizeof (SignInRequest));
   request.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
   request.error = NULL;
 
   sign_in_identity (self,
                     identifier,
                     password,
+                    preauth_source,
                     cancellable,
                     (GAsyncReadyCallback)
                     on_account_signed_in,
                     &request);
 
   g_main_loop_run (request.loop);
   g_main_loop_unref (request.loop);
 
   if (request.error != NULL)
     {
       g_propagate_error (error, request.error);
       goto out;
     }
 
   ret = TRUE;
 out:
+  g_clear_object (&ticketing);
+
   if (credentials != NULL)
     g_variant_unref (credentials);
 
   return ret;
 }
 
 static void
 notify_is_temporary_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
 {
   GoaAccount *account;
   gboolean is_temporary;
 
   account = GOA_ACCOUNT (object);
   g_object_get (account, "is-temporary", &is_temporary, NULL);
 
   /* Toggle IsTemporary */
   goa_utils_keyfile_set_boolean (account, "IsTemporary", is_temporary);
 
   /* Set/unset SessionId */
   if (is_temporary)
     {
       GDBusConnection *connection;
       const gchar *guid;
 
       connection = G_DBUS_CONNECTION (user_data);
       guid = g_dbus_connection_get_guid (connection);
       goa_utils_keyfile_set_string (account, "SessionId", guid);
     }
   else
     goa_utils_keyfile_remove_key (account, "SessionId");
@@ -831,69 +857,79 @@ build_object (GoaProvider         *provider,
               GDBusConnection     *connection,
               gboolean             just_added,
               GError             **error)
 {
   GoaAccount   *account;
   GoaTicketing *ticketing;
   gboolean      ticketing_enabled;
   gboolean      ret;
 
   ticketing = NULL;
   ret = FALSE;
 
   if (!GOA_PROVIDER_CLASS (goa_kerberos_provider_parent_class)->build_object (provider,
                                                                               object,
                                                                               key_file,
                                                                               group,
                                                                               connection,
                                                                               just_added,
                                                                               error))
     goto out;
 
   account = goa_object_get_account (GOA_OBJECT (object));
 
   ticketing = goa_object_get_ticketing (GOA_OBJECT (object));
   ticketing_enabled = g_key_file_get_boolean (key_file, group, "TicketingEnabled", NULL);
 
   if (ticketing_enabled)
     {
       if (ticketing == NULL)
         {
+          char            *preauthentication_source;
+          GVariantBuilder  details;
+
           ticketing = goa_ticketing_skeleton_new ();
 
           g_signal_connect (ticketing,
                             "handle-get-ticket",
                             G_CALLBACK (on_handle_get_ticket),
                             NULL);
 
           goa_object_skeleton_set_ticketing (object, ticketing);
 
+          g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+
+	  preauthentication_source = g_key_file_get_string (key_file, group, "PreauthenticationSource", NULL);
+          if (preauthentication_source)
+            g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauthentication_source);
+
+	  g_object_set (G_OBJECT (ticketing), "details", g_variant_builder_end (&details), NULL);
         }
     }
   else if (ticketing != NULL)
     {
       goa_object_skeleton_set_ticketing (object, NULL);
     }
 
   if (just_added)
     {
       goa_account_set_ticketing_disabled (account, !ticketing_enabled);
 
       g_signal_connect (account,
                         "notify::is-temporary",
                         G_CALLBACK (notify_is_temporary_cb),
                         connection);
 
       g_signal_connect (account,
                         "notify::ticketing-disabled",
                         G_CALLBACK (goa_util_account_notify_property_cb),
                         "TicketingEnabled");
     }
 
   ret = TRUE;
 
  out:
   g_clear_object (&ticketing);
 
   return ret;
 }
 
@@ -1266,99 +1302,106 @@ on_initial_sign_in_done (GoaKerberosProvider *self,
            */
           g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
           g_variant_builder_add (&builder,
                                  "{sv}",
                                  "password",
                                  g_variant_new_string (password));
 
           error = NULL;
           goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (self),
                                                        object,
                                                        g_variant_builder_end (&builder),
                                                        NULL,
                                                        NULL);
         }
     }
 
   g_simple_async_result_complete_in_idle (operation_result);
   g_object_unref (operation_result);
 }
 
 static void
 on_system_prompt_answered_for_initial_sign_in (GcrPrompt          *prompt,
                                                GAsyncResult       *result,
                                                GSimpleAsyncResult *operation_result)
 {
   GoaKerberosProvider *self;
   GCancellable        *cancellable;
   GError              *error;
   const char          *principal;
   const char          *password;
+  const char          *preauth_source;
   GcrSecretExchange   *secret_exchange;
 
   self = GOA_KERBEROS_PROVIDER (g_async_result_get_source_object (G_ASYNC_RESULT (operation_result)));
   principal = g_object_get_data (G_OBJECT (operation_result), "principal");
   cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
 
+  /* We currently don't prompt the user to choose a preauthentication source during initial sign in
+   * so we assume there's no preauthentication source
+   */
+  preauth_source = NULL;
+
   error = NULL;
   password = gcr_prompt_password_finish (prompt, result, &error);
 
   if (password == NULL)
     {
       gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL);
 
       if (error != NULL)
         g_simple_async_result_take_error (operation_result, error);
       else
         g_cancellable_cancel (cancellable);
 
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
       return;
     }
 
   secret_exchange = gcr_system_prompt_get_secret_exchange (GCR_SYSTEM_PROMPT (prompt));
   g_object_set_data_full (G_OBJECT (operation_result),
                           "secret-exchange",
                           g_object_ref (secret_exchange),
                           (GDestroyNotify)
                           g_object_unref);
 
   g_object_set_data (G_OBJECT (operation_result),
                      "remember-password",
                      GINT_TO_POINTER (gcr_prompt_get_choice_chosen (prompt)));
 
   gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL);
 
   sign_in_identity (self,
                     principal,
                     password,
+                    preauth_source,
                     cancellable,
                     (GAsyncReadyCallback)
                     on_initial_sign_in_done,
                     operation_result);
 }
 
 static void
 on_system_prompt_open_for_initial_sign_in (GcrSystemPrompt     *system_prompt,
                                            GAsyncResult        *result,
                                            GSimpleAsyncResult  *operation_result)
 {
   GCancellable *cancellable;
   GcrPrompt    *prompt;
   GError       *error;
 
   cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
   error = NULL;
   prompt = gcr_system_prompt_open_finish (result, &error);
 
   if (prompt == NULL)
     {
       g_simple_async_result_take_error (operation_result, error);
 
       g_simple_async_result_complete_in_idle (operation_result);
       g_object_unref (operation_result);
 
       return;
     }
 
   gcr_prompt_set_title (prompt, _("Log In to Realm"));
diff --git a/src/goaidentity/goaidentitymanager.c b/src/goaidentity/goaidentitymanager.c
index b71fb23..8fa7b22 100644
--- a/src/goaidentity/goaidentitymanager.c
+++ b/src/goaidentity/goaidentitymanager.c
@@ -178,70 +178,72 @@ goa_identity_manager_list_identities_finish (GoaIdentityManager  *self,
                                                                         result,
                                                                         error);
 }
 
 void
 goa_identity_manager_renew_identity (GoaIdentityManager  *self,
                                      GoaIdentity         *identity,
                                      GCancellable        *cancellable,
                                      GAsyncReadyCallback  callback,
                                      gpointer             user_data)
 {
   GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity (self,
                                                          identity,
                                                          cancellable,
                                                          callback,
                                                          user_data);
 }
 
 void
 goa_identity_manager_renew_identity_finish (GoaIdentityManager  *self,
                                             GAsyncResult        *result,
                                             GError             **error)
 {
   GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity_finish (self, result, error);
 }
 
 void
 goa_identity_manager_sign_identity_in (GoaIdentityManager     *self,
                                        const char             *identifier,
                                        gconstpointer           initial_password,
+                                       const char             *preauth_source,
                                        GoaIdentitySignInFlags  flags,
                                        GoaIdentityInquiryFunc  inquiry_func,
                                        gpointer                inquiry_data,
                                        GCancellable           *cancellable,
                                        GAsyncReadyCallback     callback,
                                        gpointer                user_data)
 {
   GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in (self,
                                                            identifier,
                                                            initial_password,
+                                                           preauth_source,
                                                            flags,
                                                            inquiry_func,
                                                            inquiry_data,
                                                            cancellable,
                                                            callback,
                                                            user_data);
 }
 
 GoaIdentity *
 goa_identity_manager_sign_identity_in_finish (GoaIdentityManager  *self,
                                               GAsyncResult        *result,
                                               GError             **error)
 {
   return GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in_finish (self,
                                                                          result,
                                                                          error);
 }
 
 void
 goa_identity_manager_sign_identity_out (GoaIdentityManager  *self,
                                         GoaIdentity         *identity,
                                         GCancellable        *cancellable,
                                         GAsyncReadyCallback  callback,
                                         gpointer             user_data)
 {
   GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out (self,
                                                             identity,
                                                             cancellable,
                                                             callback,
                                                             user_data);
diff --git a/src/goaidentity/goaidentitymanager.h b/src/goaidentity/goaidentitymanager.h
index 5b60c24..05e6ca8 100644
--- a/src/goaidentity/goaidentitymanager.h
+++ b/src/goaidentity/goaidentitymanager.h
@@ -54,60 +54,61 @@ struct _GoaIdentityManagerInterface
                              GoaIdentity        *identity);
   void (* identity_refreshed) (GoaIdentityManager *identity_manager,
                                GoaIdentity        *identity);
   void (* identity_needs_renewal) (GoaIdentityManager *identity_manager,
                                    GoaIdentity        *identity);
   void (* identity_expiring) (GoaIdentityManager *identity_manager,
                               GoaIdentity        *identity);
   void (* identity_expired) (GoaIdentityManager *identity_manager,
                              GoaIdentity        *identity);
 
   /* Virtual Functions */
   void (* get_identity) (GoaIdentityManager *identity_manager,
                          const char         *identifier,
                          GCancellable       *cancellable,
                          GAsyncReadyCallback callback,
                          gpointer            user_data);
   GoaIdentity * (* get_identity_finish) (GoaIdentityManager  *identity_manager,
                                          GAsyncResult        *result,
                                          GError             **error);
   void (* list_identities) (GoaIdentityManager  *identity_manager,
                             GCancellable        *cancellable,
                             GAsyncReadyCallback  callback,
                             gpointer             user_data);
   GList * (* list_identities_finish) (GoaIdentityManager  *identity_manager,
                                       GAsyncResult        *result,
                                       GError             **error);
 
   void (* sign_identity_in) (GoaIdentityManager     *identity_manager,
                              const char             *identifier,
                              gconstpointer           initial_password,
+                             const char             *preauth_source,
                              GoaIdentitySignInFlags  flags,
                              GoaIdentityInquiryFunc  inquiry_func,
                              gpointer                inquiry_data,
                              GCancellable           *cancellable,
                              GAsyncReadyCallback     callback,
                              gpointer                user_data);
   GoaIdentity * (* sign_identity_in_finish) (GoaIdentityManager  *identity_manager,
                                              GAsyncResult        *result,
                                              GError             **error);
 
   void (* sign_identity_out) (GoaIdentityManager  *identity_manager,
                               GoaIdentity         *identity,
                               GCancellable        *cancellable,
                               GAsyncReadyCallback  callback,
                               gpointer             user_data);
   void (* sign_identity_out_finish) (GoaIdentityManager  *identity_manager,
                                      GAsyncResult        *result,
                                      GError             **error);
 
   void (* renew_identity) (GoaIdentityManager *identity_manager,
                            GoaIdentity        *identity,
                            GCancellable       *cancellable,
                            GAsyncReadyCallback callback,
                            gpointer            user_data);
   void (* renew_identity_finish) (GoaIdentityManager  *identity_manager,
                                   GAsyncResult        *result,
                                   GError             **error);
 
   char * (* name_identity) (GoaIdentityManager *identity_manager,
                             GoaIdentity        *identity);
@@ -117,60 +118,61 @@ typedef enum
 {
   GOA_IDENTITY_MANAGER_ERROR_INITIALIZING,
   GOA_IDENTITY_MANAGER_ERROR_IDENTITY_NOT_FOUND,
   GOA_IDENTITY_MANAGER_ERROR_CREATING_IDENTITY,
   GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS,
   GOA_IDENTITY_MANAGER_ERROR_UNSUPPORTED_CREDENTIALS
 } GoaIdentityManagerError;
 
 GType  goa_identity_manager_get_type    (void);
 GQuark goa_identity_manager_error_quark (void);
 
 void goa_identity_manager_get_identity (GoaIdentityManager  *identity_manager,
                                         const char          *identifier,
                                         GCancellable        *cancellable,
                                         GAsyncReadyCallback  callback,
                                         gpointer             user_data);
 GoaIdentity *goa_identity_manager_get_identity_finish (GoaIdentityManager  *identity_manager,
                                                        GAsyncResult        *result,
                                                        GError             **error);
 void goa_identity_manager_list_identities (GoaIdentityManager *identity_manager,
                                            GCancellable       *cancellable,
                                            GAsyncReadyCallback callback,
                                            gpointer            user_data);
 GList *goa_identity_manager_list_identities_finish (GoaIdentityManager  *identity_manager,
                                                     GAsyncResult        *result,
                                                     GError             **error);
 
 void goa_identity_manager_sign_identity_in (GoaIdentityManager     *identity_manager,
                                             const char             *identifier,
                                             gconstpointer           initial_password,
+                                            const char             *preauth_source,
                                             GoaIdentitySignInFlags  flags,
                                             GoaIdentityInquiryFunc  inquiry_func,
                                             gpointer                inquiry_data,
                                             GCancellable           *cancellable,
                                             GAsyncReadyCallback     callback,
                                             gpointer                user_data);
 GoaIdentity *goa_identity_manager_sign_identity_in_finish (GoaIdentityManager  *identity_manager,
                                                            GAsyncResult        *result,
                                                            GError             **error);
 
 void goa_identity_manager_sign_identity_out (GoaIdentityManager *identity_manager,
                                              GoaIdentity        *identity,
                                              GCancellable       *cancellable,
                                              GAsyncReadyCallback callback,
                                              gpointer            user_data);
 void goa_identity_manager_sign_identity_out_finish (GoaIdentityManager  *identity_manager,
                                                     GAsyncResult        *result,
                                                     GError             **error);
 
 void goa_identity_manager_renew_identity (GoaIdentityManager  *identity_manager,
                                           GoaIdentity         *identity,
                                           GCancellable        *cancellable,
                                           GAsyncReadyCallback  callback,
                                           gpointer             user_data);
 void goa_identity_manager_renew_identity_finish (GoaIdentityManager  *identity_manager,
                                                  GAsyncResult        *result,
                                                  GError             **error);
 
 char *goa_identity_manager_name_identity (GoaIdentityManager *identity_manager,
                                           GoaIdentity        *identity);
diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c
index d51d6bd..b570e09 100644
--- a/src/goaidentity/goaidentityservice.c
+++ b/src/goaidentity/goaidentityservice.c
@@ -38,60 +38,61 @@
 #include "goaidentityutils.h"
 
 #include "goakerberosidentitymanager.h"
 #include "goalogging.h"
 
 struct _GoaIdentityServicePrivate
 {
   GDBusConnection          *connection;
   GDBusObjectManagerServer *object_manager_server;
   guint                     bus_id;
 
   GoaIdentityManager       *identity_manager;
 
   GHashTable               *watched_client_connections;
   GHashTable               *key_holders;
   GHashTable               *pending_temporary_account_results;
 
   /* FIXME: This is a little ucky, since the goa service
    * is in process, we should able to use direct calls.
    */
   GoaClient                *client;
   GoaManager               *accounts_manager;
 };
 
 static void identity_service_manager_interface_init (GoaIdentityServiceManagerIface *interface);
 
 static void
 sign_in (GoaIdentityService     *self,
          const char             *identifier,
          gconstpointer           initial_password,
+         const char             *preauth_source,
          GoaIdentitySignInFlags  flags,
          GCancellable           *cancellable,
          GAsyncReadyCallback     callback,
          gpointer                user_data);
 
 G_DEFINE_TYPE_WITH_CODE (GoaIdentityService,
                          goa_identity_service,
                          GOA_IDENTITY_SERVICE_TYPE_MANAGER_SKELETON,
                          G_IMPLEMENT_INTERFACE (GOA_IDENTITY_SERVICE_TYPE_MANAGER,
                                                 identity_service_manager_interface_init));
 
 static char *
 get_object_path_for_identity (GoaIdentityService *self,
                               GoaIdentity        *identity)
 {
   const char *identifier;
   char       *escaped_identifier;
   char       *object_path;
 
   identifier = goa_identity_get_identifier (identity);
   escaped_identifier = goa_identity_utils_escape_object_path (identifier,
                                                               strlen (identifier));
   object_path = g_strdup_printf ("/org/gnome/Identity/Identities/%s", escaped_identifier);
 
   g_free (escaped_identifier);
   return object_path;
 }
 
 static char *
 export_identity (GoaIdentityService *self,
@@ -290,151 +291,158 @@ ensure_account_credentials (GoaIdentityService *self,
                                        on_credentials_ensured,
                                        self);
 }
 
 static void
 on_sign_in_handled (GoaIdentityService    *self,
                     GAsyncResult          *result,
                     GDBusMethodInvocation *invocation)
 {
   GError *error = NULL;
 
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
     {
       g_dbus_method_invocation_take_error (invocation, error);
     }
   else
     {
       const char *object_path;
 
       object_path = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
       goa_identity_service_manager_complete_sign_in (GOA_IDENTITY_SERVICE_MANAGER (self),
                                                      invocation,
                                                      object_path);
     }
 }
 
 static void
 read_sign_in_details (GoaIdentityServiceManager  *manager,
                       GVariant                   *details,
                       GoaIdentitySignInFlags     *flags,
-                      char                      **secret_key)
+                      char                      **secret_key,
+                      char                      **preauth_source)
 {
   GVariantIter  iter;
   char          *key;
   char          *value;
 
   *flags = GOA_IDENTITY_SIGN_IN_FLAGS_NONE;
   g_variant_iter_init (&iter, details);
   while (g_variant_iter_loop (&iter, "{ss}", &key, &value))
     {
       if (g_strcmp0 (key, "initial-password") == 0)
         *secret_key = g_strdup (value);
+      else if (g_strcmp0 (key, "preauthentication-source") == 0)
+        *preauth_source = g_strdup (value);
       else if (g_strcmp0 (key, "disallow-renewal") == 0
                && g_strcmp0 (value, "true") == 0)
         *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL;
       else if (g_strcmp0 (key, "disallow-forwarding") == 0
                && g_strcmp0 (value, "true") == 0)
         *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING;
       else if (g_strcmp0 (key, "disallow-proxying") == 0
                && g_strcmp0 (value, "true") == 0)
         *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING;
     }
 }
 
 static gboolean
 goa_identity_service_handle_sign_in (GoaIdentityServiceManager *manager,
                                      GDBusMethodInvocation     *invocation,
                                      const char                *identifier,
                                      GVariant                  *details)
 {
   GoaIdentityService     *self = GOA_IDENTITY_SERVICE (manager);
   GSimpleAsyncResult     *operation_result;
   GoaIdentitySignInFlags  flags;
   char                   *secret_key;
+  char                   *preauth_source;
   gconstpointer           initial_password;
   GCancellable           *cancellable;
 
   secret_key = NULL;
+  preauth_source = NULL;
   initial_password = NULL;
 
-  read_sign_in_details (manager, details, &flags, &secret_key);
+  read_sign_in_details (manager, details, &flags, &secret_key, &preauth_source);
 
   if (secret_key != NULL)
     {
       GcrSecretExchange *secret_exchange;
 
       secret_exchange = g_hash_table_lookup (self->priv->key_holders,
                                              g_dbus_method_invocation_get_sender (invocation));
 
       if (secret_exchange == NULL)
         {
           g_free (secret_key);
           g_dbus_method_invocation_return_error (invocation,
                                                  GOA_IDENTITY_MANAGER_ERROR,
                                                  GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS,
                                                  _("initial secret passed before secret key exchange"));
           return TRUE;
         }
 
       gcr_secret_exchange_receive (secret_exchange, secret_key);
       g_free (secret_key);
 
       initial_password = gcr_secret_exchange_get_secret (secret_exchange, NULL);
     }
 
   operation_result = g_simple_async_result_new (G_OBJECT (self),
                                                 (GAsyncReadyCallback)
                                                 on_sign_in_handled,
                                                 g_object_ref (invocation),
                                                 g_strdup (identifier));
   cancellable = g_cancellable_new ();
   g_object_set_data (G_OBJECT (operation_result),
                      "cancellable",
                      cancellable);
   g_object_set_data (G_OBJECT (operation_result),
                      "initial-password",
                      (gpointer)
                      initial_password);
   g_object_set_data (G_OBJECT (operation_result),
                      "flags",
                      GINT_TO_POINTER ((int) flags));
 
   sign_in (self,
            identifier,
            initial_password,
+           preauth_source,
            flags,
            cancellable,
            (GAsyncReadyCallback)
            on_sign_in_done,
            operation_result);
 
+  g_free (preauth_source);
   g_object_unref (cancellable);
 
   return TRUE;
 }
 
 static void
 on_sign_out_handled (GoaIdentityService    *self,
                      GAsyncResult          *result,
                      GDBusMethodInvocation *invocation)
 {
   GError *error;
 
   error = NULL;
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
     g_dbus_method_invocation_take_error (invocation, error);
   else
     goa_identity_service_manager_complete_sign_out (GOA_IDENTITY_SERVICE_MANAGER (self),
                                                     invocation);
 }
 
 static void
 on_identity_signed_out (GoaIdentityManager *manager,
                         GAsyncResult       *result,
                         GSimpleAsyncResult *operation_result)
 {
   GoaIdentityService *self;
   GError             *error;
   GoaIdentity        *identity;
   const char         *identifier;
   GoaObject          *object;
@@ -846,117 +854,122 @@ on_account_added (GoaManager         *manager,
       return;
     }
 
   if (object_path != NULL && object_path[0] != '\0')
     {
       goa_debug ("Created account for identity with object path %s", object_path);
 
       object_manager = goa_client_get_object_manager (self->priv->client);
       object = GOA_OBJECT (g_dbus_object_manager_get_object (object_manager,
                                                              object_path));
       g_free (object_path);
     }
 
   if (object == NULL)
     g_simple_async_result_set_op_res_gpointer (operation_result, NULL, NULL);
   else
     g_simple_async_result_set_op_res_gpointer (operation_result,
                                                object,
                                                (GDestroyNotify)
                                                g_object_unref);
 
   g_simple_async_result_complete_in_idle (operation_result);
   g_object_unref (operation_result);
 }
 
 static void
 add_temporary_account (GoaIdentityService *self,
                        GoaIdentity        *identity)
 {
   char               *realm;
+  char               *preauth_source;
   const char         *principal;
   char               *principal_for_display;
   GSimpleAsyncResult *operation_result;
   GVariantBuilder     credentials;
   GVariantBuilder     details;
   GoaObject *object;
 
   principal = goa_identity_get_identifier (identity);
 
   object = g_hash_table_lookup (self->priv->pending_temporary_account_results,
                                 principal);
 
   if (object != NULL)
     {
       goa_debug ("GoaIdentityService: would add temporary identity %s, but it's already pending", principal);
       return;
     }
 
   goa_debug ("GoaIdentityService: adding temporary identity %s", principal);
 
   /* If there's no account for this identity then create a temporary one.
    */
   principal_for_display = goa_identity_manager_name_identity (self->priv->identity_manager,
                                                               identity);
 
   realm = goa_kerberos_identity_get_realm_name (GOA_KERBEROS_IDENTITY (identity));
+  preauth_source = goa_kerberos_identity_get_preauthentication_source (GOA_KERBEROS_IDENTITY (identity));
 
   g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
 
   g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
   g_variant_builder_add (&details, "{ss}", "Realm", realm);
   g_variant_builder_add (&details, "{ss}", "IsTemporary", "true");
+  if (preauth_source != NULL)
+    g_variant_builder_add (&details, "{ss}", "PreauthenticationSource", preauth_source);
   g_variant_builder_add (&details, "{ss}", "TicketingEnabled", "true");
 
 
   goa_debug ("GoaIdentityService: asking to sign back in");
 
   operation_result = g_simple_async_result_new (G_OBJECT (self),
                                                 (GAsyncReadyCallback)
                                                 on_temporary_account_created_for_identity,
                                                 identity,
                                                 add_temporary_account);
   g_hash_table_insert (self->priv->pending_temporary_account_results,
                        g_strdup (principal),
                        g_object_ref (operation_result));
 
   goa_manager_call_add_account (self->priv->accounts_manager,
                                 "kerberos",
                                 principal,
                                 principal_for_display,
                                 g_variant_builder_end (&credentials),
                                 g_variant_builder_end (&details),
                                 NULL,
                                 (GAsyncReadyCallback)
                                 on_account_added,
                                 operation_result);
   g_free (realm);
+  g_free (preauth_source);
   g_free (principal_for_display);
 }
 
 static void
 on_identity_added (GoaIdentityManager *identity_manager,
                    GoaIdentity        *identity,
                    GoaIdentityService *self)
 {
   GoaObject *object;
   const char *identifier;
 
   export_identity (self, identity);
 
   identifier = goa_identity_get_identifier (identity);
 
   object = find_object_with_principal (self, identifier, FALSE);
 
   if (object == NULL)
     add_temporary_account (self, identity);
 }
 
 static void
 on_identity_removed (GoaIdentityManager *identity_manager,
                      GoaIdentity        *identity,
                      GoaIdentityService *self)
 {
   GoaObject *object;
   const char *identifier;
 
   identifier = goa_identity_get_identifier (identity);
@@ -1239,87 +1252,89 @@ on_identity_inquiry (GoaIdentityInquiry *inquiry,
   request = system_prompt_open_request_new (self, inquiry, cancellable);
   gcr_system_prompt_open_async (-1,
                                 cancellable,
                                 (GAsyncReadyCallback)
                                 on_system_prompt_open,
                                 request);
 }
 
 static void
 cancel_sign_in (GoaIdentityManager *identity_manager,
                 GoaIdentity        *identity,
                 GSimpleAsyncResult *operation_result)
 {
   GoaIdentity *operation_identity;
 
   operation_identity = g_simple_async_result_get_source_tag (operation_result);
   if (operation_identity == identity)
     {
       GCancellable *cancellable;
 
       cancellable = g_object_get_data (G_OBJECT (operation_result),
                                        "cancellable");
       g_cancellable_cancel (cancellable);
     }
 }
 
 static void
 sign_in (GoaIdentityService     *self,
          const char             *identifier,
          gconstpointer           initial_password,
+         const char             *preauth_source,
          GoaIdentitySignInFlags  flags,
          GCancellable           *cancellable,
          GAsyncReadyCallback     callback,
          gpointer                user_data)
 {
   GSimpleAsyncResult *operation_result;
 
   goa_debug ("GoaIdentityService: asking to sign in");
 
   operation_result = g_simple_async_result_new (G_OBJECT (self),
                                                 callback,
                                                 user_data,
                                                 NULL);
   g_simple_async_result_set_check_cancellable (operation_result, cancellable);
 
   g_object_set_data (G_OBJECT (operation_result),
                      "cancellable",
                      cancellable);
   g_signal_connect_object (G_OBJECT (self->priv->identity_manager),
                            "identity-refreshed",
                            G_CALLBACK (cancel_sign_in),
                            operation_result,
                            0);
 
   goa_identity_manager_sign_identity_in (self->priv->identity_manager,
                                          identifier,
                                          initial_password,
+                                         preauth_source,
                                          flags,
                                          (GoaIdentityInquiryFunc)
                                          on_identity_inquiry,
                                          self,
                                          cancellable,
                                          (GAsyncReadyCallback)
                                          on_identity_signed_in,
                                          operation_result);
 }
 
 static void
 on_identity_expiring (GoaIdentityManager *identity_manager,
                       GoaIdentity        *identity,
                       GoaIdentityService *self)
 {
   const char *principal;
   GoaObject  *object;
 
   principal = goa_identity_get_identifier (identity);
 
   goa_debug ("GoaIdentityService: identity %s expiring", principal);
 
   object = find_object_with_principal (self, principal, TRUE);
 
   if (object == NULL)
     return;
 
   ensure_account_credentials (self, object);
 }
 
diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index c3a65f6..7641102 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -25,60 +25,62 @@
 #include "goaidentity.h"
 #include "goakerberosidentity.h"
 #include "goakerberosidentityinquiry.h"
 #include "goaalarm.h"
 #include "goalogging.h"
 
 #include <netinet/in.h>
 #include <arpa/nameser.h>
 #include <resolv.h>
 
 #include <string.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 
 typedef enum
 {
   VERIFICATION_LEVEL_UNVERIFIED,
   VERIFICATION_LEVEL_ERROR,
   VERIFICATION_LEVEL_EXISTS,
   VERIFICATION_LEVEL_SIGNED_IN
 } VerificationLevel;
 
 struct _GoaKerberosIdentityPrivate
 {
   krb5_context kerberos_context;
   krb5_ccache  credentials_cache;
 
   char *identifier;
   guint identifier_idle_id;
 
+  char *preauth_identity_source;
+
   krb5_timestamp expiration_time;
   guint          expiration_time_idle_id;
 
   GoaAlarm     *expiration_alarm;
   GoaAlarm     *expiring_alarm;
   GoaAlarm     *renewal_alarm;
 
   VerificationLevel cached_verification_level;
   guint             is_signed_in_idle_id;
 };
 
 enum
 {
   EXPIRING,
   EXPIRED,
   UNEXPIRED,
   NEEDS_RENEWAL,
   NEEDS_REFRESH,
   NUMBER_OF_SIGNALS,
 };
 
 enum
 {
   PROP_0,
   PROP_IDENTIFIER,
   PROP_IS_SIGNED_IN,
   PROP_EXPIRATION_TIMESTAMP
 };
 
 static guint signals[NUMBER_OF_SIGNALS] = { 0 };
@@ -86,60 +88,62 @@ static guint signals[NUMBER_OF_SIGNALS] = { 0 };
 static void identity_interface_init (GoaIdentityInterface *interface);
 static void initable_interface_init (GInitableIface *interface);
 static void reset_alarms (GoaKerberosIdentity *self);
 static void clear_alarms (GoaKerberosIdentity *self);
 static gboolean goa_kerberos_identity_is_signed_in (GoaIdentity *identity);
 static void set_error_from_krb5_error_code (GoaKerberosIdentity  *self,
                                             GError              **error,
                                             gint                  code,
                                             krb5_error_code       error_code,
                                             const char           *format,
                                             ...);
 
 G_LOCK_DEFINE_STATIC (identity_lock);
 
 G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentity,
                          goa_kerberos_identity,
                          G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                 initable_interface_init)
                          G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY,
                                                 identity_interface_init));
 static void
 goa_kerberos_identity_dispose (GObject *object)
 {
   GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
 
   G_LOCK (identity_lock);
   g_clear_object (&self->priv->renewal_alarm);
   g_clear_object (&self->priv->expiring_alarm);
   g_clear_object (&self->priv->expiration_alarm);
+  g_clear_pointer (&self->priv->preauth_identity_source,
+                   g_free);
   G_UNLOCK (identity_lock);
 
   G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->dispose (object);
 
 }
 
 static void
 goa_kerberos_identity_finalize (GObject *object)
 {
   GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
 
   g_free (self->priv->identifier);
 
   if (self->priv->credentials_cache != NULL)
     krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache);
 
   G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->finalize (object);
 }
 
 static void
 goa_kerberos_identity_get_property (GObject    *object,
                                     guint       property_id,
                                     GValue     *value,
                                     GParamSpec *param_spec)
 {
   GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
 
   switch (property_id)
     {
     case PROP_IDENTIFIER:
@@ -385,108 +389,152 @@ goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self)
   krb5_principal principal;
   krb5_error_code error_code;
   krb5_data *realm;
   char *realm_name;
 
   if (self->priv->identifier == NULL)
     return NULL;
 
   error_code = krb5_parse_name (self->priv->kerberos_context,
                                 self->priv->identifier, &principal);
 
   if (error_code != 0)
     {
       const char *error_message;
       error_message =
         krb5_get_error_message (self->priv->kerberos_context, error_code);
       goa_debug
         ("GoaKerberosIdentity: Error parsing identity %s into kerberos principal: %s",
          self->priv->identifier, error_message);
       krb5_free_error_message (self->priv->kerberos_context, error_message);
       return NULL;
     }
 
   realm = krb5_princ_realm (self->priv->kerberos_context, principal);
   realm_name = g_strndup (realm->data, realm->length);
   krb5_free_principal (self->priv->kerberos_context, principal);
 
   return realm_name;
 }
 
+char *
+goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self)
+{
+  return g_strdup (self->priv->preauth_identity_source);
+}
+
 static const char *
 goa_kerberos_identity_get_identifier (GoaIdentity *identity)
 {
   GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (identity);
 
   return self->priv->identifier;
 }
 
 static gboolean
 credentials_validate_existence (GoaKerberosIdentity *self,
                                 krb5_principal principal, krb5_creds * credentials)
 {
   /* Checks if default principal associated with the cache has a valid
    * ticket granting ticket in the passed in credentials
    */
 
   if (krb5_is_config_principal (self->priv->kerberos_context, credentials->server))
     return FALSE;
 
   /* looking for the krbtgt / REALM pair, so it should be exactly 2 items */
   if (krb5_princ_size (self->priv->kerberos_context, credentials->server) != 2)
     return FALSE;
 
   if (!krb5_realm_compare (self->priv->kerberos_context,
                            credentials->server, principal))
     {
       /* credentials are from some other realm */
       return FALSE;
     }
 
   if (strncmp (credentials->server->data[0].data,
                KRB5_TGS_NAME, credentials->server->data[0].length) != 0)
     {
       /* credentials aren't for ticket granting */
       return FALSE;
     }
 
   if (credentials->server->data[1].length != principal->realm.length ||
       memcmp (credentials->server->data[1].data,
               principal->realm.data, principal->realm.length) != 0)
     {
       /* credentials are for some other realm */
       return FALSE;
     }
 
   return TRUE;
 }
 
+static gboolean
+snoop_preauth_identity_from_credentials (GoaKerberosIdentity  *self,
+                                         krb5_creds           *credentials,
+                                         char                **identity_source)
+{
+  GRegex *regex;
+  GMatchInfo *match_info = NULL;
+  gboolean identity_source_exposed = FALSE;
+
+  if (!krb5_is_config_principal (self->priv->kerberos_context, credentials->server))
+    return FALSE;
+
+  regex = g_regex_new ("\"X509_user_identity\":\"(?P<identity_source>[^\"]*)\"",
+                        G_REGEX_MULTILINE | G_REGEX_CASELESS | G_REGEX_RAW,
+                        0,
+                        NULL);
+
+  if (regex == NULL)
+    return FALSE;
+
+  g_regex_match_full (regex, credentials->ticket.data, credentials->ticket.length, 0, 0, &match_info, NULL);
+
+  if (match_info != NULL && g_match_info_matches (match_info))
+    {
+      if (identity_source)
+        {
+          g_free (*identity_source);
+          *identity_source = g_match_info_fetch_named (match_info, "identity_source");
+        }
+      identity_source_exposed = TRUE;
+    }
+
+  g_match_info_free (match_info);
+  g_regex_unref (regex);
+
+  return identity_source_exposed;
+}
+
 static krb5_timestamp
 get_current_time (GoaKerberosIdentity *self)
 {
   krb5_timestamp current_time;
   krb5_error_code error_code;
 
   error_code = krb5_timeofday (self->priv->kerberos_context, &current_time);
 
   if (error_code != 0)
     {
       const char *error_message;
 
       error_message =
         krb5_get_error_message (self->priv->kerberos_context, error_code);
       goa_debug ("GoaKerberosIdentity: Error getting current time: %s", error_message);
       krb5_free_error_message (self->priv->kerberos_context, error_message);
       return 0;
     }
 
   return current_time;
 }
 
 typedef struct
 {
   GoaKerberosIdentity *self;
   guint *idle_id;
   const char *property_name;
 } NotifyRequest;
 
 static void
@@ -537,60 +585,61 @@ set_expiration_time (GoaKerberosIdentity *self,
   if (self->priv->expiration_time != expiration_time)
     {
       self->priv->expiration_time = expiration_time;
       queue_notify (self,
                     &self->priv->expiration_time_idle_id,
                     "expiration-timestamp");
     }
 }
 
 static gboolean
 credentials_are_expired (GoaKerberosIdentity *self,
                          krb5_creds          *credentials)
 {
   krb5_timestamp current_time;
 
   current_time = get_current_time (self);
 
   set_expiration_time (self, MAX (credentials->times.endtime,
                                   self->priv->expiration_time));
 
   if (credentials->times.endtime <= current_time)
     {
       return TRUE;
     }
 
   return FALSE;
 }
 
 static VerificationLevel
 verify_identity (GoaKerberosIdentity  *self,
+                 char                **preauth_identity_source,
                  GError              **error)
 {
   krb5_principal principal;
   krb5_cc_cursor cursor;
   krb5_creds credentials;
   krb5_error_code error_code;
   VerificationLevel verification_level;
 
   set_expiration_time (self, 0);
 
   if (self->priv->credentials_cache == NULL)
     return VERIFICATION_LEVEL_UNVERIFIED;
 
   error_code = krb5_cc_get_principal (self->priv->kerberos_context,
                                       self->priv->credentials_cache,
                                       &principal);
 
   if (error_code != 0)
     {
       if (error_code == KRB5_CC_END || error_code == KRB5_FCC_NOFILE)
         return VERIFICATION_LEVEL_UNVERIFIED;
 
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_NOT_FOUND,
                                       error_code,
                                       _("Could not find identity in "
                                         "credential cache: %k"));
       return VERIFICATION_LEVEL_ERROR;
     }
@@ -599,60 +648,64 @@ verify_identity (GoaKerberosIdentity  *self,
                                       self->priv->credentials_cache, &cursor);
   if (error_code != 0)
     {
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE,
                                       error_code,
                                       _("Could not find identity "
                                         "credentials in cache: %k"));
 
       verification_level = VERIFICATION_LEVEL_ERROR;
       goto out;
     }
 
   verification_level = VERIFICATION_LEVEL_UNVERIFIED;
 
   error_code = krb5_cc_next_cred (self->priv->kerberos_context,
                                   self->priv->credentials_cache,
                                   &cursor,
                                   &credentials);
 
   while (error_code == 0)
     {
       if (credentials_validate_existence (self, principal, &credentials))
         {
           if (!credentials_are_expired (self, &credentials))
             verification_level = VERIFICATION_LEVEL_SIGNED_IN;
           else
             verification_level = VERIFICATION_LEVEL_EXISTS;
         }
+      else
+        {
+          snoop_preauth_identity_from_credentials (self, &credentials, preauth_identity_source);
+        }
 
       krb5_free_cred_contents (self->priv->kerberos_context, &credentials);
 
       error_code = krb5_cc_next_cred (self->priv->kerberos_context,
                                       self->priv->credentials_cache,
                                       &cursor,
                                       &credentials);
     }
 
   if (error_code != KRB5_CC_END)
     {
       verification_level = VERIFICATION_LEVEL_ERROR;
 
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS,
                                       error_code,
                                       _("Could not sift through identity "
                                         "credentials in cache: %k"));
       goto end_sequence;
     }
 
  end_sequence:
   error_code = krb5_cc_end_seq_get (self->priv->kerberos_context,
                                     self->priv->credentials_cache,
                                     &cursor);
 
   if (error_code != 0)
     {
       verification_level = VERIFICATION_LEVEL_ERROR;
@@ -900,61 +953,61 @@ reset_alarms (GoaKerberosIdentity *self)
 static void
 clear_alarms (GoaKerberosIdentity *self)
 {
   disconnect_alarm_signals (self);
   clear_alarm_and_unref_on_idle (self, &self->priv->renewal_alarm);
   clear_alarm_and_unref_on_idle (self, &self->priv->expiring_alarm);
   clear_alarm_and_unref_on_idle (self, &self->priv->expiration_alarm);
 }
 
 static gboolean
 goa_kerberos_identity_initable_init (GInitable     *initable,
                                      GCancellable  *cancellable,
                                      GError       **error)
 {
   GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable);
   GError *verification_error;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
 
   if (self->priv->identifier == NULL)
     {
       self->priv->identifier = get_identifier (self, error);
 
       if (self->priv->identifier != NULL)
         queue_notify (self, &self->priv->identifier_idle_id, "identifier");
     }
 
   verification_error = NULL;
   self->priv->cached_verification_level =
-    verify_identity (self, &verification_error);
+    verify_identity (self, &self->priv->preauth_identity_source, &verification_error);
 
   switch (self->priv->cached_verification_level)
     {
     case VERIFICATION_LEVEL_EXISTS:
     case VERIFICATION_LEVEL_SIGNED_IN:
       reset_alarms (self);
 
       queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in");
       return TRUE;
 
     case VERIFICATION_LEVEL_UNVERIFIED:
       return TRUE;
 
     case VERIFICATION_LEVEL_ERROR:
       if (verification_error != NULL)
         {
           g_propagate_error (error, verification_error);
           return FALSE;
         }
     default:
       g_set_error (error,
                    GOA_IDENTITY_ERROR,
                    GOA_IDENTITY_ERROR_VERIFYING,
                    _("No associated identification found"));
       return FALSE;
 
     }
 }
 
 static void
@@ -1105,60 +1158,61 @@ sign_in_operation_new (GoaKerberosIdentity    *identity,
 {
   SignInOperation *operation;
 
   operation = g_slice_new0 (SignInOperation);
   operation->identity = g_object_ref (identity);
   operation->inquiry_func = inquiry_func;
   operation->inquiry_data = inquiry_data;
   operation->destroy_notify = destroy_notify;
 
   if (cancellable == NULL)
     operation->cancellable = g_cancellable_new ();
   else
     operation->cancellable = g_object_ref (cancellable);
 
   return operation;
 }
 
 static void
 sign_in_operation_free (SignInOperation *operation)
 {
   g_object_unref (operation->identity);
   g_object_unref (operation->cancellable);
 
   g_slice_free (SignInOperation, operation);
 }
 
 gboolean
 goa_kerberos_identity_sign_in (GoaKerberosIdentity     *self,
                                const char              *principal_name,
                                gconstpointer            initial_password,
+                               const char              *preauth_source,
                                GoaIdentitySignInFlags   flags,
                                GoaIdentityInquiryFunc   inquiry_func,
                                gpointer                 inquiry_data,
                                GDestroyNotify           destroy_notify,
                                GCancellable            *cancellable,
                                GError                 **error)
 {
   SignInOperation *operation;
   krb5_principal principal;
   krb5_error_code error_code;
   krb5_creds new_credentials;
   krb5_get_init_creds_opt *options;
   krb5_deltat start_time;
   char *service_name;
   gboolean signed_in;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
 
   error_code = krb5_get_init_creds_opt_alloc (self->priv->kerberos_context,
                                               &options);
   if (error_code != 0)
     {
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS,
                                       error_code, "%k");
       if (destroy_notify)
         destroy_notify (inquiry_data);
       return FALSE;
@@ -1176,60 +1230,67 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity     *self,
     {
       g_free (self->priv->identifier);
       self->priv->identifier = g_strdup (principal_name);
     }
 
   error_code = krb5_parse_name (self->priv->kerberos_context,
                                 principal_name,
                                 &principal);
 
   if (error_code != 0)
     {
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_PARSING_IDENTIFIER,
                                       error_code,
                                       "%k");
       if (destroy_notify)
         destroy_notify (inquiry_data);
       return FALSE;
     }
 
   if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING) == 0)
     krb5_get_init_creds_opt_set_forwardable (options, TRUE);
 
   if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING) == 0)
     krb5_get_init_creds_opt_set_proxiable (options, TRUE);
 
   if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL) == 0)
     krb5_get_init_creds_opt_set_renew_life (options, G_MAXINT);
 
+  if (preauth_source != NULL)
+    {
+      krb5_get_init_creds_opt_set_pa (self->priv->kerberos_context,
+                                      options,
+                                      "X509_user_identity", preauth_source);
+    }
+
   /* Poke glibc in case the network changed
    */
   res_init ();
 
   start_time = 0;
   service_name = NULL;
   error_code = krb5_get_init_creds_password (self->priv->kerberos_context,
                                              &new_credentials,
                                              principal,
                                              (char *)
                                              initial_password,
                                              (krb5_prompter_fct)
                                              on_kerberos_inquiry,
                                              operation,
                                              start_time,
                                              service_name,
                                              options);
 
   if (error_code == KRB5_LIBOS_PWDINTR)
     g_cancellable_cancel (operation->cancellable);
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     {
       if (destroy_notify)
         destroy_notify (inquiry_data);
       sign_in_operation_free (operation);
 
       krb5_free_principal (self->priv->kerberos_context, principal);
       goto done;
     }
@@ -1266,79 +1327,85 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity     *self,
   goa_debug ("GoaKerberosIdentity: identity signed in");
   signed_in = TRUE;
 done:
 
   return signed_in;
 }
 
 static void
 update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity)
 {
   char *new_identifier;
 
   new_identifier = get_identifier (self, NULL);
   if (g_strcmp0 (self->priv->identifier, new_identifier) != 0)
     {
       g_free (self->priv->identifier);
       self->priv->identifier = new_identifier;
       queue_notify (self, &self->priv->identifier_idle_id, "identifier");
     }
   else
     {
       g_free (new_identifier);
     }
 }
 
 void
 goa_kerberos_identity_update (GoaKerberosIdentity *self,
                               GoaKerberosIdentity *new_identity)
 {
   VerificationLevel verification_level;
+  char *preauth_identity_source = NULL;
 
   if (self->priv->credentials_cache != NULL)
     krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache);
 
   krb5_cc_dup (new_identity->priv->kerberos_context,
                new_identity->priv->credentials_cache,
                &self->priv->credentials_cache);
 
   G_LOCK (identity_lock);
   update_identifier (self, new_identity);
   G_UNLOCK (identity_lock);
 
-  verification_level = verify_identity (self, NULL);
+  verification_level = verify_identity (self, &preauth_identity_source, NULL);
 
   if (verification_level == VERIFICATION_LEVEL_SIGNED_IN)
     reset_alarms (self);
   else
     clear_alarms (self);
 
+  G_LOCK (identity_lock);
+  g_free (self->priv->preauth_identity_source);
+  self->priv->preauth_identity_source = preauth_identity_source;
+  G_UNLOCK (identity_lock);
+
   if (verification_level != self->priv->cached_verification_level)
     {
       if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN &&
           verification_level == VERIFICATION_LEVEL_EXISTS)
         {
 
           G_LOCK (identity_lock);
           self->priv->cached_verification_level = verification_level;
           G_UNLOCK (identity_lock);
 
           g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0);
         }
       else if (self->priv->cached_verification_level == VERIFICATION_LEVEL_EXISTS &&
                verification_level == VERIFICATION_LEVEL_SIGNED_IN)
         {
 
           G_LOCK (identity_lock);
           self->priv->cached_verification_level = verification_level;
           G_UNLOCK (identity_lock);
 
           g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0);
         }
       else
         {
           G_LOCK (identity_lock);
           self->priv->cached_verification_level = verification_level;
           G_UNLOCK (identity_lock);
         }
       queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in");
     }
diff --git a/src/goaidentity/goakerberosidentity.h b/src/goaidentity/goakerberosidentity.h
index 6e82835..e163016 100644
--- a/src/goaidentity/goakerberosidentity.h
+++ b/src/goaidentity/goakerberosidentity.h
@@ -43,47 +43,49 @@ typedef enum _GoaKerberosIdentityDescriptionLevel
   GoaKerberosIdentityDescriptionLevel;
 
 enum _GoaKerberosIdentityDescriptionLevel
 {
   GOA_KERBEROS_IDENTITY_DESCRIPTION_REALM,
   GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_AND_REALM,
   GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_ROLE_AND_REALM
 };
 
 struct _GoaKerberosIdentity
 {
   GObject parent;
 
   GoaKerberosIdentityPrivate *priv;
 };
 
 struct _GoaKerberosIdentityClass
 {
   GObjectClass parent_class;
 };
 
 GType goa_kerberos_identity_get_type (void);
 
 GoaIdentity *goa_kerberos_identity_new (krb5_context   kerberos_context,
                                         krb5_ccache    cache,
                                         GError       **error);
 
 gboolean goa_kerberos_identity_sign_in (GoaKerberosIdentity     *self,
                                         const char              *principal_name,
                                         gconstpointer            initial_password,
+                                        const char              *preauth_source,
                                         GoaIdentitySignInFlags   flags,
                                         GoaIdentityInquiryFunc   inquiry_func,
                                         gpointer                 inquiry_data,
                                         GDestroyNotify           destroy_notify,
                                         GCancellable            *cancellable,
                                         GError                 **error);
 void goa_kerberos_identity_update (GoaKerberosIdentity *identity,
                                    GoaKerberosIdentity *new_identity);
 gboolean goa_kerberos_identity_renew (GoaKerberosIdentity  *self,
                                       GError              **error);
 gboolean goa_kerberos_identity_erase (GoaKerberosIdentity *self,
                                       GError              **error);
 
 char *goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self);
 char *goa_kerberos_identity_get_realm_name     (GoaKerberosIdentity *self);
+char *goa_kerberos_identity_get_preauthentication_source     (GoaKerberosIdentity *self);
 G_END_DECLS
 #endif /* __GOA_KERBEROS_IDENTITY_H__ */
diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index c4da420..1fa0807 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -59,60 +59,61 @@ struct _GoaKerberosIdentityManagerPrivate
   volatile int pending_refresh_count;
 
   guint polling_timeout_id;
 };
 
 typedef enum
 {
   OPERATION_TYPE_REFRESH,
   OPERATION_TYPE_GET_IDENTITY,
   OPERATION_TYPE_LIST,
   OPERATION_TYPE_RENEW,
   OPERATION_TYPE_SIGN_IN,
   OPERATION_TYPE_SIGN_OUT,
   OPERATION_TYPE_STOP_JOB
 } OperationType;
 
 typedef struct
 {
   GCancellable *cancellable;
   GoaKerberosIdentityManager *manager;
   OperationType type;
   GSimpleAsyncResult *result;
   GIOSchedulerJob *job;
   union
   {
     GoaIdentity *identity;
     struct
     {
       const char *identifier;
       gconstpointer initial_password;
+      char *preauth_source;
       GoaIdentitySignInFlags sign_in_flags;
       GoaIdentityInquiry *inquiry;
       GoaIdentityInquiryFunc inquiry_func;
       gpointer inquiry_data;
       GMutex inquiry_lock;
       GCond inquiry_finished_condition;
       volatile gboolean is_inquiring;
     };
   };
 } Operation;
 
 typedef struct
 {
   GoaKerberosIdentityManager *manager;
   GoaIdentity *identity;
 } IdentitySignalWork;
 
 static GoaIdentityManager *goa_kerberos_identity_manager_singleton;
 
 static void identity_manager_interface_init (GoaIdentityManagerInterface *
                                              interface);
 static void initable_interface_init (GInitableIface *interface);
 
 static void on_identity_expired (GoaIdentity                *identity,
                                  GoaKerberosIdentityManager *self);
 
 G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
                          goa_kerberos_identity_manager,
                          G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER,
@@ -129,64 +130,68 @@ operation_new (GoaKerberosIdentityManager *self,
 {
   Operation *operation;
 
   operation = g_slice_new0 (Operation);
 
   operation->manager = self;
   operation->type = type;
 
   if (cancellable == NULL)
     cancellable = g_cancellable_new ();
   else
     g_object_ref (cancellable);
   operation->cancellable = cancellable;
 
   if (result != NULL)
     g_object_ref (result);
   operation->result = result;
 
   operation->identity = NULL;
 
   return operation;
 }
 
 static void
 operation_free (Operation *operation)
 {
   g_clear_object (&operation->cancellable);
 
   if (operation->type != OPERATION_TYPE_SIGN_IN &&
       operation->type != OPERATION_TYPE_GET_IDENTITY)
-    g_clear_object (&operation->identity);
+    {
+      g_clear_object (&operation->identity);
+    }
   else
-    g_clear_pointer (&operation->identifier, g_free);
-
+    {
+      g_clear_pointer (&operation->identifier, g_free);
+      g_clear_pointer (&operation->preauth_source, g_free);
+    }
   g_clear_object (&operation->result);
 
   g_slice_free (Operation, operation);
 }
 
 static void
 schedule_refresh (GoaKerberosIdentityManager *self)
 {
   Operation *operation;
 
   g_atomic_int_inc (&self->priv->pending_refresh_count);
 
   operation = operation_new (self, NULL, OPERATION_TYPE_REFRESH, NULL);
   g_async_queue_push (self->priv->pending_operations, operation);
 }
 
 static IdentitySignalWork *
 identity_signal_work_new (GoaKerberosIdentityManager *self,
                           GoaIdentity                *identity)
 {
   IdentitySignalWork *work;
 
   work = g_slice_new (IdentitySignalWork);
   work->manager = self;
   work->identity = g_object_ref (identity);
 
   return work;
 }
 
 static void
@@ -841,60 +846,61 @@ sign_in_identity (GoaKerberosIdentityManager *self,
                                            _("Could not create credential cache for identity"));
           g_simple_async_result_set_op_res_gpointer (operation->result, NULL, NULL);
           return;
         }
 
       identity = goa_kerberos_identity_new (self->priv->kerberos_context,
                                             credentials_cache,
                                             &error);
       krb5_cc_close (self->priv->kerberos_context, credentials_cache);
       if (identity == NULL)
         {
           g_simple_async_result_take_error (operation->result, error);
           g_simple_async_result_set_op_res_gpointer (operation->result,
                                                      NULL,
                                                      NULL);
           return;
         }
     }
   else
     {
       g_object_ref (identity);
     }
 
   g_hash_table_replace (self->priv->identities,
                         g_strdup (operation->identifier),
                         g_object_ref (identity));
 
   if (!goa_kerberos_identity_sign_in (GOA_KERBEROS_IDENTITY (identity),
                                       operation->identifier,
                                       operation->initial_password,
+                                      operation->preauth_source,
                                       operation->sign_in_flags,
                                       (GoaIdentityInquiryFunc)
                                       on_kerberos_identity_inquiry,
                                       operation,
                                       NULL,
                                       operation->cancellable,
                                       &error))
     {
       g_simple_async_result_set_from_error (operation->result, error);
       g_simple_async_result_set_op_res_gpointer (operation->result,
                                                  NULL,
                                                  NULL);
 
     }
   else
     {
       g_simple_async_result_set_op_res_gpointer (operation->result,
                                                  g_object_ref (identity),
                                                  (GDestroyNotify)
                                                  g_object_unref);
     }
 
   g_object_unref (identity);
 }
 
 static void
 sign_out_identity (GoaKerberosIdentityManager *self,
                    Operation                  *operation)
 {
   GError *error;
@@ -1156,83 +1162,85 @@ goa_kerberos_identity_manager_renew_identity (GoaIdentityManager  *manager,
   GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager);
   GSimpleAsyncResult *result;
   Operation *operation;
 
   result = g_simple_async_result_new (G_OBJECT (self),
                                       callback,
                                       user_data,
                                       goa_kerberos_identity_manager_renew_identity);
   operation = operation_new (self, cancellable, OPERATION_TYPE_RENEW, result);
   g_object_unref (result);
 
   operation->identity = g_object_ref (identity);
 
   g_async_queue_push (self->priv->pending_operations, operation);
 }
 
 static void
 goa_kerberos_identity_manager_renew_identity_finish (GoaIdentityManager  *self,
                                                      GAsyncResult        *result,
                                                      GError             **error)
 {
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
                                              error))
     return;
 }
 
 static void
 goa_kerberos_identity_manager_sign_identity_in (GoaIdentityManager     *manager,
                                                 const char             *identifier,
                                                 gconstpointer           initial_password,
+                                                const char             *preauth_source,
                                                 GoaIdentitySignInFlags  flags,
                                                 GoaIdentityInquiryFunc  inquiry_func,
                                                 gpointer                inquiry_data,
                                                 GCancellable           *cancellable,
                                                 GAsyncReadyCallback     callback,
                                                 gpointer                user_data)
 {
   GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager);
   GSimpleAsyncResult *result;
   Operation *operation;
 
   result = g_simple_async_result_new (G_OBJECT (self),
                                       callback,
                                       user_data,
                                       goa_kerberos_identity_manager_sign_identity_in);
   operation = operation_new (self, cancellable, OPERATION_TYPE_SIGN_IN, result);
   g_object_unref (result);
 
   operation->identifier = g_strdup (identifier);
   /* Not duped. Caller is responsible for ensuring it stays alive
    * for duration of operation
    */
   operation->initial_password = initial_password;
+  operation->preauth_source = g_strdup (preauth_source);
   operation->sign_in_flags = flags;
   operation->inquiry_func = inquiry_func;
   operation->inquiry_data = inquiry_data;
   g_mutex_init (&operation->inquiry_lock);
   g_cond_init (&operation->inquiry_finished_condition);
   operation->is_inquiring = FALSE;
 
   g_async_queue_push (self->priv->pending_operations, operation);
 }
 
 static GoaIdentity *
 goa_kerberos_identity_manager_sign_identity_in_finish (GoaIdentityManager  *self,
                                                        GAsyncResult        *result,
                                                        GError             **error)
 {
   GoaIdentity *identity;
 
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
     return NULL;
 
   identity =
     g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
 
   return identity;
 }
 
 static void
 goa_kerberos_identity_manager_sign_identity_out (GoaIdentityManager  *manager,
                                                  GoaIdentity         *identity,
                                                  GCancellable        *cancellable,
-- 
1.8.3.1