diff --git a/README.txt b/README.txt
index 9dbe607..86384ce 100644
--- a/README.txt
+++ b/README.txt
@@ -1,6 +1,6 @@
 Tivoli Access Manager Authentication Module for Drupal 7
 
-This module allows site users to authenicate using Tivoli Access Manager (TAM),
+This module allows site users to authenticate using Tivoli Access Manager (TAM),
 when a user visits user/tamauth and they have the appropriate headers in their 
 request.
 
@@ -8,8 +8,7 @@ When a user visits the TAM url they will be authenticated and have their roles
 synced from the headers to Drupal.
 
 All configuration variables must be set in the $conf array in your
-settings.php. An admin user interface _may_ be added in the future. A patch
-that implements this will be welcome.
+settings.php or through an admin user interface at /admin/config/people/tamauth.
 
 ** Configuration Variables **
 tamauth_map_roles
diff --git a/tamauth.admin.inc b/tamauth.admin.inc
index bbafb3e..7c8a3aa 100644
--- a/tamauth.admin.inc
+++ b/tamauth.admin.inc
@@ -10,18 +10,66 @@ define('TAM_HEADER_FIELD_MAPPING_PATTERN', '/(.+)=(.+)/');
  * Page callback for admin/config/tamauth.
  */
 function tamauth_settings_form($form, $form_state) {
-  $mappings = tamauth_field_mappings();
-  $mappings_text = '';
+  $role_mappings = tamauth_role_mappings();
+  ksort($role_mappings);
 
-  foreach ($mappings as $tam_header => $drupal_field_name) {
-    $mappings_text .= $tam_header . '=' . $drupal_field_name . "\n";
+  $header = array();
+  $header[] = array('data' => t('TAM Role'));
+  $header[] = array('data' => t('Drupal Role'));
+  $header[] = array('data' => t('Operations'));
+
+  $rows = array();
+  $destination = drupal_get_destination();
+  foreach ($role_mappings as $tam_role => $drupal_role) {
+    $row = array();
+    $row['data']['tam_role'] = $tam_role;
+    $row['data']['drupal_role'] = $drupal_role;
+
+    $operations = array();
+    $operations['delete'] = array(
+      'title' => t('delete'),
+      'href' => "admin/config/people/tamauth/delete/$tam_role",
+      'query' => $destination,
+    );
+    $row['data']['operations'] = array(
+      'data' => array(
+        '#theme' => 'links',
+        '#links' => $operations,
+        '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
+      ),
+    );
+
+    $rows[] = $row;
+  }
+
+  $form['role_mappings_table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+    '#empty' => t('No Role mappings configured. <a href="@link">Add Role Mapping</a>.', array('@link' => url('admin/config/people/tamauth/add'))),
+  );
+
+
+  $field_mappings = tamauth_field_mappings();
+  $field_mappings_text = '';
+
+  foreach ($field_mappings as $tam_header => $drupal_field_name) {
+    $field_mappings_text .= $tam_header . '=' . $drupal_field_name . "\n";
   }
 
   $form['tam_field_mappings'] = array(
     '#title' => t('Header / Field Mappings'),
     '#type' => 'textarea',
     '#description' => t('Enter your header to field mappings, one per line in the format:') . '<br />' . t('TAM_HEADER=drupal_field_name'),
-    '#default_value' => $mappings_text,
+    '#default_value' => $field_mappings_text,
+  );
+
+  $form['tam_redirect'] = array(
+    '#title' => t('TAM Auth Redirect'),
+    '#type' => 'textfield',
+    '#description' => t("User is redirected here after authentication at the 'user/tam' path. Leading slash is not required. Supports tokens such as @front.", array('@front' => '<front>')),
+    '#default_value' => tamauth_redirect(),
+    '#required' => FALSE,
   );
 
   $form['actions'] = array(
@@ -41,22 +89,141 @@ function tamauth_settings_form($form, $form_state) {
  * Validate handler for tamauth_settings_form.
  */
 function tamauth_settings_form_validate($form, $form_state) {
-  $mappings = trim($form_state['values']['tam_field_mappings']);
-  if (!empty($mappings) && !preg_match(TAM_HEADER_FIELD_MAPPING_PATTERN, $mappings)) {
+  $field_mappings = trim($form_state['values']['tam_field_mappings']);
+  if (!empty($field_mappings) && !preg_match(TAM_HEADER_FIELD_MAPPING_PATTERN, $field_mappings)) {
     form_set_error('tam_field_mappings', t('Header / Field Mappings invalid'));
   }
+
+  $redirect_path = &$form_state['values']['tam_redirect'];
+  $redirect_path= drupal_get_normal_path($redirect_path);
+  if (!drupal_valid_path($redirect_path)) {
+    form_set_error('tam_redirect', t("The path '@redirect_path' is either invalid or you do not have access to it.", array('@redirect_path' => $redirect_path)));
+  }
 }
 
 /**
  * Submit handler for tamauth_settings_form.
  */
 function tamauth_settings_form_submit($form, $form_state) {
-  $mappings = trim($form_state['values']['tam_field_mappings']);
+  $field_mappings = trim($form_state['values']['tam_field_mappings']);
   $tam_field_mappings = array();
-  if (preg_match_all(TAM_HEADER_FIELD_MAPPING_PATTERN, $mappings, $matches)) {
+  if (preg_match_all(TAM_HEADER_FIELD_MAPPING_PATTERN, $field_mappings, $matches)) {
     $tam_headers = array_map('trim', $matches[1]);
     $drupal_field_names = array_map('trim', $matches[2]);
     $tam_field_mappings = array_combine($tam_headers, $drupal_field_names);
   }
   variable_set('tam_field_mappings', $tam_field_mappings);
+  variable_set('tamauth_redirect', $form_state['values']['tam_redirect']);
+}
+
+/**
+ * Form constructor for the tamauth role mapping form.
+ *
+ * @ingroup forms
+ * @see tamauth_role_mapping_form_validate()
+ * @see tamauth_role_mapping_form_submit()
+ * @see tamauth_role_mapping_form_delete_submit()
+ */
+function tamauth_role_mapping_form($form, &$form_state) {
+
+  $form['tam_role'] = array(
+    '#type' => 'textfield',
+    '#title' => t('TAM Role'),
+    '#default_value' => '',
+    '#maxlength' => 255,
+    '#size' => 45,
+    '#description' => t('Specify the TAM Role you wish to map.'),
+    '#required' => TRUE,
+  );
+
+  // Roles
+  $roles = user_roles(TRUE);
+
+  $form['drupal_role'] = array(
+    '#type' => 'select',
+    '#title' => t('Drupal Role'),
+    '#options' => $roles,
+    '#default_value' => '',
+    '#multiple' => FALSE,
+    '#required' => TRUE,
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+
+  return $form;
+}
+
+/**
+ * Form validation handler for tamauth_role_mapping_form().
+ *
+ * @see tamauth_role_mapping_form_submit()
+ * @see tamauth_role_mapping_form_delete_submit()
+ */
+function tamauth_role_mapping_form_validate($form, &$form_state) {
+  $tam_role = $form_state['values']['tam_role'];
+  $role_mappings = tamauth_role_mappings();
+
+  if (!empty($role_mappings[$tam_role])) {
+    form_set_error('tam_role', t('The TAM role %tam_role is already mapped.', array('%tam_role' => $tam_role)));
+  }
+}
+
+/**
+ * Form submission handler for tamauth_role_mapping_form().
+ *
+ * @see tamauth_role_mapping_form_validate()
+ * @see tamauth_role_mapping_form_delete_submit()
+ */
+function tamauth_role_mapping_form_submit($form, &$form_state) {
+  $tam_role = $form_state['values']['tam_role'];
+  $drupal_role = user_role_load($form_state['values']['drupal_role']);
+
+  $role_mappings = tamauth_role_mappings();
+  $role_mappings[$tam_role] = $drupal_role->name;
+  variable_set('tamauth_map_roles', $role_mappings);
+
+  drupal_set_message(t('The role mapping has been saved.'));
+  $form_state['redirect'] = 'admin/config/people/tamauth';
+}
+
+/**
+ * Form constructor for the role mappings deletion form.
+ *
+ * @param $tam_role
+ *   The tam_role mapping that will be deleted.
+ *
+ * @see tamauth_role_mapping_delete_confirm_submit()
+ */
+function tamauth_role_mapping_delete_confirm($form, &$form_state, $tam_role) {
+  if (user_access('administer tamauth')) {
+    $form['tam_role'] = array(
+      '#type' => 'value',
+      '#value' => $tam_role,
+    );
+
+    return confirm_form(
+      $form,
+      t('Are you sure you want to delete the %tam_role role mapping?',
+      array('%tam_role' => $tam_role)),
+      'admin/config/people/tamauth'
+    );
+  }
+  return array();
+}
+
+/**
+ * Form submission handler for tamauth_role_mapping_delete_confirm().
+ */
+function tamauth_role_mapping_delete_confirm_submit($form, &$form_state) {
+  if ($form_state['values']['confirm']) {
+    $role_mappings = tamauth_role_mappings();
+    unset($role_mappings[$form_state['values']['tam_role']]);
+    variable_set('tamauth_map_roles', $role_mappings);
+
+    $form_state['redirect'] = 'admin/config/people/tamauth';
+  }
 }
diff --git a/tamauth.install b/tamauth.install
new file mode 100644
index 0000000..7f972be
--- /dev/null
+++ b/tamauth.install
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Implementation of hook_uninstall().
+ */
+function tamauth_uninstall() {
+  variable_del('tamauth_redirect');
+  variable_del('tamauth_map_roles');
+  variable_del('tam_field_mappings');
+}
diff --git a/tamauth.module b/tamauth.module
index e3c15d6..4cb1a04 100644
--- a/tamauth.module
+++ b/tamauth.module
@@ -27,6 +27,24 @@ function tamauth_menu() {
     'file' => 'tamauth.admin.inc',
     'type' => MENU_NORMAL_ITEM,
   );
+
+  $items['admin/config/people/tamauth/add'] = array(
+    'title' => 'Add Role Mapping',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tamauth_role_mapping_form'),
+    'access arguments' => array('administer tamauth'),
+    'file' => 'tamauth.admin.inc',
+    'type' => MENU_LOCAL_ACTION,
+  );
+
+  $items['admin/config/people/tamauth/delete/%'] = array(
+    'title' => 'Delete Role Mapping',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tamauth_role_mapping_delete_confirm', 5),
+    'access arguments' => array('administer tamauth'),
+    'file' => 'tamauth.admin.inc',
+  );
+
   return $items;
 }
 
@@ -46,7 +64,7 @@ function tamauth_permission() {
 /**
  * Page Callback
  *
- * Delegte to tam processes and redirect.
+ * Delegate to tam processes and redirect.
  */
 function tamauth_page() {
   global $user, $conf;
@@ -56,14 +74,13 @@ function tamauth_page() {
 
   $name = tamauth_get_tam_name_header();
   $roles = tamauth_get_tam_groups_header();
-  $user = user_load_by_name($name);
-  $role_map = variable_get('tamauth_map_roles', array());
+  $role_map = tamauth_role_mappings();
   $roles_valid = (bool) count(array_intersect($roles, array_keys($role_map)));
 
   if (!$name || !$roles_valid) {
     watchdog('tamauth', implode(' ', array(t('Incomplete headers'), serialize($name), serialize($roles))));
     drupal_add_http_header('Status', '400 Bad Request');
-    echo t("Nice try...");
+    echo t("Invalid authentication.");
     drupal_exit();
   }
   else if (tamauth_user_is_not_tam($name)) {
@@ -73,10 +90,21 @@ function tamauth_page() {
     drupal_exit();
   }
 
+  $user = user_load_by_name($name);
   user_external_login_register($name, 'tamauth');
   tamauth_set_roles($user, tamauth_map_roles($roles));
   tamauth_set_user_mapped_header_fields($user);
-  drupal_goto(variable_get('tamauth_redirect', 'admin'));
+
+  // Make sure at least one module implements our hook.
+  if (sizeof(module_implements('tamauth_login_event')) > 0) {
+    // Call all modules that implement the hook
+    module_invoke_all('tamauth_login_event', $user);
+  }
+
+  watchdog('tamauth', t('Login success : @name', array('@name' => $name)));
+
+
+  drupal_goto(tamauth_redirect());
 }
 
 /**
@@ -199,7 +227,7 @@ function tamauth_get_tam_groups_header() {
  *   List of valid drupal groups.
  */
 function tamauth_map_roles($tam_roles) {
-  $map = variable_get('tamauth_map_roles', array());
+  $map = tamauth_role_mappings();
   $drupal_roles = array();
   foreach ($tam_roles as $role) {
     if (isset($map[$role])) {
@@ -217,3 +245,21 @@ function tamauth_map_roles($tam_roles) {
 function tamauth_field_mappings() {
   return variable_get('tam_field_mappings', array());
 }
+
+/**
+ * Provides the mappings between TAM roles and Drupal roles.
+ *
+ * @return array TAM role => Drupal role.
+ */
+function tamauth_role_mappings() {
+  return variable_get('tamauth_map_roles', array());
+}
+
+/**
+ * Provides the configured value for the redirect.
+ *
+ * @return the redirect path.
+ */
+function tamauth_redirect() {
+  return variable_get('tamauth_redirect', 'admin');
+}
