Add provider registry support.
diff --git a/authenticate.go b/authenticate.go
index 19fabf6..754823c 100644
--- a/authenticate.go
+++ b/authenticate.go
@@ -1,13 +1,17 @@
 package gophercloud
 
-import (
-	"fmt"
-)
-
 type AuthOptions struct {
 	Username, Password, TenantId string
 }
 
-func Authenticate(provider string, options AuthOptions) (*int, error) {
-	return nil, fmt.Errorf("Not implemented.")
+func (c *Context) Authenticate(provider string, options AuthOptions) (*int, error) {
+	_, err := c.ProviderByName(provider)
+	if err != nil {
+		return nil, err
+	}
+
+	if (options.Username == "") || (options.Password == "") {
+		return nil, ErrCredentials
+	}
+	return nil, nil
 }
diff --git a/authenticate_test.go b/authenticate_test.go
new file mode 100644
index 0000000..596df40
--- /dev/null
+++ b/authenticate_test.go
@@ -0,0 +1,55 @@
+package gophercloud
+
+import (
+	"testing"
+)
+
+func TestAuthProvider(t *testing.T) {
+	c := TestContext()
+
+	_, err := c.Authenticate("", AuthOptions{})
+	if err == nil {
+		t.Error("Expected error for empty provider string")
+		return
+	}
+	_, err = c.Authenticate("unknown-provider", AuthOptions{Username: "u", Password: "p"})
+	if err == nil {
+		t.Error("Expected error for unknown service provider")
+		return
+	}
+
+	err = c.RegisterProvider("provider", &Provider{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	_, err = c.Authenticate("provider", AuthOptions{Username: "u", Password: "p"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+}
+
+func TestUserNameAndPassword(t *testing.T) {
+	c := TestContext()
+	c.RegisterProvider("provider", &Provider{})
+
+	auths := []AuthOptions{
+		AuthOptions{},
+		AuthOptions{Username: "u"},
+		AuthOptions{Password: "p"},
+	}
+	for i, auth := range auths {
+		_, err := c.Authenticate("provider", auth)
+		if err == nil {
+			t.Error("Expected error from missing credentials (%d)", i)
+			return
+		}
+	}
+
+	_, err := c.Authenticate("provider", AuthOptions{Username: "u", Password: "p"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+}
diff --git a/context.go b/context.go
new file mode 100644
index 0000000..0a73890
--- /dev/null
+++ b/context.go
@@ -0,0 +1,11 @@
+package gophercloud
+
+type Context struct {
+	providerMap map[string]*Provider
+}
+
+func TestContext() *Context {
+	return &Context{
+		providerMap: make(map[string]*Provider),
+	}
+}
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..53397e1
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,9 @@
+package gophercloud
+
+import (
+	"fmt"
+)
+
+var ErrNotImplemented = fmt.Errorf("Not implemented")
+var ErrProvider = fmt.Errorf("Missing or incorrect provider")
+var ErrCredentials = fmt.Errorf("Missing or incomplete credentials")
diff --git a/provider.go b/provider.go
new file mode 100644
index 0000000..f2249c3
--- /dev/null
+++ b/provider.go
@@ -0,0 +1,24 @@
+package gophercloud
+
+import (
+)
+
+type Provider struct {
+	// empty.
+}
+
+var providerMap = make(map[string]*Provider)
+
+func (c *Context) RegisterProvider(name string, p *Provider) error {
+	c.providerMap[name] = p
+	return nil
+}
+
+func (c *Context) ProviderByName(name string) (p *Provider, err error) {
+	for provider, descriptor := range c.providerMap {
+		if name == provider {
+			return descriptor, nil
+		}
+	}
+	return nil, ErrProvider
+}
\ No newline at end of file
diff --git a/provider_test.go b/provider_test.go
new file mode 100644
index 0000000..df3d877
--- /dev/null
+++ b/provider_test.go
@@ -0,0 +1,22 @@
+package gophercloud
+
+import (
+	"testing"
+)
+
+func TestProviderRegistry(t *testing.T) {
+	c := TestContext()
+
+	_, err := c.ProviderByName("aProvider")
+	if err == nil {
+		t.Error("Expected error when looking for a provider by non-existant name")
+		return
+	}
+
+	_ = c.RegisterProvider("aProvider", &Provider{})
+	_, err = c.ProviderByName("aProvider")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+}