diff --git services.install services.install
index f48725c..716199b 100644
--- services.install
+++ services.install
@@ -86,6 +86,7 @@ function services_uninstall() {
   variable_del('services_use_key');
   variable_del('services_use_sessid');
   variable_del('services_debug');
+  variable_del('services_auth_module');
 }
 
 /**
diff --git services.module services.module
index 59572f8..ec5dc06 100644
--- services.module
+++ services.module
@@ -127,6 +127,12 @@ function services_menu() {
     'type'              => MENU_DEFAULT_LOCAL_TASK,
     'weight'            => -10,
   );
+  $items['admin/services/ahah/security-options'] = array(
+    'file'              => 'services_admin_browse.inc',
+    'page callback'     => '_services_ahah_security_options',
+    'access arguments'  => array('administer services'),
+    'type'              => MENU_CALLBACK,
+  );
   $items['crossdomain.xml'] = array(
     'access arguments'  => array('access services'),
     'page callback'     => 'services_crossdomain_xml',
@@ -240,6 +246,38 @@ function services_error($message) {
   return $message;
 }
 
+function services_services_authentication() {
+  return array(
+    'title' => t('Key authentication'),
+    'description' => t('The default key-based authentication'),
+  );
+}
+
+function services_auth_invoke($method, &$arg1=NULL) {
+  $module = variable_get('services_auth_module', 'services');
+  if (module_exists($module)) {
+    module_load_include('inc', $module, $module . '.services-authentication');
+    $method = $module . '_' . $method;
+    $args = func_get_args();
+    // Replace method name and arg1 with reference to $arg1.
+    array_splice($args, 0, 2, array(&$arg1));
+    return call_user_func_array($method, $args);
+  }
+}
+
+function services_auth_invoke_custom($module, $method, &$arg1=NULL) {
+  if (module_exists($module)) {
+    module_load_include('inc', $module, $module . '.services-authentication');
+    if (is_callable($module . '_' . $method)) {
+      $args = func_get_args();
+      array_shift($args); // Remove module name.
+      array_shift($args); // Remove method name.
+      return call_user_func_array($module . '_' . $method, $args);
+    }
+  }
+}
+
+
 /**
  * This is the magic function through which all remote method calls must pass.
  */
@@ -251,7 +289,7 @@ function services_method_call($method_name, $args = array()) {
     return services_error(t('Method %name does not exist.', array('%name' => $method_name)));
   }
 
-  // Check for missing args and identify if arg is required in the hash.
+  // Check for missing args
   $hash_parameters = array();
   foreach ($method['#args'] as $key => $arg) {
     if (!$arg['#optional']) {
@@ -259,61 +297,11 @@ function services_method_call($method_name, $args = array()) {
         return services_error(t('Missing required arguments.'));
       }
     }
-
-    // Key is part of the hash
-    if (isset($arg['#signed']) && $arg['#signed'] == TRUE && variable_get('services_use_key', TRUE)) {
-      if (is_numeric($args[$key]) || !empty($args[$key])) {
-        if (is_array($args[$key]) || is_object($args[$key])) {
-          $hash_parameters[] = serialize($args[$key]);
-        }
-        else{
-          $hash_parameters[] = $args[$key];
-        }
-      }
-      else{
-        $hash_parameters[] = '';
-      }
-    }
   }
 
-  if ($method['#key'] && variable_get('services_use_key', TRUE)) {
-    $hash = array_shift($args);
-    $domain = array_shift($args);
-    $timestamp = array_shift($args);
-    $nonce = array_shift($args);
-
-    $expiry_time = $timestamp + variable_get('services_key_expiry', 30);
-
-    if ($expiry_time < time()) {
-      return services_error(t('Token has expired.'));
-    }
-
-    // Still in time but has it been used before
-    if (db_result(db_query("SELECT count(*) FROM {services_timestamp_nonce}
-        WHERE domain = '%s' AND timestamp = %d AND nonce = '%s'",
-        $domain, $timestamp, $nonce))) {
-      return services_error(t('Token has been used previously for a request.'));
-    }
-    else{
-      db_query("INSERT INTO {services_timestamp_nonce} (domain, timestamp, nonce)
-        VALUES ('%s', %d, '%s')", $domain, $timestamp, $nonce);
-    }
-
-    $api_key = db_result(db_query("SELECT kid FROM {services_keys} WHERE domain = '%s'", $domain));
-
-    if (!services_validate_key($api_key, $timestamp, $domain, $nonce, $method_name, $hash_parameters, $hash)) {
-      return services_error(t('Invalid API key.'));
-    }
-  }
-
-  // Add additonal processing for methods requiring authentication
-  $session_backup = NULL;
-  if ($method['#auth'] && variable_get('services_use_sessid', TRUE)) {
-    $sessid = array_shift($args);
-    if (empty($sessid)) {
-      return services_error(t('Invalid sessid.'));
-    }
-    $session_backup = services_session_load($sessid);
+  // Check authentication
+  if ($auth_error = services_auth_invoke('services_authenticate_call', $method, $args)) {
+    return services_error($auth_error);
   }
 
   // Load the proper file
@@ -340,11 +328,6 @@ function services_method_call($method_name, $args = array()) {
     chdir($server_root);
   }
 
-  // Add additonal processing for methods requiring authentication.
-  if ($session_backup !== NULL) {
-    services_session_unload($session_backup);
-  }
-
   return $result;
 }
 
@@ -356,39 +339,6 @@ function services_get_all() {
   if (!isset($methods_cache)) {
     $methods = module_invoke_all('service');
 
-    // api_key arg
-    $arg_api_key = array(
-      '#name' => 'hash',
-      '#type' => 'string',
-      '#description' => t('A valid API key.'),
-    );
-
-    // sessid arg
-    $arg_sessid = array(
-      '#name' => 'sessid',
-      '#type' => 'string',
-      '#description' => t('A valid sessid.'),
-    );
-
-    // domain arg
-    $arg_domain_name = array(
-      '#name' => 'domain_name',
-      '#type' => 'string',
-      '#description' => t('A valid domain for the API key.'),
-    );
-
-    $arg_domain_time_stamp = array(
-      '#name' => 'domain_time_stamp',
-      '#type' => 'string',
-      '#description' => t('Time stamp used to hash key.'),
-    );
-
-    $arg_nonce = array(
-      '#name' => 'nonce',
-      '#type' => 'string',
-      '#description' => t('One time use nonce also used hash key.'),
-    );
-
     foreach ($methods as $key => $method) {
 
       // set method defaults
@@ -411,17 +361,6 @@ function services_get_all() {
         $methods[$key]['#args'] = array();
       }
 
-      if ($methods[$key]['#auth'] && variable_get('services_use_sessid', TRUE)) {
-        $methods[$key]['#args'] = array_merge(array($arg_sessid), $methods[$key]['#args']);
-      }
-
-      if ($methods[$key]['#key'] && variable_get('services_use_key', TRUE)) {
-        $methods[$key]['#args'] = array_merge(array($arg_nonce), $methods[$key]['#args']);
-        $methods[$key]['#args'] = array_merge(array($arg_domain_time_stamp), $methods[$key]['#args']);
-        $methods[$key]['#args'] = array_merge(array($arg_domain_name), $methods[$key]['#args']);
-        $methods[$key]['#args'] = array_merge(array($arg_api_key), $methods[$key]['#args']);
-      }
-
       // set defaults for args
       foreach ($methods[$key]['#args'] as $arg_key => $arg) {
         if (is_array($arg)) {
@@ -440,6 +379,12 @@ function services_get_all() {
       }
       reset($methods[$key]['#args']);
     }
+
+    // Allow auth module to alter the methods
+    if ($altered = services_auth_invoke('services_alter_methods', $methods)) {
+      $methods = $altered;
+    }
+
     $methods_cache = $methods;
   }
   return $methods_cache;
diff --git services.services-authentication.inc services.services-authentication.inc
new file mode 100644
index 0000000..4b305ce
--- /dev/null
+++ services.services-authentication.inc
@@ -0,0 +1,174 @@
+<?php
+
+function services_services_security_settings() {
+  $form['services_use_key'] = array(
+    '#type'           => 'checkbox',
+    '#title'          => t('Use keys'),
+    '#default_value'  => variable_get('services_use_key', TRUE),
+    '#description'    => t('When enabled all method calls need to provide a validation token to autheciate themselves with the server.'),
+  );
+  $form['services_key_expiry'] = array(
+    '#type'           => 'textfield',
+    '#prefix'         => "<div id='services-key-expiry'>",
+    '#suffix'         => "</div>",
+    '#title'          => t('Token expiry time'),
+    '#default_value'  => variable_get('services_key_expiry', 30),
+    '#description'    => t('The time frame for which the token will be valid. Default is 30 secs'),
+  );
+  $form['services_use_sessid'] = array(
+    '#type'           => 'checkbox',
+    '#title'          => t('Use sessid'),
+    '#default_value'  => variable_get('services_use_sessid', TRUE),
+    '#description'    => t('When enabled, all method calls must include a valid sessid. Only disable this setting if the application will user browser-based cookies.')
+  );
+  return $form;
+}
+
+function services_services_security_settings_validate($form_state) {
+  if (!preg_match('/^\d+$/', $form_state['values']['services_key_expiry'])) {
+    form_set_error('services_key_expiry', t('The token expiry time must specified in whole seconds as a number'));
+  }
+}
+
+function services_services_security_settings_submit($form_state) {
+  // Store all values from "our" form as variables.
+  foreach (services_services_security_settings() as $key => $field) {
+    variable_set($key, $form_state['values'][$key]);
+  }
+}
+
+function services_services_alter_methods(&$methods) {
+  // sessid arg
+  $arg_sessid = array(
+    '#name' => 'sessid',
+    '#type' => 'string',
+    '#description' => t('A valid sessid.'),
+  );
+
+  $arg_domain_time_stamp = array(
+    '#name' => 'domain_time_stamp',
+    '#type' => 'string',
+    '#description' => t('Time stamp used to hash key.'),
+  );
+
+  $arg_nonce = array(
+    '#name' => 'nonce',
+    '#type' => 'string',
+    '#description' => t('One time use nonce also used hash key.'),
+  );
+
+  // domain arg
+  $arg_domain_name = array(
+    '#name' => 'domain_name',
+    '#type' => 'string',
+    '#description' => t('A valid domain for the API key.'),
+  );
+
+  // api_key arg
+  $arg_api_key = array(
+    '#name' => 'hash',
+    '#type' => 'string',
+    '#description' => t('A valid API key.'),
+  );
+
+  foreach ($methods as $key => &$method) {
+    if ($method['#auth'] and variable_get('services_use_sessid', TRUE)) {
+       array_unshift($method['#args'], $arg_sessid);
+    }
+
+    if ($method['#key'] and variable_get('services_use_key', TRUE)) {
+      array_unshift($method['#args'], $arg_nonce);
+      array_unshift($method['#args'], $arg_domain_time_stamp);
+      array_unshift($method['#args'], $arg_domain_name);
+      array_unshift($method['#args'], $arg_api_key);
+    }
+  }
+}
+
+function services_services_alter_browse_form(&$form, $method) {
+  $timestamp = time();
+  $nonce = user_password();
+  
+  foreach ($method['#args'] as $key => $arg) {
+    switch ($arg['#name']) {
+      case 'hash':
+        $form['arg'][$key]['#default_value'] = hash_hmac('sha256', 
+          $timestamp .';'. $_SERVER['HTTP_HOST'] .';'. $nonce .';'. arg(4), 
+          services_admin_browse_get_first_key()
+        );
+        break;
+      case 'sessid':
+        $form['arg'][$key]['#default_value']  = session_id();
+        break;
+      case 'domain_name':
+        $form['arg'][$key]['#default_value'] = $_SERVER['HTTP_HOST'];
+        break;
+      case 'domain_time_stamp':
+        $form['arg'][$key]['#default_value'] = $timestamp;
+        break;
+      case 'nonce':
+        $form['arg'][$key]['#default_value'] = $nonce;
+        break;
+    }
+  }
+}
+
+function services_services_authenticate_call($method, $args) {
+  // Get parameters that are used for hash
+  $hash_parameters = array();
+  foreach ($method['#args'] as $key => $arg) {
+    if (isset($arg['#signed']) && $arg['#signed'] == TRUE && variable_get('services_use_key', TRUE)) {
+      if (is_numeric($args[$key]) || !empty($args[$key])) {
+        if (is_array($args[$key]) || is_object($args[$key])) {
+          $hash_parameters[] = serialize($args[$key]);
+        }
+        else{
+          $hash_parameters[] = $args[$key];
+        }
+      }
+      else{
+        $hash_parameters[] = '';
+      }
+    }
+  }
+
+  if ($method['#key'] and variable_get('services_use_key', TRUE)) {
+    $hash = array_shift($args);
+    $domain = array_shift($args);
+    $timestamp = array_shift($args);
+    $nonce = array_shift($args);
+
+    $expiry_time = $timestamp + variable_get('services_key_expiry', 30);
+
+    if ($expiry_time < time()) {
+      return t('Token has expired.');
+    }
+
+    // Still in time but has it been used before
+    if (db_result(db_query("SELECT count(*) FROM {services_timestamp_nonce}
+        WHERE domain = '%s' AND timestamp = %d AND nonce = '%s'",
+        $domain, $timestamp, $nonce))) {
+      return t('Token has been used previously for a request.');
+    }
+    else{
+      db_query("INSERT INTO {services_timestamp_nonce} (domain, timestamp, nonce)
+        VALUES ('%s', %d, '%s')", $domain, $timestamp, $nonce);
+    }
+
+    $api_key = db_result(db_query("SELECT kid FROM {services_keys} WHERE domain = '%s'", $domain));
+
+    if (!services_validate_key($api_key, $timestamp, $domain, $nonce, $method_name, $hash_parameters, $hash)) {
+      return t('Invalid API key.');
+    }
+  }
+
+  // Add additonal processing for methods requiring session
+  $session_backup = NULL;
+  if ($method['#auth'] && variable_get('services_use_sessid', TRUE)) {
+    $sessid = array_shift($args);
+    if (empty($sessid)) {
+      return t('Invalid sessid.');
+    }
+    $session_backup = services_session_load($sessid);
+  }
+}
\ No newline at end of file
diff --git services_admin_browse.inc services_admin_browse.inc
index bf1ce26..808bc91 100644
--- services_admin_browse.inc
+++ services_admin_browse.inc
@@ -98,9 +98,7 @@ function services_admin_browse_test() {
   $method = services_method_get(arg(4));
 
   $form['arg'] = array('#tree' => TRUE);
-  $timestamp = time();
-  $nonce = user_password();
-  
+
   foreach ($method['#args'] as $key => $arg) {
     $form['name'][$key]         = array(
       '#value' => $arg['#name']
@@ -109,62 +107,20 @@ function services_admin_browse_test() {
       '#value' => ($arg['#optional']) ? t('optional') : t('required')
     );
 
-    switch ($arg['#name']) {
-      case 'hash':
-        $form['arg'][$key] = array(
-          '#title'          => 'Hash',
-          '#type'           => 'textfield',
-          '#default_value'  => hash_hmac('sha256', $timestamp .';'. $_SERVER['HTTP_HOST'] .';'. $nonce .';'. arg(4), services_admin_browse_get_first_key())
-        );
-        break;
-
-      case 'sessid':
-        $form['arg'][$key] = array(
-          '#title'          => 'Session id',
-          '#type'           => 'textfield',
-          '#default_value'  => session_id()
-        );
-        break;
-
-      case 'domain_name':
-        $form['arg'][$key] = array(
-          '#title'          => 'Domain name',
-          '#type'           => 'textfield',
-          '#default_value'  => $_SERVER['HTTP_HOST']
-        );
-        break;
-
-      case 'domain_time_stamp':
-        $form['arg'][$key] = array(
-          '#title'          => 'Timestamp',
-          '#type'           => 'textfield',
-          '#default_value'  => $timestamp
-        );
-        break;
-
-      case 'nonce':
-        $form['arg'][$key] = array(
-          '#title'          => 'Nonce',
-          '#type'           => 'textfield',
-          '#default_value'  => $nonce
-        );
-        break;
-
-      default:
-        if (isset($arg['#size']) && $arg['#size'] == 'big') {
-          $form['arg'][$key] = array(
-            '#type'           => 'textarea'
-          );
-        }
-        else {
-          $form['arg'][$key] = array(
-            '#type'           => 'textfield'
-          );
-        }
-        break;
+    if (isset($arg['#size']) && $arg['#size'] == 'big') {
+      $form['arg'][$key] = array(
+        '#type'           => 'textarea'
+      );
+    }
+    else {
+      $form['arg'][$key] = array(
+        '#type'           => 'textfield'
+      );
     }
-
   }
+
+  services_auth_invoke('services_alter_browse_form', $form, $method);
+
   $form['submit'] = array(
     '#type'           => 'submit',
     '#value'          => t('Call method')
@@ -257,50 +213,105 @@ function theme_services_admin_browse_test($form) {
  * Callback for admin page
  */
 function services_admin_settings() {
-  $node_types = node_get_types('names');
-  $defaults = isset($node_types['blog']) ? array('blog' => 1) : array();
-  $form['security'] = array(
-    '#title'        => t('Security'),
-    '#type'         => 'fieldset',
-    '#description'  => t('Changing security settings will require you to adjust all method calls. This will affect all applications using site services.'),
-  );
-  $form['security']['services_use_key'] = array(
-    '#type'           => 'checkbox',
-    '#title'          => t('Use keys'),
-    '#default_value'  => variable_get('services_use_key', TRUE),
-    '#description'    => t('When enabled all method calls need to provide a validation token to autheciate themselves with the server.'),
-  );
-  $form['security']['services_key_expiry'] = array(
-    '#type'           => 'textfield',
-    '#prefix'         => "<div id='services-key-expiry'>",
-    '#suffix'         => "</div>",
-    '#title'          => t('Token expiry time'),
-    '#default_value'  => variable_get('services_key_expiry', 30),
-    '#description'    => t('The time frame for which the token will be valid. Default is 30 secs'),
-  );
-  $form['security']['services_use_sessid'] = array(
-    '#type'           => 'checkbox',
-    '#title'          => t('Use sessid'),
-    '#default_value'  => variable_get('services_use_sessid', TRUE),
-    '#description'    => t('When enabled, all method calls must include a valid sessid. Only disable this setting if the application will use browser-based cookies.')
+  $auth_modules = module_implements('services_authentication');
+
+  // Add security options.
+  if (!empty($auth_modules)) {
+    $auth_options = array(''=>t('-- Select a authorization module'));
+    foreach ($auth_modules as $module) {
+      $info = call_user_func($module . '_services_authentication');
+      $auth_options[$info['description']][$module] = $info['title'];
+    }
+
+    $form['security'] = array(
+      '#title'        => t('Security'),
+      '#type'         => 'fieldset',
+      '#description'  => t('Changing security settings will require you to adjust all method calls. This will affect all applications using site services.'),
+    );
+
+    $form['security']['auth_module'] = array(
+      '#type' => 'select',
+      '#title' => t('Authorization module'),
+      '#options' => $auth_options,
+      '#required' => FALSE,
+      '#default_value' => variable_get('services_auth_module', 'services'),
+      '#ahah' => array(
+        'path' => 'admin/services/ahah/security-options',
+        'wrapper' => 'security-module-options',
+        'method' => 'replace',
+      ),
+    );
+
+    // Placeholder for the auth module options
+    // also used as wrapper for ahah.
+    $form['security']['options'] = array(
+      '#prefix' => '<div id="security-module-options">',
+      '#suffix' => '</div>',
+      'settings' => array(
+        '#value' => sprintf('<div class="description">%s</div>',
+          t('Select a authorization module to configure security')),
+      ),
+    );
+    // Get the configuration form for the authorization module
+    $settings = services_auth_invoke('services_security_settings');
+    if ($settings) {
+      $form['security']['options']['settings'] = $settings;
+    }
+  }
+  else { // Warn if no authorization module has been installed.
+    drupal_set_message(t('No authorization modules have been installed'), 'warning');
+  }
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save settings'),
   );
 
-  services_admin_js($form);
+  return $form;
+}
 
-  return system_settings_form($form);
+function services_admin_settings_validate($form, $form_state) {
+  if (!empty($form_state['values']['auth_module'])) {
+    services_auth_invoke_custom($form_state['values']['auth_module'],
+      'services_security_settings_validate', $form_state);
+  }
+}
+
+function services_admin_settings_submit($form, $form_state) {
+  variable_set('services_auth_module', $form_state['values']['auth_module']);
+  // Allow the authorization module to handle submitted values
+  services_auth_invoke('services_security_settings_submit', $form_state);
 }
 
 /**
- * UI enhancement for services page
+ * Callback for the security configuration form ahah
  */
-function services_admin_js($form) {
-  $out = <<<EOJS
-  $(document).ready(function() {
-    $("#services-key-expiry")[$("#edit-services-use-key").attr('checked') ? 'show' : 'hide']();
-    $("#edit-services-use-key").click(function() {
-      $("#services-key-expiry")[$(this).attr('checked') ? 'show' : 'hide']();
-   });
-  });
-EOJS;
-  drupal_add_js($out, 'inline', 'footer');
-}
+function _services_ahah_security_options() {
+  $cached_form_state = array();
+  $cached_form = form_get_cache($_POST['form_build_id'], $cached_form_state);
+
+  if (!empty($_POST['auth_module'])) {
+    $settings = services_auth_invoke_custom($_POST['auth_module'], 'services_security_settings');
+  }
+
+  if ($settings) {
+    $cached_form['security']['options']['settings'] = $settings;
+  }
+  else {
+    unset($cached_form['security']['options']['settings']);
+  }
+
+  form_set_cache($_POST['form_build_id'], $cached_form, $cached_form_state);
+
+  $form_state = array('submitted' => FALSE);
+  $options = $cached_form['security']['options'];
+  unset($options['#prefix'], $options['#suffix']);
+  $options = form_builder('_services_ahah_security_options', $options, $form_state);
+  $output = drupal_render($options);
+
+  print drupal_to_js(array(
+    'status' => TRUE,
+    'data' => $output,
+  ));
+  exit;
+}
\ No newline at end of file
