From ef16febf409a26d743fa3542934a25e17c170574 Mon Sep 17 00:00:00 2001
From: Herman Slatman <hermanslatman@hotmail.com>
Date: Fri, 7 Jan 2022 16:59:55 +0100
Subject: [PATCH] Refactor ACME EAB queries

The ACME EAB keys are now also indexed by the provisioner. This
solves part of the issue in which too many EAB keys may be in
memory at a given time.
---
 acme/account.go                  |  14 +-
 acme/account_test.go             |  20 +-
 acme/api/account.go              |   4 +-
 acme/api/account_test.go         |  96 +++---
 acme/db.go                       |  48 +--
 acme/db/nosql/account.go         | 271 ++++++++++++----
 acme/db/nosql/account_test.go    | 518 ++++++++++++++++++-------------
 acme/db/nosql/nosql.go           |  26 +-
 authority/admin/api/acme.go      |  88 ++++--
 authority/admin/api/acme_test.go | 234 ++++++--------
 10 files changed, 766 insertions(+), 553 deletions(-)

diff --git a/acme/account.go b/acme/account.go
index 14a707e9..027d7be1 100644
--- a/acme/account.go
+++ b/acme/account.go
@@ -45,13 +45,13 @@ func KeyToID(jwk *jose.JSONWebKey) (string, error) {
 
 // ExternalAccountKey is an ACME External Account Binding key.
 type ExternalAccountKey struct {
-	ID          string    `json:"id"`
-	Provisioner string    `json:"provisioner"`
-	Reference   string    `json:"reference"`
-	AccountID   string    `json:"-"`
-	KeyBytes    []byte    `json:"-"`
-	CreatedAt   time.Time `json:"createdAt"`
-	BoundAt     time.Time `json:"boundAt,omitempty"`
+	ID            string    `json:"id"`
+	ProvisionerID string    `json:"provisionerID"`
+	Reference     string    `json:"reference"`
+	AccountID     string    `json:"-"`
+	KeyBytes      []byte    `json:"-"`
+	CreatedAt     time.Time `json:"createdAt"`
+	BoundAt       time.Time `json:"boundAt,omitempty"`
 }
 
 // AlreadyBound returns whether this EAK is already bound to
diff --git a/acme/account_test.go b/acme/account_test.go
index 44b815b9..33524d87 100644
--- a/acme/account_test.go
+++ b/acme/account_test.go
@@ -92,10 +92,10 @@ func TestExternalAccountKey_BindTo(t *testing.T) {
 		{
 			name: "ok",
 			eak: &ExternalAccountKey{
-				ID:          "eakID",
-				Provisioner: "prov",
-				Reference:   "ref",
-				KeyBytes:    []byte{1, 3, 3, 7},
+				ID:            "eakID",
+				ProvisionerID: "provID",
+				Reference:     "ref",
+				KeyBytes:      []byte{1, 3, 3, 7},
 			},
 			acct: &Account{
 				ID: "accountID",
@@ -105,12 +105,12 @@ func TestExternalAccountKey_BindTo(t *testing.T) {
 		{
 			name: "fail/already-bound",
 			eak: &ExternalAccountKey{
-				ID:          "eakID",
-				Provisioner: "prov",
-				Reference:   "ref",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				AccountID:   "someAccountID",
-				BoundAt:     boundAt,
+				ID:            "eakID",
+				ProvisionerID: "provID",
+				Reference:     "ref",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				AccountID:     "someAccountID",
+				BoundAt:       boundAt,
 			},
 			acct: &Account{
 				ID: "accountID",
diff --git a/acme/api/account.go b/acme/api/account.go
index 658c40a8..bf478d2a 100644
--- a/acme/api/account.go
+++ b/acme/api/account.go
@@ -144,7 +144,7 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
 				api.WriteError(w, err)
 				return
 			}
-			if err := h.db.UpdateExternalAccountKey(ctx, prov.Name, eak); err != nil {
+			if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
 				api.WriteError(w, acme.WrapErrorISE(err, "error updating external account binding key"))
 				return
 			}
@@ -274,7 +274,7 @@ func (h *Handler) validateExternalAccountBinding(ctx context.Context, nar *NewAc
 		return nil, acmeErr
 	}
 
-	externalAccountKey, err := h.db.GetExternalAccountKey(ctx, acmeProv.Name, keyID)
+	externalAccountKey, err := h.db.GetExternalAccountKey(ctx, acmeProv.ID, keyID)
 	if err != nil {
 		if _, ok := err.(*acme.Error); ok {
 			return nil, acme.WrapError(acme.ErrorUnauthorizedType, err, "the field 'kid' references an unknown key")
diff --git a/acme/api/account_test.go b/acme/api/account_test.go
index aa8d44ba..e54e3c1a 100644
--- a/acme/api/account_test.go
+++ b/acme/api/account_test.go
@@ -351,6 +351,7 @@ func TestHandler_NewAccount(t *testing.T) {
 	prov := newProv()
 	escProvName := url.PathEscape(prov.GetName())
 	baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
+	provID := prov.GetID()
 
 	type test struct {
 		db         acme.DB
@@ -554,11 +555,11 @@ func TestHandler_NewAccount(t *testing.T) {
 			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			ctx = context.WithValue(ctx, jwsContextKey, parsedJWS)
 			eak := &acme.ExternalAccountKey{
-				ID:          "eakID",
-				Provisioner: escProvName,
-				Reference:   "testeak",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   time.Now(),
+				ID:            "eakID",
+				ProvisionerID: provID,
+				Reference:     "testeak",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     time.Now(),
 			}
 			return test{
 				db: &acme.MockDB{
@@ -731,11 +732,11 @@ func TestHandler_NewAccount(t *testing.T) {
 					},
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   time.Now(),
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     time.Now(),
 						}, nil
 					},
 					MockUpdateExternalAccountKey: func(ctx context.Context, provisionerName string, eak *acme.ExternalAccountKey) error {
@@ -1056,6 +1057,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 	acmeProv := newACMEProv(t)
 	escProvName := url.PathEscape(acmeProv.GetName())
 	baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
+	provID := acmeProv.GetID()
 	type test struct {
 		db  acme.DB
 		ctx context.Context
@@ -1128,11 +1130,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt,
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     createdAt,
 						}, nil
 					},
 				},
@@ -1142,11 +1144,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 					ExternalAccountBinding: eab,
 				},
 				eak: &acme.ExternalAccountKey{
-					ID:          "eakID",
-					Provisioner: escProvName,
-					Reference:   "testeak",
-					KeyBytes:    []byte{1, 3, 3, 7},
-					CreatedAt:   createdAt,
+					ID:            "eakID",
+					ProvisionerID: provID,
+					Reference:     "testeak",
+					KeyBytes:      []byte{1, 3, 3, 7},
+					CreatedAt:     createdAt,
 				},
 				err: nil,
 			}
@@ -1492,12 +1494,12 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							CreatedAt:   createdAt,
-							AccountID:   "some-account-id",
-							BoundAt:     boundAt,
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							CreatedAt:     createdAt,
+							AccountID:     "some-account-id",
+							BoundAt:       boundAt,
 						}, nil
 					},
 				},
@@ -1549,11 +1551,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 2, 3, 4},
-							CreatedAt:   time.Now(),
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 2, 3, 4},
+							CreatedAt:     time.Now(),
 						}, nil
 					},
 				},
@@ -1607,11 +1609,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   time.Now(),
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     time.Now(),
 						}, nil
 					},
 				},
@@ -1662,11 +1664,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   time.Now(),
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     time.Now(),
 						}, nil
 					},
 				},
@@ -1718,11 +1720,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 				db: &acme.MockDB{
 					MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
 						return &acme.ExternalAccountKey{
-							ID:          "eakID",
-							Provisioner: escProvName,
-							Reference:   "testeak",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   time.Now(),
+							ID:            "eakID",
+							ProvisionerID: provID,
+							Reference:     "testeak",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     time.Now(),
 						}, nil
 					},
 				},
@@ -1765,7 +1767,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
 					assert.NotNil(t, tc.eak)
 					assert.Equals(t, got.ID, tc.eak.ID)
 					assert.Equals(t, got.KeyBytes, tc.eak.KeyBytes)
-					assert.Equals(t, got.Provisioner, tc.eak.Provisioner)
+					assert.Equals(t, got.ProvisionerID, tc.eak.ProvisionerID)
 					assert.Equals(t, got.Reference, tc.eak.Reference)
 					assert.Equals(t, got.CreatedAt, tc.eak.CreatedAt)
 					assert.Equals(t, got.AccountID, tc.eak.AccountID)
diff --git a/acme/db.go b/acme/db.go
index bed55c85..708bce9e 100644
--- a/acme/db.go
+++ b/acme/db.go
@@ -19,12 +19,12 @@ type DB interface {
 	GetAccountByKeyID(ctx context.Context, kid string) (*Account, error)
 	UpdateAccount(ctx context.Context, acc *Account) error
 
-	CreateExternalAccountKey(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
-	GetExternalAccountKey(ctx context.Context, provisionerName, keyID string) (*ExternalAccountKey, error)
-	GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
-	GetExternalAccountKeyByReference(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
-	DeleteExternalAccountKey(ctx context.Context, provisionerName, keyID string) error
-	UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
+	CreateExternalAccountKey(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
+	GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*ExternalAccountKey, error)
+	GetExternalAccountKeys(ctx context.Context, provisionerID string) ([]*ExternalAccountKey, error)
+	GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
+	DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error
+	UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *ExternalAccountKey) error
 
 	CreateNonce(ctx context.Context) (Nonce, error)
 	DeleteNonce(ctx context.Context, nonce Nonce) error
@@ -56,12 +56,12 @@ type MockDB struct {
 	MockGetAccountByKeyID func(ctx context.Context, kid string) (*Account, error)
 	MockUpdateAccount     func(ctx context.Context, acc *Account) error
 
-	MockCreateExternalAccountKey         func(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
-	MockGetExternalAccountKey            func(ctx context.Context, provisionerName, keyID string) (*ExternalAccountKey, error)
-	MockGetExternalAccountKeys           func(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
-	MockGetExternalAccountKeyByReference func(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
-	MockDeleteExternalAccountKey         func(ctx context.Context, provisionerName, keyID string) error
-	MockUpdateExternalAccountKey         func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
+	MockCreateExternalAccountKey         func(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
+	MockGetExternalAccountKey            func(ctx context.Context, provisionerID, keyID string) (*ExternalAccountKey, error)
+	MockGetExternalAccountKeys           func(ctx context.Context, provisionerID string) ([]*ExternalAccountKey, error)
+	MockGetExternalAccountKeyByReference func(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
+	MockDeleteExternalAccountKey         func(ctx context.Context, provisionerID, keyID string) error
+	MockUpdateExternalAccountKey         func(ctx context.Context, provisionerID string, eak *ExternalAccountKey) error
 
 	MockCreateNonce func(ctx context.Context) (Nonce, error)
 	MockDeleteNonce func(ctx context.Context, nonce Nonce) error
@@ -129,9 +129,9 @@ func (m *MockDB) UpdateAccount(ctx context.Context, acc *Account) error {
 }
 
 // CreateExternalAccountKey mock
-func (m *MockDB) CreateExternalAccountKey(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error) {
+func (m *MockDB) CreateExternalAccountKey(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error) {
 	if m.MockCreateExternalAccountKey != nil {
-		return m.MockCreateExternalAccountKey(ctx, provisionerName, reference)
+		return m.MockCreateExternalAccountKey(ctx, provisionerID, reference)
 	} else if m.MockError != nil {
 		return nil, m.MockError
 	}
@@ -139,9 +139,9 @@ func (m *MockDB) CreateExternalAccountKey(ctx context.Context, provisionerName,
 }
 
 // GetExternalAccountKey mock
-func (m *MockDB) GetExternalAccountKey(ctx context.Context, provisionerName, keyID string) (*ExternalAccountKey, error) {
+func (m *MockDB) GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*ExternalAccountKey, error) {
 	if m.MockGetExternalAccountKey != nil {
-		return m.MockGetExternalAccountKey(ctx, provisionerName, keyID)
+		return m.MockGetExternalAccountKey(ctx, provisionerID, keyID)
 	} else if m.MockError != nil {
 		return nil, m.MockError
 	}
@@ -149,9 +149,9 @@ func (m *MockDB) GetExternalAccountKey(ctx context.Context, provisionerName, key
 }
 
 // GetExternalAccountKeys mock
-func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error) {
+func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerID string) ([]*ExternalAccountKey, error) {
 	if m.MockGetExternalAccountKeys != nil {
-		return m.MockGetExternalAccountKeys(ctx, provisionerName)
+		return m.MockGetExternalAccountKeys(ctx, provisionerID)
 	} else if m.MockError != nil {
 		return nil, m.MockError
 	}
@@ -159,9 +159,9 @@ func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName str
 }
 
 // GetExternalAccountKeyByReference mock
-func (m *MockDB) GetExternalAccountKeyByReference(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error) {
+func (m *MockDB) GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error) {
 	if m.MockGetExternalAccountKeyByReference != nil {
-		return m.MockGetExternalAccountKeyByReference(ctx, provisionerName, reference)
+		return m.MockGetExternalAccountKeyByReference(ctx, provisionerID, reference)
 	} else if m.MockError != nil {
 		return nil, m.MockError
 	}
@@ -169,9 +169,9 @@ func (m *MockDB) GetExternalAccountKeyByReference(ctx context.Context, provision
 }
 
 // DeleteExternalAccountKey mock
-func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, provisionerName, keyID string) error {
+func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error {
 	if m.MockDeleteExternalAccountKey != nil {
-		return m.MockDeleteExternalAccountKey(ctx, provisionerName, keyID)
+		return m.MockDeleteExternalAccountKey(ctx, provisionerID, keyID)
 	} else if m.MockError != nil {
 		return m.MockError
 	}
@@ -179,9 +179,9 @@ func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, provisionerName,
 }
 
 // UpdateExternalAccountKey mock
-func (m *MockDB) UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error {
+func (m *MockDB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *ExternalAccountKey) error {
 	if m.MockUpdateExternalAccountKey != nil {
-		return m.MockUpdateExternalAccountKey(ctx, provisionerName, eak)
+		return m.MockUpdateExternalAccountKey(ctx, provisionerID, eak)
 	} else if m.MockError != nil {
 		return m.MockError
 	}
diff --git a/acme/db/nosql/account.go b/acme/db/nosql/account.go
index d340ec5c..d3b9949a 100644
--- a/acme/db/nosql/account.go
+++ b/acme/db/nosql/account.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"crypto/rand"
 	"encoding/json"
+	"sync"
 	"time"
 
 	"github.com/pkg/errors"
@@ -12,6 +13,9 @@ import (
 	"go.step.sm/crypto/jose"
 )
 
+// Mutex for locking referencesByProvisioner index operations.
+var referencesByProvisionerIndexMux sync.Mutex
+
 // dbAccount represents an ACME account.
 type dbAccount struct {
 	ID            string           `json:"id"`
@@ -28,13 +32,13 @@ func (dba *dbAccount) clone() *dbAccount {
 }
 
 type dbExternalAccountKey struct {
-	ID          string    `json:"id"`
-	Provisioner string    `json:"provisioner"`
-	Reference   string    `json:"reference"`
-	AccountID   string    `json:"accountID,omitempty"`
-	KeyBytes    []byte    `json:"key"`
-	CreatedAt   time.Time `json:"createdAt"`
-	BoundAt     time.Time `json:"boundAt"`
+	ID            string    `json:"id"`
+	ProvisionerID string    `json:"provisionerID"`
+	Reference     string    `json:"reference"`
+	AccountID     string    `json:"accountID,omitempty"`
+	KeyBytes      []byte    `json:"key"`
+	CreatedAt     time.Time `json:"createdAt"`
+	BoundAt       time.Time `json:"boundAt"`
 }
 
 type dbExternalAccountKeyReference struct {
@@ -170,7 +174,7 @@ func (db *DB) UpdateAccount(ctx context.Context, acc *acme.Account) error {
 }
 
 // CreateExternalAccountKey creates a new External Account Binding key with a name
-func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
+func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
 	keyID, err := randID()
 	if err != nil {
 		return nil, err
@@ -183,106 +187,125 @@ func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerName, ref
 	}
 
 	dbeak := &dbExternalAccountKey{
-		ID:          keyID,
-		Provisioner: provisionerName,
-		Reference:   reference,
-		KeyBytes:    random,
-		CreatedAt:   clock.Now(),
+		ID:            keyID,
+		ProvisionerID: provisionerID,
+		Reference:     reference,
+		KeyBytes:      random,
+		CreatedAt:     clock.Now(),
 	}
 
 	if err := db.save(ctx, keyID, dbeak, nil, "external_account_key", externalAccountKeyTable); err != nil {
 		return nil, err
 	}
 
+	if err := db.addEAKID(ctx, provisionerID, dbeak.ID); err != nil {
+		return nil, err
+	}
+
 	if dbeak.Reference != "" {
 		dbExternalAccountKeyReference := &dbExternalAccountKeyReference{
 			Reference:            dbeak.Reference,
 			ExternalAccountKeyID: dbeak.ID,
 		}
-		if err := db.save(ctx, dbeak.Reference, dbExternalAccountKeyReference, nil, "external_account_key_reference", externalAccountKeysByReferenceTable); err != nil {
+		if err := db.save(ctx, referenceKey(provisionerID, dbeak.Reference), dbExternalAccountKeyReference, nil, "external_account_key_reference", externalAccountKeysByReferenceTable); err != nil {
 			return nil, err
 		}
 	}
 
 	return &acme.ExternalAccountKey{
-		ID:          dbeak.ID,
-		Provisioner: dbeak.Provisioner,
-		Reference:   dbeak.Reference,
-		AccountID:   dbeak.AccountID,
-		KeyBytes:    dbeak.KeyBytes,
-		CreatedAt:   dbeak.CreatedAt,
-		BoundAt:     dbeak.BoundAt,
+		ID:            dbeak.ID,
+		ProvisionerID: dbeak.ProvisionerID,
+		Reference:     dbeak.Reference,
+		AccountID:     dbeak.AccountID,
+		KeyBytes:      dbeak.KeyBytes,
+		CreatedAt:     dbeak.CreatedAt,
+		BoundAt:       dbeak.BoundAt,
 	}, nil
 }
 
 // GetExternalAccountKey retrieves an External Account Binding key by KeyID
-func (db *DB) GetExternalAccountKey(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
+func (db *DB) GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*acme.ExternalAccountKey, error) {
 	dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
 	if err != nil {
 		return nil, err
 	}
 
-	if dbeak.Provisioner != provisionerName {
-		return nil, acme.NewError(acme.ErrorUnauthorizedType, "name of provisioner does not match provisioner for which the EAB key was created")
+	if dbeak.ProvisionerID != provisionerID {
+		return nil, acme.NewError(acme.ErrorUnauthorizedType, "provisioner does not match provisioner for which the EAB key was created")
 	}
 
 	return &acme.ExternalAccountKey{
-		ID:          dbeak.ID,
-		Provisioner: dbeak.Provisioner,
-		Reference:   dbeak.Reference,
-		AccountID:   dbeak.AccountID,
-		KeyBytes:    dbeak.KeyBytes,
-		CreatedAt:   dbeak.CreatedAt,
-		BoundAt:     dbeak.BoundAt,
+		ID:            dbeak.ID,
+		ProvisionerID: dbeak.ProvisionerID,
+		Reference:     dbeak.Reference,
+		AccountID:     dbeak.AccountID,
+		KeyBytes:      dbeak.KeyBytes,
+		CreatedAt:     dbeak.CreatedAt,
+		BoundAt:       dbeak.BoundAt,
 	}, nil
 }
 
-func (db *DB) DeleteExternalAccountKey(ctx context.Context, provisionerName, keyID string) error {
+func (db *DB) DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error {
 	dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
 	if err != nil {
 		return errors.Wrapf(err, "error loading ACME EAB Key with Key ID %s", keyID)
 	}
-	if dbeak.Provisioner != provisionerName {
-		return errors.New("name of provisioner does not match provisioner for which the EAB key was created")
+
+	if dbeak.ProvisionerID != provisionerID {
+		return errors.New("provisioner does not match provisioner for which the EAB key was created")
 	}
+
 	if dbeak.Reference != "" {
-		err = db.db.Del(externalAccountKeysByReferenceTable, []byte(dbeak.Reference))
-		if err != nil {
-			return errors.Wrapf(err, "error deleting ACME EAB Key Reference with Key ID %s and reference %s", keyID, dbeak.Reference)
+		if err := db.db.Del(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, dbeak.Reference))); err != nil {
+			return errors.Wrapf(err, "error deleting ACME EAB Key reference with Key ID %s and reference %s", keyID, dbeak.Reference)
 		}
 	}
-	if err = db.db.Del(externalAccountKeyTable, []byte(keyID)); err != nil {
+	if err := db.db.Del(externalAccountKeyTable, []byte(keyID)); err != nil {
 		return errors.Wrapf(err, "error deleting ACME EAB Key with Key ID %s", keyID)
 	}
+	if err := db.deleteEAKID(ctx, provisionerID, keyID); err != nil {
+		return errors.Wrapf(err, "error removing ACME EAB Key ID %s", keyID)
+	}
+
 	return nil
 }
 
 // GetExternalAccountKeys retrieves all External Account Binding keys for a provisioner
-func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
+func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerID string) ([]*acme.ExternalAccountKey, error) {
 
-	// TODO: lookup by provisioner based on index
-	entries, err := db.db.List(externalAccountKeyTable)
+	// TODO: mutex?
+
+	var eakIDs []string
+	r, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
 	if err != nil {
-		return nil, err
+		if !nosqlDB.IsErrNotFound(err) {
+			return nil, errors.Wrapf(err, "error loading ACME EAB Key IDs for provisioner %s", provisionerID)
+		}
+	} else {
+		if err := json.Unmarshal(r, &eakIDs); err != nil {
+			return nil, errors.Wrapf(err, "error unmarshaling ACME EAB Key IDs for provisioner %s", provisionerID)
+		}
 	}
 
 	keys := []*acme.ExternalAccountKey{}
-	for _, entry := range entries { // entries is sorted alphabetically on the key (ID) of the EAK; no need to sort this again.
-		dbeak := new(dbExternalAccountKey)
-		if err = json.Unmarshal(entry.Value, dbeak); err != nil {
-			return nil, errors.Wrapf(err, "error unmarshaling external account key %s into ExternalAccountKey", string(entry.Key))
+	for _, eakID := range eakIDs {
+		if eakID == "" {
+			continue // shouldn't happen; just in case
 		}
-		if dbeak.Provisioner != provisionerName {
-			continue
+		eak, err := db.getDBExternalAccountKey(ctx, eakID)
+		if err != nil {
+			if !nosqlDB.IsErrNotFound(err) {
+				return nil, errors.Wrapf(err, "error retrieving ACME EAB Key for provisioner %s and keyID %s", provisionerID, eakID)
+			}
 		}
 		keys = append(keys, &acme.ExternalAccountKey{
-			ID:          dbeak.ID,
-			KeyBytes:    dbeak.KeyBytes,
-			Provisioner: dbeak.Provisioner,
-			Reference:   dbeak.Reference,
-			AccountID:   dbeak.AccountID,
-			CreatedAt:   dbeak.CreatedAt,
-			BoundAt:     dbeak.BoundAt,
+			ID:            eak.ID,
+			KeyBytes:      eak.KeyBytes,
+			ProvisionerID: eak.ProvisionerID,
+			Reference:     eak.Reference,
+			AccountID:     eak.AccountID,
+			CreatedAt:     eak.CreatedAt,
+			BoundAt:       eak.BoundAt,
 		})
 	}
 
@@ -290,11 +313,12 @@ func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName string
 }
 
 // GetExternalAccountKeyByReference retrieves an External Account Binding key with unique reference
-func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
+func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
 	if reference == "" {
 		return nil, nil
 	}
-	k, err := db.db.Get(externalAccountKeysByReferenceTable, []byte(reference))
+
+	k, err := db.db.Get(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, reference)))
 	if nosqlDB.IsErrNotFound(err) {
 		return nil, acme.ErrNotFound
 	} else if err != nil {
@@ -304,28 +328,139 @@ func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerN
 	if err := json.Unmarshal(k, dbExternalAccountKeyReference); err != nil {
 		return nil, errors.Wrapf(err, "error unmarshaling ACME EAB key for reference %s", reference)
 	}
-	return db.GetExternalAccountKey(ctx, provisionerName, dbExternalAccountKeyReference.ExternalAccountKeyID)
+
+	return db.GetExternalAccountKey(ctx, provisionerID, dbExternalAccountKeyReference.ExternalAccountKeyID)
 }
 
-func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *acme.ExternalAccountKey) error {
+func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *acme.ExternalAccountKey) error {
 	old, err := db.getDBExternalAccountKey(ctx, eak.ID)
 	if err != nil {
 		return err
 	}
 
-	if old.Provisioner != provisionerName {
-		return errors.New("name of provisioner does not match provisioner for which the EAB key was created")
+	if old.ProvisionerID != provisionerID {
+		return errors.New("provisioner does not match provisioner for which the EAB key was created")
+	}
+
+	if old.ProvisionerID != eak.ProvisionerID {
+		return errors.New("cannot change provisioner for an existing ACME EAB Key")
+	}
+
+	if old.Reference != eak.Reference {
+		return errors.New("cannot change reference for an existing ACME EAB Key")
 	}
 
 	nu := dbExternalAccountKey{
-		ID:          eak.ID,
-		Provisioner: eak.Provisioner,
-		Reference:   eak.Reference,
-		AccountID:   eak.AccountID,
-		KeyBytes:    eak.KeyBytes,
-		CreatedAt:   eak.CreatedAt,
-		BoundAt:     eak.BoundAt,
+		ID:            eak.ID,
+		ProvisionerID: eak.ProvisionerID,
+		Reference:     eak.Reference,
+		AccountID:     eak.AccountID,
+		KeyBytes:      eak.KeyBytes,
+		CreatedAt:     eak.CreatedAt,
+		BoundAt:       eak.BoundAt,
 	}
 
 	return db.save(ctx, nu.ID, nu, old, "external_account_key", externalAccountKeyTable)
 }
+
+func (db *DB) addEAKID(ctx context.Context, provisionerID, eakID string) error {
+	referencesByProvisionerIndexMux.Lock()
+	defer referencesByProvisionerIndexMux.Unlock()
+
+	var eakIDs []string
+	b, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
+	if err != nil {
+		if !nosqlDB.IsErrNotFound(err) {
+			return errors.Wrapf(err, "error loading eakIDs for provisioner %s", provisionerID)
+		}
+	} else {
+		if err := json.Unmarshal(b, &eakIDs); err != nil {
+			return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
+		}
+	}
+
+	var newEAKIDs []string
+	newEAKIDs = append(newEAKIDs, eakIDs...)
+	newEAKIDs = append(newEAKIDs, eakID)
+	var (
+		_old interface{} = eakIDs
+		_new interface{} = newEAKIDs
+	)
+
+	if len(eakIDs) == 0 {
+		_old = nil
+	}
+
+	if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeysByProvisionerID", externalAccountKeysByProvisionerIDTable); err != nil {
+		return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID)
+	}
+
+	return nil
+}
+
+func (db *DB) deleteEAKID(ctx context.Context, provisionerID, eakID string) error {
+	referencesByProvisionerIndexMux.Lock()
+	defer referencesByProvisionerIndexMux.Unlock()
+
+	var eakIDs []string
+	b, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
+	if err != nil {
+		if !nosqlDB.IsErrNotFound(err) {
+			return errors.Wrapf(err, "error loading reference IDs for provisioner %s", provisionerID)
+		}
+	} else {
+		if err := json.Unmarshal(b, &eakIDs); err != nil {
+			return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
+		}
+	}
+
+	newEAKIDs := removeElement(eakIDs, eakID)
+	var (
+		_old interface{} = eakIDs
+		_new interface{} = newEAKIDs
+	)
+
+	switch {
+	case len(eakIDs) == 0:
+		_old = nil
+	case len(newEAKIDs) == 0:
+		_new = nil
+	}
+
+	if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeysByProvisionerID", externalAccountKeysByProvisionerIDTable); err != nil {
+		return errors.Wrapf(err, "error saving referenceIDs index for provisioner %s", provisionerID)
+	}
+
+	return nil
+}
+
+// referenceKey returns a unique key for a reference per provisioner
+func referenceKey(provisionerID, reference string) string {
+	return provisionerID + "." + reference
+}
+
+// sliceIndex finds the index of item in slice
+func sliceIndex(slice []string, item string) int {
+	for i := range slice {
+		if slice[i] == item {
+			return i
+		}
+	}
+	return -1
+}
+
+// removeElement deletes the item if it exists in the
+// slice. It returns a new slice, keeping the old one intact.
+func removeElement(slice []string, item string) []string {
+
+	newSlice := make([]string, 0)
+	index := sliceIndex(slice, item)
+	if index < 0 {
+		newSlice = append(newSlice, slice...)
+		return newSlice
+	}
+
+	newSlice = append(newSlice, slice[:index]...)
+
+	return append(newSlice, slice[index+1:]...)
+}
diff --git a/acme/db/nosql/account_test.go b/acme/db/nosql/account_test.go
index 4b94e40f..77937c2e 100644
--- a/acme/db/nosql/account_test.go
+++ b/acme/db/nosql/account_test.go
@@ -3,6 +3,7 @@ package nosql
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"testing"
 	"time"
 
@@ -307,7 +308,7 @@ func TestDB_GetAccountByKeyID(t *testing.T) {
 							assert.Equals(t, string(key), accID)
 							return nil, errors.New("force")
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, errors.New("force")
 						}
 					},
@@ -340,7 +341,7 @@ func TestDB_GetAccountByKeyID(t *testing.T) {
 							assert.Equals(t, string(key), accID)
 							return b, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, errors.New("force")
 						}
 					},
@@ -462,7 +463,7 @@ func TestDB_CreateAccount(t *testing.T) {
 							assert.True(t, dbacc.DeactivatedAt.IsZero())
 							return nil, false, errors.New("force")
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, false, errors.New("force")
 						}
 					},
@@ -506,7 +507,7 @@ func TestDB_CreateAccount(t *testing.T) {
 							assert.True(t, dbacc.DeactivatedAt.IsZero())
 							return nu, true, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, false, errors.New("force")
 						}
 					},
@@ -699,6 +700,7 @@ func TestDB_UpdateAccount(t *testing.T) {
 
 func TestDB_getDBExternalAccountKey(t *testing.T) {
 	keyID := "keyID"
+	provID := "provID"
 	type test struct {
 		db      nosql.DB
 		err     error
@@ -709,12 +711,12 @@ func TestDB_getDBExternalAccountKey(t *testing.T) {
 		"ok": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: "prov",
-				Reference:   "ref",
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     "ref",
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b, err := json.Marshal(dbeak)
 			assert.FatalError(t, err)
@@ -790,7 +792,7 @@ func TestDB_getDBExternalAccountKey(t *testing.T) {
 			} else if assert.Nil(t, tc.err) {
 				assert.Equals(t, dbeak.ID, tc.dbeak.ID)
 				assert.Equals(t, dbeak.KeyBytes, tc.dbeak.KeyBytes)
-				assert.Equals(t, dbeak.Provisioner, tc.dbeak.Provisioner)
+				assert.Equals(t, dbeak.ProvisionerID, tc.dbeak.ProvisionerID)
 				assert.Equals(t, dbeak.Reference, tc.dbeak.Reference)
 				assert.Equals(t, dbeak.CreatedAt, tc.dbeak.CreatedAt)
 				assert.Equals(t, dbeak.AccountID, tc.dbeak.AccountID)
@@ -802,7 +804,7 @@ func TestDB_getDBExternalAccountKey(t *testing.T) {
 
 func TestDB_GetExternalAccountKey(t *testing.T) {
 	keyID := "keyID"
-	prov := "acmeProv"
+	provID := "provID"
 	type test struct {
 		db      nosql.DB
 		err     error
@@ -813,12 +815,12 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 		"ok": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   "ref",
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     "ref",
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b, err := json.Marshal(dbeak)
 			assert.FatalError(t, err)
@@ -831,12 +833,12 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 					},
 				},
 				eak: &acme.ExternalAccountKey{
-					ID:          keyID,
-					Provisioner: prov,
-					Reference:   "ref",
-					AccountID:   "",
-					KeyBytes:    []byte{1, 3, 3, 7},
-					CreatedAt:   now,
+					ID:            keyID,
+					ProvisionerID: provID,
+					Reference:     "ref",
+					AccountID:     "",
+					KeyBytes:      []byte{1, 3, 3, 7},
+					CreatedAt:     now,
 				},
 			}
 		},
@@ -856,12 +858,12 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 		"fail/non-matching-provisioner": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: "aDifferentProv",
-				Reference:   "ref",
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: "aDifferentProvID",
+				Reference:     "ref",
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b, err := json.Marshal(dbeak)
 			assert.FatalError(t, err)
@@ -874,14 +876,14 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 					},
 				},
 				eak: &acme.ExternalAccountKey{
-					ID:          keyID,
-					Provisioner: prov,
-					Reference:   "ref",
-					AccountID:   "",
-					KeyBytes:    []byte{1, 3, 3, 7},
-					CreatedAt:   now,
+					ID:            keyID,
+					ProvisionerID: provID,
+					Reference:     "ref",
+					AccountID:     "",
+					KeyBytes:      []byte{1, 3, 3, 7},
+					CreatedAt:     now,
 				},
-				acmeErr: acme.NewError(acme.ErrorUnauthorizedType, "name of provisioner does not match provisioner for which the EAB key was created"),
+				acmeErr: acme.NewError(acme.ErrorUnauthorizedType, "provisioner does not match provisioner for which the EAB key was created"),
 			}
 		},
 	}
@@ -889,7 +891,7 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			if eak, err := d.GetExternalAccountKey(context.Background(), prov, keyID); err != nil {
+			if eak, err := d.GetExternalAccountKey(context.Background(), provID, keyID); err != nil {
 				switch k := err.(type) {
 				case *acme.Error:
 					if assert.NotNil(t, tc.acmeErr) {
@@ -907,7 +909,7 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 			} else if assert.Nil(t, tc.err) {
 				assert.Equals(t, eak.ID, tc.eak.ID)
 				assert.Equals(t, eak.KeyBytes, tc.eak.KeyBytes)
-				assert.Equals(t, eak.Provisioner, tc.eak.Provisioner)
+				assert.Equals(t, eak.ProvisionerID, tc.eak.ProvisionerID)
 				assert.Equals(t, eak.Reference, tc.eak.Reference)
 				assert.Equals(t, eak.CreatedAt, tc.eak.CreatedAt)
 				assert.Equals(t, eak.AccountID, tc.eak.AccountID)
@@ -919,7 +921,7 @@ func TestDB_GetExternalAccountKey(t *testing.T) {
 
 func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 	keyID := "keyID"
-	prov := "acmeProv"
+	provID := "provID"
 	ref := "ref"
 	type test struct {
 		db      nosql.DB
@@ -932,12 +934,12 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 		"ok": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			dbref := &dbExternalAccountKeyReference{
 				Reference:            ref,
@@ -953,24 +955,24 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return dbrefBytes, nil
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return b, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, errors.New("force")
 						}
 					},
 				},
 				eak: &acme.ExternalAccountKey{
-					ID:          keyID,
-					Provisioner: prov,
-					Reference:   ref,
-					AccountID:   "",
-					KeyBytes:    []byte{1, 3, 3, 7},
-					CreatedAt:   now,
+					ID:            keyID,
+					ProvisionerID: provID,
+					Reference:     ref,
+					AccountID:     "",
+					KeyBytes:      []byte{1, 3, 3, 7},
+					CreatedAt:     now,
 				},
 				err: nil,
 			}
@@ -988,7 +990,7 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 				db: &db.MockNoSQLDB{
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
-						assert.Equals(t, string(key), ref)
+						assert.Equals(t, string(key), provID+"."+ref)
 						return nil, nosqldb.ErrNotFound
 					},
 				},
@@ -1001,7 +1003,7 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 				db: &db.MockNoSQLDB{
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
-						assert.Equals(t, string(key), ref)
+						assert.Equals(t, string(key), provID+"."+ref)
 						return nil, errors.New("force")
 					},
 				},
@@ -1014,7 +1016,7 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 				db: &db.MockNoSQLDB{
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
-						assert.Equals(t, string(key), ref)
+						assert.Equals(t, string(key), provID+"."+ref)
 						return []byte{0}, nil
 					},
 				},
@@ -1034,13 +1036,13 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return dbrefBytes, nil
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return nil, errors.New("force")
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, errors.New("force")
 						}
 					},
@@ -1053,7 +1055,7 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			if eak, err := d.GetExternalAccountKeyByReference(context.Background(), prov, tc.ref); err != nil {
+			if eak, err := d.GetExternalAccountKeyByReference(context.Background(), provID, tc.ref); err != nil {
 				switch k := err.(type) {
 				case *acme.Error:
 					if assert.NotNil(t, tc.acmeErr) {
@@ -1074,7 +1076,7 @@ func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
 				assert.Equals(t, eak.BoundAt, tc.eak.BoundAt)
 				assert.Equals(t, eak.CreatedAt, tc.eak.CreatedAt)
 				assert.Equals(t, eak.KeyBytes, tc.eak.KeyBytes)
-				assert.Equals(t, eak.Provisioner, tc.eak.Provisioner)
+				assert.Equals(t, eak.ProvisionerID, tc.eak.ProvisionerID)
 				assert.Equals(t, eak.Reference, tc.eak.Reference)
 			}
 		})
@@ -1085,7 +1087,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 	keyID1 := "keyID1"
 	keyID2 := "keyID2"
 	keyID3 := "keyID3"
-	prov := "acmeProv"
+	provID := "provID"
 	ref := "ref"
 	type test struct {
 		db      nosql.DB
@@ -1097,105 +1099,147 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 		"ok": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak1 := &dbExternalAccountKey{
-				ID:          keyID1,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID1,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b1, err := json.Marshal(dbeak1)
 			assert.FatalError(t, err)
 			dbeak2 := &dbExternalAccountKey{
-				ID:          keyID2,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID2,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b2, err := json.Marshal(dbeak2)
 			assert.FatalError(t, err)
 			dbeak3 := &dbExternalAccountKey{
-				ID:          keyID3,
-				Provisioner: "differentProvisioner",
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID3,
+				ProvisionerID: "aDifferentProvID",
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b3, err := json.Marshal(dbeak3)
 			assert.FatalError(t, err)
 			return test{
 				db: &db.MockNoSQLDB{
+					MGet: func(bucket, key []byte) ([]byte, error) {
+						switch string(bucket) {
+						case string(externalAccountKeysByProvisionerIDTable):
+							keys := []string{keyID1, keyID2}
+							b, err := json.Marshal(keys)
+							assert.FatalError(t, err)
+							return b, nil
+						case string(externalAccountKeyTable):
+							switch string(key) {
+							case keyID1:
+								return b1, nil
+							case keyID2:
+								return b2, nil
+							default:
+								assert.FatalError(t, errors.Errorf("unexpected key %s", string(key)))
+								return nil, errors.New("force")
+							}
+						default:
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, errors.New("force")
+						}
+					},
+					// TODO: remove the MList
 					MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
-						assert.Equals(t, bucket, externalAccountKeyTable)
-						return []*nosqldb.Entry{
-							{
-								Bucket: bucket,
-								Key:    []byte(keyID1),
-								Value:  b1,
-							},
-							{
-								Bucket: bucket,
-								Key:    []byte(keyID2),
-								Value:  b2,
-							},
-							{
-								Bucket: bucket,
-								Key:    []byte(keyID3),
-								Value:  b3,
-							},
-						}, nil
+						switch string(bucket) {
+						case string(externalAccountKeyTable):
+							return []*nosqldb.Entry{
+								{
+									Bucket: bucket,
+									Key:    []byte(keyID1),
+									Value:  b1,
+								},
+								{
+									Bucket: bucket,
+									Key:    []byte(keyID2),
+									Value:  b2,
+								},
+								{
+									Bucket: bucket,
+									Key:    []byte(keyID3),
+									Value:  b3,
+								},
+							}, nil
+						case string(externalAccountKeysByProvisionerIDTable):
+							keys := []string{keyID1, keyID2}
+							b, err := json.Marshal(keys)
+							assert.FatalError(t, err)
+							return []*nosqldb.Entry{
+								{
+									Bucket: bucket,
+									Key:    []byte(provID),
+									Value:  b,
+								},
+							}, nil
+						default:
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, errors.New("force default")
+						}
 					},
 				},
 				eaks: []*acme.ExternalAccountKey{
 					{
-						ID:          keyID1,
-						Provisioner: prov,
-						Reference:   ref,
-						AccountID:   "",
-						KeyBytes:    []byte{1, 3, 3, 7},
-						CreatedAt:   now,
+						ID:            keyID1,
+						ProvisionerID: provID,
+						Reference:     ref,
+						AccountID:     "",
+						KeyBytes:      []byte{1, 3, 3, 7},
+						CreatedAt:     now,
 					},
 					{
-						ID:          keyID2,
-						Provisioner: prov,
-						Reference:   ref,
-						AccountID:   "",
-						KeyBytes:    []byte{1, 3, 3, 7},
-						CreatedAt:   now,
+						ID:            keyID2,
+						ProvisionerID: provID,
+						Reference:     ref,
+						AccountID:     "",
+						KeyBytes:      []byte{1, 3, 3, 7},
+						CreatedAt:     now,
 					},
 				},
 			}
 		},
-		"fail/db.List-error": func(t *testing.T) test {
+		"fail/db.Get-externalAccountKeysByProvisionerIDTable": func(t *testing.T) test {
 			return test{
 				db: &db.MockNoSQLDB{
-					MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
-						assert.Equals(t, string(bucket), string(externalAccountKeyTable))
+					MGet: func(bucket, key []byte) ([]byte, error) {
+						assert.Equals(t, string(bucket), string(externalAccountKeysByProvisionerIDTable))
 						return nil, errors.New("force")
 					},
 				},
-				err: errors.New("force"),
+				err: errors.New("error loading ACME EAB Key IDs for provisioner provID: force"),
 			}
 		},
-		"fail/unmarshal-error": func(t *testing.T) test {
+		"fail/db.getDBExternalAccountKey": func(t *testing.T) test {
 			return test{
 				db: &db.MockNoSQLDB{
-					MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
-						assert.Equals(t, bucket, externalAccountKeyTable)
-						return []*nosqldb.Entry{
-							{
-								Bucket: bucket,
-								Key:    []byte(keyID1),
-								Value:  []byte("foo"),
-							},
-						}, nil
+					MGet: func(bucket, key []byte) ([]byte, error) {
+						switch string(bucket) {
+						case string(externalAccountKeysByProvisionerIDTable):
+							keys := []string{keyID1, keyID2}
+							b, err := json.Marshal(keys)
+							assert.FatalError(t, err)
+							return b, nil
+						case string(externalAccountKeyTable):
+							return nil, errors.New("force")
+						default:
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, errors.New("force bucket")
+						}
 					},
 				},
-				eaks: []*acme.ExternalAccountKey{},
-				err:  errors.Errorf("error unmarshaling external account key %s into ExternalAccountKey", keyID1),
+				err: errors.New("error retrieving ACME EAB Key for provisioner provID and keyID keyID1: error loading external account key keyID1: force"),
 			}
 		},
 	}
@@ -1203,7 +1247,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			if eaks, err := d.GetExternalAccountKeys(context.Background(), prov); err != nil {
+			if eaks, err := d.GetExternalAccountKeys(context.Background(), provID); err != nil {
 				switch k := err.(type) {
 				case *acme.Error:
 					if assert.NotNil(t, tc.acmeErr) {
@@ -1215,7 +1259,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 					}
 				default:
 					if assert.NotNil(t, tc.err) {
-						assert.HasPrefix(t, err.Error(), tc.err.Error())
+						assert.Equals(t, tc.err.Error(), err.Error())
 					}
 				}
 			} else if assert.Nil(t, tc.err) {
@@ -1223,7 +1267,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 				for i, eak := range eaks {
 					assert.Equals(t, eak.ID, tc.eaks[i].ID)
 					assert.Equals(t, eak.KeyBytes, tc.eaks[i].KeyBytes)
-					assert.Equals(t, eak.Provisioner, tc.eaks[i].Provisioner)
+					assert.Equals(t, eak.ProvisionerID, tc.eaks[i].ProvisionerID)
 					assert.Equals(t, eak.Reference, tc.eaks[i].Reference)
 					assert.Equals(t, eak.CreatedAt, tc.eaks[i].CreatedAt)
 					assert.Equals(t, eak.AccountID, tc.eaks[i].AccountID)
@@ -1236,7 +1280,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
 
 func TestDB_DeleteExternalAccountKey(t *testing.T) {
 	keyID := "keyID"
-	prov := "acmeProv"
+	provID := "provID"
 	ref := "ref"
 	type test struct {
 		db      nosql.DB
@@ -1247,12 +1291,12 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 		"ok": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			dbref := &dbExternalAccountKeyReference{
 				Reference:            ref,
@@ -1267,27 +1311,46 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 					MGet: func(bucket, key []byte) ([]byte, error) {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return dbrefBytes, nil
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return b, nil
+						case string(externalAccountKeysByProvisionerIDTable):
+							assert.Equals(t, provID, string(key))
+							b, err := json.Marshal([]string{keyID})
+							assert.FatalError(t, err)
+							return b, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
-							return nil, errors.New("force")
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, errors.New("force default")
 						}
 					},
 					MDel: func(bucket, key []byte) error {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return nil
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
-							return errors.New("force")
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return errors.New("force default")
+						}
+					},
+					MCmpAndSwap: func(bucket, key, old, new []byte) ([]byte, bool, error) {
+						fmt.Println(string(bucket))
+						switch string(bucket) {
+						case string(externalAccountKeysByReferenceTable):
+							assert.Equals(t, provID+"."+ref, string(key))
+							return nil, true, nil
+						case string(externalAccountKeysByProvisionerIDTable):
+							assert.Equals(t, provID, string(key))
+							return nil, true, nil
+						default:
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, false, errors.New("force default")
 						}
 					},
 				},
@@ -1302,18 +1365,18 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 						return nil, nosqldb.ErrNotFound
 					},
 				},
-				err: errors.New("error loading ACME EAB Key with Key ID keyID"),
+				err: errors.New("error loading ACME EAB Key with Key ID keyID: not found"),
 			}
 		},
 		"fail/non-matching-provisioner": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: "differentProvisioner",
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: "aDifferentProvID",
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b, err := json.Marshal(dbeak)
 			assert.FatalError(t, err)
@@ -1325,18 +1388,18 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 						return b, nil
 					},
 				},
-				err: errors.New("name of provisioner does not match provisioner for which the EAB key was created"),
+				err: errors.New("provisioner does not match provisioner for which the EAB key was created"),
 			}
 		},
 		"fail/delete-reference": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			dbref := &dbExternalAccountKeyReference{
 				Reference:            ref,
@@ -1357,36 +1420,36 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 							assert.Equals(t, string(key), keyID)
 							return b, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
-							return nil, errors.New("force")
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, errors.New("force default")
 						}
 					},
 					MDel: func(bucket, key []byte) error {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return errors.New("force")
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
-							return errors.New("force")
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return errors.New("force default")
 						}
 					},
 				},
-				err: errors.New("error deleting ACME EAB Key Reference with Key ID keyID and reference ref"),
+				err: errors.New("error deleting ACME EAB Key reference with Key ID keyID and reference ref: force"),
 			}
 		},
 		"fail/delete-eak": func(t *testing.T) test {
 			now := clock.Now()
 			dbeak := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			dbref := &dbExternalAccountKeyReference{
 				Reference:            ref,
@@ -1407,25 +1470,25 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 							assert.Equals(t, string(key), keyID)
 							return b, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, errors.New("force")
 						}
 					},
 					MDel: func(bucket, key []byte) error {
 						switch string(bucket) {
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, string(key), provID+"."+ref)
 							return nil
 						case string(externalAccountKeyTable):
 							assert.Equals(t, string(key), keyID)
 							return errors.New("force")
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return errors.New("force")
 						}
 					},
 				},
-				err: errors.New("error deleting ACME EAB Key with Key ID keyID"),
+				err: errors.New("error deleting ACME EAB Key with Key ID keyID: force"),
 			}
 		},
 	}
@@ -1433,7 +1496,7 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			if err := d.DeleteExternalAccountKey(context.Background(), prov, keyID); err != nil {
+			if err := d.DeleteExternalAccountKey(context.Background(), provID, keyID); err != nil {
 				switch k := err.(type) {
 				case *acme.Error:
 					if assert.NotNil(t, tc.acmeErr) {
@@ -1445,7 +1508,7 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 					}
 				default:
 					if assert.NotNil(t, tc.err) {
-						assert.HasPrefix(t, err.Error(), tc.err.Error())
+						assert.Equals(t, err.Error(), tc.err.Error())
 					}
 				}
 			} else {
@@ -1457,7 +1520,7 @@ func TestDB_DeleteExternalAccountKey(t *testing.T) {
 
 func TestDB_CreateExternalAccountKey(t *testing.T) {
 	keyID := "keyID"
-	prov := "acmeProv"
+	provID := "provID"
 	ref := "ref"
 	type test struct {
 		db  nosql.DB
@@ -1473,30 +1536,38 @@ func TestDB_CreateExternalAccountKey(t *testing.T) {
 			)
 			now := clock.Now()
 			eak := &acme.ExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   "ref",
-				AccountID:   "",
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     "ref",
+				AccountID:     "",
+				CreatedAt:     now,
 			}
 			return test{
 				db: &db.MockNoSQLDB{
+					MGet: func(bucket, key []byte) ([]byte, error) {
+						assert.Equals(t, string(bucket), string(externalAccountKeysByProvisionerIDTable))
+						assert.Equals(t, provID, string(key))
+						b, _ := json.Marshal([]string{})
+						return b, nil
+					},
 					MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
-
 						switch string(bucket) {
+						case string(externalAccountKeysByProvisionerIDTable):
+							assert.Equals(t, provID, string(key))
+							return nu, true, nil
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
-							assert.Equals(t, old, nil)
+							assert.Equals(t, provID+"."+ref, string(key))
+							assert.Equals(t, nil, old)
 							return nu, true, nil
 						case string(externalAccountKeyTable):
-							assert.Equals(t, old, nil)
+							assert.Equals(t, nil, old)
 
 							id = string(key)
 
 							dbeak := new(dbExternalAccountKey)
 							assert.FatalError(t, json.Unmarshal(nu, dbeak))
 							assert.Equals(t, string(key), dbeak.ID)
-							assert.Equals(t, eak.Provisioner, dbeak.Provisioner)
+							assert.Equals(t, eak.ProvisionerID, dbeak.ProvisionerID)
 							assert.Equals(t, eak.Reference, dbeak.Reference)
 							assert.Equals(t, 32, len(dbeak.KeyBytes))
 							assert.False(t, dbeak.CreatedAt.IsZero())
@@ -1504,8 +1575,8 @@ func TestDB_CreateExternalAccountKey(t *testing.T) {
 							assert.True(t, dbeak.BoundAt.IsZero())
 							return nu, true, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
-							return nil, false, errors.New("force")
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
+							return nil, false, errors.New("force default")
 						}
 					},
 				},
@@ -1527,34 +1598,42 @@ func TestDB_CreateExternalAccountKey(t *testing.T) {
 							assert.Equals(t, old, nil)
 							return nu, true, errors.New("force")
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, false, errors.New("force")
 						}
 					},
 				},
-				err: errors.New("error saving acme external_account_key"),
+				err: errors.New("error saving acme external_account_key: force"),
 			}
 		},
 		"fail/externalAccountKeyReference-cmpAndSwap-error": func(t *testing.T) test {
 			return test{
 				db: &db.MockNoSQLDB{
+					MGet: func(bucket, key []byte) ([]byte, error) {
+						assert.Equals(t, string(bucket), string(externalAccountKeysByProvisionerIDTable))
+						assert.Equals(t, provID, string(key))
+						b, _ := json.Marshal([]string{})
+						return b, nil
+					},
 					MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
-
 						switch string(bucket) {
+						case string(externalAccountKeysByProvisionerIDTable):
+							assert.Equals(t, provID, string(key))
+							return nu, true, nil
 						case string(externalAccountKeysByReferenceTable):
-							assert.Equals(t, string(key), ref)
+							assert.Equals(t, provID+"."+ref, string(key))
 							assert.Equals(t, old, nil)
 							return nu, true, errors.New("force")
 						case string(externalAccountKeyTable):
 							assert.Equals(t, old, nil)
 							return nu, true, nil
 						default:
-							assert.FatalError(t, errors.Errorf("unrecognized bucket %s", string(bucket)))
+							assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
 							return nil, false, errors.New("force")
 						}
 					},
 				},
-				err: errors.New("error saving acme external_account_key"),
+				err: errors.New("error saving acme external_account_key_reference: force"),
 			}
 		},
 	}
@@ -1562,14 +1641,15 @@ func TestDB_CreateExternalAccountKey(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			eak, err := d.CreateExternalAccountKey(context.Background(), prov, ref)
+			eak, err := d.CreateExternalAccountKey(context.Background(), provID, ref)
+			fmt.Println(name, err)
 			if err != nil {
 				if assert.NotNil(t, tc.err) {
-					assert.HasPrefix(t, err.Error(), tc.err.Error())
+					assert.Equals(t, err.Error(), tc.err.Error())
 				}
 			} else if assert.Nil(t, tc.err) {
 				assert.Equals(t, *tc._id, eak.ID)
-				assert.Equals(t, prov, eak.Provisioner)
+				assert.Equals(t, provID, eak.ProvisionerID)
 				assert.Equals(t, ref, eak.Reference)
 				assert.Equals(t, "", eak.AccountID)
 				assert.False(t, eak.CreatedAt.IsZero())
@@ -1582,16 +1662,16 @@ func TestDB_CreateExternalAccountKey(t *testing.T) {
 
 func TestDB_UpdateExternalAccountKey(t *testing.T) {
 	keyID := "keyID"
-	prov := "acmeProv"
+	provID := "provID"
 	ref := "ref"
 	now := clock.Now()
 	dbeak := &dbExternalAccountKey{
-		ID:          keyID,
-		Provisioner: prov,
-		Reference:   ref,
-		AccountID:   "",
-		KeyBytes:    []byte{1, 3, 3, 7},
-		CreatedAt:   now,
+		ID:            keyID,
+		ProvisionerID: provID,
+		Reference:     ref,
+		AccountID:     "",
+		KeyBytes:      []byte{1, 3, 3, 7},
+		CreatedAt:     now,
 	}
 	b, err := json.Marshal(dbeak)
 	assert.FatalError(t, err)
@@ -1604,12 +1684,12 @@ func TestDB_UpdateExternalAccountKey(t *testing.T) {
 
 		"ok": func(t *testing.T) test {
 			eak := &acme.ExternalAccountKey{
-				ID:          keyID,
-				Provisioner: prov,
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: provID,
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			return test{
 				eak: eak,
@@ -1627,7 +1707,7 @@ func TestDB_UpdateExternalAccountKey(t *testing.T) {
 						dbNew := new(dbExternalAccountKey)
 						assert.FatalError(t, json.Unmarshal(nu, dbNew))
 						assert.Equals(t, dbNew.ID, dbeak.ID)
-						assert.Equals(t, dbNew.Provisioner, dbeak.Provisioner)
+						assert.Equals(t, dbNew.ProvisionerID, dbeak.ProvisionerID)
 						assert.Equals(t, dbNew.Reference, dbeak.Reference)
 						assert.Equals(t, dbNew.AccountID, dbeak.AccountID)
 						assert.Equals(t, dbNew.CreatedAt, dbeak.CreatedAt)
@@ -1640,12 +1720,12 @@ func TestDB_UpdateExternalAccountKey(t *testing.T) {
 		},
 		"fail/provisioner-mismatch": func(t *testing.T) test {
 			newDBEAK := &dbExternalAccountKey{
-				ID:          keyID,
-				Provisioner: "differentProvisioner",
-				Reference:   ref,
-				AccountID:   "",
-				KeyBytes:    []byte{1, 3, 3, 7},
-				CreatedAt:   now,
+				ID:            keyID,
+				ProvisionerID: "aDifferentProvID",
+				Reference:     ref,
+				AccountID:     "",
+				KeyBytes:      []byte{1, 3, 3, 7},
+				CreatedAt:     now,
 			}
 			b, err := json.Marshal(newDBEAK)
 			assert.FatalError(t, err)
@@ -1661,7 +1741,7 @@ func TestDB_UpdateExternalAccountKey(t *testing.T) {
 						return b, nil
 					},
 				},
-				err: errors.New("name of provisioner does not match provisioner for which the EAB key was created"),
+				err: errors.New("provisioner does not match provisioner for which the EAB key was created"),
 			}
 		},
 		"fail/db.Get-error": func(t *testing.T) test {
@@ -1685,13 +1765,13 @@ func TestDB_UpdateExternalAccountKey(t *testing.T) {
 		tc := run(t)
 		t.Run(name, func(t *testing.T) {
 			d := DB{db: tc.db}
-			if err := d.UpdateExternalAccountKey(context.Background(), prov, tc.eak); err != nil {
+			if err := d.UpdateExternalAccountKey(context.Background(), provID, tc.eak); err != nil {
 				if assert.NotNil(t, tc.err) {
 					assert.HasPrefix(t, err.Error(), tc.err.Error())
 				}
 			} else if assert.Nil(t, tc.err) {
 				assert.Equals(t, dbeak.ID, tc.eak.ID)
-				assert.Equals(t, dbeak.Provisioner, tc.eak.Provisioner)
+				assert.Equals(t, dbeak.ProvisionerID, tc.eak.ProvisionerID)
 				assert.Equals(t, dbeak.Reference, tc.eak.Reference)
 				assert.Equals(t, dbeak.AccountID, tc.eak.AccountID)
 				assert.Equals(t, dbeak.CreatedAt, tc.eak.CreatedAt)
diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go
index 3c7019e2..8343c196 100644
--- a/acme/db/nosql/nosql.go
+++ b/acme/db/nosql/nosql.go
@@ -11,17 +11,18 @@ import (
 )
 
 var (
-	accountTable                        = []byte("acme_accounts")
-	accountByKeyIDTable                 = []byte("acme_keyID_accountID_index")
-	authzTable                          = []byte("acme_authzs")
-	challengeTable                      = []byte("acme_challenges")
-	nonceTable                          = []byte("nonces")
-	orderTable                          = []byte("acme_orders")
-	ordersByAccountIDTable              = []byte("acme_account_orders_index")
-	certTable                           = []byte("acme_certs")
-	certBySerialTable                   = []byte("acme_serial_certs_index")
-	externalAccountKeyTable             = []byte("acme_external_account_keys")
-	externalAccountKeysByReferenceTable = []byte("acme_external_account_key_reference_index")
+	accountTable                            = []byte("acme_accounts")
+	accountByKeyIDTable                     = []byte("acme_keyID_accountID_index")
+	authzTable                              = []byte("acme_authzs")
+	challengeTable                          = []byte("acme_challenges")
+	nonceTable                              = []byte("nonces")
+	orderTable                              = []byte("acme_orders")
+	ordersByAccountIDTable                  = []byte("acme_account_orders_index")
+	certTable                               = []byte("acme_certs")
+	certBySerialTable                       = []byte("acme_serial_certs_index")
+	externalAccountKeyTable                 = []byte("acme_external_account_keys")
+	externalAccountKeysByReferenceTable     = []byte("acme_external_account_key_reference_index")
+	externalAccountKeysByProvisionerIDTable = []byte("acme_external_account_keyID_provisionerID_index")
 )
 
 // DB is a struct that implements the AcmeDB interface.
@@ -33,7 +34,8 @@ type DB struct {
 func New(db nosqlDB.DB) (*DB, error) {
 	tables := [][]byte{accountTable, accountByKeyIDTable, authzTable,
 		challengeTable, nonceTable, orderTable, ordersByAccountIDTable,
-		certTable, certBySerialTable, externalAccountKeyTable, externalAccountKeysByReferenceTable,
+		certTable, certBySerialTable, externalAccountKeyTable,
+		externalAccountKeysByReferenceTable, externalAccountKeysByProvisionerIDTable,
 	}
 	for _, b := range tables {
 		if err := db.CreateTable(b); err != nil {
diff --git a/authority/admin/api/acme.go b/authority/admin/api/acme.go
index dbcca15b..82850010 100644
--- a/authority/admin/api/acme.go
+++ b/authority/admin/api/acme.go
@@ -15,6 +15,11 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
+const (
+	// provisionerContextKey provisioner key
+	provisionerContextKey = ContextKey("provisioner")
+)
+
 // CreateExternalAccountKeyRequest is the type for POST /admin/acme/eab requests
 type CreateExternalAccountKeyRequest struct {
 	Reference string `json:"reference"`
@@ -37,47 +42,63 @@ type GetExternalAccountKeysResponse struct {
 // before serving requests that act on ACME EAB credentials.
 func (h *Handler) requireEABEnabled(next nextHTTP) nextHTTP {
 	return func(w http.ResponseWriter, r *http.Request) {
-		prov := chi.URLParam(r, "prov")
-		eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), prov)
+		ctx := r.Context()
+		provName := chi.URLParam(r, "prov")
+		eabEnabled, prov, err := h.provisionerHasEABEnabled(ctx, provName)
 		if err != nil {
 			api.WriteError(w, err)
 			return
 		}
 		if !eabEnabled {
-			api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", prov))
+			api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", prov.GetName()))
 			return
 		}
-		next(w, r)
+		ctx = context.WithValue(ctx, provisionerContextKey, prov)
+		next(w, r.WithContext(ctx))
 	}
 }
 
 // provisionerHasEABEnabled determines if the "requireEAB" setting for an ACME
 // provisioner is set to true and thus has EAB enabled.
-func (h *Handler) provisionerHasEABEnabled(ctx context.Context, provisionerName string) (bool, error) {
+func (h *Handler) provisionerHasEABEnabled(ctx context.Context, provisionerName string) (bool, *linkedca.Provisioner, error) {
 	var (
 		p   provisioner.Interface
 		err error
 	)
 	if p, err = h.auth.LoadProvisionerByName(provisionerName); err != nil {
-		return false, admin.WrapErrorISE(err, "error loading provisioner %s", provisionerName)
+		return false, nil, admin.WrapErrorISE(err, "error loading provisioner %s", provisionerName)
 	}
 
 	prov, err := h.db.GetProvisioner(ctx, p.GetID())
 	if err != nil {
-		return false, admin.WrapErrorISE(err, "error getting provisioner with ID: %s", p.GetID())
+		return false, nil, admin.WrapErrorISE(err, "error getting provisioner with ID: %s", p.GetID())
 	}
 
 	details := prov.GetDetails()
 	if details == nil {
-		return false, admin.NewErrorISE("error getting details for provisioner with ID: %s", p.GetID())
+		return false, nil, admin.NewErrorISE("error getting details for provisioner with ID: %s", p.GetID())
 	}
 
 	acmeProvisioner := details.GetACME()
 	if acmeProvisioner == nil {
-		return false, admin.NewErrorISE("error getting ACME details for provisioner with ID: %s", p.GetID())
+		return false, nil, admin.NewErrorISE("error getting ACME details for provisioner with ID: %s", p.GetID())
 	}
 
-	return acmeProvisioner.GetRequireEab(), nil
+	return acmeProvisioner.GetRequireEab(), prov, nil
+}
+
+// provisionerFromContext searches the context for a provisioner. Returns the
+// provisioner or an error.
+func provisionerFromContext(ctx context.Context) (*linkedca.Provisioner, error) {
+	val := ctx.Value(provisionerContextKey)
+	if val == nil {
+		return nil, admin.NewErrorISE("provisioner expected in request context")
+	}
+	pval, ok := val.(*linkedca.Provisioner)
+	if !ok || pval == nil {
+		return nil, admin.NewErrorISE("provisioner in context is not a linkedca.Provisioner")
+	}
+	return pval, nil
 }
 
 // CreateExternalAccountKey creates a new External Account Binding key
@@ -93,12 +114,17 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
 		return
 	}
 
-	prov := chi.URLParam(r, "prov")
-	reference := body.Reference
+	ctx := r.Context()
+	prov, err := provisionerFromContext(ctx)
+	if err != nil {
+		api.WriteError(w, admin.WrapErrorISE(err, "error getting provisioner from context"))
+		return
+	}
 
 	// check if a key with the reference does not exist (only when a reference was in the request)
+	reference := body.Reference
 	if reference != "" {
-		k, err := h.acmeDB.GetExternalAccountKeyByReference(r.Context(), prov, reference)
+		k, err := h.acmeDB.GetExternalAccountKeyByReference(ctx, prov.GetId(), reference)
 		// retrieving an EAB key from DB results in an error if it doesn't exist, which is what we're looking for,
 		// but other errors can also happen. Return early if that happens; continuing if it was acme.ErrNotFound.
 		if shouldWriteError := err != nil && !errors.Is(err, acme.ErrNotFound); shouldWriteError {
@@ -107,7 +133,7 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
 		}
 		// if a key was found, return HTTP 409 conflict
 		if k != nil {
-			err := admin.NewError(admin.ErrorBadRequestType, "an ACME EAB key for provisioner %s with reference %s already exists", prov, reference)
+			err := admin.NewError(admin.ErrorBadRequestType, "an ACME EAB key for provisioner '%s' with reference '%s' already exists", prov.GetName(), reference)
 			err.Status = 409
 			api.WriteError(w, err)
 			return
@@ -115,9 +141,9 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
 		// continue execution if no key was found for the reference
 	}
 
-	eak, err := h.acmeDB.CreateExternalAccountKey(r.Context(), prov, reference)
+	eak, err := h.acmeDB.CreateExternalAccountKey(ctx, prov.GetId(), reference)
 	if err != nil {
-		msg := fmt.Sprintf("error creating ACME EAB key for provisioner '%s'", prov)
+		msg := fmt.Sprintf("error creating ACME EAB key for provisioner '%s'", prov.GetName())
 		if reference != "" {
 			msg += fmt.Sprintf(" and reference '%s'", reference)
 		}
@@ -128,7 +154,7 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
 	response := &linkedca.EABKey{
 		Id:          eak.ID,
 		HmacKey:     eak.KeyBytes,
-		Provisioner: eak.Provisioner,
+		Provisioner: prov.GetName(),
 		Reference:   eak.Reference,
 	}
 
@@ -137,10 +163,17 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
 
 // DeleteExternalAccountKey deletes an ACME External Account Key.
 func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Request) {
-	prov := chi.URLParam(r, "prov")
+
 	keyID := chi.URLParam(r, "id")
 
-	if err := h.acmeDB.DeleteExternalAccountKey(r.Context(), prov, keyID); err != nil {
+	ctx := r.Context()
+	prov, err := provisionerFromContext(ctx)
+	if err != nil {
+		api.WriteError(w, admin.WrapErrorISE(err, "error getting provisioner from context"))
+		return
+	}
+
+	if err := h.acmeDB.DeleteExternalAccountKey(ctx, prov.GetId(), keyID); err != nil {
 		api.WriteError(w, admin.WrapErrorISE(err, "error deleting ACME EAB Key '%s'", keyID))
 		return
 	}
@@ -152,8 +185,6 @@ func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Reques
 // only the ExternalAccountKey with that reference is returned. Otherwise all
 // ExternalAccountKeys in the system for a specific provisioner are returned.
 func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
-	prov := chi.URLParam(r, "prov")
-	reference := chi.URLParam(r, "ref")
 
 	var (
 		key  *acme.ExternalAccountKey
@@ -161,8 +192,16 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
 		err  error
 	)
 
+	ctx := r.Context()
+	prov, err := provisionerFromContext(ctx)
+	if err != nil {
+		api.WriteError(w, admin.WrapErrorISE(err, "error getting provisioner from context"))
+		return
+	}
+
+	reference := chi.URLParam(r, "ref")
 	if reference != "" {
-		if key, err = h.acmeDB.GetExternalAccountKeyByReference(r.Context(), prov, reference); err != nil {
+		if key, err = h.acmeDB.GetExternalAccountKeyByReference(ctx, prov.GetId(), reference); err != nil {
 			api.WriteError(w, admin.WrapErrorISE(err, "error retrieving external account key with reference '%s'", reference))
 			return
 		}
@@ -170,18 +209,19 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
 			keys = []*acme.ExternalAccountKey{key}
 		}
 	} else {
-		if keys, err = h.acmeDB.GetExternalAccountKeys(r.Context(), prov); err != nil {
+		if keys, err = h.acmeDB.GetExternalAccountKeys(ctx, prov.GetId()); err != nil {
 			api.WriteError(w, admin.WrapErrorISE(err, "error retrieving external account keys"))
 			return
 		}
 	}
 
+	provisionerName := prov.GetName()
 	eaks := make([]*linkedca.EABKey, len(keys))
 	for i, k := range keys {
 		eaks[i] = &linkedca.EABKey{
 			Id:          k.ID,
 			HmacKey:     []byte{},
-			Provisioner: k.Provisioner,
+			Provisioner: provisionerName,
 			Reference:   k.Reference,
 			Account:     k.AccountID,
 			CreatedAt:   timestamppb.New(k.CreatedAt),
diff --git a/authority/admin/api/acme_test.go b/authority/admin/api/acme_test.go
index 7162ea98..f7de9290 100644
--- a/authority/admin/api/acme_test.go
+++ b/authority/admin/api/acme_test.go
@@ -368,12 +368,13 @@ func TestHandler_provisionerHasEABEnabled(t *testing.T) {
 				auth:   tc.auth,
 				acmeDB: nil,
 			}
-			got, err := h.provisionerHasEABEnabled(context.TODO(), tc.provisionerName)
+			got, prov, err := h.provisionerHasEABEnabled(context.TODO(), tc.provisionerName)
 			if (err != nil) != (tc.err != nil) {
 				t.Errorf("Handler.provisionerHasEABEnabled() error = %v, want err %v", err, tc.err)
 				return
 			}
 			if tc.err != nil {
+				assert.Type(t, &linkedca.Provisioner{}, prov)
 				assert.Type(t, &admin.Error{}, err)
 				adminError, _ := err.(*admin.Error)
 				assert.Equals(t, tc.err.Type, adminError.Type)
@@ -434,6 +435,10 @@ func TestCreateExternalAccountKeyRequest_Validate(t *testing.T) {
 }
 
 func TestHandler_CreateExternalAccountKey(t *testing.T) {
+	prov := &linkedca.Provisioner{
+		Id:   "provID",
+		Name: "provName",
+	}
 	type test struct {
 		ctx        context.Context
 		db         acme.DB
@@ -487,14 +492,15 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "an-external-key-reference",
 			}
 			body, err := json.Marshal(req)
 			assert.FatalError(t, err)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, errors.New("force")
 				},
@@ -517,22 +523,23 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "an-external-key-reference",
 			}
 			body, err := json.Marshal(req)
 			assert.FatalError(t, err)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					past := time.Now().Add(-24 * time.Hour)
 					return &acme.ExternalAccountKey{
-						ID:          "eakID",
-						Provisioner: "provName",
-						Reference:   "an-external-key-reference",
-						KeyBytes:    []byte{1, 3, 3, 7},
-						CreatedAt:   past,
+						ID:            "eakID",
+						ProvisionerID: "provID",
+						Reference:     "an-external-key-reference",
+						KeyBytes:      []byte{1, 3, 3, 7},
+						CreatedAt:     past,
 					}, nil
 				},
 			}
@@ -546,7 +553,7 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 					Type:    admin.ErrorBadRequestType.String(),
 					Status:  409,
 					Detail:  "bad request",
-					Message: "an ACME EAB key for provisioner provName with reference an-external-key-reference already exists",
+					Message: "an ACME EAB key for provisioner 'provName' with reference 'an-external-key-reference' already exists",
 				},
 			}
 		},
@@ -554,14 +561,15 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "",
 			}
 			body, err := json.Marshal(req)
 			assert.FatalError(t, err)
 			db := &acme.MockDB{
-				MockCreateExternalAccountKey: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockCreateExternalAccountKey: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "", reference)
 					return nil, errors.New("force")
 				},
@@ -583,19 +591,20 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "an-external-key-reference",
 			}
 			body, err := json.Marshal(req)
 			assert.FatalError(t, err)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, acme.ErrNotFound // simulating not found; skipping 409 conflict
 				},
-				MockCreateExternalAccountKey: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockCreateExternalAccountKey: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, errors.New("force")
 				},
@@ -617,6 +626,7 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "",
 			}
@@ -624,15 +634,15 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			assert.FatalError(t, err)
 			now := time.Now()
 			db := &acme.MockDB{
-				MockCreateExternalAccountKey: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockCreateExternalAccountKey: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "", reference)
 					return &acme.ExternalAccountKey{
-						ID:          "eakID",
-						Provisioner: "provName",
-						Reference:   "",
-						KeyBytes:    []byte{1, 3, 3, 7},
-						CreatedAt:   now,
+						ID:            "eakID",
+						ProvisionerID: "provID",
+						Reference:     "",
+						KeyBytes:      []byte{1, 3, 3, 7},
+						CreatedAt:     now,
 					}, nil
 				},
 			}
@@ -653,6 +663,7 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			chiCtx := chi.NewRouteContext()
 			chiCtx.URLParams.Add("prov", "provName")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			req := CreateExternalAccountKeyRequest{
 				Reference: "an-external-key-reference",
 			}
@@ -660,20 +671,20 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 			assert.FatalError(t, err)
 			now := time.Now()
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, acme.ErrNotFound // simulating not found; skipping 409 conflict
 				},
-				MockCreateExternalAccountKey: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockCreateExternalAccountKey: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return &acme.ExternalAccountKey{
-						ID:          "eakID",
-						Provisioner: "provName",
-						Reference:   "an-external-key-reference",
-						KeyBytes:    []byte{1, 3, 3, 7},
-						CreatedAt:   now,
+						ID:            "eakID",
+						ProvisionerID: "provID",
+						Reference:     "an-external-key-reference",
+						KeyBytes:      []byte{1, 3, 3, 7},
+						CreatedAt:     now,
 					}, nil
 				},
 			}
@@ -737,6 +748,10 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
 }
 
 func TestHandler_DeleteExternalAccountKey(t *testing.T) {
+	prov := &linkedca.Provisioner{
+		Id:   "provID",
+		Name: "provName",
+	}
 	type test struct {
 		ctx        context.Context
 		db         acme.DB
@@ -749,9 +764,10 @@ func TestHandler_DeleteExternalAccountKey(t *testing.T) {
 			chiCtx.URLParams.Add("prov", "provName")
 			chiCtx.URLParams.Add("id", "keyID")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			db := &acme.MockDB{
-				MockDeleteExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) error {
-					assert.Equals(t, "provName", provisionerName)
+				MockDeleteExternalAccountKey: func(ctx context.Context, provisionerID, keyID string) error {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "keyID", keyID)
 					return errors.New("force")
 				},
@@ -773,9 +789,10 @@ func TestHandler_DeleteExternalAccountKey(t *testing.T) {
 			chiCtx.URLParams.Add("prov", "provName")
 			chiCtx.URLParams.Add("id", "keyID")
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			db := &acme.MockDB{
-				MockDeleteExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) error {
-					assert.Equals(t, "provName", provisionerName)
+				MockDeleteExternalAccountKey: func(ctx context.Context, provisionerID, keyID string) error {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "keyID", keyID)
 					return nil
 				},
@@ -831,6 +848,10 @@ func TestHandler_DeleteExternalAccountKey(t *testing.T) {
 }
 
 func TestHandler_GetExternalAccountKeys(t *testing.T) {
+	prov := &linkedca.Provisioner{
+		Id:   "provID",
+		Name: "provName",
+	}
 	type test struct {
 		ctx        context.Context
 		db         acme.DB
@@ -846,9 +867,10 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
 			chiCtx.URLParams.Add("ref", "an-external-key-reference")
 			req := httptest.NewRequest("GET", "/foo", nil)
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, errors.New("force")
 				},
@@ -871,9 +893,10 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
 			chiCtx.URLParams.Add("prov", "provName")
 			req := httptest.NewRequest("GET", "/foo", nil)
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeys: func(ctx context.Context, provisionerID string) ([]*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					return nil, errors.New("force")
 				},
 			}
@@ -896,9 +919,10 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
 			chiCtx.URLParams.Add("ref", "an-external-key-reference")
 			req := httptest.NewRequest("GET", "/foo", nil)
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return nil, nil // returning nil; no key found
 				},
@@ -920,17 +944,18 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
 			chiCtx.URLParams.Add("ref", "an-external-key-reference")
 			req := httptest.NewRequest("GET", "/foo", nil)
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			createdAt := time.Now().Add(-24 * time.Hour)
 			var boundAt time.Time
 			db := &acme.MockDB{
-				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerName, reference string) (*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeyByReference: func(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					assert.Equals(t, "an-external-key-reference", reference)
 					return &acme.ExternalAccountKey{
-						ID:          "eakID",
-						Provisioner: "provName",
-						Reference:   "an-external-key-reference",
-						CreatedAt:   createdAt,
+						ID:            "eakID",
+						ProvisionerID: "provID",
+						Reference:     "an-external-key-reference",
+						CreatedAt:     createdAt,
 					}, nil
 				},
 			}
@@ -958,107 +983,36 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
 			chiCtx.URLParams.Add("prov", "provName")
 			req := httptest.NewRequest("GET", "/foo", nil)
 			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
+			ctx = context.WithValue(ctx, provisionerContextKey, prov)
 			createdAt := time.Now().Add(-24 * time.Hour)
 			var boundAt time.Time
 			boundAtSet := time.Now().Add(-12 * time.Hour)
 			db := &acme.MockDB{
-				MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
+				MockGetExternalAccountKeys: func(ctx context.Context, provisionerID string) ([]*acme.ExternalAccountKey, error) {
+					assert.Equals(t, "provID", provisionerID)
 					return []*acme.ExternalAccountKey{
 						{
-							ID:          "eakID1",
-							Provisioner: "provName",
-							Reference:   "some-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt,
+							ID:            "eakID1",
+							ProvisionerID: "provID",
+							Reference:     "some-external-key-reference",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     createdAt,
 						},
 						{
-							ID:          "eakID2",
-							Provisioner: "provName",
-							Reference:   "some-other-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt.Add(1 * time.Hour),
+							ID:            "eakID2",
+							ProvisionerID: "provID",
+							Reference:     "some-other-external-key-reference",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     createdAt.Add(1 * time.Hour),
 						},
 						{
-							ID:          "eakID3",
-							Provisioner: "provName",
-							Reference:   "another-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt,
-							BoundAt:     boundAtSet,
-							AccountID:   "accountID",
-						},
-					}, nil
-				},
-			}
-			return test{
-				ctx:        ctx,
-				statusCode: 200,
-				req:        req,
-				resp: GetExternalAccountKeysResponse{
-					EAKs: []*linkedca.EABKey{
-						{
-							Id:          "eakID1",
-							Provisioner: "provName",
-							Reference:   "some-external-key-reference",
-							CreatedAt:   timestamppb.New(createdAt),
-							BoundAt:     timestamppb.New(boundAt),
-						},
-						{
-							Id:          "eakID2",
-							Provisioner: "provName",
-							Reference:   "some-other-external-key-reference",
-							CreatedAt:   timestamppb.New(createdAt.Add(1 * time.Hour)),
-							BoundAt:     timestamppb.New(boundAt),
-						},
-						{
-							Id:          "eakID3",
-							Provisioner: "provName",
-							Reference:   "another-external-key-reference",
-							CreatedAt:   timestamppb.New(createdAt),
-							BoundAt:     timestamppb.New(boundAtSet),
-							Account:     "accountID",
-						},
-					},
-				},
-				db:  db,
-				err: nil,
-			}
-		},
-		"ok/multiple-keys-with-cursor-and-limit": func(t *testing.T) test {
-			chiCtx := chi.NewRouteContext()
-			chiCtx.URLParams.Add("prov", "provName")
-			req := httptest.NewRequest("GET", "/foo?cursor=eakID1&limit=10", nil)
-			ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
-			createdAt := time.Now().Add(-24 * time.Hour)
-			var boundAt time.Time
-			boundAtSet := time.Now().Add(-12 * time.Hour)
-			db := &acme.MockDB{
-				MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
-					assert.Equals(t, "provName", provisionerName)
-					return []*acme.ExternalAccountKey{
-						{
-							ID:          "eakID1",
-							Provisioner: "provName",
-							Reference:   "some-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt,
-						},
-						{
-							ID:          "eakID2",
-							Provisioner: "provName",
-							Reference:   "some-other-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt.Add(1 * time.Hour),
-						},
-						{
-							ID:          "eakID3",
-							Provisioner: "provName",
-							Reference:   "another-external-key-reference",
-							KeyBytes:    []byte{1, 3, 3, 7},
-							CreatedAt:   createdAt,
-							BoundAt:     boundAtSet,
-							AccountID:   "accountID",
+							ID:            "eakID3",
+							ProvisionerID: "provID",
+							Reference:     "another-external-key-reference",
+							KeyBytes:      []byte{1, 3, 3, 7},
+							CreatedAt:     createdAt,
+							BoundAt:       boundAtSet,
+							AccountID:     "accountID",
 						},
 					}, nil
 				},