diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go
index a2458680cbfb94b65c6a6cd877c5945ab51fb2e9..dcc765c9656ec147b66e8cb0330889a35655f89b 100644
--- a/storage/conformance/conformance.go
+++ b/storage/conformance/conformance.go
@@ -18,12 +18,10 @@ import (
 // ensure that values being tested on never expire.
 var neverExpire = time.Now().UTC().Add(time.Hour * 24 * 365 * 100)
 
-// StorageFactory is a method for creating a new storage. The returned storage sould be initialized
-// but shouldn't have any existing data in it.
-type StorageFactory func() storage.Storage
-
-// RunTestSuite runs a set of conformance tests against a storage.
-func RunTestSuite(t *testing.T, sf StorageFactory) {
+// RunTests runs a set of conformance tests against a storage. newStorage should
+// return an initialized but empty storage. The storage will be closed at the
+// end of each test run.
+func RunTests(t *testing.T, newStorage func() storage.Storage) {
 	tests := []struct {
 		name string
 		run  func(t *testing.T, s storage.Storage)
@@ -33,10 +31,13 @@ func RunTestSuite(t *testing.T, sf StorageFactory) {
 		{"ClientCRUD", testClientCRUD},
 		{"RefreshTokenCRUD", testRefreshTokenCRUD},
 		{"PasswordCRUD", testPasswordCRUD},
+		{"GarbageCollection", testGC},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			test.run(t, sf())
+			s := newStorage()
+			test.run(t, s)
+			s.Close()
 		})
 	}
 }
@@ -276,3 +277,92 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) {
 		t.Errorf("after deleting password expected storage.ErrNotFound, got %v", err)
 	}
 }
+
+func testGC(t *testing.T, s storage.Storage) {
+	n := time.Now()
+	c := storage.AuthCode{
+		ID:            storage.NewID(),
+		ClientID:      "foobar",
+		RedirectURI:   "https://localhost:80/callback",
+		Nonce:         "foobar",
+		Scopes:        []string{"openid", "email"},
+		Expiry:        n.Add(time.Second),
+		ConnectorID:   "ldap",
+		ConnectorData: []byte(`{"some":"data"}`),
+		Claims: storage.Claims{
+			UserID:        "1",
+			Username:      "jane",
+			Email:         "jane.doe@example.com",
+			EmailVerified: true,
+			Groups:        []string{"a", "b"},
+		},
+	}
+
+	if err := s.CreateAuthCode(c); err != nil {
+		t.Fatalf("failed creating auth code: %v", err)
+	}
+
+	if _, err := s.GarbageCollect(n); err != nil {
+		t.Errorf("garbage collection failed: %v", err)
+	}
+	if _, err := s.GetAuthCode(c.ID); err != nil {
+		t.Errorf("expected to be able to get auth code after GC: %v", err)
+	}
+
+	if r, err := s.GarbageCollect(n.Add(time.Minute)); err != nil {
+		t.Errorf("garbage collection failed: %v", err)
+	} else if r.AuthCodes != 1 {
+		t.Errorf("expected to garbage collect 1 objects, got %d", r.AuthCodes)
+	}
+
+	if _, err := s.GetAuthCode(c.ID); err == nil {
+		t.Errorf("expected auth code to be GC'd")
+	} else if err != storage.ErrNotFound {
+		t.Errorf("expected storage.ErrNotFound, got %v", err)
+	}
+
+	a := storage.AuthRequest{
+		ID:                  storage.NewID(),
+		ClientID:            "foobar",
+		ResponseTypes:       []string{"code"},
+		Scopes:              []string{"openid", "email"},
+		RedirectURI:         "https://localhost:80/callback",
+		Nonce:               "foo",
+		State:               "bar",
+		ForceApprovalPrompt: true,
+		LoggedIn:            true,
+		Expiry:              n,
+		ConnectorID:         "ldap",
+		ConnectorData:       []byte(`{"some":"data"}`),
+		Claims: storage.Claims{
+			UserID:        "1",
+			Username:      "jane",
+			Email:         "jane.doe@example.com",
+			EmailVerified: true,
+			Groups:        []string{"a", "b"},
+		},
+	}
+
+	if err := s.CreateAuthRequest(a); err != nil {
+		t.Fatalf("failed creating auth request: %v", err)
+	}
+
+	if _, err := s.GarbageCollect(n); err != nil {
+		t.Errorf("garbage collection failed: %v", err)
+	}
+	if _, err := s.GetAuthRequest(a.ID); err != nil {
+		t.Errorf("expected to be able to get auth code after GC: %v", err)
+	}
+
+	if r, err := s.GarbageCollect(n.Add(time.Minute)); err != nil {
+		t.Errorf("garbage collection failed: %v", err)
+	} else if r.AuthRequests != 1 {
+		t.Errorf("expected to garbage collect 1 objects, got %d", r.AuthRequests)
+	}
+
+	if _, err := s.GetAuthRequest(a.ID); err == nil {
+		t.Errorf("expected auth code to be GC'd")
+	} else if err != storage.ErrNotFound {
+		t.Errorf("expected storage.ErrNotFound, got %v", err)
+	}
+}