diff --git a/debian/control b/debian/control
index af6d8a7a46e25b8f1476999903b8c4572586bcb8..344dcc3c210a673eadd67133a093559dd2715f09 100644
--- a/debian/control
+++ b/debian/control
@@ -4,6 +4,7 @@ Priority: optional
 Maintainer: Guido Günther <agx@sigxcpu.org>
 Build-Depends:
  debhelper (>= 10),
+ libgcr-3-dev,
  libgnome-desktop-3-dev,
  libgtk-3-dev,
  libhandy-0.0-dev (>= 0.0.1),
diff --git a/src/meson.build b/src/meson.build
index 1e0a8fdec7b78a4c20cc63016d50cc59c36b73aa..e54800f4be075d8dfaaaa265fa7f089aa1f0aea1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -55,6 +55,10 @@ phosh_sources = [
   'lockshield.h',
   'monitor-manager.c',
   'monitor-manager.h',
+  'system-prompt.c',
+  'system-prompt.h',
+  'system-prompter.c',
+  'system-prompter.h',
   'panel.c',
   'panel.h',
   'phosh.c',
@@ -73,6 +77,7 @@ phosh_sources = [
 ]
 
 phosh_deps = [
+  dependency('gcr-3', version: '>= 3.7.5'),
   dependency('gnome-desktop-3.0', version: '>=3.26'),
   dependency('gtk+-3.0', version: '>=3.22'),
   dependency('gtk+-wayland-3.0', version: '>=3.22'),
diff --git a/src/phosh.c b/src/phosh.c
index 0eb703200cf99954c154c53ac7c06e5e146e2f5e..b4cb85feea6af9b20958100a054df736527c86c7 100644
--- a/src/phosh.c
+++ b/src/phosh.c
@@ -31,6 +31,7 @@
 #include "home.h"
 #include "favorites.h"
 #include "settings.h"
+#include "system-prompter.h"
 
 
 enum {
@@ -467,6 +468,8 @@ phosh_shell_dispose (GObject *object)
   g_clear_pointer (&priv->panel, gtk_widget_destroy);
   g_clear_object (&priv->lockscreen_manager);
   g_clear_object (&priv->monitor_manager);
+  phosh_system_prompter_unregister ();
+
   G_OBJECT_CLASS (phosh_shell_parent_class)->dispose (object);
 }
 
@@ -498,6 +501,7 @@ phosh_shell_constructed (GObject *object)
   /* Create background after panel since it needs the panel's size */
   background_create (self);
   priv->lockscreen_manager = phosh_lockscreen_manager_new ();
+  phosh_system_prompter_register ();
 }
 
 
diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml
index f450527f400fc818961c6eaf74c4a762c3a315d6..4cc7ea7d40f7f8992355dfa2ee2d23dff38f1fc8 100644
--- a/src/phosh.gresources.xml
+++ b/src/phosh.gresources.xml
@@ -4,6 +4,7 @@
     <file preprocess="xml-stripblanks">ui/home.ui</file>
     <file preprocess="xml-stripblanks">ui/lockscreen.ui</file>
     <file preprocess="xml-stripblanks">ui/settings-menu.ui</file>
+    <file preprocess="xml-stripblanks">ui/system-prompt.ui</file>
     <file preprocess="xml-stripblanks">ui/top-panel.ui</file>
     <file compressed="true">style.css</file>
   </gresource>
diff --git a/src/system-prompt.c b/src/system-prompt.c
new file mode 100644
index 0000000000000000000000000000000000000000..1f6ddd134ccf0caf3aa4a636a112c73425034b1a
--- /dev/null
+++ b/src/system-prompt.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * SPDX-License-Identifier: GPL-3.0+
+ * Author: Guido Günther <agx@sigxcpu.org>
+ *
+ * Based on  gnome-shell's shell-keyring-prompt.c
+ * Author: Stef Walter <stefw@gnome.org>
+ */
+
+#define G_LOG_DOMAIN "phosh-system-prompt"
+
+#include "config.h"
+
+#include "system-prompt.h"
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+
+#include <glib/gi18n.h>
+
+/**
+ * SECTION:phosh-system-prompt
+ * @short_description: A modal system prompt
+ * @Title: PhoshSystemPrompt
+ *
+ * The #PhoshSystemPrompt is used to ask for PINs and passwords
+ */
+
+enum {
+  PROP_0,
+
+  /* GcrPromptIface */
+  PROP_TITLE,
+  PROP_MESSAGE,
+  PROP_DESCRIPTION,
+  PROP_WARNING,
+  PROP_CHOICE_LABEL,
+  PROP_CHOICE_CHOSEN,
+  PROP_PASSWORD_NEW,
+  PROP_PASSWORD_STRENGTH,
+  PROP_CALLER_WINDOW,
+  PROP_CONTINUE_LABEL,
+  PROP_CANCEL_LABEL,
+
+  /* our own */
+  PROP_PASSWORD_VISIBLE,
+  PROP_CONFIRM_VISIBLE,
+  PROP_WARNING_VISIBLE,
+  PROP_CHOICE_VISIBLE,
+  PROP_LAST_PROP,
+};
+
+typedef enum
+{
+  PROMPTING_NONE,
+  PROMPTING_FOR_CONFIRM,
+  PROMPTING_FOR_PASSWORD
+} PromptingMode;
+
+
+typedef struct
+{
+  gchar *title;
+  gchar *message;
+  gchar *description;
+  gchar *warning;
+  gchar *choice_label;
+  gboolean choice_chosen;
+  gboolean password_new;
+  guint password_strength;
+  gchar *continue_label;
+  gchar *cancel_label;
+
+  GtkWidget *grid;
+  GtkWidget *btn_continue;
+  GtkEntryBuffer *password_buffer;
+  GtkEntryBuffer *confirm_buffer;
+
+  GTask *task;
+  GcrPromptReply last_reply;
+  PromptingMode mode;
+  gboolean shown;
+} PhoshSystemPromptPrivate;
+
+
+struct _PhoshSystemPrompt
+{
+  PhoshLayerSurface parent;
+};
+
+
+static void phosh_system_prompt_iface_init (GcrPromptIface *iface);
+G_DEFINE_TYPE_WITH_CODE(PhoshSystemPrompt, phosh_system_prompt, PHOSH_TYPE_LAYER_SURFACE,
+                        G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT,
+                                               phosh_system_prompt_iface_init)
+                        G_ADD_PRIVATE (PhoshSystemPrompt));
+
+
+static void
+phosh_system_prompt_set_property (GObject      *obj,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (obj);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+
+  switch (prop_id) {
+  case PROP_TITLE:
+    g_free (priv->title);
+    priv->title = g_value_dup_string (value);
+    g_object_notify (obj, "title");
+    break;
+  case PROP_MESSAGE:
+    g_free (priv->message);
+    priv->message = g_value_dup_string (value);
+    g_object_notify (obj, "message");
+    break;
+  case PROP_DESCRIPTION:
+    g_free (priv->description);
+    priv->description = g_value_dup_string (value);
+    g_object_notify (obj, "description");
+    break;
+  case PROP_WARNING:
+    g_free (priv->warning);
+    priv->warning = g_value_dup_string (value);
+    g_object_notify (obj, "warning");
+    g_object_notify (obj, "warning-visible");
+    break;
+  case PROP_CHOICE_LABEL:
+    g_free (priv->choice_label);
+    priv->choice_label = g_value_dup_string (value);
+    g_object_notify (obj, "choice-label");
+    g_object_notify (obj, "choice-visible");
+    break;
+  case PROP_CHOICE_CHOSEN:
+    priv->choice_chosen = g_value_get_boolean (value);
+    g_object_notify (obj, "choice-chosen");
+    break;
+  case PROP_PASSWORD_NEW:
+    priv->password_new = g_value_get_boolean (value);
+    g_object_notify (obj, "password-new");
+    g_object_notify (obj, "confirm-visible");
+    break;
+  case PROP_CALLER_WINDOW:
+    /* ignored */
+    break;
+  case PROP_CONTINUE_LABEL:
+    g_free (priv->continue_label);
+    priv->continue_label = g_value_dup_string (value);
+    g_object_notify (obj, "continue-label");
+    break;
+  case PROP_CANCEL_LABEL:
+    g_free (priv->cancel_label);
+    priv->cancel_label = g_value_dup_string (value);
+    g_object_notify (obj, "cancel-label");
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+    break;
+  }
+}
+
+
+static void
+phosh_system_prompt_get_property (GObject    *obj,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (obj);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+
+  switch (prop_id) {
+  case PROP_TITLE:
+    g_value_set_string (value, priv->title ? priv->title : "");
+    break;
+  case PROP_MESSAGE:
+    g_value_set_string (value, priv->message ? priv->message : "");
+    break;
+  case PROP_DESCRIPTION:
+    g_value_set_string (value, priv->description ? priv->description : "");
+    break;
+  case PROP_WARNING:
+    g_value_set_string (value, priv->warning ? priv->warning : "");
+    break;
+  case PROP_CHOICE_LABEL:
+    g_value_set_string (value, priv->choice_label ? priv->choice_label : "");
+    break;
+  case PROP_CHOICE_CHOSEN:
+    g_value_set_boolean (value, priv->choice_chosen);
+    break;
+  case PROP_PASSWORD_NEW:
+    g_value_set_boolean (value, priv->password_new);
+    break;
+  case PROP_PASSWORD_STRENGTH:
+    g_value_set_int (value, priv->password_strength);
+    break;
+  case PROP_CALLER_WINDOW:
+    g_value_set_string (value, "");
+    break;
+  case PROP_CONTINUE_LABEL:
+    g_value_set_string (value, priv->continue_label);
+    break;
+  case PROP_CANCEL_LABEL:
+    g_value_set_string (value, priv->cancel_label);
+    break;
+  case PROP_PASSWORD_VISIBLE:
+    g_value_set_boolean (value, priv->mode == PROMPTING_FOR_PASSWORD);
+    break;
+  case PROP_CONFIRM_VISIBLE:
+    g_value_set_boolean (value, priv->password_new &&
+                         priv->mode == PROMPTING_FOR_CONFIRM);
+    break;
+  case PROP_WARNING_VISIBLE:
+    g_value_set_boolean (value, priv->warning && priv->warning[0]);
+    break;
+  case PROP_CHOICE_VISIBLE:
+    g_value_set_boolean (value, priv->choice_label && priv->choice_label[0]);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+    break;
+  }
+}
+
+
+
+static void
+phosh_system_prompt_password_async (GcrPrompt *prompt,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (prompt);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+  GObject *obj;
+
+  g_debug ("Starting system password prompt: %s", __func__);
+  if (priv->task != NULL) {
+    g_warning ("this prompt can only show one prompt at a time");
+    return;
+  }
+  priv->mode = PROMPTING_FOR_PASSWORD;
+  priv->task = g_task_new (self, NULL, callback, user_data);
+  g_task_set_source_tag (priv->task, phosh_system_prompt_password_async);
+
+  gtk_widget_set_sensitive (priv->btn_continue, TRUE);
+  gtk_widget_set_sensitive (priv->grid, TRUE);
+
+  obj = G_OBJECT (self);
+  g_object_notify (obj, "password-visible");
+  g_object_notify (obj, "confirm-visible");
+  g_object_notify (obj, "warning-visible");
+  g_object_notify (obj, "choice-visible");
+  priv->shown = TRUE;
+}
+
+
+static const gchar *
+phosh_system_prompt_password_finish (GcrPrompt *prompt,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+  g_return_val_if_fail (g_task_get_source_object (G_TASK (result)) == prompt, NULL);
+  g_return_val_if_fail (g_async_result_is_tagged (result,
+                                                  phosh_system_prompt_password_async), NULL);
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+
+static void
+phosh_system_prompt_close (GcrPrompt *prompt)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (prompt);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+
+  if (!priv->shown)
+    return;
+
+  priv->shown = FALSE;
+  gtk_widget_destroy (GTK_WIDGET (self));
+}
+
+
+static void
+phosh_system_prompt_confirm_async (GcrPrompt *prompt,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (prompt);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+  GObject *obj;
+
+  g_debug ("Starting system confirmation prompt: %s", __func__);
+  if (priv->task != NULL) {
+    g_warning ("this prompt can only show one prompt at a time");
+    return;
+  }
+  priv->mode = PROMPTING_FOR_CONFIRM;
+  priv->task = g_task_new (self, NULL, callback, user_data);
+  g_task_set_source_tag (priv->task, phosh_system_prompt_confirm_async);
+
+  gtk_widget_set_sensitive (priv->btn_continue, TRUE);
+  gtk_widget_set_sensitive (priv->grid, TRUE);
+
+  obj = G_OBJECT (self);
+  g_object_notify (obj, "password-visible");
+  g_object_notify (obj, "confirm-visible");
+  g_object_notify (obj, "warning-visible");
+  g_object_notify (obj, "choice-visible");
+  priv->shown = TRUE;
+}
+
+
+static GcrPromptReply
+phosh_system_prompt_confirm_finish (GcrPrompt *prompt,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+  GTask *task = G_TASK (result);
+  gssize res;
+
+  g_debug ("Finishing system confirmation prompt: %s", __func__);
+  g_return_val_if_fail (g_task_get_source_object (task) == prompt,
+                        GCR_PROMPT_REPLY_CANCEL);
+  g_return_val_if_fail (g_async_result_is_tagged (result,
+                                                  phosh_system_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
+
+  res = g_task_propagate_int (task, error);
+  return res == -1 ? GCR_PROMPT_REPLY_CANCEL : (GcrPromptReply)res;
+}
+
+
+static gboolean
+prompt_complete (PhoshSystemPrompt *self)
+{
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+  GTask *res;
+  PromptingMode mode;
+  const gchar *password;
+  const gchar *confirm;
+  const gchar *env;
+
+  g_return_val_if_fail (PHOSH_IS_SYSTEM_PROMPT (self), FALSE);
+  g_return_val_if_fail (priv->mode != PROMPTING_NONE, FALSE);
+  g_return_val_if_fail (priv->task != NULL, FALSE);
+
+  password = gtk_entry_buffer_get_text (priv->password_buffer);
+
+  if (priv->mode == PROMPTING_FOR_CONFIRM) {
+    /* Is it a new password? */
+    if (priv->password_new) {
+      confirm = gtk_entry_buffer_get_text (priv->confirm_buffer);
+      /* Do the passwords match? */
+      if (!g_str_equal (password, confirm)) {
+        gcr_prompt_set_warning (GCR_PROMPT (self), _("Passwords do not match."));
+        return FALSE;
+      }
+
+      /* Don't allow blank passwords if in paranoid mode */
+      env = g_getenv ("GNOME_KEYRING_PARANOID");
+      if (env && *env) {
+        gcr_prompt_set_warning (GCR_PROMPT (self), _("Password cannot be blank"));
+        return FALSE;
+      }
+    }
+  }
+
+  res = priv->task;
+  mode = priv->mode;
+  priv->task = NULL;
+  priv->mode = PROMPTING_NONE;
+
+  if (mode == PROMPTING_FOR_CONFIRM)
+    g_task_return_int (res, (gssize)GCR_PROMPT_REPLY_CONTINUE);
+  else
+    g_task_return_pointer (res, (gpointer)password, NULL);
+  g_object_unref (res);
+
+  gtk_widget_set_sensitive (priv->btn_continue, FALSE);
+  gtk_widget_set_sensitive (priv->grid, FALSE);
+
+  return TRUE;
+}
+
+
+static void
+prompt_cancel (PhoshSystemPrompt *self)
+{
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+  GTask *res;
+  PromptingMode mode;
+
+  g_debug ("Canceling system password prompt: %s", __func__);
+  g_return_if_fail (PHOSH_IS_SYSTEM_PROMPT (self));
+  /*
+   * If cancelled while not prompting, we should just close the prompt,
+   * the user wants it to go away.
+   */
+  if (priv->mode == PROMPTING_NONE) {
+    if (priv->shown)
+      gcr_prompt_close (GCR_PROMPT (self));
+    return;
+  }
+
+  g_return_if_fail (priv->task != NULL);
+
+  res = priv->task;
+  mode = priv->mode;
+  priv->task = NULL;
+  priv->mode = PROMPTING_NONE;
+
+  if (mode == PROMPTING_FOR_CONFIRM)
+    g_task_return_int (res, (gssize) GCR_PROMPT_REPLY_CANCEL);
+  else
+    g_task_return_pointer (res, NULL, NULL);
+  g_object_unref (res);
+}
+
+
+static void
+on_password_changed (GtkEditable *editable,
+                     gpointer user_data)
+{
+  int upper, lower, digit, misc;
+  const char *password;
+  gdouble pwstrength;
+  int length, i;
+
+  password = gtk_entry_get_text (GTK_ENTRY (editable));
+
+  /*
+   * This code is based on the Master Password dialog in Firefox
+   * (pref-masterpass.js)
+   * Original code triple-licensed under the MPL, GPL, and LGPL
+   * so is license-compatible with this file
+   */
+
+  length = strlen (password);
+  upper = 0;
+  lower = 0;
+  digit = 0;
+  misc = 0;
+
+  for ( i = 0; i < length ; i++) {
+    if (g_ascii_isdigit (password[i]))
+      digit++;
+    else if (g_ascii_islower (password[i]))
+      lower++;
+    else if (g_ascii_isupper (password[i]))
+      upper++;
+    else
+      misc++;
+  }
+
+  if (length > 5)
+    length = 5;
+  if (digit > 3)
+    digit = 3;
+  if (upper > 3)
+    upper = 3;
+  if (misc > 3)
+    misc = 3;
+
+  pwstrength = ((length * 0.1) - 0.2) +
+    (digit * 0.1) +
+    (misc * 0.15) +
+    (upper * 0.1);
+
+  if (pwstrength < 0.0)
+    pwstrength = 0.0;
+  if (pwstrength > 1.0)
+    pwstrength = 1.0;
+
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (user_data), pwstrength);
+}
+
+
+static void
+btn_continue_clicked_cb (PhoshSystemPrompt *self, GtkButton *btn)
+{
+  prompt_complete (self);
+}
+
+
+static void
+btn_cancel_clicked_cb (PhoshSystemPrompt *self, GtkButton *btn)
+{
+  prompt_cancel (self);
+}
+
+static void
+phosh_system_prompt_iface_init (GcrPromptIface *iface)
+{
+  iface->prompt_confirm_async = phosh_system_prompt_confirm_async;
+  iface->prompt_confirm_finish = phosh_system_prompt_confirm_finish;
+  iface->prompt_password_async = phosh_system_prompt_password_async;
+  iface->prompt_password_finish = phosh_system_prompt_password_finish;
+  iface->prompt_close = phosh_system_prompt_close;
+}
+
+static gboolean
+draw_cb (GtkWidget *widget, cairo_t *cr, gpointer unused)
+{
+  GtkStyleContext *context = gtk_widget_get_style_context (widget);
+  GdkRGBA c;
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+    gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &c);
+  G_GNUC_END_IGNORE_DEPRECATIONS
+    cairo_set_source_rgba (cr, c.red, c.green, c.blue, 0.7);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_paint (cr);
+  return FALSE;
+}
+
+
+static void
+phosh_system_prompt_dispose (GObject *obj)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (obj);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+
+  if (priv->shown)
+    gcr_prompt_close (GCR_PROMPT (self));
+
+  if (priv->task)
+    prompt_cancel (self);
+
+  g_assert (priv->task == NULL);
+  G_OBJECT_CLASS (phosh_system_prompt_parent_class)->dispose (obj);
+}
+
+
+static void
+phosh_system_prompt_finalize (GObject *obj)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (obj);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+
+  g_free (priv->title);
+  g_free (priv->message);
+  g_free (priv->description);
+  g_free (priv->warning);
+  g_free (priv->choice_label);
+  g_free (priv->continue_label);
+  g_free (priv->cancel_label);
+
+  G_OBJECT_CLASS (phosh_system_prompt_parent_class)->finalize (obj);
+}
+
+
+static void
+phosh_system_prompt_constructed (GObject *object)
+{
+  PhoshSystemPrompt *self = PHOSH_SYSTEM_PROMPT (object);
+  PhoshSystemPromptPrivate *priv = phosh_system_prompt_get_instance_private (self);
+  GtkBuilder *builder;
+  GtkWidget *widget;
+  GtkEntry *entry;
+
+  G_OBJECT_CLASS (phosh_system_prompt_parent_class)->constructed (object);
+
+  /*
+   * We use gtk_buidler_* instead of the nicer
+   * gtk_widget_class_set_template_from_resource since
+   * PhoshLayerSuface isn't supported as type in glade yet.
+   */
+  builder = gtk_builder_new_from_resource ("/sm/puri/phosh/ui/system-prompt.ui");
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "grid_system_prompt"));
+  gtk_container_add (GTK_CONTAINER (self), widget);
+  gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
+  gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
+  priv->grid = widget;
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "lbl_message"));
+  g_object_bind_property (self, "message", widget, "label", G_BINDING_DEFAULT);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "lbl_description"));
+  g_object_bind_property (self, "description", widget, "label", G_BINDING_DEFAULT);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "lbl_password"));
+  g_object_bind_property (self, "password-visible", widget, "visible", G_BINDING_DEFAULT);
+
+  priv->password_buffer = gcr_secure_entry_buffer_new ();
+  entry =
+    GTK_ENTRY (gtk_builder_get_object (builder, "entry_password"));
+  gtk_entry_set_buffer (GTK_ENTRY (entry), GTK_ENTRY_BUFFER (priv->password_buffer));
+  g_object_bind_property (self, "password-visible", entry, "visible", G_BINDING_DEFAULT);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "lbl_confirm"));
+  g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+
+  priv->confirm_buffer = gcr_secure_entry_buffer_new ();
+  widget =
+    GTK_WIDGET (gtk_builder_get_object (builder, "entry_confirm"));
+  gtk_entry_set_buffer (GTK_ENTRY (widget), GTK_ENTRY_BUFFER (priv->confirm_buffer));
+  g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "pbar_quality"));
+  g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+  g_signal_connect (entry, "changed", G_CALLBACK (on_password_changed), widget);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "lbl_warning"));
+  g_object_bind_property (self, "warning", widget, "label", G_BINDING_DEFAULT);
+  g_object_bind_property (self, "warning-visible", widget, "visible", G_BINDING_DEFAULT);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbtn_choice"));
+  g_object_bind_property (self, "choice-label", widget, "label", G_BINDING_DEFAULT);
+  g_object_bind_property (self, "choice-visible", widget, "visible", G_BINDING_DEFAULT);
+  g_object_bind_property (self, "choice-chosen", widget, "active", G_BINDING_BIDIRECTIONAL);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "btn_cancel"));
+  g_object_bind_property (self, "cancel-label", widget, "label", G_BINDING_DEFAULT);
+  g_signal_connect_object (widget,
+                           "clicked",
+                           G_CALLBACK (btn_cancel_clicked_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (builder, "btn_continue"));
+  g_object_bind_property (self, "continue-label", widget, "label", G_BINDING_DEFAULT);
+  g_signal_connect_object (widget,
+                           "clicked",
+                           G_CALLBACK (btn_continue_clicked_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+  gtk_widget_grab_default (widget);
+  priv->btn_continue = widget;
+
+  gtk_style_context_add_class (
+    gtk_widget_get_style_context (GTK_WIDGET (self)),
+    "phosh-system-prompt");
+
+  gtk_widget_set_app_paintable(GTK_WIDGET (self), TRUE);
+  g_signal_connect (G_OBJECT(self),
+                    "draw",
+                    G_CALLBACK(draw_cb),
+                    NULL);
+}
+
+
+static void
+phosh_system_prompt_class_init (PhoshSystemPromptClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *)klass;
+
+  object_class->get_property = phosh_system_prompt_get_property;
+  object_class->set_property = phosh_system_prompt_set_property;
+  object_class->constructed = phosh_system_prompt_constructed;
+  object_class->dispose = phosh_system_prompt_dispose;
+  object_class->finalize = phosh_system_prompt_finalize;
+
+  g_object_class_override_property (object_class, PROP_TITLE, "title");
+  g_object_class_override_property (object_class, PROP_MESSAGE, "message");
+  g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+  g_object_class_override_property (object_class, PROP_WARNING, "warning");
+  g_object_class_override_property (object_class, PROP_PASSWORD_NEW, "password-new");
+  g_object_class_override_property (object_class, PROP_PASSWORD_STRENGTH, "password-strength");
+  g_object_class_override_property (object_class, PROP_CHOICE_LABEL, "choice-label");
+  g_object_class_override_property (object_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+  g_object_class_override_property (object_class, PROP_CALLER_WINDOW, "caller-window");
+  g_object_class_override_property (object_class, PROP_CONTINUE_LABEL, "continue-label");
+  g_object_class_override_property (object_class, PROP_CANCEL_LABEL, "cancel-label");
+
+  /**
+   * GcrPromptDialog:password-visible:
+   *
+   * Whether the password entry is visible or not.
+   */
+  g_object_class_install_property (object_class, PROP_PASSWORD_VISIBLE,
+                                   g_param_spec_boolean ("password-visible", "Password visible", "Password field is visible",
+                                                         FALSE, G_PARAM_READABLE));
+
+  /**
+   * GcrPromptDialog:confirm-visible:
+   *
+   * Whether the password confirm entry is visible or not.
+   */
+  g_object_class_install_property (object_class, PROP_CONFIRM_VISIBLE,
+                                   g_param_spec_boolean ("confirm-visible", "Confirm visible", "Confirm field is visible",
+                                                         FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * PhoshSystemPrompt:warning-visible:
+   *
+   * Whether the warning label is visible or not.
+   */
+  g_object_class_install_property (object_class, PROP_WARNING_VISIBLE,
+                                   g_param_spec_boolean ("warning-visible", "Warning visible", "Warning is visible",
+                                                         FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * PhoshSystemPrompt:choice-visible:
+   *
+   * Whether the choice check box is visible or not.
+   */
+  g_object_class_install_property (object_class, PROP_CHOICE_VISIBLE,
+                                   g_param_spec_boolean ("choice-visible", "Choice visible", "Choice is visible",
+                                                         FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
+
+
+static void
+phosh_system_prompt_init (PhoshSystemPrompt *self)
+{
+  /*
+   * This is a stupid hack to help the window act like a normal object
+   * with regards to reference counting and unref. (see gcr's
+   * gcr_prompt_dialog_init ui/gcr-prompt-dialog.c as of
+   * e0a506eeb29bc6be01a96e805e0244a03428ebf5.
+   * Otherwise it gets clean up too early.
+   */
+  gtk_window_set_has_user_ref_count (GTK_WINDOW (self), FALSE);
+}
+
+
+GtkWidget *
+phosh_system_prompt_new (gpointer layer_shell,
+                         gpointer wl_output)
+{
+  return g_object_new (PHOSH_TYPE_SYSTEM_PROMPT,
+                       "layer-shell", layer_shell,
+                       "wl-output", wl_output,
+                       "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+                       ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+                       ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+                       ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
+                       "layer", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+                       "kbd-interactivity", TRUE,
+                       "exclusive-zone", -1,
+                       "namespace", "phosh prompter",
+                       NULL);
+}
diff --git a/src/system-prompt.h b/src/system-prompt.h
new file mode 100644
index 0000000000000000000000000000000000000000..a9b486d506fe7a2edec978c625d37c0de921270f
--- /dev/null
+++ b/src/system-prompt.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ *
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+#pragma once
+
+#include <gtk/gtk.h>
+#include "layersurface.h"
+
+#define PHOSH_TYPE_SYSTEM_PROMPT (phosh_system_prompt_get_type())
+
+G_DECLARE_FINAL_TYPE (PhoshSystemPrompt, phosh_system_prompt, PHOSH, SYSTEM_PROMPT, PhoshLayerSurface)
+
+GtkWidget *phosh_system_prompt_new (gpointer layer_shell,
+                                    gpointer wl_output);
diff --git a/src/system-prompter.c b/src/system-prompter.c
new file mode 100644
index 0000000000000000000000000000000000000000..81d71c94c706773cb993c545ffa15e230b8f7adb
--- /dev/null
+++ b/src/system-prompter.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * SPDX-License-Identifier: GPL-3.0+
+ * Author: Guido Günther <agx@sigxcpu.org>
+ */
+
+#define G_LOG_DOMAIN "phosh-system-prompter"
+
+#include "config.h"
+
+#include "system-prompt.h"
+#include "system-prompter.h"
+#include "phosh.h"
+#include "phosh-wayland.h"
+
+/**
+ * SECTION:phosh-system_prompter
+ * @short_description: Manages system prompter registration
+ * @Title: PhoshSystemPrompter
+ *
+ * The #PhoshSystemPrompter is responsible for displaying system
+ * wide modal #PhoshSystemPrompt dialogs
+ */
+static GcrSystemPrompter *_prompter;
+static ulong owner_id;
+static gboolean registered_prompter;
+static gboolean acquired_prompter;
+
+
+static GcrPrompt *
+new_prompt_cb (GcrSystemPrompter *prompter,
+               gpointer user_data)
+{
+  PhoshWayland *wl = phosh_wayland_get_default ();
+  PhoshShell *shell = phosh_shell_get_default ();
+
+  g_debug ("Building new system prompt");
+  g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (prompter), NULL);
+  return GCR_PROMPT (phosh_system_prompt_new (phosh_wayland_get_zwlr_layer_shell_v1 (wl),
+                                              phosh_shell_get_primary_monitor (shell)));
+}
+
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar *name,
+                 gpointer user_data)
+{
+  g_debug ("bus acquired for %s", name);
+
+  if (!registered_prompter) {
+    gcr_system_prompter_register (_prompter, connection);
+    g_debug ("registered prompter");
+  }
+  registered_prompter = TRUE;
+}
+
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar *name,
+              gpointer user_data)
+{
+  g_debug ("lost name: %s", name);
+
+  /* Called like so when no connection can be made */
+  if (connection == NULL) {
+    g_warning ("couldn't connect to session bus");
+    phosh_system_prompter_unregister ();
+  }
+}
+
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+  g_debug ("acquired name: %s", name);
+  acquired_prompter = TRUE;
+}
+
+
+GcrSystemPrompter *
+phosh_system_prompter_register ()
+{
+  _prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE, 0);
+
+  g_signal_connect (_prompter, "new-prompt",
+                    G_CALLBACK (new_prompt_cb), NULL);
+
+  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                             "org.gnome.keyring.SystemPrompter",
+                             G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                             on_bus_acquired,
+                             on_name_acquired,
+                             on_name_lost,
+                             NULL,
+                             NULL);
+  return _prompter;
+}
+
+
+void
+phosh_system_prompter_unregister()
+{
+  if (_prompter) {
+    gcr_system_prompter_unregister (_prompter, TRUE);
+    _prompter = NULL;
+  }
+
+  if (acquired_prompter) {
+    g_bus_unown_name (owner_id);
+    owner_id = 0;
+    acquired_prompter = FALSE;
+  }
+}
diff --git a/src/system-prompter.h b/src/system-prompter.h
new file mode 100644
index 0000000000000000000000000000000000000000..99e7e15ff35bfebfb5d454fec6ea19a9d4b459e3
--- /dev/null
+++ b/src/system-prompter.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ *
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+#pragma once
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr-base.h>
+
+GcrSystemPrompter *phosh_system_prompter_register();
+void               phosh_system_prompter_unregister();
diff --git a/src/ui/system-prompt.ui b/src/ui/system-prompt.ui
new file mode 100644
index 0000000000000000000000000000000000000000..fd8da1f46b014f6b2a82d1fff3404e1990f4b46c
--- /dev/null
+++ b/src/ui/system-prompt.ui
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkGrid" id="grid_system_prompt">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="margin_left">20</property>
+    <property name="margin_right">20</property>
+    <property name="margin_top">20</property>
+    <property name="margin_bottom">20</property>
+    <property name="row_spacing">12</property>
+    <property name="column_spacing">12</property>
+    <child>
+      <object class="GtkLabel" id="lbl_description">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="margin_bottom">4</property>
+        <property name="hexpand">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="lbl_password">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="label" translatable="yes">Password:</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="entry_password">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="has_focus">True</property>
+        <property name="valign">center</property>
+        <property name="hexpand">True</property>
+        <property name="visibility">False</property>
+        <property name="invisible_char">●</property>
+        <property name="activates_default">True</property>
+        <property name="primary_icon_sensitive">False</property>
+        <property name="secondary_icon_sensitive">False</property>
+        <property name="input_purpose">password</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="lbl_confirm">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="label" translatable="yes">Confirm:</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="entry_confirm">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="valign">center</property>
+        <property name="hexpand">True</property>
+        <property name="visibility">False</property>
+        <property name="invisible_char">●</property>
+        <property name="activates_default">True</property>
+        <property name="primary_icon_sensitive">False</property>
+        <property name="secondary_icon_sensitive">False</property>
+        <property name="input_purpose">password</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkProgressBar" id="pbar_quality">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">4</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="lbl_warning">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <attributes>
+          <attribute name="style" value="italic"/>
+        </attributes>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">5</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkCheckButton" id="checkbtn_choice">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">False</property>
+        <property name="use_underline">True</property>
+        <property name="draw_indicator">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">6</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="btn_continue">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="is_focus">True</property>
+        <property name="can_default">True</property>
+        <property name="has_default">True</property>
+        <property name="receives_default">True</property>
+        <property name="halign">start</property>
+        <property name="valign">center</property>
+        <property name="use_stock">True</property>
+        <style>
+          <class name="suggested-action"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">7</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="btn_cancel">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="can_default">True</property>
+        <property name="halign">end</property>
+        <property name="valign">center</property>
+        <property name="use_stock">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">7</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="lbl_message">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="margin_bottom">8</property>
+        <property name="hexpand">True</property>
+        <property name="wrap">True</property>
+        <attributes>
+          <attribute name="scale" value="1.2"/>
+        </attributes>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkImage">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="icon_name">dialog-password-symbolic</property>
+        <property name="icon_size">6</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+  </object>
+</interface>