You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ma...@apache.org on 2018/07/02 09:04:07 UTC

[incubator-openwhisk] branch master updated: Store and pass variant data in the Identity's authentication. (#3788)

This is an automated email from the ASF dual-hosted git repository.

markusthoemmes pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new e40e286  Store and pass variant data in the Identity's authentication. (#3788)
e40e286 is described below

commit e40e286dfef893f1b67cf5aa86901b35380fcefd
Author: Martin Henke <ma...@web.de>
AuthorDate: Mon Jul 2 11:04:02 2018 +0200

    Store and pass variant data in the Identity's authentication. (#3788)
    
    This is preparing the introduction of an authentication SPI. One prerequisite for supporting different authorization mechanisms is the ability to pass the information contained in this authorization artifacts down to the user containers (e.g. to call out to other services using these artifacts). This ability is implemented by serializing the information into a JSON structure
    that is mixed in into environment available to the runtimes.
---
 ...hKey.scala => BasicAuthenticationAuthKey.scala} | 48 ++++++++--------------
 .../main/scala/whisk/core/entity/CacheKey.scala    |  4 +-
 .../scala/whisk/core/entity/GenericAuthKey.scala   | 48 ++++++++++++++++++++++
 .../main/scala/whisk/core/entity/Identity.scala    |  7 ++--
 .../main/scala/whisk/core/entity/WhiskAuth.scala   |  4 +-
 .../scala/whisk/core/controller/Authenticate.scala |  4 +-
 .../scala/whisk/core/controller/Triggers.scala     | 22 ++++++----
 .../core/loadBalancer/InvokerSupervision.scala     |  6 ++-
 .../whisk/core/containerpool/ContainerProxy.scala  | 19 +++++----
 .../scala/whisk/core/admin/WskAdminTests.scala     |  8 ++--
 .../logging/ElasticSearchLogStoreTests.scala       |  3 +-
 .../logging/SplunkLogStoreTests.scala              |  3 +-
 .../test/DockerToActivationLogStoreTests.scala     |  3 +-
 .../containerpool/test/ContainerPoolTests.scala    |  2 +-
 .../containerpool/test/ContainerProxyTests.scala   |  5 ++-
 .../core/controller/test/AuthenticateTests.scala   | 20 +++++----
 .../controller/test/EntitlementProviderTests.scala |  6 ++-
 .../core/controller/test/WebActionsApiTests.scala  |  4 +-
 .../core/controller/test/WhiskAuthHelpers.scala    | 14 ++-----
 .../whisk/core/database/UserCommandTests.scala     | 26 ++++++------
 .../scala/whisk/core/database/test/DbUtils.scala   |  6 +--
 .../test/behavior/ArtifactStoreBehaviorBase.scala  |  2 +-
 .../ArtifactStoreSubjectQueryBehaviors.scala       | 24 ++++++-----
 .../scala/whisk/core/entity/test/SchemaTests.scala | 37 ++++++++---------
 .../invoker/test/NamespaceBlacklistTests.scala     | 10 ++---
 .../test/InvokerSupervisionTests.scala             |  2 +-
 .../scala/whisk/core/database/UserCommand.scala    | 19 +++------
 27 files changed, 200 insertions(+), 156 deletions(-)

diff --git a/common/scala/src/main/scala/whisk/core/entity/AuthKey.scala b/common/scala/src/main/scala/whisk/core/entity/BasicAuthenticationAuthKey.scala
similarity index 57%
rename from common/scala/src/main/scala/whisk/core/entity/AuthKey.scala
rename to common/scala/src/main/scala/whisk/core/entity/BasicAuthenticationAuthKey.scala
index bd1a24c..7007711 100644
--- a/common/scala/src/main/scala/whisk/core/entity/AuthKey.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/BasicAuthenticationAuthKey.scala
@@ -17,42 +17,25 @@
 
 package whisk.core.entity
 
+import akka.http.scaladsl.model.headers.{BasicHttpCredentials, HttpCredentials}
 import spray.json._
 import spray.json.DefaultJsonProtocol._
 
 /**
- * Authentication key, consisting of a UUID and Secret.
+ * Authentication key for Basic Authentication, consisting of a UUID and Secret.
  *
- * It is a value type (hence == is .equals, immutable and cannot be assigned null).
- * The constructor is private so that argument requirements are checked and normalized
- * before creating a new instance.
- *
- * @param k (uuid, key) the uuid and key, assured to be non-null because both types are values
+ * @param uuid the uuid assured to be non-null because both types are values
+ * @param key the key assured to be non-null because both types are values
  */
-protected[core] class AuthKey private (private val k: (UUID, Secret)) extends AnyVal {
-  def uuid: UUID = k._1
-  def key: Secret = k._2
-  def revoke = new AuthKey(uuid, Secret())
+protected[core] case class BasicAuthenticationAuthKey(uuid: UUID, key: Secret)
+    extends GenericAuthKey(JsObject("api_key" -> s"$uuid:$key".toJson)) {
+  def revoke = new BasicAuthenticationAuthKey(uuid, Secret())
   def compact: String = s"$uuid:$key"
   override def toString: String = uuid.toString
+  override def getCredentials: Option[HttpCredentials] = Some(BasicHttpCredentials(uuid.asString, key.asString))
 }
 
-protected[core] object AuthKey {
-
-  /**
-   * Creates AuthKey.
-   *
-   * @param uuid the uuid, assured to be non-null because UUID is a value
-   * @param key the key, assured to be non-null because Secret is a value
-   */
-  protected[core] def apply(uuid: UUID, key: Secret): AuthKey = new AuthKey(uuid, key)
-
-  /**
-   * Creates an auth key for a randomly generated UUID with a randomly generated secret.
-   *
-   * @return AuthKey
-   */
-  protected[core] def apply(): AuthKey = new AuthKey(UUID(), Secret())
+protected[core] object BasicAuthenticationAuthKey {
 
   /**
    * Creates AuthKey from a string where the uuid and key are separated by a colon.
@@ -64,18 +47,19 @@ protected[core] object AuthKey {
    * @throws IllegalArgumentException if argument is not well formed
    */
   @throws[IllegalArgumentException]
-  protected[core] def apply(str: String): AuthKey = {
+  protected[core] def apply(str: String): BasicAuthenticationAuthKey = {
     val (uuid, secret) = str.split(':').toList match {
       case k :: v :: _ => (k, v)
       case k :: Nil    => (k, "")
       case Nil         => ("", "")
     }
 
-    new AuthKey(UUID(uuid.trim), Secret(secret.trim))
+    new BasicAuthenticationAuthKey(UUID(uuid.trim), Secret(secret.trim))
   }
 
-  protected[core] implicit val serdes: RootJsonFormat[AuthKey] = new RootJsonFormat[AuthKey] {
-    def write(k: AuthKey) = JsString(k.compact)
-    def read(value: JsValue) = AuthKey(value.convertTo[String])
-  }
+  /**
+   * Creates an auth key for a randomly generated UUID with a randomly generated secret.
+   */
+  protected[core] def apply(): BasicAuthenticationAuthKey = new BasicAuthenticationAuthKey(UUID(), Secret())
+
 }
diff --git a/common/scala/src/main/scala/whisk/core/entity/CacheKey.scala b/common/scala/src/main/scala/whisk/core/entity/CacheKey.scala
index 8fc8f83..bc60a64 100644
--- a/common/scala/src/main/scala/whisk/core/entity/CacheKey.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/CacheKey.scala
@@ -39,8 +39,8 @@ object CacheKey extends DefaultJsonProtocol {
 
   def apply(key: Any): CacheKey = {
     key match {
-      case e: EntityName => CacheKey(e.asString, None)
-      case a: AuthKey    => CacheKey(a.uuid.asString, Some(a.key.asString))
+      case e: EntityName                 => CacheKey(e.asString, None)
+      case a: BasicAuthenticationAuthKey => CacheKey(a.uuid.asString, Some(a.key.asString))
       case d: DocInfo => {
         val revision = if (d.rev.empty) None else Some(d.rev.asString)
         CacheKey(d.id.asString, revision)
diff --git a/common/scala/src/main/scala/whisk/core/entity/GenericAuthKey.scala b/common/scala/src/main/scala/whisk/core/entity/GenericAuthKey.scala
new file mode 100644
index 0000000..5db744f
--- /dev/null
+++ b/common/scala/src/main/scala/whisk/core/entity/GenericAuthKey.scala
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package whisk.core.entity
+
+import akka.http.scaladsl.model.headers.HttpCredentials
+import spray.json._
+
+/**
+ * Base class for Authentication
+ *
+ * This is used to transport data generated by the authentication directive to the invoker.
+ * The invoker can passed this data to the user container.
+ *
+ * Be aware that this class can by itself not be used to provide meaningful authentication.
+ */
+protected[core] class GenericAuthKey(val toEnvironment: JsObject) {
+  def getCredentials: Option[HttpCredentials] = None
+  def canEqual(a: Any) = a.isInstanceOf[GenericAuthKey]
+  override def equals(that: Any): Boolean =
+    that match {
+      case that: GenericAuthKey => (this.toEnvironment == that.asInstanceOf[GenericAuthKey].toEnvironment)
+      case _                    => false
+    }
+}
+
+protected[core] object GenericAuthKey {
+
+  protected[core] implicit val serdes: RootJsonFormat[GenericAuthKey] = new RootJsonFormat[GenericAuthKey] {
+    def write(k: GenericAuthKey) = k.toEnvironment
+    def read(value: JsValue) = new GenericAuthKey(value.asJsObject)
+  }
+
+}
diff --git a/common/scala/src/main/scala/whisk/core/entity/Identity.scala b/common/scala/src/main/scala/whisk/core/entity/Identity.scala
index f5526f9..f0ebc21 100644
--- a/common/scala/src/main/scala/whisk/core/entity/Identity.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/Identity.scala
@@ -45,7 +45,7 @@ protected[core] object Namespace extends DefaultJsonProtocol {
 
 protected[core] case class Identity(subject: Subject,
                                     namespace: Namespace,
-                                    authkey: AuthKey,
+                                    authkey: GenericAuthKey,
                                     rights: Set[Privilege],
                                     limits: UserLimits = UserLimits())
 
@@ -86,7 +86,8 @@ object Identity extends MultipleReadersSingleWriterCache[Identity, DocInfo] with
       })
   }
 
-  def get(datastore: AuthStore, authkey: AuthKey)(implicit transid: TransactionId): Future[Identity] = {
+  def get(datastore: AuthStore, authkey: BasicAuthenticationAuthKey)(
+    implicit transid: TransactionId): Future[Identity] = {
     implicit val logger: Logging = datastore.logging
     implicit val ec = datastore.executionContext
 
@@ -132,7 +133,7 @@ object Identity extends MultipleReadersSingleWriterCache[Identity, DocInfo] with
         Identity(
           subject,
           Namespace(EntityName(namespace), UUID(uuid)),
-          AuthKey(UUID(uuid), Secret(secret)),
+          BasicAuthenticationAuthKey(UUID(uuid), Secret(secret)),
           Privilege.ALL,
           limits)
       case _ =>
diff --git a/common/scala/src/main/scala/whisk/core/entity/WhiskAuth.scala b/common/scala/src/main/scala/whisk/core/entity/WhiskAuth.scala
index 2de9a86..56d6d34 100644
--- a/common/scala/src/main/scala/whisk/core/entity/WhiskAuth.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/WhiskAuth.scala
@@ -25,7 +25,7 @@ import scala.util.Try
  * database. Each namespace has its own key which is used to determine
  * the {@ Identity} of the user calling.
  */
-protected[core] case class WhiskNamespace(namespace: Namespace, authkey: AuthKey)
+protected[core] case class WhiskNamespace(namespace: Namespace, authkey: BasicAuthenticationAuthKey)
 
 protected[core] object WhiskNamespace extends DefaultJsonProtocol {
   implicit val serdes = new RootJsonFormat[WhiskNamespace] {
@@ -36,7 +36,7 @@ protected[core] object WhiskNamespace extends DefaultJsonProtocol {
       Try {
         value.asJsObject.getFields("name", "uuid", "key") match {
           case Seq(JsString(n), JsString(u), JsString(k)) =>
-            WhiskNamespace(Namespace(EntityName(n), UUID(u)), AuthKey(UUID(u), Secret(k)))
+            WhiskNamespace(Namespace(EntityName(n), UUID(u)), BasicAuthenticationAuthKey(UUID(u), Secret(k)))
         }
       } getOrElse deserializationError("namespace record malformed")
   }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala b/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
index cb60100..bcf4b21 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
@@ -28,7 +28,7 @@ import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.database.NoDocumentException
 import whisk.core.entity.types.AuthStore
-import whisk.core.entity.AuthKey
+import whisk.core.entity.BasicAuthenticationAuthKey
 import whisk.core.entity.Identity
 import whisk.core.entity.Secret
 import whisk.core.entity.UUID
@@ -49,7 +49,7 @@ trait Authenticate {
     credentials flatMap { pw =>
       Try {
         // authkey deserialization is wrapped in a try to guard against malformed values
-        val authkey = AuthKey(UUID(pw.username), Secret(pw.password))
+        val authkey = BasicAuthenticationAuthKey(UUID(pw.username), Secret(pw.password))
         logging.info(this, s"authenticate: ${authkey.uuid}")
         val future = Identity.get(authStore, authkey) map { result =>
           if (authkey == result.authkey) {
diff --git a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
index 88010d7..a295b9f 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -28,7 +28,7 @@ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
 import akka.http.scaladsl.model.HttpMethods.POST
 import akka.http.scaladsl.model.StatusCodes.{Accepted, BadRequest, InternalServerError, NoContent, OK, ServerError}
 import akka.http.scaladsl.model.Uri.Path
-import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
+import akka.http.scaladsl.model.headers.Authorization
 import akka.http.scaladsl.model._
 import akka.http.scaladsl.server.{RequestContext, RouteResult}
 import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller}
@@ -387,15 +387,17 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
       .map(pkg => Path / pkg.namespace / rule.action.name.asString)
       .getOrElse(Path / rule.action.name.asString)
 
-    val request = HttpRequest(
-      method = POST,
-      uri = url.withPath(actionUrl ++ actionPath),
-      headers = List(
-        Authorization(BasicHttpCredentials(user.authkey.uuid.asString, user.authkey.key.asString)),
-        transid.toHeader),
-      entity = HttpEntity(MediaTypes.`application/json`, args.compactPrint))
+    user.authkey.getCredentials
+      .map { creds =>
+        val request = HttpRequest(
+          method = POST,
+          uri = url.withPath(actionUrl ++ actionPath),
+          headers = List(Authorization(creds), transid.toHeader),
+          entity = HttpEntity(MediaTypes.`application/json`, args.compactPrint))
 
-    singleRequest(request)
+        singleRequest(request)
+      }
+      .getOrElse(Future.failed(new NoCredentialsAvailable()))
   }
 
   /** Contains the result of invoking a rule */
@@ -422,4 +424,6 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
   /** Custom unmarshaller for query parameters "skip" for "list" operations. */
   private implicit val stringToListSkip: Unmarshaller[String, ListSkip] = RestApiCommons.stringToListSkip(collection)
 
+  private case class NoCredentialsAvailable() extends IllegalArgumentException
+
 }
diff --git a/core/controller/src/main/scala/whisk/core/loadBalancer/InvokerSupervision.scala b/core/controller/src/main/scala/whisk/core/loadBalancer/InvokerSupervision.scala
index b8f29f1..15646b8 100644
--- a/core/controller/src/main/scala/whisk/core/loadBalancer/InvokerSupervision.scala
+++ b/core/controller/src/main/scala/whisk/core/loadBalancer/InvokerSupervision.scala
@@ -222,7 +222,11 @@ object InvokerPool {
   val healthActionIdentity = {
     val whiskSystem = "whisk.system"
     val uuid = UUID()
-    Identity(Subject(whiskSystem), Namespace(EntityName(whiskSystem), uuid), AuthKey(uuid, Secret()), Set[Privilege]())
+    Identity(
+      Subject(whiskSystem),
+      Namespace(EntityName(whiskSystem), uuid),
+      BasicAuthenticationAuthKey(uuid, Secret()),
+      Set[Privilege]())
   }
 
   /** An action to use for monitoring invoker health. */
diff --git a/core/invoker/src/main/scala/whisk/core/containerpool/ContainerProxy.scala b/core/invoker/src/main/scala/whisk/core/containerpool/ContainerProxy.scala
index c0914b9..566d800 100644
--- a/core/invoker/src/main/scala/whisk/core/containerpool/ContainerProxy.scala
+++ b/core/invoker/src/main/scala/whisk/core/containerpool/ContainerProxy.scala
@@ -354,8 +354,9 @@ class ContainerProxy(
       .flatMap { initInterval =>
         val parameters = job.msg.content getOrElse JsObject()
 
+        val authEnvironment = job.msg.user.authkey.toEnvironment
+
         val environment = JsObject(
-          "api_key" -> job.msg.user.authkey.compact.toJson,
           "namespace" -> job.msg.user.namespace.name.toJson,
           "action_name" -> job.msg.action.qualifiedNameWithLeadingSlash.toJson,
           "activation_id" -> job.msg.activationId.toString.toJson,
@@ -363,13 +364,15 @@ class ContainerProxy(
           // but potentially under-estimates actual deadline
           "deadline" -> (Instant.now.toEpochMilli + actionTimeout.toMillis).toString.toJson)
 
-        container.run(parameters, environment, actionTimeout)(job.msg.transid).map {
-          case (runInterval, response) =>
-            val initRunInterval = initInterval
-              .map(i => Interval(runInterval.start.minusMillis(i.duration.toMillis), runInterval.end))
-              .getOrElse(runInterval)
-            ContainerProxy.constructWhiskActivation(job, initInterval, initRunInterval, response)
-        }
+        container
+          .run(parameters, JsObject(authEnvironment.fields ++ environment.fields), actionTimeout)(job.msg.transid)
+          .map {
+            case (runInterval, response) =>
+              val initRunInterval = initInterval
+                .map(i => Interval(runInterval.start.minusMillis(i.duration.toMillis), runInterval.end))
+                .getOrElse(runInterval)
+              ContainerProxy.constructWhiskActivation(job, initInterval, initRunInterval, response)
+          }
       }
       .recover {
         case InitializationError(interval, response) =>
diff --git a/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala b/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
index 3b5874f..c76dff9 100644
--- a/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
+++ b/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
@@ -29,7 +29,7 @@ import common.TestHelpers
 import common.rest.WskRest
 import common.WskAdmin
 import common.WskProps
-import whisk.core.entity.AuthKey
+import whisk.core.entity.BasicAuthenticationAuthKey
 import whisk.core.entity.Subject
 import common.TestUtils
 import scala.util.Try
@@ -61,7 +61,7 @@ class WskAdminTests extends TestHelpers with Matchers with BeforeAndAfterAll {
 
   it should "CRD a subject" in {
     val wskadmin = new RunWskAdminCmd {}
-    val auth = AuthKey()
+    val auth = BasicAuthenticationAuthKey()
     val subject = Subject().asString
     try {
       println(s"CRD subject: $subject")
@@ -104,7 +104,7 @@ class WskAdminTests extends TestHelpers with Matchers with BeforeAndAfterAll {
 
   it should "list all namespaces for a subject" in {
     val wskadmin = new RunWskAdminCmd {}
-    val auth = AuthKey()
+    val auth = BasicAuthenticationAuthKey()
     val subject = Subject().asString
     try {
       println(s"CRD subject: $subject")
@@ -131,7 +131,7 @@ class WskAdminTests extends TestHelpers with Matchers with BeforeAndAfterAll {
 
   it should "block and unblock a user respectively" in {
     val wskadmin = new RunWskAdminCmd {}
-    val auth = AuthKey()
+    val auth = BasicAuthenticationAuthKey()
     val subject1 = Subject().asString
     val subject2 = Subject().asString
     val commonNamespace = "testspace"
diff --git a/tests/src/test/scala/whisk/core/containerpool/logging/ElasticSearchLogStoreTests.scala b/tests/src/test/scala/whisk/core/containerpool/logging/ElasticSearchLogStoreTests.scala
index 7ed3910..bd3e0a0 100644
--- a/tests/src/test/scala/whisk/core/containerpool/logging/ElasticSearchLogStoreTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/logging/ElasticSearchLogStoreTests.scala
@@ -53,7 +53,8 @@ class ElasticSearchLogStoreTests
   implicit val materializer: ActorMaterializer = ActorMaterializer()
 
   private val uuid = UUID()
-  private val user = Identity(Subject(), Namespace(EntityName("testSpace"), uuid), AuthKey(uuid, Secret()), Set())
+  private val user =
+    Identity(Subject(), Namespace(EntityName("testSpace"), uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set())
   private val activationId = ActivationId.generate()
 
   private val defaultLogSchema =
diff --git a/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala b/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala
index a087a86..526b17c 100644
--- a/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala
@@ -69,7 +69,8 @@ class SplunkLogStoreTests
   val endTime = "2007-12-03T10:15:45Z"
   val endTimePlus5 = "2007-12-03T10:15:50Z" //queried end time range is endTime+5
   val uuid = UUID()
-  val user = Identity(Subject(), Namespace(EntityName("testSpace"), uuid), AuthKey(uuid, Secret()), Set())
+  val user =
+    Identity(Subject(), Namespace(EntityName("testSpace"), uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set())
   val request = HttpRequest(
     method = POST,
     uri = "https://some.url",
diff --git a/tests/src/test/scala/whisk/core/containerpool/logging/test/DockerToActivationLogStoreTests.scala b/tests/src/test/scala/whisk/core/containerpool/logging/test/DockerToActivationLogStoreTests.scala
index 4df8bde..8241799 100644
--- a/tests/src/test/scala/whisk/core/containerpool/logging/test/DockerToActivationLogStoreTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/logging/test/DockerToActivationLogStoreTests.scala
@@ -41,7 +41,8 @@ class DockerToActivationLogStoreTests extends FlatSpec with Matchers with WskAct
   def await[T](future: Future[T]) = Await.result(future, 1.minute)
 
   val uuid = UUID()
-  val user = Identity(Subject(), Namespace(EntityName("testSpace"), uuid), AuthKey(uuid, Secret()), Set())
+  val user =
+    Identity(Subject(), Namespace(EntityName("testSpace"), uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set())
   val exec = CodeExecAsString(RuntimeManifest("actionKind", ImageName("testImage")), "testCode", None)
   val action = ExecutableWhiskAction(user.namespace.name.toPath, EntityName("actionName"), exec)
   val activation =
diff --git a/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala b/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
index 9388b76..66630b2 100644
--- a/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
@@ -76,7 +76,7 @@ class ContainerPoolTests
       TransactionId.testing,
       action.fullyQualifiedName(true),
       action.rev,
-      Identity(Subject(), Namespace(invocationNamespace, uuid), AuthKey(uuid, Secret()), Set()),
+      Identity(Subject(), Namespace(invocationNamespace, uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set()),
       ActivationId.generate(),
       ControllerInstanceId("0"),
       blocking = false,
diff --git a/tests/src/test/scala/whisk/core/containerpool/test/ContainerProxyTests.scala b/tests/src/test/scala/whisk/core/containerpool/test/ContainerProxyTests.scala
index 87ed494..9c5ef66 100644
--- a/tests/src/test/scala/whisk/core/containerpool/test/ContainerProxyTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/test/ContainerProxyTests.scala
@@ -85,7 +85,7 @@ class ContainerProxyTests
     messageTransId,
     action.fullyQualifiedName(true),
     action.rev,
-    Identity(Subject(), Namespace(invocationNamespace, uuid), AuthKey(uuid, Secret()), Set()),
+    Identity(Subject(), Namespace(invocationNamespace, uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set()),
     ActivationId.generate(),
     ControllerInstanceId("0"),
     blocking = false,
@@ -761,10 +761,11 @@ class ContainerProxyTests
     override def run(parameters: JsObject, environment: JsObject, timeout: FiniteDuration)(
       implicit transid: TransactionId): Future[(Interval, ActivationResponse)] = {
       runCount += 1
-      environment.fields("api_key") shouldBe message.user.authkey.toJson
       environment.fields("namespace") shouldBe invocationNamespace.name.toJson
       environment.fields("action_name") shouldBe message.action.qualifiedNameWithLeadingSlash.toJson
       environment.fields("activation_id") shouldBe message.activationId.toJson
+      val authEnvironment = environment.fields.filterKeys(message.user.authkey.toEnvironment.fields.contains)
+      message.user.authkey.toEnvironment shouldBe authEnvironment.toJson.asJsObject
       val deadline = Instant.ofEpochMilli(environment.fields("deadline").convertTo[String].toLong)
       val maxDeadline = Instant.now.plusMillis(timeout.toMillis)
 
diff --git a/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala b/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
index 8a6d254..4287459 100644
--- a/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
@@ -52,8 +52,12 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
     val uuid2 = UUID()
 
     val namespaces = Set(
-      WhiskNamespace(Namespace(MakeName.next("authenticatev_tests"), uuid1), AuthKey(uuid1, Secret())),
-      WhiskNamespace(Namespace(MakeName.next("authenticatev_tests"), uuid2), AuthKey(uuid2, Secret())))
+      WhiskNamespace(
+        Namespace(MakeName.next("authenticatev_tests"), uuid1),
+        BasicAuthenticationAuthKey(uuid1, Secret())),
+      WhiskNamespace(
+        Namespace(MakeName.next("authenticatev_tests"), uuid2),
+        BasicAuthenticationAuthKey(uuid2, Secret())))
     val entry = WhiskAuth(subject, namespaces)
     put(authStore, entry) // this test entry is reclaimed when the test completes
 
@@ -81,7 +85,7 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
     // check that invalid keys are rejected
     val ns = namespaces.head
     val key = ns.authkey.key.asString
-    Seq(key.drop(1), key.dropRight(1), key + "x", AuthKey().key.asString).foreach { k =>
+    Seq(key.drop(1), key.dropRight(1), key + "x", BasicAuthenticationAuthKey().key.asString).foreach { k =>
       val pass = BasicHttpCredentials(ns.authkey.uuid.asString, k)
       val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
       user shouldBe empty
@@ -91,17 +95,17 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
   it should "not log key during validation" in {
     implicit val tid = transid()
     val creds = WhiskAuthHelpers.newIdentity()
-    val pass = BasicHttpCredentials(creds.authkey.uuid.asString, creds.authkey.key.asString)
-    val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
+    val pass = creds.authkey.getCredentials.asInstanceOf[Option[BasicHttpCredentials]]
+    val user = Await.result(validateCredentials(pass), dbOpTimeout)
     user should be(None)
-    stream.toString should not include creds.authkey.key.asString
+    stream.toString should not include pass.get.password
   }
 
   it should "not authorize an unknown user" in {
     implicit val tid = transid()
     val creds = WhiskAuthHelpers.newIdentity()
-    val pass = BasicHttpCredentials(creds.authkey.uuid.asString, creds.authkey.key.asString)
-    val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
+    val pass = creds.authkey.getCredentials.asInstanceOf[Option[BasicHttpCredentials]]
+    val user = Await.result(validateCredentials(pass), dbOpTimeout)
     user should be(None)
   }
 
diff --git a/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala b/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
index f95c5c0..5a77b14 100644
--- a/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
@@ -157,7 +157,11 @@ class EntitlementProviderTests extends ControllerTestCommon with ScalaFutures {
     val subject = Subject()
     val uuid = UUID()
     val someUser =
-      Identity(subject, Namespace(EntityName(subject.asString), uuid), AuthKey(uuid, Secret()), Set(Privilege.ACTIVATE))
+      Identity(
+        subject,
+        Namespace(EntityName(subject.asString), uuid),
+        BasicAuthenticationAuthKey(uuid, Secret()),
+        Set(Privilege.ACTIVATE))
     val collections = Seq(ACTIONS, RULES, TRIGGERS)
     val resources = collections map { Resource(someUser.namespace.name.toPath, _, Some("xyz")) }
     resources foreach { r =>
diff --git a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
index d01381d..1fd2ab6 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -123,7 +123,7 @@ class WebActionsApiTests extends FlatSpec with Matchers with WebActionsApiBaseTe
 trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEach with WhiskWebActionsApi {
   val uuid = UUID()
   val systemId = Subject()
-  val systemKey = AuthKey(uuid, Secret())
+  val systemKey = BasicAuthenticationAuthKey(uuid, Secret())
   val systemIdentity =
     Future.successful(Identity(systemId, Namespace(EntityName(systemId.asString), uuid), systemKey, Privilege.ALL))
   override lazy val entitlementProvider = new TestingEntitlementProvider(whiskConfig, loadBalancer)
@@ -354,7 +354,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
       }
     }
 
-    it should s"reject requests when identity, package or action lookup fail or missing annotation (auth? ${creds.isDefined})" in {
+    it should s"reject requests when Identity, package or action lookup fail or missing annotation (auth? ${creds.isDefined})" in {
       implicit val tid = transid()
 
       // the first of these fails in the identity lookup,
diff --git a/tests/src/test/scala/whisk/core/controller/test/WhiskAuthHelpers.scala b/tests/src/test/scala/whisk/core/controller/test/WhiskAuthHelpers.scala
index 7d21aee..e5b28c5 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WhiskAuthHelpers.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WhiskAuthHelpers.scala
@@ -17,21 +17,15 @@
 
 package whisk.core.controller.test
 
-import whisk.core.entity.EntityName
-import whisk.core.entity.AuthKey
-import whisk.core.entity.WhiskNamespace
-import whisk.core.entity.WhiskAuth
-import whisk.core.entity.Subject
+import whisk.core.entity._
 import whisk.core.entitlement.Privilege
-import whisk.core.entity.Identity
-import whisk.core.entity.Namespace
 
 object WhiskAuthHelpers {
-  def newAuth(s: Subject = Subject(), k: AuthKey = AuthKey()) = {
+  def newAuth(s: Subject = Subject(), k: BasicAuthenticationAuthKey = BasicAuthenticationAuthKey()) = {
     WhiskAuth(s, Set(WhiskNamespace(Namespace(EntityName(s.asString), k.uuid), k)))
   }
 
-  def newIdentity(s: Subject = Subject(), k: AuthKey = AuthKey()) = {
-    Identity(s, Namespace(EntityName(s.asString), k.uuid), k, Privilege.ALL)
+  def newIdentity(s: Subject = Subject(), uuid: UUID = UUID(), k: GenericAuthKey = BasicAuthenticationAuthKey()) = {
+    Identity(s, Namespace(EntityName(s.asString), uuid), k, Privilege.ALL)
   }
 }
diff --git a/tests/src/test/scala/whisk/core/database/UserCommandTests.scala b/tests/src/test/scala/whisk/core/database/UserCommandTests.scala
index 680e2ea..69cbb07 100644
--- a/tests/src/test/scala/whisk/core/database/UserCommandTests.scala
+++ b/tests/src/test/scala/whisk/core/database/UserCommandTests.scala
@@ -22,7 +22,7 @@ import org.scalatest.FlatSpec
 import org.scalatest.junit.JUnitRunner
 import whisk.common.TransactionId
 import whisk.core.cli.{CommandMessages, Conf, WhiskAdmin}
-import whisk.core.entity.{AuthKey, DocId, DocInfo, EntityName, Identity, Namespace, Subject, WhiskAuth, WhiskNamespace}
+import whisk.core.entity._
 
 import scala.collection.mutable.ListBuffer
 import scala.concurrent.duration.Duration
@@ -56,7 +56,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
 
   it should "create a user" in {
     val subject = newSubject()
-    val key = AuthKey()
+    val key = BasicAuthenticationAuthKey()
     val conf = new Conf(Seq("user", "create", "--auth", key.compact, subject))
     val admin = WhiskAdmin(conf)
     admin.executeCommand().futureValue.right.get shouldBe key.compact
@@ -70,13 +70,13 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
 
   it should "add namespace to existing user" in {
     val subject = newSubject()
-    val key = AuthKey()
+    val key = BasicAuthenticationAuthKey()
 
     //Create user
     WhiskAdmin(new Conf(Seq("user", "create", "--auth", key.compact, subject))).executeCommand().futureValue
 
     //Add new namespace
-    val key2 = AuthKey()
+    val key2 = BasicAuthenticationAuthKey()
     resultOk("user", "create", "--auth", key2.compact, "--namespace", "foo", subject) shouldBe key2.compact
 
     //Adding same namespace should fail
@@ -91,7 +91,8 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
   it should "not add namespace to a blocked user" in {
     val subject = newSubject()
     val ns = randomString()
-    val blockedAuth = new ExtendedAuth(Subject(subject), Set(newNS(EntityName(ns), AuthKey())), Some(true))
+    val blockedAuth =
+      new ExtendedAuth(Subject(subject), Set(newNS(EntityName(ns), BasicAuthenticationAuthKey())), Some(true))
     val authStore2 = UserCommand.createDataStore()
 
     implicit val tid = transid()
@@ -110,7 +111,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
 
   it should "delete existing user" in {
     val subject = newSubject()
-    val key = AuthKey()
+    val key = BasicAuthenticationAuthKey()
 
     //Create user
     WhiskAdmin(new Conf(Seq("user", "create", "--auth", key.compact, subject))).executeCommand().futureValue
@@ -157,7 +158,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
 
     val ns1 = newNS()
     val ns2 = newNS()
-    val ns3 = newNS(EntityName(subject), AuthKey())
+    val ns3 = newNS(EntityName(subject), BasicAuthenticationAuthKey())
 
     val auth = WhiskAuth(Subject(subject), Set(ns1, ns2, ns3))
     put(authStore, auth)
@@ -181,7 +182,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
   behavior of "whois"
 
   it should "not get subject for missing subject" in {
-    resultNotOk("user", "whois", AuthKey().compact) shouldBe CommandMessages.subjectMissing
+    resultNotOk("user", "whois", BasicAuthenticationAuthKey().compact) shouldBe CommandMessages.subjectMissing
   }
 
   it should "get key for existing user" in {
@@ -189,7 +190,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
     val subject = newSubject()
 
     val ns1 = newNS()
-    val ns3 = newNS(EntityName(subject), AuthKey())
+    val ns3 = newNS(EntityName(subject), BasicAuthenticationAuthKey())
 
     val auth = WhiskAuth(Subject(subject), Set(ns1, ns3))
     put(authStore, auth)
@@ -205,7 +206,7 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
   it should "list keys associated with given namespace" in {
     implicit val tid = transid()
     def newWhiskAuth(ns: String*) =
-      WhiskAuth(Subject(newSubject()), ns.map(n => newNS(EntityName(n), AuthKey())).toSet)
+      WhiskAuth(Subject(newSubject()), ns.map(n => newNS(EntityName(n), BasicAuthenticationAuthKey())).toSet)
 
     def key(a: WhiskAuth, ns: String) = a.namespaces.find(_.namespace.name.asString == ns).map(_.authkey).get
 
@@ -279,9 +280,10 @@ class UserCommandTests extends FlatSpec with WhiskAdminCliTestBase {
     super.cleanup()
   }
 
-  private def newNS(): WhiskNamespace = newNS(EntityName(randomString()), AuthKey())
+  private def newNS(): WhiskNamespace = newNS(EntityName(randomString()), BasicAuthenticationAuthKey())
 
-  private def newNS(name: EntityName, authKey: AuthKey) = WhiskNamespace(Namespace(name, authKey.uuid), authKey)
+  private def newNS(name: EntityName, authKey: BasicAuthenticationAuthKey) =
+    WhiskNamespace(Namespace(name, authKey.uuid), authKey)
 
   private def newSubject(): String = {
     val subject = randomString()
diff --git a/tests/src/test/scala/whisk/core/database/test/DbUtils.scala b/tests/src/test/scala/whisk/core/database/test/DbUtils.scala
index 10ffcc2..6d39304 100644
--- a/tests/src/test/scala/whisk/core/database/test/DbUtils.scala
+++ b/tests/src/test/scala/whisk/core/database/test/DbUtils.scala
@@ -153,9 +153,9 @@ trait DbUtils extends Assertions {
    * Wait on view for the authentication table. This is like the other waitOnViews but
    * specific to the WhiskAuth records.
    */
-  def waitOnView(db: AuthStore, authkey: AuthKey, count: Int)(implicit context: ExecutionContext,
-                                                              transid: TransactionId,
-                                                              timeout: Duration) = {
+  def waitOnView(db: AuthStore, authkey: BasicAuthenticationAuthKey, count: Int)(implicit context: ExecutionContext,
+                                                                                 transid: TransactionId,
+                                                                                 timeout: Duration) = {
     val success = retry(() => {
       Identity.list(db, List(authkey.uuid.asString, authkey.key.asString)) map { l =>
         if (l.length != count) {
diff --git a/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreBehaviorBase.scala b/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreBehaviorBase.scala
index 96824ea..38ef12e 100644
--- a/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreBehaviorBase.scala
+++ b/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreBehaviorBase.scala
@@ -109,7 +109,7 @@ trait ArtifactStoreBehaviorBase
 
   protected def wskNS(name: String) = {
     val uuid = UUID()
-    WhiskNamespace(Namespace(EntityName(name), uuid), AuthKey(uuid, Secret()))
+    WhiskNamespace(Namespace(EntityName(name), uuid), BasicAuthenticationAuthKey(uuid, Secret()))
   }
 
   private val exec = BlackBoxExec(ExecManifest.ImageName("image"), None, None, native = false)
diff --git a/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreSubjectQueryBehaviors.scala b/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreSubjectQueryBehaviors.scala
index ace1955..a50b29c 100644
--- a/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreSubjectQueryBehaviors.scala
+++ b/tests/src/test/scala/whisk/core/database/test/behavior/ArtifactStoreSubjectQueryBehaviors.scala
@@ -35,8 +35,8 @@ trait ArtifactStoreSubjectQueryBehaviors extends ArtifactStoreBehaviorBase {
     implicit val tid: TransactionId = transid()
     val uuid1 = UUID()
     val uuid2 = UUID()
-    val ak1 = AuthKey(uuid1, Secret())
-    val ak2 = AuthKey(uuid2, Secret())
+    val ak1 = BasicAuthenticationAuthKey(uuid1, Secret())
+    val ak2 = BasicAuthenticationAuthKey(uuid2, Secret())
     val ns1 = Namespace(aname(), uuid1)
     val ns2 = Namespace(aname(), uuid2)
     val subs =
@@ -57,7 +57,7 @@ trait ArtifactStoreSubjectQueryBehaviors extends ArtifactStoreBehaviorBase {
     implicit val tid: TransactionId = transid()
     val uuid1 = UUID()
     val ns1 = Namespace(aname(), uuid1)
-    val ak1 = AuthKey()
+    val ak1 = BasicAuthenticationAuthKey()
     val auth = new ExtendedAuth(Subject(), Set(WhiskNamespace(ns1, ak1)), blocked = true)
     put(authStore, auth)
 
@@ -68,18 +68,20 @@ trait ArtifactStoreSubjectQueryBehaviors extends ArtifactStoreBehaviorBase {
     implicit val tid: TransactionId = transid()
     val uuid1 = UUID()
     val uuid2 = UUID()
-    val ak1 = AuthKey(uuid1, Secret())
-    val ak2 = AuthKey(uuid2, Secret())
+    val ak1 = BasicAuthenticationAuthKey(uuid1, Secret())
+    val ak2 = BasicAuthenticationAuthKey(uuid2, Secret())
     val ns1 = Namespace(aname(), uuid1)
     val ns2 = Namespace(aname(), uuid2)
 
     val auth = WhiskAuth(
       Subject(),
-      Set(WhiskNamespace(ns1, AuthKey(ak1.uuid, ak2.key)), WhiskNamespace(ns2, AuthKey(ak2.uuid, ak1.key))))
+      Set(
+        WhiskNamespace(ns1, BasicAuthenticationAuthKey(ak1.uuid, ak2.key)),
+        WhiskNamespace(ns2, BasicAuthenticationAuthKey(ak2.uuid, ak1.key))))
 
     put(authStore, auth)
 
-    waitOnView(authStore, AuthKey(ak1.uuid, ak2.key), 1)
+    waitOnView(authStore, BasicAuthenticationAuthKey(ak1.uuid, ak2.key), 1)
     Identity.get(authStore, ak1).failed.futureValue shouldBe a[NoDocumentException]
   }
 
@@ -87,8 +89,8 @@ trait ArtifactStoreSubjectQueryBehaviors extends ArtifactStoreBehaviorBase {
     implicit val tid: TransactionId = transid()
     val uuid1 = UUID()
     val uuid2 = UUID()
-    val ak1 = AuthKey(uuid1, Secret())
-    val ak2 = AuthKey(uuid2, Secret())
+    val ak1 = BasicAuthenticationAuthKey(uuid1, Secret())
+    val ak2 = BasicAuthenticationAuthKey(uuid2, Secret())
     val name1 = Namespace(aname(), uuid1)
     val name2 = Namespace(aname(), uuid2)
     val subs = Array(
@@ -118,8 +120,8 @@ trait ArtifactStoreSubjectQueryBehaviors extends ArtifactStoreBehaviorBase {
 
     val uuid1 = UUID()
     val uuid2 = UUID()
-    val ak1 = AuthKey(uuid1, Secret())
-    val ak2 = AuthKey(uuid2, Secret())
+    val ak1 = BasicAuthenticationAuthKey(uuid1, Secret())
+    val ak2 = BasicAuthenticationAuthKey(uuid2, Secret())
 
     //Create 3 limits entry where one has limits > 0 thus non blacklisted
     //And one blocked subject with 2 namespaces
diff --git a/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala b/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
index 05e2f8d..717eeba 100644
--- a/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/SchemaTests.scala
@@ -45,22 +45,6 @@ class SchemaTests extends FlatSpec with BeforeAndAfter with ExecHelpers with Mat
 
   behavior of "AuthKey"
 
-  it should "accept well formed keys" in {
-    val uuid = UUID()
-    val secret = Secret()
-    Seq(s"$uuid:$secret", s" $uuid: $secret", s"$uuid:$secret ", s" $uuid : $secret ").foreach { i =>
-      val k = AuthKey(i)
-      assert(k.uuid == uuid)
-      assert(k.key == secret)
-    }
-  }
-
-  it should "reject malformed ids" in {
-    Seq("", " ", ":", " : ", " :", ": ", "a:b").foreach { i =>
-      an[IllegalArgumentException] should be thrownBy AuthKey(i)
-    }
-  }
-
   behavior of "Privilege"
 
   it should "serdes a right" in {
@@ -108,16 +92,31 @@ class SchemaTests extends FlatSpec with BeforeAndAfter with ExecHelpers with Mat
 
   behavior of "Identity"
 
-  it should "serdes an identity" in {
+  it should "serdes write an identity" in {
     val i = WhiskAuthHelpers.newIdentity()
     val expected = JsObject(
       "subject" -> i.subject.asString.toJson,
       "namespace" -> i.namespace.toJson,
-      "authkey" -> i.authkey.compact.toJson,
+      "authkey" -> i.authkey.toEnvironment,
       "rights" -> Array("READ", "PUT", "DELETE", "ACTIVATE").toJson,
       "limits" -> JsObject())
     Identity.serdes.write(i) shouldBe expected
-    Identity.serdes.read(expected) shouldBe i
+  }
+
+  it should "serdes read an generic identity" in {
+    val uuid = UUID()
+    val subject = Subject("test_subject")
+    val entity = EntityName("test_subject")
+    val genericAuthKey = new GenericAuthKey(JsObject("test_key" -> "test_value".toJson))
+    val i = WhiskAuthHelpers.newIdentity(subject, uuid, genericAuthKey)
+
+    val json = JsObject(
+      "subject" -> Subject("test_subject").toJson,
+      "namespace" -> Namespace(entity, uuid).toJson,
+      "authkey" -> JsObject("test_key" -> "test_value".toJson),
+      "rights" -> Array("READ", "PUT", "DELETE", "ACTIVATE").toJson,
+      "limits" -> JsObject())
+    Identity.serdes.read(json) shouldBe i
   }
 
   behavior of "DocInfo"
diff --git a/tests/src/test/scala/whisk/core/invoker/test/NamespaceBlacklistTests.scala b/tests/src/test/scala/whisk/core/invoker/test/NamespaceBlacklistTests.scala
index eb5f74d..89afd07 100644
--- a/tests/src/test/scala/whisk/core/invoker/test/NamespaceBlacklistTests.scala
+++ b/tests/src/test/scala/whisk/core/invoker/test/NamespaceBlacklistTests.scala
@@ -69,27 +69,27 @@ class NamespaceBlacklistTests
     Identity(
       Subject(),
       Namespace(EntityName("testnamespace1"), uuid1),
-      AuthKey(uuid1, Secret()),
+      BasicAuthenticationAuthKey(uuid1, Secret()),
       Set.empty,
       UserLimits(invocationsPerMinute = Some(0))),
     Identity(
       Subject(),
       Namespace(EntityName("testnamespace2"), uuid2),
-      AuthKey(uuid2, Secret()),
+      BasicAuthenticationAuthKey(uuid2, Secret()),
       Set.empty,
       UserLimits(concurrentInvocations = Some(0))),
     Identity(
       Subject(),
       Namespace(EntityName("testnamespace3"), uuid3),
-      AuthKey(uuid3, Secret()),
+      BasicAuthenticationAuthKey(uuid3, Secret()),
       Set.empty,
       UserLimits(invocationsPerMinute = Some(1), concurrentInvocations = Some(1))))
 
   /* Subject document needed for the second test */
   val uuid4 = UUID()
   val uuid5 = UUID()
-  val ak4 = AuthKey(uuid4, Secret())
-  val ak5 = AuthKey(uuid5, Secret())
+  val ak4 = BasicAuthenticationAuthKey(uuid4, Secret())
+  val ak5 = BasicAuthenticationAuthKey(uuid5, Secret())
   val ns4 = Namespace(EntityName("different1"), uuid4)
   val ns5 = Namespace(EntityName("different2"), uuid5)
   val subject = WhiskAuth(Subject(), Set(WhiskNamespace(ns4, ak4), WhiskNamespace(ns5, ak5)))
diff --git a/tests/src/test/scala/whisk/core/loadBalancer/test/InvokerSupervisionTests.scala b/tests/src/test/scala/whisk/core/loadBalancer/test/InvokerSupervisionTests.scala
index 8e11417..36d0e42 100644
--- a/tests/src/test/scala/whisk/core/loadBalancer/test/InvokerSupervisionTests.scala
+++ b/tests/src/test/scala/whisk/core/loadBalancer/test/InvokerSupervisionTests.scala
@@ -190,7 +190,7 @@ class InvokerSupervisionTests
       user = Identity(
         Subject("unhealthyInvokerCheck"),
         Namespace(EntityName("unhealthyInvokerCheck"), uuid),
-        AuthKey(uuid, Secret()),
+        BasicAuthenticationAuthKey(uuid, Secret()),
         Set[Privilege]()),
       activationId = new ActivationIdGenerator {}.make(),
       rootControllerIndex = ControllerInstanceId("0"),
diff --git a/tools/admin/src/main/scala/whisk/core/database/UserCommand.scala b/tools/admin/src/main/scala/whisk/core/database/UserCommand.scala
index f4119dd..b491e8f 100644
--- a/tools/admin/src/main/scala/whisk/core/database/UserCommand.scala
+++ b/tools/admin/src/main/scala/whisk/core/database/UserCommand.scala
@@ -28,17 +28,7 @@ import whisk.common.{Logging, TransactionId}
 import whisk.core.cli.{CommandError, CommandMessages, IllegalState, WhiskCommand}
 import whisk.core.database.UserCommand.ExtendedAuth
 import whisk.core.entity.types._
-import whisk.core.entity.{
-  AuthKey,
-  DocInfo,
-  EntityName,
-  Identity,
-  Namespace,
-  Subject,
-  WhiskAuth,
-  WhiskDocumentReader,
-  WhiskNamespace
-}
+import whisk.core.entity._
 import whisk.http.Messages
 import whisk.spi.SpiLoader
 
@@ -85,7 +75,8 @@ class UserCommand extends Subcommand("user") with WhiskCommand {
 
     def isUUID(u: String) = Try(UUID.fromString(u)).isSuccess
 
-    def desiredNamespace(authKey: AuthKey) = Namespace(EntityName(namespace.getOrElse(subject()).trim), authKey.uuid)
+    def desiredNamespace(authKey: BasicAuthenticationAuthKey) =
+      Namespace(EntityName(namespace.getOrElse(subject()).trim), authKey.uuid)
   }
 
   val create = new CreateUserCmd
@@ -169,7 +160,7 @@ class UserCommand extends Subcommand("user") with WhiskCommand {
 
   def createUser(authStore: AuthStore)(implicit transid: TransactionId,
                                        ec: ExecutionContext): Future[Either[CommandError, String]] = {
-    val authKey = create.auth.map(AuthKey(_)).getOrElse(AuthKey())
+    val authKey = create.auth.map(BasicAuthenticationAuthKey(_)).getOrElse(BasicAuthenticationAuthKey())
     authStore
       .get[ExtendedAuth](DocInfo(create.subject()))
       .flatMap { auth =>
@@ -242,7 +233,7 @@ class UserCommand extends Subcommand("user") with WhiskCommand {
   def whoIs(authStore: AuthStore)(implicit transid: TransactionId,
                                   ec: ExecutionContext): Future[Either[CommandError, String]] = {
     Identity
-      .get(authStore, AuthKey(whois.authkey()))
+      .get(authStore, BasicAuthenticationAuthKey(whois.authkey()))
       .map { i =>
         val msg = Seq(s"subject: ${i.subject}", s"namespace: ${i.namespace}").mkString(Properties.lineSeparator)
         Right(msg)