diff --git a/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php
deleted file mode 100644
index 9f8f0f737949b67e6a05161c46e4eba34b5d799d..0000000000000000000000000000000000000000
--- a/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Implementation of {@link UserDetailsAttributeMapper} for use in local development.
- */
-class LocalUserDetailsAttributeMapper implements UserDetailsAttributeMapper
-{
-
-    /**
-     * {@inheritdoc}
-     */
-    public function mapUser()
-    {
-        $jsonString = file_get_contents(__DIR__ . "/../../../resources/localuser.json");
-        if ($jsonString === false) {
-            return null;
-        }
-
-        // Return user attributes into a standard PHP array (true specifies array)
-        return json_decode($jsonString, true);
-    }
-
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/LocalUserDetailsProvider.php b/src/main/edu/wisc/doit/LocalUserDetailsProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..57fc3961edc76289060565be20f783b16593283e
--- /dev/null
+++ b/src/main/edu/wisc/doit/LocalUserDetailsProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * LocalUserDetailsProvider provides a developer with a {@link UWUserDetails} suitable for use in local development.
+ */
+class LocalUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/localuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+
+        return new UWUserDetails(
+            $attributes[UserDetailsProvider::EPPN],
+            $attributes[UserDetailsProvider::PVI],
+            $attributes[UserDetailsProvider::FULLNAME],
+            $attributes[UserDetailsProvider::UDDS],
+            $attributes[UserDetailsProvider::EMAIL],
+            $attributes[UserDetailsProvider::SOURCE],
+            $attributes[UserDetailsProvider::ISIS_EMPLID],
+            $attributes[UserDetailsProvider::FIRST_NAME],
+            $attributes[UserDetailsProvider::LAST_NAME]
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/LocalUserDetailsService.php b/src/main/edu/wisc/doit/LocalUserDetailsService.php
deleted file mode 100644
index 42704aa298f8a484739c5a3eb9db78e66a30d31f..0000000000000000000000000000000000000000
--- a/src/main/edu/wisc/doit/LocalUserDetailsService.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Implementation of {@link UserDetailsService} for use in local development.
- */
-class LocalUserDetailsService implements UserDetailsService
-{
-
-    /** @var UserDetailsAttributeMapper */
-    private $attributeMapper;
-    
-    /**
-     * LocalUserDetailsService constructor.
-     * @param UserDetailsAttributeMapper $mapper
-     */
-    public function __construct(UserDetailsAttributeMapper $mapper = null)
-    {
-        if ($mapper == null) {
-            $this->attributeMapper = new LocalUserDetailsAttributeMapper();
-        } else {
-            $this->attributeMapper = $mapper;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function loadUser()
-    {
-        $userAttributes = $this->attributeMapper->mapUser();
-
-        // Return null if attribute reading failed
-        if ($userAttributes == null) {
-            return null;
-        }
-        
-        return new UWUserDetails(
-            $userAttributes[UserDetailsAttributeMapper::EPPN],
-            $userAttributes[UserDetailsAttributeMapper::PVI],
-            $userAttributes[UserDetailsAttributeMapper::FULLNAME],
-            $userAttributes[UserDetailsAttributeMapper::UDDS],
-            $userAttributes[UserDetailsAttributeMapper::EMAIL],
-            $userAttributes[UserDetailsAttributeMapper::SOURCE],
-            $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID],
-            $userAttributes[UserDetailsAttributeMapper::FIRST_NAME],
-            $userAttributes[UserDetailsAttributeMapper::LAST_NAME]
-        );
-    }
-    
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php b/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..718a0c89d7637e77dc54068117d4ef8769c70cf9
--- /dev/null
+++ b/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * PreauthHTTPUserDetailsProvider provides an application with a {@link UWUserDetails} when the server is configured
+ * to send Shibboleth attributes as HTTP headers.
+ */
+class PreauthHTTPUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $userDetails = new UWUserDetails(
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::PVI)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::FULLNAME)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::UDDS)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::EMAIL)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::SOURCE)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::ISIS_EMPLID)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::FIRST_NAME)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::LAST_NAME)]
+        );
+
+        // Require EPPN, PVI and FULLNAME to be set to consider user loading successful
+        if (empty($userDetails->getEppn())) {
+            return null;
+        }
+
+        return $userDetails;
+    }
+
+    /**
+     * Map a Shibboleth attribute to its associated HTTP header name.
+     * 
+     * @param string $attribute attribute to map
+     * @return Shibboleth attribute name mapped to its equivalent HTTP header name
+     */
+    private function mapAttribute($attribute)
+    {
+        return 'HTTP_' . strtoupper($attribute);
+    }
+    
+}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php
deleted file mode 100644
index d0b81187dd222752d2b344a62af18d4f69f542f1..0000000000000000000000000000000000000000
--- a/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Default implementation of {@UserDetailsAttributeMapper} for use in preauthenticated (Shibboleth) environments.
- */
-class PreauthUserDetailsAttributeMapper implements UserDetailsAttributeMapper
-{
-
-    /**
-     * {@inheritdoc}
-     */
-    public function mapUser()
-    {
-        $userAttributes[UserDetailsAttributeMapper::EPPN] = $_SERVER[UserDetailsAttributeMapper::EPPN];
-        $userAttributes[UserDetailsAttributeMapper::PVI] = $_SERVER[UserDetailsAttributeMapper::PVI];
-        $userAttributes[UserDetailsAttributeMapper::FULLNAME] = $_SERVER[UserDetailsAttributeMapper::FULLNAME];
-        $userAttributes[UserDetailsAttributeMapper::FIRST_NAME] = $_SERVER[UserDetailsAttributeMapper::FIRST_NAME];
-        $userAttributes[UserDetailsAttributeMapper::LAST_NAME] = $_SERVER[UserDetailsAttributeMapper::LAST_NAME];
-        $userAttributes[UserDetailsAttributeMapper::EMAIL] = $_SERVER[UserDetailsAttributeMapper::EMAIL];
-        $userAttributes[UserDetailsAttributeMapper::UDDS] = $_SERVER[UserDetailsAttributeMapper::UDDS];
-        $userAttributes[UserDetailsAttributeMapper::SOURCE] = $_SERVER[UserDetailsAttributeMapper::SOURCE];
-        $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID] = $_SERVER[UserDetailsAttributeMapper::ISIS_EMPLID];
-
-        // Require EPPN, PVI and FULLNAME to be set to consider user loading successful
-        if (empty($userAttributes[UserDetailsAttributeMapper::EPPN]) ||
-            empty($userAttributes[UserDetailsAttributeMapper::PVI]) ||
-            empty($userAttributes[UserDetailsAttributeMapper::FULLNAME])) {
-            return null;
-        }
-        
-        return $userAttributes;
-    }
-
-}
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php b/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..c86c2f710695519f7dbb5ba3e55fd5cf8bb50c79
--- /dev/null
+++ b/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * PreauthUserDetailsProvider provides an application with a {@link UWUserDetails} from Shibboleth attributes.
+ */
+class PreauthUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $userDetails = new UWUserDetails(
+            $_SERVER[UserDetailsProvider::EPPN],
+            $_SERVER[UserDetailsProvider::PVI],
+            $_SERVER[UserDetailsProvider::FULLNAME],
+            $_SERVER[UserDetailsProvider::UDDS],
+            $_SERVER[UserDetailsProvider::EMAIL],
+            $_SERVER[UserDetailsProvider::SOURCE],
+            $_SERVER[UserDetailsProvider::ISIS_EMPLID],
+            $_SERVER[UserDetailsProvider::FIRST_NAME],
+            $_SERVER[UserDetailsProvider::LAST_NAME]
+        );
+
+        // Require EPPN to be set to consider user loading successful
+        if (empty($userDetails->getEppn())) {
+            return null;
+        }
+        
+        return $userDetails;
+    }
+
+}
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsService.php b/src/main/edu/wisc/doit/PreauthUserDetailsService.php
deleted file mode 100644
index 11a5aa01131f4a8b3983cbe966deaff5514a018a..0000000000000000000000000000000000000000
--- a/src/main/edu/wisc/doit/PreauthUserDetailsService.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Default implementation of {@UserDetailsAttributeMapper} for use in preauthenticated (Shibboleth) environments.
- */
-class PreauthUserDetailsService implements UserDetailsService
-{
-
-    /** @var UserDetailsAttributeMapper */
-    private $attributeMapper;
-
-    /**
-     * PreauthUserDetailsService constructor.
-     * @param UserDetailsAttributeMapper|null $mapper
-     */
-    public function __construct(UserDetailsAttributeMapper $mapper = null)
-    {
-        if ($mapper == null) {
-            $this->attributeMapper = new PreauthUserDetailsAttributeMapper();
-        } else {
-            $this->attributeMapper = $mapper;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function loadUser()
-    {
-        $userAttributes = $this->attributeMapper->mapUser();
-
-        // Return null if attribute reading failed
-        if ($userAttributes == null) {
-            return null;
-        }
-
-        return new UWUserDetails(
-            $userAttributes[UserDetailsAttributeMapper::EPPN],
-            $userAttributes[UserDetailsAttributeMapper::PVI],
-            $userAttributes[UserDetailsAttributeMapper::FULLNAME],
-            $userAttributes[UserDetailsAttributeMapper::UDDS],
-            $userAttributes[UserDetailsAttributeMapper::EMAIL],
-            $userAttributes[UserDetailsAttributeMapper::SOURCE],
-            $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID],
-            $userAttributes[UserDetailsAttributeMapper::FIRST_NAME],
-            $userAttributes[UserDetailsAttributeMapper::LAST_NAME]
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/UserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/UserDetailsProvider.php
similarity index 65%
rename from src/main/edu/wisc/doit/UserDetailsAttributeMapper.php
rename to src/main/edu/wisc/doit/UserDetailsProvider.php
index 08ca6f3592fb6e18bb87af01560c3167d1b6cd38..2a4e37e14060b1472f0fcc6ad0fed2ed87c6bd76 100644
--- a/src/main/edu/wisc/doit/UserDetailsAttributeMapper.php
+++ b/src/main/edu/wisc/doit/UserDetailsProvider.php
@@ -3,10 +3,9 @@
 namespace edu\wisc\doit;
 
 /**
- * UserDetailsAttributeMapper defines an interface for mapping common UW user attributes to an associative array. The
- * constants defined in this interface represent headers sent by UW Federated login that identify a user.
+ * TODO: Write documentation
  */
-interface UserDetailsAttributeMapper
+interface UserDetailsProvider
 {
     
     // Constants representing UW Federated login Shibboleth headers which should be mapped by concrete implementations.
@@ -23,8 +22,8 @@ interface UserDetailsAttributeMapper
     /**
      * Map Shibboleth header values to an associative array.
      *
-     * @return array
+     * @return UserDetails
      */
-    public function mapUser();
+    public function loadUser();
 
 }
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/UserDetailsService.php b/src/main/edu/wisc/doit/UserDetailsService.php
deleted file mode 100644
index 6dbb8fc45ba36741a615cbaa1f30041df88a63ad..0000000000000000000000000000000000000000
--- a/src/main/edu/wisc/doit/UserDetailsService.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * UserDetailsService is the interface that provides an instance of {@link UserDetails}, typically a {@link UWUserDetails}.
- * Two concrete implementations are provided, {@link LocalUserDetailsService} and {@link PreauthUserDetailsService}.
- */
-interface UserDetailsService
-{
-
-    /**
-     * Return a {@link UserDetails} hydrated by a {@link UserDetailsAttributeMapper}, or null if attribute
-     * mapping failed.
-     *
-     * @return UserDetails|null
-     */
-    public function loadUser();
-    
-}
\ No newline at end of file
diff --git a/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php b/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php
deleted file mode 100644
index d79a9483f357c9ed2e079025b09cd1d5350a51a8..0000000000000000000000000000000000000000
--- a/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link LocalUserDetailsAttributeMapper}
- */
-class LocalUserDetailsAttributeMapperTest extends \PHPUnit_Framework_TestCase
-{
-
-    /**
-     * Test attribute mapping for local development.
-     */
-    public function testMapLocalUser() {
-        $attributeMapper = new LocalUserDetailsAttributeMapper();
-        $userAttributes = $attributeMapper->mapUser();
-        $this->assertEquals("bbadger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EPPN]);
-        $this->assertEquals("UW123A456", $userAttributes[UserDetailsAttributeMapper::PVI]);
-        $this->assertEquals("BUCKINGHAM BADGER", $userAttributes[UserDetailsAttributeMapper::FULLNAME]);
-        $this->assertEquals("bucky.badger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EMAIL]);
-        $this->assertEquals("a_source", $userAttributes[UserDetailsAttributeMapper::SOURCE]);
-        $this->assertEquals("123456789", $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID]);
-        $this->assertEquals("BUCKINGHAM", $userAttributes[UserDetailsAttributeMapper::FIRST_NAME]);
-        $this->assertEquals("BADGER", $userAttributes[UserDetailsAttributeMapper::LAST_NAME]);
-        $this->assertEquals(["UW123A456", "UW234A567"], $userAttributes[UserDetailsAttributeMapper::UDDS]);
-    }
-    
-}
diff --git a/src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php b/src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
similarity index 78%
rename from src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php
rename to src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
index 5a1afda6d908a2c209e7735262e9743c14298a07..99b51317a8bb4ddd9e6a479ca014f9b94e4a540e 100644
--- a/src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php
+++ b/src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
@@ -3,14 +3,14 @@
 namespace edu\wisc\doit;
 
 /**
- * Tests for {@link LocalUserDetailsService}.
+ * Tests for {@link LocalUserDetailsProvider}.
  */
-class LocalUserDetailsServiceTest extends \PHPUnit_Framework_TestCase
+class LocalUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
 {
 
     public function testLoadUser()
     {
-        $userDetailsService = new LocalUserDetailsService();
+        $userDetailsService = new LocalUserDetailsProvider();
         $user = $userDetailsService->loadUser();
         $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
         $this->assertEquals("UW123A456", $user->getPvi());
diff --git a/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php b/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f8d1d50d058cf6e12d75f5bb46c043c06b293f5
--- /dev/null
+++ b/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * Tests for {@link PreauthHTTPUserDetailsProvider}.
+ */
+class PreauthHTTPUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
+{
+
+    /** @var UserDetailsProvider */
+    private $userService;
+
+    /**
+     * Populate $_SERVER with Shib attributes to simulate a logged in user
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)] = $attributes[UserDetailsProvider::EPPN];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::PVI)] = $attributes[UserDetailsProvider::PVI];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::FULLNAME)] = $attributes[UserDetailsProvider::FULLNAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::FIRST_NAME)] = $attributes[UserDetailsProvider::FIRST_NAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::LAST_NAME)] = $attributes[UserDetailsProvider::LAST_NAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::UDDS)] = $attributes[UserDetailsProvider::UDDS];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EMAIL)] = $attributes[UserDetailsProvider::EMAIL];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::SOURCE)] = $attributes[UserDetailsProvider::SOURCE];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::ISIS_EMPLID)] = $attributes[UserDetailsProvider::ISIS_EMPLID];
+
+        $this->userService = new PreauthHTTPUserDetailsProvider();
+    }
+
+    public function testLoadUser() {
+        $user = $this->userService->loadUser();
+        $this->assertNotNull($user);
+        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
+        $this->assertEquals("UW123A456", $user->getPvi());
+        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
+        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
+        $this->assertEquals("a_source", $user->getSource());
+        $this->assertEquals("123456789", $user->getIsisEmplid());
+        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
+        $this->assertEquals("BADGER", $user->getLastName());
+    }
+
+    public function testLoadUserWithNoEPPN() {
+        // Clear EPPN to simulate no EPPN
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)] = null;
+        $user = $this->userService->loadUser();
+        $this->assertNull($user);
+    }
+
+    /**
+     * Map Shibboleth attribute to equivalent HTTP header value.
+     *
+     * @param $attribute
+     * @return string
+     */
+    private function mapAttribute($attribute)
+    {
+        return 'HTTP_' . strtoupper($attribute);
+    }
+
+}
diff --git a/src/test/edu/wisc/doit/PreauthTestCase.php b/src/test/edu/wisc/doit/PreauthTestCase.php
deleted file mode 100644
index b79a69bf6f8c1564c3052e1135e74d6df40cc62f..0000000000000000000000000000000000000000
--- a/src/test/edu/wisc/doit/PreauthTestCase.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Class to do basic setup needed to simulate a logged in Shibboleth user.
- */
-abstract class PreauthTestCase extends \PHPUnit_Framework_TestCase
-{
-
-    /**
-     * Populate $_SERVER with Shib attributes to simulate a logged in user
-     */
-    protected function setUp()
-    {
-        parent::setUp();
-        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
-        if ($jsonString === false) {
-            return null;
-        }
-
-        $attributes = json_decode($jsonString, true);
-        $_SERVER[UserDetailsAttributeMapper::EPPN] = $attributes[UserDetailsAttributeMapper::EPPN];
-        $_SERVER[UserDetailsAttributeMapper::PVI] = $attributes[UserDetailsAttributeMapper::PVI];
-        $_SERVER[UserDetailsAttributeMapper::FULLNAME] = $attributes[UserDetailsAttributeMapper::FULLNAME];
-        $_SERVER[UserDetailsAttributeMapper::FIRST_NAME] = $attributes[UserDetailsAttributeMapper::FIRST_NAME];
-        $_SERVER[UserDetailsAttributeMapper::LAST_NAME] = $attributes[UserDetailsAttributeMapper::LAST_NAME];
-        $_SERVER[UserDetailsAttributeMapper::UDDS] = $attributes[UserDetailsAttributeMapper::UDDS];
-        $_SERVER[UserDetailsAttributeMapper::EMAIL] = $attributes[UserDetailsAttributeMapper::EMAIL];
-        $_SERVER[UserDetailsAttributeMapper::SOURCE] = $attributes[UserDetailsAttributeMapper::SOURCE];
-        $_SERVER[UserDetailsAttributeMapper::ISIS_EMPLID] = $attributes[UserDetailsAttributeMapper::ISIS_EMPLID];
-    }
-
-}
\ No newline at end of file
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php
deleted file mode 100644
index 63258c4be3e06e21199ae6072c34391207d88744..0000000000000000000000000000000000000000
--- a/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link PreauthUserDetailsAttributeMapper}.
- */
-class PreauthUserDetailsAttributeMapperTest extends PreauthTestCase
-{
-
-    public function testMapUser() {
-        $attributeMapper = new PreauthUserDetailsAttributeMapper();
-        $userAttributes = $attributeMapper->mapUser();
-        $this->assertEquals("bbadger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EPPN]);
-        $this->assertEquals("UW123A456", $userAttributes[UserDetailsAttributeMapper::PVI]);
-        $this->assertEquals("BUCKINGHAM BADGER", $userAttributes[UserDetailsAttributeMapper::FULLNAME]);
-        $this->assertEquals("bucky.badger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EMAIL]);
-        $this->assertEquals("a_source", $userAttributes[UserDetailsAttributeMapper::SOURCE]);
-        $this->assertEquals("123456789", $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID]);
-        $this->assertEquals("BUCKINGHAM", $userAttributes[UserDetailsAttributeMapper::FIRST_NAME]);
-        $this->assertEquals("BADGER", $userAttributes[UserDetailsAttributeMapper::LAST_NAME]);
-        $this->assertEquals(["UW123A456", "UW234A567"], $userAttributes[UserDetailsAttributeMapper::UDDS]);
-    }
-
-}
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a3c4d272cf3d2a5b25d576049263842dd8c8760f
--- /dev/null
+++ b/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * Tests for {@link PreauthUserDetailsProvider}.
+ */
+class PreauthUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
+{
+
+    /** @var UserDetailsProvider */
+    private $userService;
+
+    /**
+     * Populate $_SERVER with Shib attributes to simulate a logged in user
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+        $_SERVER[UserDetailsProvider::EPPN] = $attributes[UserDetailsProvider::EPPN];
+        $_SERVER[UserDetailsProvider::PVI] = $attributes[UserDetailsProvider::PVI];
+        $_SERVER[UserDetailsProvider::FULLNAME] = $attributes[UserDetailsProvider::FULLNAME];
+        $_SERVER[UserDetailsProvider::FIRST_NAME] = $attributes[UserDetailsProvider::FIRST_NAME];
+        $_SERVER[UserDetailsProvider::LAST_NAME] = $attributes[UserDetailsProvider::LAST_NAME];
+        $_SERVER[UserDetailsProvider::UDDS] = $attributes[UserDetailsProvider::UDDS];
+        $_SERVER[UserDetailsProvider::EMAIL] = $attributes[UserDetailsProvider::EMAIL];
+        $_SERVER[UserDetailsProvider::SOURCE] = $attributes[UserDetailsProvider::SOURCE];
+        $_SERVER[UserDetailsProvider::ISIS_EMPLID] = $attributes[UserDetailsProvider::ISIS_EMPLID];
+
+        $this->userService = new PreauthUserDetailsProvider();
+    }
+
+    public function testLoadUser() {
+        $user = $this->userService->loadUser();
+        $this->assertNotNull($user);
+        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
+        $this->assertEquals("UW123A456", $user->getPvi());
+        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
+        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
+        $this->assertEquals("a_source", $user->getSource());
+        $this->assertEquals("123456789", $user->getIsisEmplid());
+        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
+        $this->assertEquals("BADGER", $user->getLastName());
+    }
+
+    public function testLoadUserWithNoEPPN() {
+        // Clear EPPN to simulate no EPPN
+        $_SERVER[UserDetailsProvider::EPPN] = null;
+        $user = $this->userService->loadUser();
+        $this->assertNull($user);
+    }
+
+}
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php
deleted file mode 100644
index ffa97c12aabd516814cae2ea2709f52eba879094..0000000000000000000000000000000000000000
--- a/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link PreauthUserDetailsService}.
- */
-class PreauthUserDetailsServiceTest extends PreauthTestCase
-{
-
-    /** @var UserDetailsService */
-    private $userService;
-
-    protected function setUp()
-    {
-        parent::setUp();
-        $this->userService = new PreauthUserDetailsService();
-    }
-
-    public function testLoadUser() {
-        $user = $this->userService->loadUser();
-        $this->assertNotNull($user);
-        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
-        $this->assertEquals("UW123A456", $user->getPvi());
-        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
-        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
-        $this->assertEquals("a_source", $user->getSource());
-        $this->assertEquals("123456789", $user->getIsisEmplid());
-        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
-        $this->assertEquals("BADGER", $user->getLastName());
-    }
-
-    public function testLoadUserWithNoEPPN() {
-        // Clear EPPN to simulate no EPPN
-        $_SERVER[UserDetailsAttributeMapper::EPPN] = null;
-        $user = $this->userService->loadUser();
-        $this->assertNull($user);
-    }
-
-}