diff --git a/actions.go b/actions.go
index bb2dc5e9e619d71bac4158fecf154ee26b2b8ad7..61f0abfbf40f3a3d5e68424e57b2dd3ff207b546 100644
--- a/actions.go
+++ b/actions.go
@@ -407,7 +407,7 @@ func (s *AccountService) DeleteApplicationSpecificPassword(ctx context.Context,
 		// password (we're going to find it via its ID).
 		keys, err := tx.GetUserEncryptionKeys(ctx, user)
 		if err != nil {
-			return err
+			return newBackendError(err)
 		}
 		if len(keys) == 0 {
 			return nil
@@ -510,7 +510,7 @@ func (s *AccountService) MoveResource(ctx context.Context, tx TX, req *MoveResou
 		// Collect all related resources, as they should all be moved at once.
 		r, err := tx.GetResource(ctx, req.ResourceID)
 		if err != nil {
-			return err
+			return newBackendError(err)
 		}
 		var resources []*Resource
 		if r.Group != "" {
@@ -522,7 +522,7 @@ func (s *AccountService) MoveResource(ctx context.Context, tx TX, req *MoveResou
 		for _, r := range resources {
 			r.Shard = req.Shard
 			if err := tx.UpdateResource(ctx, r); err != nil {
-				return err
+				return newBackendError(err)
 			}
 			resp.MovedIDs = append(resp.MovedIDs, r.ID.String())
 		}
@@ -572,6 +572,7 @@ func (s *AccountService) EnableOTP(ctx context.Context, tx TX, req *EnableOTPReq
 		}
 
 		resp.TOTPSecret = req.TOTPSecret
+		s.audit.Log(ctx, ResourceID{}, "totp enabled")
 		return nil
 	})
 	return &resp, err
@@ -589,6 +590,7 @@ func (s *AccountService) DisableOTP(ctx context.Context, tx TX, req *DisableOTPR
 		if err := tx.DeleteUserTOTPSecret(ctx, user); err != nil {
 			return newBackendError(err)
 		}
+		s.audit.Log(ctx, ResourceID{}, "totp disabled")
 		return nil
 	})
 }
@@ -652,7 +654,7 @@ func (s *AccountService) DeleteEmailAlias(ctx context.Context, tx TX, req *Delet
 		}
 		r.Email.Aliases = aliases
 		if err := tx.UpdateResource(ctx, r); err != nil {
-			return err
+			return newBackendError(err)
 		}
 
 		s.audit.Log(ctx, r.ID, fmt.Sprintf("removed alias %s", req.Addr))
@@ -729,8 +731,9 @@ func (s *AccountService) CreateResources(ctx context.Context, tx TX, req *Create
 	err := s.handleAdminRequest(ctx, tx, req, req.SSO, func(ctx context.Context) error {
 		for _, r := range req.Resources {
 			if err := tx.CreateResource(ctx, r); err != nil {
-				return err
+				return newBackendError(err)
 			}
+			s.audit.Log(ctx, r.ID, "resource created")
 			resp.Resources = append(resp.Resources, r)
 		}
 		return nil
@@ -786,7 +789,7 @@ func (req *CreateUserRequest) Validate(ctx context.Context, s *AccountService, _
 	return nil
 }
 
-// CreateUserResponse is the request type for AccountService.CreateUser().
+// CreateUserResponse is the response type for AccountService.CreateUser().
 type CreateUserResponse struct {
 	User     *User  `json:"user,omitempty"`
 	Password string `json:"password"`
@@ -812,7 +815,7 @@ func (s *AccountService) CreateUser(ctx context.Context, tx TX, req *CreateUserR
 	err := s.handleAdminRequest(ctx, tx, req, req.SSO, func(ctx context.Context) (err error) {
 		// Create the user first, along with all the resources.
 		if err := tx.CreateUser(ctx, req.User); err != nil {
-			return err
+			return newBackendError(err)
 		}
 		resp.User = req.User
 
@@ -826,7 +829,9 @@ func (s *AccountService) CreateUser(ctx context.Context, tx TX, req *CreateUserR
 		}
 		resp.Password = newPassword
 
+		s.audit.Log(ctx, ResourceID{}, "user created")
 		for _, r := range req.User.Resources {
+			s.audit.Log(ctx, r.ID, "resource created")
 			if resourceHasPassword(r) {
 				if _, err := s.doResetResourcePassword(ctx, tx, r); err != nil {
 					// Just log, don't fail.
diff --git a/actions_test.go b/actions_test.go
index ed87188ad328f816d7ca33701ff8ef7ad1ddd6dc..c0423b31e5dcc0b2e9a14dd0799c1591da80b102 100644
--- a/actions_test.go
+++ b/actions_test.go
@@ -116,12 +116,16 @@ func (b *fakeBackend) HasAnyResource(_ context.Context, rsrcs []FindResourceRequ
 
 const testAdminGroupName = "admins"
 
+// Fake SSO validator: the sso ticket username is just the ticket
+// itself. The only invalid value is the empty string.
 type fakeValidator struct {
 	adminUser string
 }
 
 func (v *fakeValidator) Validate(tkt, nonce, service string, _ []string) (*sso.Ticket, error) {
-	// The sso ticket username is just the ticket itself.
+	if tkt == "" {
+		return nil, errors.New("empty sso ticket")
+	}
 	var groups []string
 	if tkt == v.adminUser {
 		groups = []string{testAdminGroupName}
@@ -422,17 +426,18 @@ func TestService_CreateResource_List(t *testing.T) {
 func TestService_CreateUser(t *testing.T) {
 	svc, tx := testService("admin")
 
+	emailResourceID := NewResourceID(ResourceTypeEmail, "testuser2@example.com", "testuser2@example.com")
 	req := &CreateUserRequest{
 		SSO: "admin",
 		User: &User{
 			Name: "testuser2@example.com",
 			Resources: []*Resource{
 				&Resource{
-					ID:            NewResourceID(ResourceTypeEmail, "testuser2@example.com", "testuser2@example.com"),
-					Name:          "testuser2@example.com",
-					Status:        ResourceStatusActive,
-					Shard:         "host2",
-					OriginalShard: "host2",
+					ID:   emailResourceID,
+					Name: "testuser2@example.com",
+					//Status:        ResourceStatusActive,
+					//Shard:         "host2",
+					//OriginalShard: "host2",
 					Email: &Email{
 						Maildir: "example.com/testuser2",
 					},
@@ -449,18 +454,33 @@ func TestService_CreateUser(t *testing.T) {
 	if resp.User.Name != "testuser2@example.com" {
 		t.Fatalf("unexpected user in response: got %s, expected testuser2", resp.User.Name)
 	}
+
+	user, _ := tx.GetUser(context.Background(), "testuser2@example.com")
+	if user == nil {
+		t.Fatal("GetUser returned nil")
+	}
+
+	// Verify that the new resource has default fields set.
+	resource := user.GetResourceByID(emailResourceID)
+	if resource == nil {
+		t.Fatalf("user.GetResourceByID(%s) failed", emailResourceID)
+	}
+	if resource.Shard == "" {
+		t.Fatalf("resource shard is unset: %+v", resource)
+	}
 }
 
 func TestService_CreateUser_FailIfNotAdmin(t *testing.T) {
 	svc, tx := testService("admin")
 
+	emailResourceID := NewResourceID(ResourceTypeEmail, "testuser2@example.com", "testuser2@example.com")
 	req := &CreateUserRequest{
 		SSO: "testuser",
 		User: &User{
 			Name: "testuser2@example.com",
 			Resources: []*Resource{
 				&Resource{
-					ID:            NewResourceID(ResourceTypeEmail, "testuser2@example.com", "testuser2@example.com"),
+					ID:            emailResourceID,
 					Name:          "testuser2@example.com",
 					Status:        ResourceStatusActive,
 					Shard:         "host2",
diff --git a/service.go b/service.go
index 8b46fcb1d24b11fc3be02b0e76f4cd2c5574ff55..3c82d693b3164f8ecb3a9bda09edfa7e6dfd8170 100644
--- a/service.go
+++ b/service.go
@@ -160,7 +160,7 @@ type authUserCtxKeyType int
 var authUserCtxKey authUserCtxKeyType
 
 func authUserFromContext(ctx context.Context) string {
-	s, ok := ctx.Value(userCtxKey).(string)
+	s, ok := ctx.Value(authUserCtxKey).(string)
 	if ok {
 		return s
 	}