[gnome-keyring] gck: Integrate GTlsInteraction into libgck



commit 31d109c1114afb6e74424c8190c03797bb16f159
Author: Stef Walter <stefw collabora co uk>
Date:   Mon Oct 3 13:23:39 2011 +0200

    gck: Integrate GTlsInteraction into libgck
    
     * Deprecate the GckModule signals which were used for password auth
     * Fire deprecated GckModule signals from a compat GTlsInteraction
     * Add some tests for this integration

 docs/reference/gck/gck-sections.txt |   23 ++
 gck/Makefile.am                     |    2 +
 gck/gck-enumerator.c                |  311 +++++++++++-------
 gck/gck-interaction.c               |  176 ++++++++++
 gck/gck-misc.c                      |   15 +
 gck/gck-module.c                    |   82 +-----
 gck/gck-modules.c                   |   12 +-
 gck/gck-password.c                  |  255 ++++++++++++++
 gck/gck-private.h                   |   48 ++-
 gck/gck-session.c                   |  643 ++++++++++++++++++++++-------------
 gck/gck-slot.c                      |  184 +++++++---
 gck/gck.h                           |   56 +++-
 gck/gck.symbols                     |    9 +
 gck/tests/Makefile.am               |    4 +
 gck/tests/mock-interaction.c        |   97 ++++++
 gck/tests/mock-interaction.h        |   43 +++
 gck/tests/test-gck-crypto.c         |    2 +
 gck/tests/test-gck-enumerator.c     |   76 ++++
 gck/tests/test-gck-session.c        |    2 +
 19 files changed, 1543 insertions(+), 497 deletions(-)
---
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index 14a7d30..0c9ea15 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -124,6 +124,8 @@ gck_slot_hash
 gck_slot_get_module
 gck_slot_get_handle
 gck_slot_get_info
+gck_slot_get_interaction
+gck_slot_set_interaction
 gck_slot_match
 gck_slot_get_token_info
 gck_slot_get_mechanisms
@@ -172,6 +174,7 @@ gck_session_get_module
 gck_session_get_slot
 gck_session_get_handle
 gck_session_get_info
+gck_session_get_interaction
 gck_session_login
 gck_session_login_async
 gck_session_login_finish
@@ -319,6 +322,8 @@ gck_uri_flags_get_type
 <SECTION>
 <FILE>gck-enumerator</FILE>
 GckEnumerator
+gck_enumerator_get_interaction
+gck_enumerator_set_interaction
 gck_enumerator_next
 gck_enumerator_next_async
 gck_enumerator_next_finish
@@ -336,6 +341,24 @@ GCK_TYPE_ENUMERATOR
 </SECTION>
 
 <SECTION>
+<FILE>gck-password</FILE>
+GckPassword
+GckPasswordClass
+gck_password_get_key
+gck_password_get_module
+gck_password_get_token
+<SUBSECTION Private>
+gck_password_get_type
+GckPasswordPrivate
+GCK_IS_PASSWORD
+GCK_IS_PASSWORD_CLASS
+GCK_PASSWORD
+GCK_PASSWORD_CLASS
+GCK_PASSWORD_GET_CLASS
+GCK_TYPE_PASSWORD
+</SECTION>
+
+<SECTION>
 <FILE>gck-misc</FILE>
 GckError
 gck_list_ref_copy
diff --git a/gck/Makefile.am b/gck/Makefile.am
index a6d9a97..6a2a6c3 100644
--- a/gck/Makefile.am
+++ b/gck/Makefile.am
@@ -39,6 +39,7 @@ PUBLIC_FILES = \
 	gck-module.c \
 	gck-modules.c \
 	gck-object.c \
+	gck-password.c \
 	gck-session.c \
 	gck-slot.c \
 	gck-uri.c
@@ -47,6 +48,7 @@ INTERNAL_FILES = \
 	gck-call.c \
 	gck-debug.c gck-debug.h \
 	gck-deprecated.h \
+	gck-interaction.c \
 	gck-private.h \
 	pkcs11.h
 
diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c
index dfb5bf4..c57297e 100644
--- a/gck/gck-enumerator.c
+++ b/gck/gck-enumerator.c
@@ -43,6 +43,11 @@
  * gck_enumerator_next_async() functions.
  */
 
+enum {
+	PROP_0,
+	PROP_INTERACTION
+};
+
 /**
  * GckEnumerator:
  * @parent: derived from this.
@@ -57,7 +62,6 @@ typedef gpointer (*GckEnumeratorFunc) (GckEnumeratorState *args, gboolean forwar
 struct _GckEnumeratorState {
 	/* For the current call */
 	gint want_objects;
-	gboolean want_password;
 
 	/* The state we're currently in */
 	GckEnumeratorFunc handler;
@@ -65,9 +69,8 @@ struct _GckEnumeratorState {
 	/* Input to enumerator */
 	GList *modules;
 	GckUriData *match;
-	guint session_options;
-	gboolean authenticate;
-	gchar *password;
+	GckSessionOptions session_options;
+	GTlsInteraction *interaction;
 
 	/* state_slots */
 	GList *slots;
@@ -88,9 +91,9 @@ struct _GckEnumeratorState {
 };
 
 struct _GckEnumeratorPrivate {
-	/* Data here is set atomically */
-	gpointer state;
-	gint mode;
+	GMutex *mutex;
+	GckEnumeratorState *the_state;
+	GTlsInteraction *interaction;
 };
 
 G_DEFINE_TYPE (GckEnumerator, gck_enumerator, G_TYPE_OBJECT);
@@ -152,11 +155,7 @@ cleanup_state (GckEnumeratorState *args)
 	gck_list_unref_free (args->modules);
 	args->modules = NULL;
 
-	/* TODO: Can we use secure memory here? */
-	if (args->password) {
-		g_free (args->password);
-		args->password  = NULL;
-	}
+	g_clear_object (&args->interaction);
 
 	if (args->match) {
 		if (args->match->attributes)
@@ -321,65 +320,38 @@ state_slot (GckEnumeratorState *args, gboolean forward)
 static gpointer
 state_session (GckEnumeratorState *args, gboolean forward)
 {
-	GckSessionInfo *sinfo;
-	CK_ULONG n_pin;
+	GTlsInteraction *interaction;
 	CK_RV rv;
 
 	g_assert (args->funcs);
 	g_assert (args->session);
-	g_assert (!args->want_password);
 	g_assert (args->token_info);
 
 	/* session to authenticated state */
 	if (forward) {
 
 		/* Don't want to authenticate? */
-		if (!args->authenticate) {
+		if ((args->session_options & GCK_SESSION_LOGIN_USER) == 0) {
 			_gck_debug ("no authentication necessary, skipping");
 			return state_authenticated;
 		}
 
-		/* No login necessary */
-		if ((args->token_info->flags & CKF_LOGIN_REQUIRED) == 0) {
-			_gck_debug ("no login required, skipping");
-			return state_authenticated;
-		}
+		/* Compatibility, hook into GckModule signals if no interaction set */
+		if (args->interaction)
+			interaction = g_object_ref (args->interaction);
+		else
+			interaction = _gck_interaction_new (args->slot);
 
-		/* Next check if session is logged in */
-		sinfo = gck_session_get_info (args->session);
-		if (sinfo == NULL) {
-			g_message ("couldn't get session info when enumerating");
-			return rewind_state (args, state_slots);
-		}
+		rv = _gck_session_authenticate_token (args->funcs,
+		                                      gck_session_get_handle (args->session),
+		                                      args->slot, interaction, NULL);
 
-		/* Already logged in? */
-		if (sinfo->state == CKS_RW_USER_FUNCTIONS ||
-		    sinfo->state == CKS_RO_USER_FUNCTIONS ||
-		    sinfo->state == CKS_RW_SO_FUNCTIONS) {
-			gck_session_info_free (sinfo);
-			_gck_debug ("already logged in, skipping");
-			return state_authenticated;
-		}
-
-		gck_session_info_free (sinfo);
-		_gck_debug ("trying to log into session");
-
-		/* Try to log in */
-		n_pin = args->password ? strlen (args->password) : 0;
-		rv = (args->funcs->C_Login) (gck_session_get_handle (args->session), CKU_USER,
-		                             (CK_BYTE_PTR)args->password, n_pin);
-
-		/* Authentication failed, ask for a password */
-		if (rv == CKR_PIN_INCORRECT) {
-			_gck_debug ("login was incorrect, want password");
-			args->want_password = TRUE;
-			return NULL;
+		g_object_unref (interaction);
 
-		/* Any other failure continue without authentication */
-		} else if (rv != CKR_OK) {
+		if (rv != CKR_OK)
 			g_message ("couldn't authenticate when enumerating: %s", gck_message_from_rv (rv));
-		}
 
+		/* We try to proceed anyway with the enumeration */
 		return state_authenticated;
 
 	/* Session to slot state */
@@ -406,7 +378,6 @@ state_authenticated (GckEnumeratorState *args, gboolean forward)
 	/* This is where we do the actual searching */
 
 	g_assert (args->session);
-	g_assert (!args->want_password);
 	g_assert (args->want_objects);
 	g_assert (args->funcs);
 
@@ -506,38 +477,95 @@ state_results (GckEnumeratorState *args, gboolean forward)
 static void
 gck_enumerator_init (GckEnumerator *self)
 {
-	GckEnumeratorState *args;
-
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_ENUMERATOR, GckEnumeratorPrivate);
-	args = g_new0 (GckEnumeratorState, 1);
-	g_atomic_pointer_set (&self->pv->state, args);
+	self->pv->mutex = g_mutex_new ();
+	self->pv->the_state = g_new0 (GckEnumeratorState, 1);
+}
+
+static void
+gck_enumerator_get_property (GObject *obj,
+                             guint prop_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+	GckEnumerator *self = GCK_ENUMERATOR (obj);
+
+	switch (prop_id) {
+	case PROP_INTERACTION:
+		g_value_take_object (value, gck_enumerator_get_interaction (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_enumerator_set_property (GObject *obj,
+                             guint prop_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	GckEnumerator *self = GCK_ENUMERATOR (obj);
+
+	switch (prop_id) {
+	case PROP_INTERACTION:
+		gck_enumerator_set_interaction (self, g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_enumerator_dispose (GObject *obj)
+{
+	GckEnumerator *self = GCK_ENUMERATOR (obj);
+
+	gck_enumerator_set_interaction (self, NULL);
+
+	G_OBJECT_CLASS (gck_enumerator_parent_class)->dispose (obj);
 }
 
 static void
 gck_enumerator_finalize (GObject *obj)
 {
 	GckEnumerator *self = GCK_ENUMERATOR (obj);
-	GckEnumeratorState *state = g_atomic_pointer_get (&self->pv->state);
 
-	if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, state, NULL))
-		g_assert_not_reached ();
+	g_assert (self->pv->interaction == NULL);
+
+	g_assert (self->pv->the_state != NULL);
+	cleanup_state (self->pv->the_state);
+	g_free (self->pv->the_state);
 
-	g_assert (state);
-	cleanup_state (state);
-	g_free (state);
+	g_mutex_free (self->pv->mutex);
 
 	G_OBJECT_CLASS (gck_enumerator_parent_class)->finalize (obj);
 }
 
-
 static void
 gck_enumerator_class_init (GckEnumeratorClass *klass)
 {
 	GObjectClass *gobject_class = (GObjectClass*)klass;
-	gck_enumerator_parent_class = g_type_class_peek_parent (klass);
 
+	gobject_class->get_property = gck_enumerator_get_property;
+	gobject_class->set_property = gck_enumerator_set_property;
+	gobject_class->dispose = gck_enumerator_dispose;
 	gobject_class->finalize = gck_enumerator_finalize;
+
 	g_type_class_add_private (klass, sizeof (GckEnumeratorPrivate));
+
+	/**
+	 * GckEnumerator:interaction:
+	 *
+	 * Interaction object used to ask the user for pins when opening
+	 * sessions. Used if the session_options of the enumerator have
+	 * %GCK_SESSION_LOGIN_USER
+	 */
+	g_object_class_install_property (gobject_class, PROP_INTERACTION,
+		g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins",
+		                     G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
 }
 
 /* ----------------------------------------------------------------------------
@@ -545,14 +573,15 @@ gck_enumerator_class_init (GckEnumeratorClass *klass)
  */
 
 GckEnumerator*
-_gck_enumerator_new (GList *modules_or_slots, guint session_options,
+_gck_enumerator_new (GList *modules_or_slots,
+                     GckSessionOptions session_options,
                      GckUriData *uri_data)
 {
 	GckEnumerator *self;
 	GckEnumeratorState *state;
 
 	self = g_object_new (GCK_TYPE_ENUMERATOR, NULL);
-	state = g_atomic_pointer_get (&self->pv->state);
+	state = self->pv->the_state;
 
 	state->session_options = session_options;
 
@@ -596,7 +625,6 @@ perform_enumerate_next (EnumerateNext *args)
 	g_assert (args->state);
 	state = args->state;
 
-	g_assert (!state->want_password);
 	g_assert (state->handler);
 
 	for (;;) {
@@ -610,43 +638,105 @@ perform_enumerate_next (EnumerateNext *args)
 	return CKR_OK;
 }
 
-static gboolean
-complete_enumerate_next (EnumerateNext *args, CK_RV result)
+static void
+free_enumerate_next (EnumerateNext *args)
 {
-	GckEnumeratorState *state;
-	GckModule *module;
-	gboolean ret = TRUE;
+	/* Should have been assigned back to enumerator */
+	g_assert (!args->state);
 
-	g_assert (args->state);
-	state = args->state;
+	g_free (args);
+}
 
-	if (state->want_password) {
-		g_assert (state->slot);
+/**
+ * gck_enumerator_get_interaction:
+ * @self: the enumerator
+ *
+ * Get the interaction used when a pin is needed
+ *
+ * Returns: (transfer full) (allow-none): the interaction or %NULL
+ */
+GTlsInteraction *
+gck_enumerator_get_interaction (GckEnumerator *self)
+{
+	GTlsInteraction *result = NULL;
 
-		_gck_debug ("wants password, emitting authenticate-slot");
+	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
 
-		/* TODO: Should we be using secure memory here? */
-		g_free (state->password);
-		state->password = NULL;
+	g_mutex_lock (self->pv->mutex);
 
-		module = gck_slot_get_module (state->slot);
-		ret = _gck_module_fire_authenticate_slot (module, state->slot, NULL, &state->password);
-		g_object_unref (module);
+		if (self->pv->interaction)
+			result = g_object_ref (self->pv->interaction);
 
-		/* If authenticate returns TRUE then call is not complete */
-		ret = !ret;
-	}
+	g_mutex_unlock (self->pv->mutex);
 
-	return ret;
+	return result;
+}
+
+/**
+ * gck_enumerator_set_interaction:
+ * @self: the enumerator
+ * @interaction: (allow-none): the interaction or %NULL
+ *
+ * Set the interaction used when a pin is needed
+ */
+void
+gck_enumerator_set_interaction (GckEnumerator *self,
+                                GTlsInteraction *interaction)
+{
+	GTlsInteraction *previous = NULL;
+
+	g_return_if_fail (GCK_IS_ENUMERATOR (self));
+	g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction));
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (interaction != self->pv->interaction) {
+			previous = self->pv->interaction;
+			self->pv->interaction = interaction;
+			if (interaction)
+				g_object_ref (interaction);
+		}
+
+	g_mutex_unlock (self->pv->mutex);
+
+	g_clear_object (&previous);
+	g_object_notify (G_OBJECT (self), "interaction");
+}
+
+static GckEnumeratorState *
+check_out_enumerator_state (GckEnumerator *self)
+{
+	GckEnumeratorState *state = NULL;
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (self->pv->the_state) {
+			state = self->pv->the_state;
+			self->pv->the_state = NULL;
+
+			g_clear_object (&state->interaction);
+			if (self->pv->interaction)
+				state->interaction = g_object_ref (self->pv->interaction);
+		}
+
+	g_mutex_unlock (self->pv->mutex);
+
+	if (state == NULL)
+		g_warning ("this enumerator is already running a next operation");
+
+	return state;
 }
 
 static void
-free_enumerate_next (EnumerateNext *args)
+check_in_enumerator_state (GckEnumerator *self,
+                           GckEnumeratorState *state)
 {
-	/* Should have been assigned back to enumerator */
-	g_assert (!args->state);
+	g_mutex_lock (self->pv->mutex);
 
-	g_free (args);
+		g_assert (self->pv->the_state == NULL);
+		self->pv->the_state = state;
+
+	g_mutex_unlock (self->pv->mutex);
 }
 
 /**
@@ -672,12 +762,8 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er
 	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
-	/* Remove the state and own it ourselves */
-	args.state = g_atomic_pointer_get (&self->pv->state);
-	if (!args.state || !g_atomic_pointer_compare_and_exchange (&self->pv->state, args.state, NULL)) {
-		g_warning ("this enumerator is already running a next operation");
-		return NULL;
-	}
+	args.state = check_out_enumerator_state (self);
+	g_return_val_if_fail (args.state != NULL, NULL);
 
 	/* A result from a previous run? */
 	result = extract_result (args.state);
@@ -685,7 +771,7 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er
 		args.state->want_objects = 1;
 
 		/* Run the operation and steal away the results */
-		if (_gck_call_sync (NULL, perform_enumerate_next, complete_enumerate_next, &args, cancellable, error)) {
+		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) {
 			if (args.state->results) {
 				g_assert (g_list_length (args.state->results) == 1);
 				result = g_object_ref (args.state->results->data);
@@ -698,8 +784,7 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er
 	}
 
 	/* Put the state back */
-	if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, args.state))
-		g_assert_not_reached ();
+	check_in_enumerator_state (self, args.state);
 
 	return result;
 }
@@ -733,16 +818,13 @@ gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *canc
 	g_return_val_if_fail (!error || !*error, NULL);
 
 	/* Remove the state and own it ourselves */
-	args.state = g_atomic_pointer_get (&self->pv->state);
-	if (!args.state || !g_atomic_pointer_compare_and_exchange (&self->pv->state, args.state, NULL)) {
-		g_warning ("this enumerator is already running a next operation");
-		return NULL;
-	}
+	args.state = check_out_enumerator_state (self);
+	g_return_val_if_fail (args.state != NULL, NULL);
 
 	args.state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
 
 	/* Run the operation and steal away the results */
-	if (_gck_call_sync (NULL, perform_enumerate_next, complete_enumerate_next, &args, cancellable, error)) {
+	if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) {
 		results = args.state->results;
 		args.state->results = NULL;
 	}
@@ -750,8 +832,7 @@ gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *canc
 	args.state->want_objects = 0;
 
 	/* Put the state back */
-	if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, args.state))
-		g_assert_not_reached ();
+	check_in_enumerator_state (self, args.state);
 
 	return results;
 }
@@ -782,14 +863,11 @@ gck_enumerator_next_async (GckEnumerator *self, gint max_objects, GCancellable *
 	g_object_ref (self);
 
 	/* Remove the state and own it ourselves */
-	state = g_atomic_pointer_get (&self->pv->state);
-	if (!state || !g_atomic_pointer_compare_and_exchange (&self->pv->state, state, NULL)) {
-		g_warning ("this enumerator is already running a next operation");
-		return;
-	}
+	state = check_out_enumerator_state (self);
+	g_return_if_fail (state != NULL);
 
 	state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
-	args =  _gck_call_async_prep (NULL, self, perform_enumerate_next, complete_enumerate_next,
+	args =  _gck_call_async_prep (NULL, self, perform_enumerate_next, NULL,
 	                               sizeof (*args), free_enumerate_next);
 
 	args->state = state;
@@ -831,8 +909,7 @@ gck_enumerator_next_finish (GckEnumerator *self, GAsyncResult *result, GError **
 	}
 
 	/* Put the state back */
-	if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, state))
-		g_assert_not_reached ();
+	check_in_enumerator_state (self, state);
 
 	g_object_unref (self);
 
diff --git a/gck/gck-interaction.c b/gck/gck-interaction.c
new file mode 100644
index 0000000..33d5ad3
--- /dev/null
+++ b/gck/gck-interaction.c
@@ -0,0 +1,176 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-interaction.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2011 Collabora Ltd
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gck-private.h"
+
+#include <string.h>
+
+#define GCK_INTERACTION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_INTERACTION, GckInteraction))
+#define GCK_IS_INTERACTION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_INTERACTION))
+#define GCK_INTERACTION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_INTERACTION, GckInteractionClass))
+
+typedef struct _GckInteractionClass GckInteractionClass;
+
+struct _GckInteraction {
+	GTlsInteraction interaction;
+	GckModule *module;
+};
+
+struct _GckInteractionClass {
+	GTlsInteractionClass parent;
+};
+
+enum {
+	PROP_0,
+	PROP_MODULE
+};
+
+G_DEFINE_TYPE (GckInteraction, _gck_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+_gck_interaction_init (GckInteraction *self)
+{
+
+}
+
+static void
+_gck_interaction_get_property (GObject *obj,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+	GckInteraction *self = GCK_INTERACTION (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_value_set_object (value, self->module);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gck_interaction_set_property (GObject *obj,
+                             guint prop_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	GckInteraction *self = GCK_INTERACTION (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_return_if_fail (self->module == NULL);
+		self->module = g_value_dup_object (value);
+		g_return_if_fail (self->module != NULL);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gck_interaction_dispose (GObject *obj)
+{
+	GckInteraction *self = GCK_INTERACTION (obj);
+
+	g_clear_object (&self->module);
+
+	G_OBJECT_CLASS (_gck_interaction_parent_class)->dispose (obj);
+}
+
+static GTlsInteractionResult
+_gck_interaction_ask_password (GTlsInteraction *interaction,
+                               GTlsPassword *password,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	GckInteraction *self = GCK_INTERACTION (interaction);
+	gchar *value = NULL;
+	gboolean ret = FALSE;
+	GckSlot *token;
+	GckObject *key;
+
+	if (!self->module)
+		return G_TLS_INTERACTION_UNHANDLED;
+
+	token = gck_password_get_token (GCK_PASSWORD (password));
+	if (token != NULL) {
+		g_signal_emit_by_name (self->module, "authenticate-slot", token,
+		                       g_tls_password_get_description (password),
+		                       &value, &ret);
+		g_object_unref (token);
+
+	} else {
+		key = gck_password_get_key (GCK_PASSWORD (password));
+		g_return_val_if_fail (GCK_IS_OBJECT (key), G_TLS_INTERACTION_UNHANDLED);
+
+		g_signal_emit_by_name (self->module, "authenticate-object", key,
+		                       g_tls_password_get_description (password),
+		                       &value, &ret);
+	}
+
+	if (ret) {
+		g_tls_password_set_value_full (password, (guchar *)value, -1, g_free);
+		return G_TLS_INTERACTION_HANDLED;
+	} else {
+		return G_TLS_INTERACTION_UNHANDLED;
+	}
+}
+
+static void
+_gck_interaction_class_init (GckInteractionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+	object_class->get_property = _gck_interaction_get_property;
+	object_class->set_property = _gck_interaction_set_property;
+	object_class->dispose = _gck_interaction_dispose;
+
+	interaction_class->ask_password = _gck_interaction_ask_password;
+
+	g_object_class_install_property (object_class, PROP_MODULE,
+		g_param_spec_object ("module", "Module", "PKCS11 Module",
+		                     GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GTlsInteraction *
+_gck_interaction_new (gpointer token_or_key)
+{
+	GTlsInteraction *result;
+	GModule *module = NULL;
+
+	g_return_val_if_fail (GCK_IS_SLOT (token_or_key) ||
+	                      GCK_IS_OBJECT (token_or_key), NULL);
+
+	g_object_get (token_or_key, "module", &module, NULL);
+	result = g_object_new (GCK_TYPE_INTERACTION, "module", module, NULL);
+	g_object_unref (module);
+
+	return result;
+}
diff --git a/gck/gck-misc.c b/gck/gck-misc.c
index 595f619..47d444e 100644
--- a/gck/gck-misc.c
+++ b/gck/gck-misc.c
@@ -218,6 +218,21 @@ _gck_stringize_rv (CK_RV rv)
 	}
 }
 
+CK_RV
+_gck_rv_from_error (GError *error,
+                    CK_RV catch_all_code)
+{
+	g_return_val_if_fail (error != NULL, CKR_GENERAL_ERROR);
+
+	if (error->domain == GCK_ERROR)
+		return error->code;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return CKR_FUNCTION_CANCELED;
+
+	return catch_all_code;
+}
+
 /**
  * SECTION:gck-misc
  * @title: Miscellaneous Functions
diff --git a/gck/gck-module.c b/gck/gck-module.c
index 8806dd7..cdd9d45 100644
--- a/gck/gck-module.c
+++ b/gck/gck-module.c
@@ -106,76 +106,6 @@ G_DEFINE_TYPE (GckModule, gck_module, G_TYPE_OBJECT);
 static guint signals[LAST_SIGNAL] = { 0 };
 
 /* ----------------------------------------------------------------------------
- * INTERNAL
- */
-
-gboolean
-_gck_module_fire_authenticate_slot (GckModule *self, GckSlot *slot, gchar *label, gchar **password)
-{
-	GckTokenInfo *info;
-	gchar *allocated = NULL;
-	gboolean ret;
-
-	g_assert (GCK_IS_MODULE (self));
-
-	info = gck_slot_get_token_info (slot);
-	if (info != NULL) {
-
-		/*
-		 * We'll have tried to login at least once at this point,
-		 * with NULL password. This means that CKF_PROTECTED_AUTHENTICATION_PATH
-		 * tokens have had their chance and we don't need to prompt for it.
-		 */
-
-		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)
-			return FALSE;
-
-		if (label == NULL)
-			label = allocated = g_strdup (info->label);
-
-		gck_token_info_free (info);
-	}
-
-	g_signal_emit (self, signals[AUTHENTICATE_SLOT], 0, slot, label, password, &ret);
-	g_free (allocated);
-	return ret;
-}
-
-gboolean
-_gck_module_fire_authenticate_object (GckModule *self, GckObject *object,
-                                      gchar *label, gchar **password)
-{
-	GckTokenInfo *info;
-	GckSession *session;
-	GckSlot *slot;
-	gboolean ret;
-
-	g_assert (GCK_IS_MODULE (self));
-	g_assert (GCK_IS_OBJECT (object));
-	g_assert (password);
-
-	session = gck_object_get_session (object);
-	slot = gck_session_get_slot (session);
-	g_object_unref (session);
-
-	info = gck_slot_get_token_info (slot);
-	g_object_unref (slot);
-
-	if (info != NULL) {
-		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
-			gck_token_info_free (info);
-			*password = NULL;
-			return TRUE;
-		}
-
-		gck_token_info_free (info);
-	}
-
-	g_signal_emit (self, signals[AUTHENTICATE_OBJECT], 0, object, label, password, &ret);
-	return ret;
-}
-
-/* ----------------------------------------------------------------------------
  * OBJECT
  */
 
@@ -321,11 +251,9 @@ gck_module_class_init (GckModuleClass *klass)
 	 * @string: A displayable label which describes the object.
 	 * @password: A gchar** where a password should be returned.
 	 *
-	 * This signal is emitted when a password is needed to authenticate a PKCS&num;11
-	 * slot. If the module prompts for passwords itself, then this signal will
-	 * not be emitted.
+	 * Use gck_slot_set_interaction() instead of connecting to this signal.
 	 *
-	 * Returns: FALSE if the user cancelled, TRUE if we should proceed.
+	 * Deprecated: Since 3.4
 	 */
 	signals[AUTHENTICATE_SLOT] = g_signal_new ("authenticate-slot", GCK_TYPE_MODULE,
 			G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_slot),
@@ -339,11 +267,9 @@ gck_module_class_init (GckModuleClass *klass)
 	 * @label: A displayable label which describes the object.
 	 * @password: A gchar** where a password should be returned.
 	 *
-	 * This signal is emitted when a password is needed to authenticate a PKCS&num;11
-	 * object like a key. If the module prompts for passwords itself, then this signal will
-	 * not be emitted.
+	 * Use gck_slot_set_interaction() instead of connecting to this signal.
 	 *
-	 * Returns: FALSE if the user cancelled, TRUE if we should proceed.
+	 * Deprecated: Since 3.4
 	 */
 	signals[AUTHENTICATE_OBJECT] = g_signal_new ("authenticate-object", GCK_TYPE_MODULE,
 			G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_object),
diff --git a/gck/gck-modules.c b/gck/gck-modules.c
index 497d2ff..9a93103 100644
--- a/gck/gck-modules.c
+++ b/gck/gck-modules.c
@@ -341,7 +341,9 @@ gck_modules_tokens_for_uri (GList *modules,
  * g_object_unref(), or %NULL if no matching object was found.
  */
 GckObject*
-gck_modules_object_for_uri (GList *modules, const gchar *uri, guint session_options,
+gck_modules_object_for_uri (GList *modules,
+                            const gchar *uri,
+                            GckSessionOptions session_options,
                             GError **error)
 {
 	GckEnumerator *en;
@@ -377,7 +379,9 @@ gck_modules_object_for_uri (GList *modules, const gchar *uri, guint session_opti
  * was found.
  */
 GList*
-gck_modules_objects_for_uri (GList *modules, const gchar *uri, guint session_options,
+gck_modules_objects_for_uri (GList *modules,
+                             const gchar *uri,
+                             GckSessionOptions session_options,
                              GError **error)
 {
 	GckEnumerator *en;
@@ -411,7 +415,9 @@ gck_modules_objects_for_uri (GList *modules, const gchar *uri, guint session_opt
  * Returns: (transfer full): A new #GckEnumerator, or %NULL if an error occurs.
  */
 GckEnumerator*
-gck_modules_enumerate_uri (GList *modules, const gchar *uri, guint session_options,
+gck_modules_enumerate_uri (GList *modules,
+                           const gchar *uri,
+                           GckSessionOptions session_options,
                            GError **error)
 {
 	GckUriData *uri_data;
diff --git a/gck/gck-password.c b/gck/gck-password.c
new file mode 100644
index 0000000..56200ea
--- /dev/null
+++ b/gck/gck-password.c
@@ -0,0 +1,255 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-password.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2011 Collabora Ltd.
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+
+#include "egg/egg-timegm.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gck-password
+ * @title: GckPassword
+ * @short_description: Represents a password hich is requested of the user
+ *
+ * This is used in conjuction with GTlsInteraction. #GckPassword is a
+ * GTlsPassword which contains additional information about which PKCS\#11
+ * token or key the password is being requested for.
+ */
+
+/**
+ * GckPassword:
+ * @parent: parent object
+ *
+ * A #GTlsPasswordClass that contains information about the PKCS\#11 token
+ * or key the password is being requested for.
+ */
+
+/**
+ * GckPasswordClass:
+ * @parent: parent class
+ *
+ * The class for #GTlsPassword.
+ */
+enum {
+	PROP_0,
+	PROP_MODULE,
+	PROP_TOKEN,
+	PROP_KEY
+};
+
+struct _GckPasswordPrivate {
+	gboolean for_token;
+	gpointer token_or_key;
+};
+
+G_DEFINE_TYPE (GckPassword, gck_password, G_TYPE_TLS_PASSWORD);
+
+static void
+gck_password_init (GckPassword *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_PASSWORD, GckPasswordPrivate);
+}
+
+static void
+gck_password_constructed (GObject *obj)
+{
+	GckPassword *self = GCK_PASSWORD (obj);
+
+	G_OBJECT_CLASS (gck_password_parent_class)->constructed (obj);
+
+	g_return_if_fail (GCK_IS_SLOT (self->pv->token_or_key) ||
+	                  GCK_IS_OBJECT (self->pv->token_or_key));
+}
+
+static void
+gck_password_get_property (GObject *obj,
+                           guint prop_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	GckPassword *self = GCK_PASSWORD (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_value_take_object (value, gck_password_get_module (self));
+		break;
+	case PROP_TOKEN:
+		g_value_take_object (value, gck_password_get_token (self));
+		break;
+	case PROP_KEY:
+		g_value_take_object (value, gck_password_get_key (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_password_set_property (GObject *obj,
+                           guint prop_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+	GckPassword *self = GCK_PASSWORD (obj);
+	gpointer object;
+
+	/* All writes to data members below, happen only during construct phase */
+
+	switch (prop_id) {
+	case PROP_TOKEN:
+		object = g_value_dup_object (value);
+		if (object != NULL) {
+			g_assert (self->pv->token_or_key == NULL);
+			self->pv->token_or_key = object;
+			self->pv->for_token = TRUE;
+		}
+		break;
+	case PROP_KEY:
+		object = g_value_dup_object (value);
+		if (object != NULL) {
+			g_assert (self->pv->token_or_key == NULL);
+			self->pv->token_or_key = object;
+			self->pv->for_token = FALSE;
+		}
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_password_finalize (GObject *obj)
+{
+	GckPassword *self = GCK_PASSWORD (obj);
+
+	g_clear_object (&self->pv->token_or_key);
+
+	G_OBJECT_CLASS (gck_password_parent_class)->finalize (obj);
+}
+
+static void
+gck_password_class_init (GckPasswordClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+
+	gobject_class->constructed = gck_password_constructed;
+	gobject_class->get_property = gck_password_get_property;
+	gobject_class->set_property = gck_password_set_property;
+	gobject_class->finalize = gck_password_finalize;
+
+	/**
+	 * GckPassword:module:
+	 *
+	 * The PKCS\#11 module that is requesting the password
+	 */
+	g_object_class_install_property (gobject_class, PROP_MODULE,
+		g_param_spec_object ("module", "Module", "PKCS11 Module",
+		                     GCK_TYPE_MODULE, G_PARAM_READABLE));
+
+	/**
+	 * GckPassword:token:
+	 *
+	 * The PKCS\#11 token the password is for, if this is set then
+	 * the GckPassword:object property will be %NULL
+	 */
+	g_object_class_install_property (gobject_class, PROP_TOKEN,
+		g_param_spec_object ("token", "Token", "PKCS11 Token",
+		                     GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckPassword:key:
+	 *
+	 * The PKCS\#11 key that the password is being requested for. If this
+	 * is set then the GckPassword:token property will be %NULL
+	 */
+	g_object_class_install_property (gobject_class, PROP_KEY,
+		g_param_spec_object ("key", "Object", "PKCS11 Key Object",
+		                     GCK_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (gobject_class, sizeof (GckPasswordPrivate));
+}
+
+/**
+ * gck_password_get_module:
+ * @self: the password object
+ *
+ * Get the PKCS\#11 module that is requesting the password.
+ *
+ * Returns: (transfer full): the module that is requesting the password, which
+ *          must be unreferenced after use
+ */
+GckModule *
+gck_password_get_module (GckPassword *self)
+{
+	g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL);
+	if (self->pv->for_token)
+		return gck_slot_get_module (self->pv->token_or_key);
+	else
+		return gck_object_get_module (self->pv->token_or_key);
+}
+
+/**
+ * gck_password_get_token:
+ * @self: the password object
+ *
+ * If the password request is to unlock a PKCS\#11 token, then this is the
+ * slot containing that token.
+ *
+ * Returns: (transfer full): the slot that contains the token, or %NULL if not
+ *          being requested for a token; must be unreferenced after use
+ */
+GckSlot *
+gck_password_get_token (GckPassword *self)
+{
+	g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL);
+	if (!self->pv->for_token)
+		return NULL;
+	g_return_val_if_fail (GCK_IS_SLOT (self->pv->token_or_key), NULL);
+	return g_object_ref (self->pv->token_or_key);
+}
+
+/**
+ * gck_password_get_key:
+ * @self: the password object
+ *
+ * If the password request is to unlock a PKCS\#11 key, then this is the
+ * the object representing that key.
+ *
+ * Returns: (transfer full): the password is for this key, or %NULL if not
+ *          being requested for a key; must be unreferenced after use
+ */
+GckObject *
+gck_password_get_key (GckPassword *self)
+{
+	g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL);
+	if (self->pv->for_token)
+		return NULL;
+	g_return_val_if_fail (GCK_IS_OBJECT (self->pv->token_or_key), NULL);
+	return g_object_ref (self->pv->token_or_key);
+}
diff --git a/gck/gck-private.h b/gck/gck-private.h
index 64739a2..c2919cf 100644
--- a/gck/gck-private.h
+++ b/gck/gck-private.h
@@ -62,22 +62,15 @@ gboolean            _gck_ulong_equal                       (gconstpointer v1,
 
 const gchar *       _gck_stringize_rv                      (CK_RV rv);
 
+CK_RV               _gck_rv_from_error                     (GError *error,
+                                                            CK_RV catch_all_code);
+
 /* ----------------------------------------------------------------------------
  * MODULE
  */
 
 GckModule*          _gck_module_new_initialized            (CK_FUNCTION_LIST_PTR funcs);
 
-gboolean            _gck_module_fire_authenticate_slot     (GckModule *module,
-                                                             GckSlot *slot,
-                                                             gchar *label,
-                                                             gchar **password);
-
-gboolean            _gck_module_fire_authenticate_object   (GckModule *module,
-                                                             GckObject *object,
-                                                             gchar *label,
-                                                             gchar **password);
-
 GckModuleInfo*      _gck_module_info_from_pkcs11            (CK_INFO_PTR info);
 
 void                _gck_module_info_to_pkcs11              (GckModuleInfo* module_info,
@@ -91,7 +84,7 @@ gboolean            _gck_module_info_match                  (GckModuleInfo *matc
  */
 
 GckEnumerator*      _gck_enumerator_new                     (GList *modules,
-                                                             guint session_options,
+                                                             GckSessionOptions session_options,
                                                              GckUriData *uri_data);
 
 /* ----------------------------------------------------------------------------
@@ -106,6 +99,39 @@ void               _gck_token_info_to_pkcs11                (GckTokenInfo *token
 gboolean           _gck_token_info_match                    (GckTokenInfo *match,
                                                              GckTokenInfo *info);
 
+CK_RV              _gck_session_authenticate_token          (CK_FUNCTION_LIST_PTR funcs,
+                                                             CK_SESSION_HANDLE session,
+                                                             GckSlot *token,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable);
+
+CK_RV              _gck_session_authenticate_key            (CK_FUNCTION_LIST_PTR funcs,
+                                                             CK_SESSION_HANDLE session,
+                                                             GckObject *key,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable);
+
+/* ----------------------------------------------------------------------------
+ * PASSWORD
+ */
+
+void               _gck_password_update                     (GckPassword *self,
+                                                             gboolean request_retry);
+
+/* ----------------------------------------------------------------------------
+ * INTERACTION
+ */
+
+#define GCK_TYPE_INTERACTION    (_gck_interaction_get_type ())
+#define GCK_INTERACTION(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_INTERACTION, GckInteraction))
+#define GCK_IS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_INTERACTION))
+
+typedef struct _GckInteraction GckInteraction;
+
+GType               _gck_interaction_get_type               (void) G_GNUC_CONST;
+
+GTlsInteraction *   _gck_interaction_new                    (gpointer token_or_key);
+
 /* ----------------------------------------------------------------------------
  * CALL
  */
diff --git a/gck/gck-session.c b/gck/gck-session.c
index eba1de6..c984eb6 100644
--- a/gck/gck-session.c
+++ b/gck/gck-session.c
@@ -77,6 +77,7 @@ enum {
 	PROP_0,
 	PROP_MODULE,
 	PROP_HANDLE,
+	PROP_INTERACTION,
 	PROP_SLOT,
 	PROP_OPTIONS,
 };
@@ -85,7 +86,8 @@ struct _GckSessionPrivate {
 	GckSlot *slot;
 	GckModule *module;
 	CK_SESSION_HANDLE handle;
-	guint options;
+	GTlsInteraction *interaction;
+	GckSessionOptions options;
 
 	/* Modified atomically */
 	gint discarded;
@@ -148,6 +150,12 @@ gck_session_get_property (GObject *obj, guint prop_id, GValue *value,
 	case PROP_OPTIONS:
 		g_value_set_uint (value, gck_session_get_options (self));
 		break;
+	case PROP_INTERACTION:
+		g_value_take_object (value, self->pv->interaction);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
 	}
 }
 
@@ -169,6 +177,10 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
 		g_return_if_fail (!self->pv->handle);
 		self->pv->handle = g_value_get_ulong (value);
 		break;
+	case PROP_INTERACTION:
+		g_return_if_fail (self->pv->interaction == NULL);
+		self->pv->interaction = g_value_dup_object (value);
+		break;
 	case PROP_SLOT:
 		g_return_if_fail (!self->pv->slot);
 		self->pv->slot = g_value_dup_object (value);
@@ -176,7 +188,10 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
 		break;
 	case PROP_OPTIONS:
 		g_return_if_fail (!self->pv->options);
-		self->pv->options = g_value_get_uint (value);
+		self->pv->options = g_value_get_flags (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
 	}
 }
@@ -210,13 +225,9 @@ gck_session_finalize (GObject *obj)
 
 	g_assert (g_atomic_int_get (&self->pv->discarded) != 0);
 
-	if (self->pv->slot)
-		g_object_unref (self->pv->slot);
-	self->pv->slot = NULL;
-
-	if (self->pv->module)
-		g_object_unref (self->pv->module);
-	self->pv->module = NULL;
+	g_clear_object (&self->pv->interaction);
+	g_clear_object (&self->pv->slot);
+	g_clear_object (&self->pv->module);
 
 	G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj);
 }
@@ -267,8 +278,20 @@ gck_session_class_init (GckSessionClass *klass)
 	 * The options this session was opened with.
 	 */
 	g_object_class_install_property (gobject_class, PROP_OPTIONS,
-		g_param_spec_uint ("options", "Session Options", "Session Options",
-		                   0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		g_param_spec_flags ("options", "Session Options", "Session Options",
+		                    GCK_TYPE_SESSION_OPTIONS, GCK_SESSION_READ_ONLY,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSession:interaction:
+	 *
+	 * Interaction object used to ask the user for pins when opening
+	 * sessions. Used if the session_options of the enumerator have
+	 * %GCK_SESSION_LOGIN_USER
+	 */
+	g_object_class_install_property (gobject_class, PROP_INTERACTION,
+		g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins",
+		                     G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
 
 	/**
 	 * GckSession::discard-handle:
@@ -364,21 +387,27 @@ gck_session_info_free (GckSessionInfo *session_info)
 GckSession *
 gck_session_from_handle (GckSlot *slot,
                          gulong session_handle,
-                         guint options)
+                         GckSessionOptions options)
 {
+	GTlsInteraction *interaction;
 	GckModule *module;
 	GckSession *session;
 
 	g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
 
 	module = gck_slot_get_module (slot);
+	interaction = gck_slot_get_interaction (slot);
+
 	session = g_object_new (GCK_TYPE_SESSION,
 	                        "module", module,
+	                        "interaction", interaction,
 	                        "handle", session_handle,
 	                        "slot", slot,
 	                        "options", options,
 	                        NULL);
+
 	g_object_unref (module);
+	g_clear_object (&interaction);
 
 	return session;
 }
@@ -518,13 +547,33 @@ gck_session_get_state (GckSession *self)
  *
  * Return value: The session options.
  **/
-guint
+GckSessionOptions
 gck_session_get_options (GckSession *self)
 {
 	g_return_val_if_fail (GCK_IS_SESSION (self), 0);
 	return self->pv->options;
 }
 
+/**
+ * gck_session_get_interaction:
+ * @self: the session
+ *
+ * Get the interaction object set on this session, which is used to prompt
+ * for pins and the like.
+ *
+ * Returns: (transfer full) (allow-none): the interaction object, or %NULL
+ */
+GTlsInteraction *
+gck_session_get_interaction (GckSession *self)
+{
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+
+	if (self->pv->interaction)
+		return g_object_ref (self->pv->interaction);
+
+	return NULL;
+}
+
 /* ---------------------------------------------------------------------------------------------
  * INIT PIN
  */
@@ -1913,191 +1962,20 @@ gck_session_derive_key_finish (GckSession *self, GAsyncResult *result, GError **
 }
 
 /* --------------------------------------------------------------------------------------------------
- * AUTHENTICATE
- */
-
-typedef enum _AuthenticateState {
-	AUTHENTICATE_NONE,
-	AUTHENTICATE_CAN,
-	AUTHENTICATE_WANT,
-	AUTHENTICATE_PERFORM
-} AuthenticateState;
-
-typedef struct _Authenticate {
-	AuthenticateState state;
-	gboolean protected_auth;
-	GckModule *module;
-	GckObject *object;
-	gchar *label;
-	gchar *password;
-} Authenticate;
-
-static CK_RV
-authenticate_perform (Authenticate *args, GckArguments *base)
-{
-	CK_ATTRIBUTE attributes[2];
-	CK_OBJECT_HANDLE handle;
-	CK_ULONG pin_len;
-	CK_BBOOL bvalue;
-	CK_RV rv;
-
-	g_assert (args);
-	g_assert (base);
-
-	switch (args->state) {
-
-	/*
-	 * Cannot authenticate for whatever reason, perhaps not
-	 * enabled, or failed incomprehensibly etc.
-	 *
-	 */
-	case AUTHENTICATE_NONE:
-		return CKR_OK;
-
-	/*
-	 * Can authenticate but haven't seen if we should, yet
-	 * check out the object in question.
-	 */
-	case AUTHENTICATE_CAN:
-
-		handle = gck_object_get_handle (args->object);
-
-		attributes[0].type = CKA_LABEL;
-		attributes[0].pValue = NULL;
-		attributes[0].ulValueLen = 0;
-		attributes[1].type = CKA_ALWAYS_AUTHENTICATE;
-		attributes[1].pValue = &bvalue;
-		attributes[1].ulValueLen = sizeof (bvalue);
-
-		rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2);
-		if (rv == CKR_ATTRIBUTE_TYPE_INVALID)
-			bvalue = CK_FALSE;
-		else if (rv != CKR_OK)
-			return rv;
-
-		/* No authentication needed, on this object */
-		if (bvalue != CK_TRUE) {
-			args->state = AUTHENTICATE_NONE;
-			return CKR_OK;
-		}
-
-		/* Protected authentication path, just go to perform */
-		if (args->protected_auth) {
-			args->state = AUTHENTICATE_PERFORM;
-			return authenticate_perform (args, base);
-		}
-
-		/* Get the label for a prompt */
-		g_assert (!args->label);
-		if (attributes[0].ulValueLen) {
-			attributes[0].pValue = g_malloc0 (attributes[0].ulValueLen + 1);
-			rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2);
-			if (rv == CKR_OK) {
-				g_assert (!args->label);
-				args->label = attributes[0].pValue;
-				args->label[attributes[0].ulValueLen] = 0;
-			} else {
-				g_free (attributes[0].pValue);
-			}
-		}
-
-		/* Need a password */
-		args->state = AUTHENTICATE_WANT;
-		return CKR_USER_NOT_LOGGED_IN;
-
-	/*
-	 * This state should be handled in verify_authenticate.
-	 */
-	case AUTHENTICATE_WANT:
-		g_assert (FALSE);
-		return CKR_GENERAL_ERROR;
-
-	/*
-	 * Do the actual login authentication.
-	 */
-	case AUTHENTICATE_PERFORM:
-		pin_len = args->password ? strlen (args->password) : 0;
-		rv = (base->pkcs11->C_Login) (base->handle, CKU_CONTEXT_SPECIFIC,
-		                              (CK_UTF8CHAR_PTR)args->password, pin_len);
-		if (rv == CKR_PIN_INCORRECT && !args->protected_auth)
-			args->state = AUTHENTICATE_WANT;
-		else
-			args->state = AUTHENTICATE_NONE;
-		return rv;
-
-	default:
-		g_assert_not_reached ();
-		return CKR_GENERAL_ERROR;
-	}
-}
-
-static gboolean
-authenticate_complete (Authenticate *auth, GckArguments *base, CK_RV result)
-{
-	g_assert (auth);
-	g_assert (base);
-
-	/* We're done here if not in this state */
-	if (auth->state == AUTHENTICATE_WANT) {
-
-		g_assert (GCK_IS_MODULE (auth->module));
-		g_assert (GCK_IS_OBJECT (auth->object));
-
-		g_free (auth->password);
-		auth->password = NULL;
-
-		if (_gck_module_fire_authenticate_object (auth->module, auth->object, auth->label, &auth->password)) {
-			auth->state = AUTHENTICATE_PERFORM;
-			return FALSE; /* Want to continue processing this call */
-		}
-	}
-
-	/* Free up various memory */
-	if (auth->module)
-		g_object_unref (auth->module);
-	if (auth->object)
-		g_object_unref (auth->object);
-	g_free (auth->label);
-	g_free (auth->password);
-
-	/* The call is complete */
-	return TRUE;
-}
-
-static void
-authenticate_init (Authenticate *auth, GckSlot *slot, GckObject *object, guint options)
-{
-	GckModule *module;
-
-	g_assert (GCK_IS_SLOT (slot));
-	g_assert (GCK_IS_OBJECT (object));
-
-	module = gck_slot_get_module (slot);
-	if ((options & GCK_SESSION_AUTHENTICATE) == GCK_SESSION_AUTHENTICATE) {
-		auth->state = AUTHENTICATE_CAN;
-		auth->protected_auth = gck_slot_has_flags (slot, CKF_PROTECTED_AUTHENTICATION_PATH);
-		auth->module = module;
-		auth->object = g_object_ref (object);
-	} else {
-		auth->state = AUTHENTICATE_NONE;
-		g_object_unref (module);
-	}
-}
-
-/* --------------------------------------------------------------------------------------------------
  * COMMON CRYPTO ROUTINES
  */
 
 typedef struct _Crypt {
 	GckArguments base;
 
-	/* Authenticator */
-	Authenticate auth;
-
 	/* Functions to call */
 	CK_C_EncryptInit init_func;
 	CK_C_Encrypt complete_func;
 
+	/* Interaction */
+	GckObject *key_object;
+	GTlsInteraction *interaction;
+
 	/* Input */
 	CK_OBJECT_HANDLE key;
 	GckMechanism mechanism;
@@ -2113,6 +1991,7 @@ typedef struct _Crypt {
 static CK_RV
 perform_crypt (Crypt *args)
 {
+	GTlsInteraction *interaction;
 	CK_RV rv;
 
 	g_assert (args);
@@ -2126,7 +2005,17 @@ perform_crypt (Crypt *args)
 	if (rv != CKR_OK)
 		return rv;
 
-	rv = authenticate_perform (&args->auth, &args->base);
+	/* Compatibility, hook into GckModule signals if no interaction set */
+	if (args->interaction)
+		interaction = g_object_ref (args->interaction);
+	else
+		interaction = _gck_interaction_new (args->key_object);
+
+	rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle,
+	                                    args->key_object, interaction, NULL);
+
+	g_object_unref (interaction);
+
 	if (rv != CKR_OK)
 		return rv;
 
@@ -2140,19 +2029,12 @@ perform_crypt (Crypt *args)
 	return (args->complete_func) (args->base.handle, args->input, args->n_input, args->result, &args->n_result);
 }
 
-static gboolean
-complete_crypt (Crypt *args, CK_RV result)
-{
-	if (!authenticate_complete (&args->auth, &args->base, result))
-		return FALSE;
-
-	/* Call is complete */
-	return TRUE;
-}
-
 static void
 free_crypt (Crypt *args)
 {
+	g_clear_object (&args->interaction);
+	g_clear_object (&args->key_object);
+
 	g_free (args->input);
 	g_free (args->result);
 	g_free (args);
@@ -2164,7 +2046,6 @@ crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guc
             CK_C_EncryptInit init_func, CK_C_Encrypt complete_func)
 {
 	Crypt args;
-	GckSlot *slot;
 
 	g_return_val_if_fail (GCK_IS_OBJECT (key), NULL);
 	g_return_val_if_fail (mechanism, NULL);
@@ -2185,11 +2066,10 @@ crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guc
 	args.init_func = init_func;
 	args.complete_func = complete_func;
 
-	slot = gck_session_get_slot (self);
-	authenticate_init (&args.auth, slot, key, self->pv->options);
-	g_object_unref (slot);
+	args.key_object = key;
+	args.interaction = self->pv->interaction;
 
-	if (!_gck_call_sync (self, perform_crypt, complete_crypt, &args, cancellable, error)) {
+	if (!_gck_call_sync (self, perform_crypt, NULL, &args, cancellable, error)) {
 		g_free (args.result);
 		return NULL;
 	}
@@ -2203,8 +2083,7 @@ crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const gu
              gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data,
              CK_C_EncryptInit init_func, CK_C_Encrypt complete_func)
 {
-	Crypt *args = _gck_call_async_prep (self, self, perform_crypt, complete_crypt, sizeof (*args), free_crypt);
-	GckSlot *slot;
+	Crypt *args = _gck_call_async_prep (self, self, perform_crypt, NULL, sizeof (*args), free_crypt);
 
 	g_return_if_fail (GCK_IS_OBJECT (key));
 	g_return_if_fail (mechanism);
@@ -2223,9 +2102,8 @@ crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const gu
 	args->init_func = init_func;
 	args->complete_func = complete_func;
 
-	slot = gck_session_get_slot (self);
-	authenticate_init (&args->auth, slot, key, self->pv->options);
-	g_object_unref (slot);
+	args->key_object = g_object_ref (key);
+	args->interaction = gck_session_get_interaction (self);
 
 	_gck_call_async_ready_go (args, cancellable, callback, user_data);
 }
@@ -2614,8 +2492,9 @@ gck_session_sign_finish (GckSession *self, GAsyncResult *result,
 typedef struct _Verify {
 	GckArguments base;
 
-	/* Authenticator */
-	Authenticate auth;
+	/* Interaction */
+	GckObject *key_object;
+	GTlsInteraction *interaction;
 
 	/* Input */
 	CK_OBJECT_HANDLE key;
@@ -2630,6 +2509,7 @@ typedef struct _Verify {
 static CK_RV
 perform_verify (Verify *args)
 {
+	GTlsInteraction *interaction;
 	CK_RV rv;
 
 	/* Initialize the crypt operation */
@@ -2637,7 +2517,18 @@ perform_verify (Verify *args)
 	if (rv != CKR_OK)
 		return rv;
 
-	rv = authenticate_perform (&args->auth, &args->base);
+	/* Compatibility, hook into GckModule signals if no interaction set */
+	if (args->interaction)
+		interaction = g_object_ref (args->interaction);
+	else
+		interaction = _gck_interaction_new (args->key_object);
+
+
+	rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle,
+	                                    args->key_object, interaction, NULL);
+
+	g_object_unref (interaction);
+
 	if (rv != CKR_OK)
 		return rv;
 
@@ -2646,19 +2537,12 @@ perform_verify (Verify *args)
 	                                      args->signature, args->n_signature);
 }
 
-static gboolean
-complete_verify (Verify *args, CK_RV result)
-{
-	if (!authenticate_complete (&args->auth, &args->base, result))
-		return FALSE;
-
-	/* Call is complete */
-	return TRUE;
-}
-
 static void
 free_verify (Verify *args)
 {
+	g_clear_object (&args->interaction);
+	g_clear_object (&args->key_object);
+
 	g_free (args->input);
 	g_free (args->signature);
 	g_free (args);
@@ -2713,7 +2597,6 @@ gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechani
                           gsize n_signature, GCancellable *cancellable, GError **error)
 {
 	Verify args;
-	GckSlot *slot;
 
 	g_return_val_if_fail (GCK_IS_OBJECT (key), FALSE);
 	g_return_val_if_fail (mechanism, FALSE);
@@ -2731,11 +2614,10 @@ gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechani
 	args.signature = (guchar*)signature;
 	args.n_signature = n_signature;
 
-	slot = gck_session_get_slot (self);
-	authenticate_init (&args.auth, slot, key, self->pv->options);
-	g_object_unref (slot);
+	args.key_object = key;
+	args.interaction = self->pv->interaction;
 
-	return _gck_call_sync (self, perform_verify, complete_verify, &args, cancellable, error);
+	return _gck_call_sync (self, perform_verify, NULL, &args, cancellable, error);
 }
 
 /**
@@ -2760,8 +2642,7 @@ gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechan
                            gsize n_signature, GCancellable *cancellable,
                            GAsyncReadyCallback callback, gpointer user_data)
 {
-	Verify *args = _gck_call_async_prep (self, self, perform_verify, complete_verify, sizeof (*args), free_verify);
-	GckSlot *slot;
+	Verify *args = _gck_call_async_prep (self, self, perform_verify, NULL, sizeof (*args), free_verify);
 
 	g_return_if_fail (GCK_IS_OBJECT (key));
 	g_return_if_fail (mechanism);
@@ -2777,9 +2658,8 @@ gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechan
 	args->signature = signature && n_signature ? g_memdup (signature, n_signature) : NULL;
 	args->n_signature = n_signature;
 
-	slot = gck_session_get_slot (self);
-	authenticate_init (&args->auth, slot, key, self->pv->options);
-	g_object_unref (slot);
+	args->key_object = g_object_ref (key);
+	args->interaction = gck_session_get_interaction (self);
 
 	_gck_call_async_ready_go (args, cancellable, callback, user_data);
 }
@@ -2799,3 +2679,302 @@ gck_session_verify_finish (GckSession *self, GAsyncResult *result, GError **erro
 {
 	return _gck_call_basic_finish (result, error);
 }
+
+static void
+update_password_for_token (GTlsPassword *password,
+                           CK_TOKEN_INFO *token_info,
+                           gboolean request_retry)
+{
+	GTlsPasswordFlags flags;
+	gchar *label;
+
+	label = gck_string_from_chars (token_info->label, sizeof (token_info->label));
+	g_tls_password_set_description (password, label);
+	g_free (label);
+
+	flags = 0;
+	if (request_retry)
+		flags |= G_TLS_PASSWORD_RETRY;
+	if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW)
+		flags |= G_TLS_PASSWORD_MANY_TRIES;
+	if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY)
+		flags |= G_TLS_PASSWORD_FINAL_TRY;
+	g_tls_password_set_flags (password, flags);
+}
+
+CK_RV
+_gck_session_authenticate_token (CK_FUNCTION_LIST_PTR funcs,
+                                 CK_SESSION_HANDLE session,
+                                 GckSlot *token,
+                                 GTlsInteraction *interaction,
+                                 GCancellable *cancellable)
+{
+	CK_SESSION_INFO session_info;
+	GTlsPassword *password = NULL;
+	CK_TOKEN_INFO token_info;
+	GTlsInteractionResult res;
+	gboolean request_retry;
+	CK_SLOT_ID slot_id;
+	CK_BYTE_PTR pin;
+	CK_ULONG n_pin;
+	CK_RV rv = CKR_OK;
+	GError *error = NULL;
+
+	g_assert (funcs != NULL);
+	g_assert (GCK_IS_SLOT (token));
+
+	slot_id = gck_slot_get_handle (token);
+	request_retry = FALSE;
+
+	do {
+		if (g_cancellable_is_cancelled (cancellable)) {
+			rv = CKR_FUNCTION_CANCELED;
+			break;
+		}
+
+		rv = (funcs->C_GetTokenInfo) (slot_id, &token_info);
+		if (rv != CKR_OK) {
+			g_warning ("couldn't get token info when logging in: %s",
+			           gck_message_from_rv (rv));
+			break;
+		}
+
+		/* No login necessary? */
+		if ((token_info.flags & CKF_LOGIN_REQUIRED) == 0) {
+			_gck_debug ("no login required for token, skipping login");
+			rv = CKR_OK;
+			break;
+		}
+
+		/* Next check if session is logged in? */
+		rv = (funcs->C_GetSessionInfo) (session, &session_info);
+		if (rv != CKR_OK) {
+			g_warning ("couldn't get session info when logging in: %s",
+			           gck_message_from_rv (rv));
+			break;
+		}
+
+		/* Already logged in? */
+		if (session_info.state == CKS_RW_USER_FUNCTIONS ||
+		    session_info.state == CKS_RO_USER_FUNCTIONS ||
+		    session_info.state == CKS_RW_SO_FUNCTIONS) {
+			_gck_debug ("already logged in, skipping login");
+			rv = CKR_OK;
+			break;
+		}
+
+		if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+			_gck_debug ("trying to log into session: protected authentication path, no password");
+
+			/* No password passed for PAP */
+			pin = NULL;
+			n_pin = 0;
+
+
+		/* Not protected auth path */
+		} else {
+			_gck_debug ("trying to log into session: want password %s",
+			            request_retry ? "login was incorrect" : "");
+
+			if (password == NULL)
+				password = g_object_new (GCK_TYPE_PASSWORD, "token", token, NULL);
+
+			update_password_for_token (password, &token_info, request_retry);
+
+			if (interaction == NULL)
+				res = G_TLS_INTERACTION_UNHANDLED;
+
+			else
+				res = g_tls_interaction_invoke_ask_password (interaction,
+				                                             G_TLS_PASSWORD (password),
+				                                             NULL, &error);
+
+			if (res == G_TLS_INTERACTION_FAILED) {
+				g_message ("interaction couldn't ask password: %s", error->message);
+				rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN);
+				g_clear_error (&error);
+				break;
+
+			} else if (res == G_TLS_INTERACTION_UNHANDLED) {
+				g_message ("couldn't authenticate: no interaction handler");
+				rv = CKR_USER_NOT_LOGGED_IN;
+				break;
+			}
+
+			pin = (CK_BYTE_PTR)g_tls_password_get_value (password, &n_pin);
+		}
+
+		/* Try to log in */
+		rv = (funcs->C_Login) (session, CKU_USER, (CK_BYTE_PTR)pin, n_pin);
+
+		/* Only one C_Login call if protected auth path */
+		if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+			break;
+
+		request_retry = TRUE;
+	} while (rv == CKR_PIN_INCORRECT);
+
+	g_clear_object (&password);
+
+	return rv;
+}
+
+static void
+update_password_for_key (GTlsPassword *password,
+                         CK_TOKEN_INFO *token_info,
+                         gboolean request_retry)
+{
+	GTlsPasswordFlags flags;
+
+	flags = 0;
+	if (request_retry)
+		flags |= G_TLS_PASSWORD_RETRY;
+	if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW)
+		flags |= G_TLS_PASSWORD_MANY_TRIES;
+	if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY)
+		flags |= G_TLS_PASSWORD_FINAL_TRY;
+	g_tls_password_set_flags (password, flags);
+}
+
+CK_RV
+_gck_session_authenticate_key (CK_FUNCTION_LIST_PTR funcs,
+                               CK_SESSION_HANDLE session,
+                               GckObject *key,
+                               GTlsInteraction *interaction,
+                               GCancellable *cancellable)
+{
+	CK_ATTRIBUTE attrs[2];
+	CK_SESSION_INFO session_info;
+	CK_TOKEN_INFO token_info;
+	GTlsPassword *password = NULL;
+	CK_OBJECT_HANDLE handle;
+	GTlsInteractionResult res;
+	gboolean request_retry;
+	GError *error = NULL;
+	CK_BYTE_PTR pin;
+	gsize pin_len;
+	CK_BBOOL bvalue;
+	gboolean got_label;
+	CK_RV rv;
+
+	g_assert (funcs != NULL);
+
+	handle = gck_object_get_handle (key);
+
+	attrs[0].type = CKA_LABEL;
+	attrs[0].pValue = NULL;
+	attrs[0].ulValueLen = 0;
+	attrs[1].type = CKA_ALWAYS_AUTHENTICATE;
+	attrs[1].pValue = &bvalue;
+	attrs[1].ulValueLen = sizeof (bvalue);
+
+	rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 2);
+	if (rv == CKR_ATTRIBUTE_TYPE_INVALID) {
+		bvalue = CK_FALSE;
+
+	} else if (rv != CKR_OK) {
+		g_message ("couldn't check whether key requires authentication, assuming it doesn't: %s",
+		           gck_message_from_rv (rv));
+		return CKR_OK;
+	}
+
+	/* No authentication needed, on this object */
+	if (bvalue != CK_TRUE) {
+		_gck_debug ("key does not require authentication");
+		return CKR_OK;
+	}
+
+	got_label = FALSE;
+	request_retry = FALSE;
+
+	do {
+		if (g_cancellable_is_cancelled (cancellable)) {
+			rv = CKR_FUNCTION_CANCELED;
+			break;
+		}
+
+		rv = (funcs->C_GetSessionInfo) (session, &session_info);
+		if (rv != CKR_OK) {
+			g_warning ("couldn't get session info when authenticating key: %s",
+			           gck_message_from_rv (rv));
+			return rv;
+		}
+
+		rv = (funcs->C_GetTokenInfo) (session_info.slotID, &token_info);
+		if (rv != CKR_OK) {
+			g_warning ("couldn't get token info when authenticating key: %s",
+			           gck_message_from_rv (rv));
+			return rv;
+		}
+
+		/* Protected authentication path, just use NULL passwords */
+		if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+
+			password = NULL;
+			pin = NULL;
+			pin_len = 0;
+
+		/* Need to prompt for a password */
+		} else {
+			_gck_debug ("trying to log into session: want password %s",
+			            request_retry ? "login was incorrect" : "");
+
+			if (password == NULL)
+				password = g_object_new (GCK_TYPE_PASSWORD, "key", key, NULL);
+
+			/* Set the password */
+			update_password_for_key (password, &token_info, request_retry);
+
+			/* Set the label properly */
+			if (!got_label) {
+				if (attrs[0].ulValueLen && attrs[0].ulValueLen != GCK_INVALID) {
+					attrs[0].pValue = g_malloc0 (attrs[0].ulValueLen + 1);
+					rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 1);
+					if (rv == CKR_OK) {
+						((gchar *)attrs[0].pValue)[attrs[0].ulValueLen] = 0;
+						g_tls_password_set_description (password, attrs[0].pValue);
+					}
+					g_free (attrs[0].pValue);
+					attrs[0].pValue = NULL;
+				}
+
+				got_label = TRUE;
+			}
+
+			if (interaction == NULL)
+				res = G_TLS_INTERACTION_UNHANDLED;
+
+			else
+				res = g_tls_interaction_invoke_ask_password (interaction,
+				                                             G_TLS_PASSWORD (password),
+				                                             NULL, &error);
+
+			if (res == G_TLS_INTERACTION_FAILED) {
+				g_message ("interaction couldn't ask password: %s", error->message);
+				rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN);
+				g_clear_error (&error);
+				break;
+
+			} else if (res == G_TLS_INTERACTION_UNHANDLED) {
+				g_message ("couldn't authenticate: no interaction handler");
+				rv = CKR_USER_NOT_LOGGED_IN;
+				break;
+			}
+
+			pin = (CK_BYTE_PTR)g_tls_password_get_value (G_TLS_PASSWORD (password), &pin_len);
+		}
+
+		/* Try to log in */
+		rv = (funcs->C_Login) (session, CKU_CONTEXT_SPECIFIC, pin, pin_len);
+
+		/* Only one C_Login call if protected auth path */
+		if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+			break;
+
+		request_retry = TRUE;
+	} while (rv == CKR_PIN_INCORRECT);
+
+	g_clear_object (&password);
+
+	return rv;
+}
diff --git a/gck/gck-slot.c b/gck/gck-slot.c
index 05c7d8e..2be6a83 100644
--- a/gck/gck-slot.c
+++ b/gck/gck-slot.c
@@ -49,12 +49,17 @@
 enum {
 	PROP_0,
 	PROP_MODULE,
-	PROP_HANDLE
+	PROP_HANDLE,
+	PROP_INTERACTION
 };
 
 struct _GckSlotPrivate {
 	GckModule *module;
 	CK_SLOT_ID handle;
+
+	/* Changable data locked by mutex */
+	GMutex *mutex;
+	GTlsInteraction *interaction;
 };
 
 G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT);
@@ -64,7 +69,9 @@ G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT);
  */
 
 static GckSession*
-make_session_object (GckSlot *self, guint options, CK_SESSION_HANDLE handle)
+make_session_object (GckSlot *self,
+                     GckSessionOptions options,
+                     CK_SESSION_HANDLE handle)
 {
 	GckSession *session;
 	GckModule *module;
@@ -89,6 +96,7 @@ static void
 gck_slot_init (GckSlot *self)
 {
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SLOT, GckSlotPrivate);
+	self->pv->mutex = g_mutex_new ();
 }
 
 static void
@@ -104,6 +112,12 @@ gck_slot_get_property (GObject *obj, guint prop_id, GValue *value,
 	case PROP_HANDLE:
 		g_value_set_ulong (value, gck_slot_get_handle (self));
 		break;
+	case PROP_INTERACTION:
+		g_value_take_object (value, gck_slot_get_interaction (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
 	}
 }
 
@@ -126,12 +140,22 @@ gck_slot_set_property (GObject *obj, guint prop_id, const GValue *value,
 		g_assert (!self->pv->handle);
 		self->pv->handle = g_value_get_ulong (value);
 		break;
+	case PROP_INTERACTION:
+		gck_slot_set_interaction (self, g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
 	}
 }
 
 static void
 gck_slot_dispose (GObject *obj)
 {
+	GckSlot *self = GCK_SLOT (obj);
+
+	gck_slot_set_interaction (self, NULL);
+
 	G_OBJECT_CLASS (gck_slot_parent_class)->dispose (obj);
 }
 
@@ -139,16 +163,16 @@ static void
 gck_slot_finalize (GObject *obj)
 {
 	GckSlot *self = GCK_SLOT (obj);
-	self->pv->handle = 0;
 
-	if (self->pv->module)
-		g_object_unref (self->pv->module);
-	self->pv->module = NULL;
+	g_assert (self->pv->interaction == NULL);
+
+	self->pv->handle = 0;
+	g_clear_object (&self->pv->module);
+	g_mutex_free (self->pv->mutex);
 
 	G_OBJECT_CLASS (gck_slot_parent_class)->finalize (obj);
 }
 
-
 static void
 gck_slot_class_init (GckSlotClass *klass)
 {
@@ -178,6 +202,17 @@ gck_slot_class_init (GckSlotClass *klass)
 		g_param_spec_ulong ("handle", "Handle", "PKCS11 Slot ID",
 		                   0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+	/**
+	 * GckSlot:interaction:
+	 *
+	 * Interaction object used to ask the user for pins when opening
+	 * sessions. Used if the session_options of the enumerator have
+	 * %GCK_SESSION_LOGIN_USER or %GCK_SESSION_AUTHENTICATE
+	 */
+	g_object_class_install_property (gobject_class, PROP_INTERACTION,
+		g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins",
+		                     G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
+
 	g_type_class_add_private (gobject_class, sizeof (GckSlotPrivate));
 }
 
@@ -909,6 +944,62 @@ gck_slot_has_flags (GckSlot *self, gulong flags)
 }
 
 /**
+ * gck_slot_get_interaction:
+ * @self: the slot
+ *
+ * Get the interaction used when a pin is needed
+ *
+ * Returns: (transfer full) (allow-none): the interaction or %NULL
+ */
+GTlsInteraction *
+gck_slot_get_interaction (GckSlot *self)
+{
+	GTlsInteraction *result = NULL;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (self->pv->interaction)
+			result = g_object_ref (self->pv->interaction);
+
+	g_mutex_unlock (self->pv->mutex);
+
+	return result;
+}
+
+/**
+ * gck_slot_set_interaction:
+ * @self: the slot
+ * @interaction: (allow-none): the interaction or %NULL
+ *
+ * Set the interaction used when a pin is needed
+ */
+void
+gck_slot_set_interaction (GckSlot *self,
+                          GTlsInteraction *interaction)
+{
+	GTlsInteraction *previous = NULL;
+
+	g_return_if_fail (GCK_IS_SLOT (self));
+	g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction));
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (interaction != self->pv->interaction) {
+			previous = self->pv->interaction;
+			self->pv->interaction = interaction;
+			if (interaction)
+				g_object_ref (interaction);
+		}
+
+	g_mutex_unlock (self->pv->mutex);
+
+	g_clear_object (&previous);
+	g_object_notify (G_OBJECT (self), "interaction");
+}
+
+/**
  * gck_slots_enumerate_objects:
  * @slots: (element-type Gck.Slot): a list of #GckSlot to enumerate objects on.
  * @attrs: Attributes that the objects must have, or empty for all objects.
@@ -938,6 +1029,7 @@ gck_slots_enumerate_objects (GList *slots,
 
 typedef struct OpenSession {
 	GckArguments base;
+	GTlsInteraction *interaction;
 	GckSlot *slot;
 	gulong flags;
 	gpointer app_data;
@@ -950,9 +1042,8 @@ typedef struct OpenSession {
 static CK_RV
 perform_open_session (OpenSession *args)
 {
-	CK_SESSION_INFO info;
+	GTlsInteraction *interaction;
 	CK_RV rv = CKR_OK;
-	CK_ULONG pin_len;
 
 	/* Can be called multiple times */
 
@@ -965,52 +1056,26 @@ perform_open_session (OpenSession *args)
 	if (rv != CKR_OK || !args->auto_login)
 		return rv;
 
-	/* Step two, check if session is logged in */
-	rv = (args->base.pkcs11->C_GetSessionInfo) (args->session, &info);
-	if (rv != CKR_OK)
-		return rv;
-
-	/* Already logged in? */
-	if (info.state != CKS_RO_PUBLIC_SESSION && info.state != CKS_RW_PUBLIC_SESSION)
-		return CKR_OK;
-
-	/* Try to login */
-	pin_len = args->password ? strlen (args->password) : 0;
-	return (args->base.pkcs11->C_Login) (args->session, CKU_USER,
-	                                     (CK_UTF8CHAR_PTR)args->password, pin_len);
-}
-
-static gboolean
-complete_open_session (OpenSession *args, CK_RV result)
-{
-	GckModule *module;
-	gboolean ret = TRUE;
-
-	g_free (args->password);
-	args->password = NULL;
-
-	/* Ask the token for a password */
-	module = gck_slot_get_module (args->slot);
-
-	if (args->auto_login && result == CKR_PIN_INCORRECT) {
+	/* Compatibility, hook into GckModule signals if no interaction set */
+	if (args->interaction)
+		interaction = g_object_ref (args->interaction);
+	else
+		interaction = _gck_interaction_new (args->slot);
 
-		ret = _gck_module_fire_authenticate_slot (module, args->slot, NULL, &args->password);
+	rv = _gck_session_authenticate_token (args->base.pkcs11, args->session,
+	                                      args->slot, interaction, NULL);
 
-		/* If authenticate returns TRUE then call is not complete */
-		ret = !ret;
-	}
-
-	g_object_unref (module);
+	g_object_unref (interaction);
 
-	return ret;
+	return rv;
 }
 
 static void
 free_open_session (OpenSession *args)
 {
+	g_clear_object (&args->interaction);
+	g_clear_object (&args->slot);
 	g_assert (!args->password);
-	if (args->slot)
-		g_object_unref (args->slot);
 	g_free (args);
 }
 
@@ -1055,8 +1120,13 @@ gck_slot_open_session (GckSlot *self,
  * Returns: (transfer full): a new session or %NULL if an error occurs
  **/
 GckSession *
-gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, gpointer app_data,
-                            CK_NOTIFY notify, GCancellable *cancellable, GError **error)
+gck_slot_open_session_full (GckSlot *self,
+                            GckSessionOptions options,
+                            gulong pkcs11_flags,
+                            gpointer app_data,
+                            CK_NOTIFY notify,
+                            GCancellable *cancellable,
+                            GError **error)
 {
 	OpenSession args = { GCK_ARGUMENTS_INIT, 0,  };
 	GckSession *session = NULL;
@@ -1072,6 +1142,7 @@ gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, g
 	args.notify = notify;
 	args.password = NULL;
 	args.session = 0;
+	args.interaction = gck_slot_get_interaction (self);
 
 	args.auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER);
 
@@ -1079,9 +1150,10 @@ gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, g
 	if ((options & GCK_SESSION_READ_WRITE) == GCK_SESSION_READ_WRITE)
 		args.flags |= CKF_RW_SESSION;
 
-	if (_gck_call_sync (self, perform_open_session, complete_open_session, &args, cancellable, error))
+	if (_gck_call_sync (self, perform_open_session, NULL, &args, cancellable, error))
 		session = make_session_object (self, options, args.session);
 
+	g_clear_object (&args.interaction);
 	g_object_unref (module);
 	g_object_unref (self);
 
@@ -1128,20 +1200,26 @@ gck_slot_open_session_async (GckSlot *self,
  * This call will return immediately and complete asynchronously.
  **/
 void
-gck_slot_open_session_full_async (GckSlot *self, guint options, gulong pkcs11_flags, gpointer app_data,
-                                  CK_NOTIFY notify, GCancellable *cancellable,
-                                  GAsyncReadyCallback callback, gpointer user_data)
+gck_slot_open_session_full_async (GckSlot *self,
+                                  GckSessionOptions options,
+                                  gulong pkcs11_flags,
+                                  gpointer app_data,
+                                  CK_NOTIFY notify,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
 {
 	OpenSession *args;
 
 	g_object_ref (self);
 
-	args =  _gck_call_async_prep (self, self, perform_open_session, complete_open_session,
+	args =  _gck_call_async_prep (self, self, perform_open_session, NULL,
 	                              sizeof (*args), free_open_session);
 
 	args->app_data = app_data;
 	args->notify = notify;
 	args->slot = g_object_ref (self);
+	args->interaction = gck_slot_get_interaction (self);
 
 	args->auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER);
 
diff --git a/gck/gck.h b/gck/gck.h
index 890854b..cb2992c 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -434,6 +434,11 @@ struct _GckEnumeratorClass {
 
 GType                 gck_enumerator_get_type                 (void) G_GNUC_CONST;
 
+GTlsInteraction *     gck_enumerator_get_interaction          (GckEnumerator *self);
+
+void                  gck_enumerator_set_interaction          (GckEnumerator *self,
+                                                               GTlsInteraction *interaction);
+
 GckObject*            gck_enumerator_next                     (GckEnumerator *self,
                                                                GCancellable *cancellable,
                                                                GError **error);
@@ -587,9 +592,10 @@ GckMechanismInfo*   gck_slot_get_mechanism_info             (GckSlot *self,
 gboolean            gck_slot_has_flags                      (GckSlot *self,
                                                              gulong flags);
 
-GckEnumerator*      gck_slots_enumerate_objects             (GList *slots,
-                                                             GckAttributes *attrs,
-                                                             GckSessionOptions options);
+GTlsInteraction *   gck_slot_get_interaction                (GckSlot *self);
+
+void                gck_slot_set_interaction                (GckSlot *self,
+                                                             GTlsInteraction *interaction);
 
 GckSession*         gck_slot_open_session                   (GckSlot *self,
                                                              GckSessionOptions options,
@@ -623,6 +629,10 @@ GckSession*         gck_slot_open_session_finish            (GckSlot *self,
                                                              GAsyncResult *result,
                                                              GError **error);
 
+GckEnumerator*      gck_slots_enumerate_objects             (GList *slots,
+                                                             GckAttributes *attrs,
+                                                             GckSessionOptions options);
+
 /* ------------------------------------------------------------------------
  * SESSION
  */
@@ -689,6 +699,8 @@ gulong              gck_session_get_state                   (GckSession *self);
 
 GckSessionOptions   gck_session_get_options                 (GckSession *self);
 
+GTlsInteraction *   gck_session_get_interaction             (GckSession *self);
+
 gboolean            gck_session_init_pin                    (GckSession *self,
                                                              const guchar *pin,
                                                              gsize n_pin,
@@ -1193,6 +1205,44 @@ GckAttributes*      gck_object_get_template_finish          (GckObject *self,
                                                              GAsyncResult *result,
                                                              GError **error);
 
+/* ------------------------------------------------------------------------
+ * PASSWORD
+ */
+
+#define GCK_TYPE_PASSWORD             (gck_password_get_type ())
+#define GCK_PASSWORD(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_PASSWORD, GckPassword))
+#define GCK_PASSWORD_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_PASSWORD, GckPassword))
+#define GCK_IS_PASSWORD(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_PASSWORD))
+#define GCK_IS_PASSWORD_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_PASSWORD))
+#define GCK_PASSWORD_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_PASSWORD, GckPasswordClass))
+
+typedef struct _GckPassword GckPassword;
+typedef struct _GckPasswordClass GckPasswordClass;
+typedef struct _GckPasswordPrivate GckPasswordPrivate;
+
+struct _GckPassword {
+	GTlsPassword parent;
+
+	/*< private >*/
+	GckPasswordPrivate *pv;
+	gpointer reserved[4];
+};
+
+struct _GckPasswordClass {
+	GTlsPasswordClass parent;
+
+	/*< private >*/
+	gpointer reserved[4];
+};
+
+GType               gck_password_get_type                   (void) G_GNUC_CONST;
+
+GckModule *         gck_password_get_module                 (GckPassword *self);
+
+GckSlot *           gck_password_get_token                  (GckPassword *self);
+
+GckObject *         gck_password_get_key                    (GckPassword *self);
+
 /* ----------------------------------------------------------------------------
  * URI
  */
diff --git a/gck/gck.symbols b/gck/gck.symbols
index 37f7f67..d5afd66 100644
--- a/gck/gck.symbols
+++ b/gck/gck.symbols
@@ -53,11 +53,13 @@ gck_attributes_new_empty
 gck_attributes_new_full
 gck_attributes_ref
 gck_attributes_unref
+gck_enumerator_get_interaction
 gck_enumerator_get_type
 gck_enumerator_next
 gck_enumerator_next_async
 gck_enumerator_next_finish
 gck_enumerator_next_n
+gck_enumerator_set_interaction
 gck_error_get_quark
 gck_error_get_type
 gck_get_error_quark
@@ -122,6 +124,10 @@ gck_object_set_template
 gck_object_set_template_async
 gck_object_set_template_finish
 gck_objects_from_handle_array
+gck_password_get_key
+gck_password_get_module
+gck_password_get_token
+gck_password_get_type
 gck_session_create_object
 gck_session_create_object_async
 gck_session_create_object_finish
@@ -147,6 +153,7 @@ gck_session_generate_key_pair_finish
 gck_session_generate_key_pair_full
 gck_session_get_handle
 gck_session_get_info
+gck_session_get_interaction
 gck_session_get_module
 gck_session_get_options
 gck_session_get_slot
@@ -188,6 +195,7 @@ gck_slot_equal
 gck_slot_from_handle
 gck_slot_get_handle
 gck_slot_get_info
+gck_slot_get_interaction
 gck_slot_get_mechanism_info
 gck_slot_get_mechanisms
 gck_slot_get_module
@@ -204,6 +212,7 @@ gck_slot_open_session_async
 gck_slot_open_session_finish
 gck_slot_open_session_full
 gck_slot_open_session_full_async
+gck_slot_set_interaction
 gck_slots_enumerate_objects
 gck_string_from_chars
 gck_string_to_chars
diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am
index 99aaf10..734e9d8 100644
--- a/gck/tests/Makefile.am
+++ b/gck/tests/Makefile.am
@@ -27,6 +27,10 @@ TEST_PROGS = \
 	test-gck-enumerator \
 	test-gck-modules
 
+test_gck_enumerator_SOURCES = \
+	test-gck-enumerator.c \
+	mock-interaction.c mock-interaction.h
+
 check_PROGRAMS = $(TEST_PROGS)
 
 test: $(TEST_PROGS)
diff --git a/gck/tests/mock-interaction.c b/gck/tests/mock-interaction.c
new file mode 100644
index 0000000..ed25a47
--- /dev/null
+++ b/gck/tests/mock-interaction.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mock-interaction.c
+
+   Copyright (C) 2011 Collabora Ltd
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "mock-interaction.h"
+
+#define MOCK_INTERACTION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), MOCK_TYPE_INTERACTION, MockInteraction))
+#define MOCK_IS_INTERACTION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), MOCK_TYPE_INTERACTION))
+#define MOCK_INTERACTION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), MOCK_TYPE_INTERACTION, MockInteractionClass))
+
+typedef struct _MockInteractionClass MockInteractionClass;
+
+struct _MockInteraction {
+	GTlsInteraction interaction;
+	gchar *password;
+};
+
+struct _MockInteractionClass {
+	GTlsInteractionClass parent;
+};
+
+G_DEFINE_TYPE (MockInteraction, mock_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+mock_interaction_init (MockInteraction *self)
+{
+
+}
+
+static void
+mock_interaction_finalize (GObject *obj)
+{
+	MockInteraction *self = MOCK_INTERACTION (obj);
+
+	g_free (self->password);
+
+	G_OBJECT_CLASS (mock_interaction_parent_class)->dispose (obj);
+}
+
+static GTlsInteractionResult
+mock_interaction_ask_password (GTlsInteraction *interaction,
+                               GTlsPassword *password,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	MockInteraction *self = MOCK_INTERACTION (interaction);
+
+	if (self->password) {
+		g_tls_password_set_value (password, (const guchar *)self->password, -1);
+		return G_TLS_INTERACTION_HANDLED;
+	} else {
+		return G_TLS_INTERACTION_UNHANDLED;
+	}
+}
+
+static void
+mock_interaction_class_init (MockInteractionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+	object_class->finalize = mock_interaction_finalize;
+
+	interaction_class->ask_password = mock_interaction_ask_password;
+}
+
+GTlsInteraction *
+mock_interaction_new (const gchar *password)
+{
+	MockInteraction *result;
+
+	result = g_object_new (MOCK_TYPE_INTERACTION, NULL);
+	result->password = g_strdup (password);
+
+	return G_TLS_INTERACTION (result);
+}
diff --git a/gck/tests/mock-interaction.h b/gck/tests/mock-interaction.h
new file mode 100644
index 0000000..0747f4b
--- /dev/null
+++ b/gck/tests/mock-interaction.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mock-interaction.h
+
+   Copyright (C) 2011 Collabora Ltd
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#ifndef MOCK_INTERACTION_H
+#define MOCK_INTERACTION_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_INTERACTION    (mock_interaction_get_type ())
+#define MOCK_INTERACTION(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOCK_TYPE_INTERACTION, MockInteraction))
+#define MOCK_IS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOCK_TYPE_INTERACTION))
+
+typedef struct _MockInteraction MockInteraction;
+
+GType               mock_interaction_get_type               (void) G_GNUC_CONST;
+
+GTlsInteraction *   mock_interaction_new                    (const gchar *password);
+
+G_END_DECLS
+
+#endif /* MOCK_INTERACTION_H */
diff --git a/gck/tests/test-gck-crypto.c b/gck/tests/test-gck-crypto.c
index 3aac0fc..9630123 100644
--- a/gck/tests/test-gck-crypto.c
+++ b/gck/tests/test-gck-crypto.c
@@ -631,6 +631,8 @@ main (int argc, char **argv)
 	g_type_init ();
 	g_test_init (&argc, &argv, NULL);
 
+	g_set_prgname ("test-gck-crypto");
+
 	g_test_add ("/gck/crypto/encrypt", Test, NULL, setup, test_encrypt, teardown);
 	g_test_add ("/gck/crypto/decrypt", Test, NULL, setup, test_decrypt, teardown);
 	g_test_add ("/gck/crypto/login_context_specific", Test, NULL, setup, test_login_context_specific, teardown);
diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c
index 9287b1b..d5d669e 100644
--- a/gck/tests/test-gck-enumerator.c
+++ b/gck/tests/test-gck-enumerator.c
@@ -28,6 +28,8 @@
 #include "gck/gck-private.h"
 #include "gck/gck-test.h"
 
+#include "mock-interaction.h"
+
 #include "egg/egg-testing.h"
 
 #include <glib.h>
@@ -242,6 +244,76 @@ test_attributes (Test *test, gconstpointer unused)
 }
 
 static void
+test_authenticate_interaction (Test *test,
+                               gconstpointer unused)
+{
+	GTlsInteraction *interaction;
+	GTlsInteraction *check;
+	GckUriData *uri_data;
+	GError *error = NULL;
+	GckEnumerator *en;
+	GckObject *obj;
+
+	uri_data = gck_uri_data_new ();
+	en = _gck_enumerator_new (test->modules, GCK_SESSION_LOGIN_USER, uri_data);
+	g_assert (GCK_IS_ENUMERATOR (en));
+
+	interaction = mock_interaction_new ("booo");
+	g_object_set (en, "interaction", interaction, NULL);
+
+	check = NULL;
+	g_object_get (en, "interaction", &check, NULL);
+	g_assert (interaction == check);
+	g_object_unref (interaction);
+
+	obj = gck_enumerator_next (en, NULL, &error);
+	g_assert (GCK_IS_OBJECT (obj));
+
+	g_object_unref (obj);
+	g_object_unref (en);
+}
+
+static gboolean
+on_authenticate_token (GckModule *module,
+                       GckSlot *slot,
+                       gchar *label,
+                       gchar **password,
+                       gpointer unused)
+{
+	g_assert (unused == GUINT_TO_POINTER (35));
+	g_assert (password != NULL);
+	g_assert (*password == NULL);
+	g_assert (GCK_IS_MODULE (module));
+	g_assert (GCK_IS_SLOT (slot));
+
+	*password = g_strdup ("booo");
+	return TRUE;
+}
+
+static void
+test_authenticate_compat (Test *test,
+                          gconstpointer unused)
+{
+	GckUriData *uri_data;
+	GError *error = NULL;
+	GckEnumerator *en;
+	GckObject *obj;
+
+	g_signal_connect (test->modules->data, "authenticate-slot",
+	                  G_CALLBACK (on_authenticate_token), GUINT_TO_POINTER (35));
+
+	uri_data = gck_uri_data_new ();
+	en = _gck_enumerator_new (test->modules, GCK_SESSION_LOGIN_USER, uri_data);
+	g_assert (GCK_IS_ENUMERATOR (en));
+
+	obj = gck_enumerator_next (en, NULL, &error);
+	g_assert (GCK_IS_OBJECT (obj));
+
+	g_object_unref (obj);
+	g_object_unref (en);
+}
+
+static void
 test_token_match (Test *test, gconstpointer unused)
 {
 	GckUriData *uri_data;
@@ -269,6 +341,8 @@ main (int argc, char **argv)
 	g_type_init ();
 	g_test_init (&argc, &argv, NULL);
 
+	g_set_prgname ("test-gck-enumerator");
+
 	g_test_add ("/gck/enumerator/create", Test, NULL, setup, test_create, teardown);
 	g_test_add ("/gck/enumerator/create_slots", Test, NULL, setup, test_create_slots, teardown);
 	g_test_add ("/gck/enumerator/next", Test, NULL, setup, test_next, teardown);
@@ -276,6 +350,8 @@ main (int argc, char **argv)
 	g_test_add ("/gck/enumerator/next_and_resume", Test, NULL, setup, test_next_and_resume, teardown);
 	g_test_add ("/gck/enumerator/next_n", Test, NULL, setup, test_next_n, teardown);
 	g_test_add ("/gck/enumerator/next_async", Test, NULL, setup, test_next_async, teardown);
+	g_test_add ("/gck/enumerator/authenticate-interaction", Test, NULL, setup, test_authenticate_interaction, teardown);
+	g_test_add ("/gck/enumerator/authenticate-compat", Test, NULL, setup, test_authenticate_compat, teardown);
 	g_test_add ("/gck/enumerator/attributes", Test, NULL, setup, test_attributes, teardown);
 	g_test_add ("/gck/enumerator/token_match", Test, NULL, setup, test_token_match, teardown);
 
diff --git a/gck/tests/test-gck-session.c b/gck/tests/test-gck-session.c
index e89ce54..632a760 100644
--- a/gck/tests/test-gck-session.c
+++ b/gck/tests/test-gck-session.c
@@ -306,6 +306,8 @@ main (int argc, char **argv)
 	g_type_init ();
 	g_test_init (&argc, &argv, NULL);
 
+	g_set_prgname ("test-gck-session");
+
 	g_test_add ("/gck/session/session_props", Test, NULL, setup, test_session_props, teardown);
 	g_test_add ("/gck/session/session_info", Test, NULL, setup, test_session_info, teardown);
 	g_test_add ("/gck/session/open_close_session", Test, NULL, setup, test_open_close_session, teardown);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]