+maxlinc - Add ApiKey authentication
diff --git a/acceptance/00-authentication.go b/acceptance/00-authentication.go
new file mode 100644
index 0000000..10ed022
--- /dev/null
+++ b/acceptance/00-authentication.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+  "github.com/rackspace/gophercloud"
+  "strings"
+  "fmt"
+  "os"
+)
+
+func main() {
+  provider, username, _, apiKey := getCredentials()
+
+  if !strings.Contains(provider, "rackspace") {
+    fmt.Fprintf(os.Stdout, "Skipping test because provider doesn't support API_KEYs\n")
+    return
+  }
+
+  _, err := gophercloud.Authenticate(
+    provider,
+    gophercloud.AuthOptions{
+      Username: username,
+      ApiKey: apiKey,
+    },
+  )
+  if err != nil {
+    panic(err)
+  }
+}
diff --git a/acceptance/01-authentication.go b/acceptance/01-authentication.go
index 91fc814..bcd3545 100644
--- a/acceptance/01-authentication.go
+++ b/acceptance/01-authentication.go
@@ -5,7 +5,7 @@
 )
 
 func main() {
-	provider, username, password := getCredentials()
+	provider, username, password, _ := getCredentials()
 
 	_, err := gophercloud.Authenticate(
 		provider,
diff --git a/acceptance/libargs.go b/acceptance/libargs.go
index 23c55f1..bc34d86 100644
--- a/acceptance/libargs.go
+++ b/acceptance/libargs.go
@@ -11,10 +11,11 @@
 // getCredentials will verify existence of needed credential information
 // provided through environment variables.  This function will not return
 // if at least one piece of required information is missing.
-func getCredentials() (provider, username, password string) {
+func getCredentials() (provider, username, password, apiKey string) {
 	provider = os.Getenv("SDK_PROVIDER")
 	username = os.Getenv("SDK_USERNAME")
 	password = os.Getenv("SDK_PASSWORD")
+	apiKey   = os.Getenv("SDK_API_KEY")
 
 	if (provider == "") || (username == "") || (password == "") {
 		fmt.Fprintf(os.Stderr, "One or more of the following environment variables aren't set:\n")
@@ -139,7 +140,7 @@
 // withIdentity authenticates the user against the provider's identity service, and provides an
 // accessor for additional services.
 func withIdentity(ar bool, f func(gophercloud.AccessProvider)) {
-	provider, username, password := getCredentials()
+	provider, username, password, _ := getCredentials()
 	acc, err := gophercloud.Authenticate(
 		provider,
 		gophercloud.AuthOptions{
diff --git a/authenticate.go b/authenticate.go
index 886a6fa..c17363c 100644
--- a/authenticate.go
+++ b/authenticate.go
@@ -14,6 +14,9 @@
 	// account's username and password.
 	Username, Password string
 
+	// ApiKey used for providers that support Api Key authentication
+	ApiKey string
+
 	// The TenantId field is optional for the Identity V2 API.
 	TenantId string
 
@@ -37,7 +40,8 @@
 // Auth provides a JSON encoding wrapper for passing credentials to the Identity
 // service.  You will not work with this structure directly.
 type Auth struct {
-	PasswordCredentials PasswordCredentials `json:"passwordCredentials"`
+	PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty"`
+	ApiKeyCredentials   *ApiKeyCredentials   `json:"RAX-KSKEY:apiKeyCredentials,omitempty"`
 	TenantId            string              `json:"tenantId,omitempty"`
 	TenantName          string              `json:"tenantName,omitempty"`
 }
@@ -49,6 +53,12 @@
 	Password string `json:"password"`
 }
 
+
+type ApiKeyCredentials struct {
+	Username string `json:"username"`
+	ApiKey string `json:"apiKey"`
+}
+
 // Access encapsulates the API token and its relevant fields, as well as the
 // services catalog that Identity API returns once authenticated.
 type Access struct {
@@ -99,6 +109,30 @@
 	VersionId, VersionInfo, VersionList string
 }
 
+
+//
+func getAuthCredentials(options AuthOptions) Auth {
+	if (options.ApiKey == "") {
+		return Auth{
+			PasswordCredentials: &PasswordCredentials{
+				Username: options.Username,
+				Password: options.Password,
+			},
+			TenantId:   options.TenantId,
+			TenantName: options.TenantName,
+		};
+	} else {
+		return Auth{
+			ApiKeyCredentials: &ApiKeyCredentials{
+				Username: options.Username,
+				ApiKey: options.ApiKey,
+			},
+			TenantId:   options.TenantId,
+			TenantName: options.TenantName,
+		};
+	}
+}
+
 // papersPlease contains the common logic between authentication and re-authentication.
 // The name, obviously a joke on the process of authentication, was chosen because
 // of how many other entities exist in the program containing the word Auth or Authorization.
@@ -106,21 +140,14 @@
 func (c *Context) papersPlease(p Provider, options AuthOptions) (*Access, error) {
 	var access *Access
 
-	if (options.Username == "") || (options.Password == "") {
+	if (options.Username == "") || (options.Password == "" && options.ApiKey == "") {
 		return nil, ErrCredentials
 	}
 
 	err := perigee.Post(p.AuthEndpoint, perigee.Options{
 		CustomClient: c.httpClient,
 		ReqBody: &AuthContainer{
-			Auth: Auth{
-				PasswordCredentials: PasswordCredentials{
-					Username: options.Username,
-					Password: options.Password,
-				},
-				TenantId:   options.TenantId,
-				TenantName: options.TenantName,
-			},
+			Auth: getAuthCredentials(options),
 		},
 		Results: &struct {
 			Access **Access `json:"access"`
diff --git a/authenticate_test.go b/authenticate_test.go
index 32329ff..b05c780 100644
--- a/authenticate_test.go
+++ b/authenticate_test.go
@@ -148,6 +148,31 @@
 	}
 }
 
+func TestUserNameAndApiKey(t *testing.T) {
+	c := TestContext().
+		WithProvider("provider", Provider{AuthEndpoint: "http://localhost/"}).
+		UseCustomClient(&http.Client{Transport: newTransport().WithResponse(SUCCESSFUL_RESPONSE)})
+
+	credentials := []AuthOptions{
+		{},
+		{Username: "u"},
+		{ApiKey: "a"},
+	}
+	for i, auth := range credentials {
+		_, 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", ApiKey: "a"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+}
+
 func TestTokenAcquisition(t *testing.T) {
 	c := TestContext().
 		UseCustomClient(&http.Client{Transport: newTransport().WithResponse(SUCCESSFUL_RESPONSE)}).