diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index b03b397..65985d3 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -215,6 +215,8 @@ type AlbumResolver interface {
 	Media(ctx context.Context, obj *models.Album, filter *models.Filter, onlyFavorites *bool) ([]*models.Media, error)
 	SubAlbums(ctx context.Context, obj *models.Album, filter *models.Filter) ([]*models.Album, error)
 
+	Owner(ctx context.Context, obj *models.Album) (*models.User, error)
+
 	Thumbnail(ctx context.Context, obj *models.Album) (*models.Media, error)
 	Path(ctx context.Context, obj *models.Album) ([]*models.Album, error)
 	Shares(ctx context.Context, obj *models.Album) ([]*models.ShareToken, error)
@@ -2366,14 +2368,14 @@ func (ec *executionContext) _Album_owner(ctx context.Context, field graphql.Coll
 		Object:     "Album",
 		Field:      field,
 		Args:       nil,
-		IsMethod:   false,
-		IsResolver: false,
+		IsMethod:   true,
+		IsResolver: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Owner, nil
+		return ec.resolvers.Album().Owner(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -2385,9 +2387,9 @@ func (ec *executionContext) _Album_owner(ctx context.Context, field graphql.Coll
 		}
 		return graphql.Null
 	}
-	res := resTmp.(models.User)
+	res := resTmp.(*models.User)
 	fc.Result = res
-	return ec.marshalNUser2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
+	return ec.marshalNUser2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Album_filePath(ctx context.Context, field graphql.CollectedField, obj *models.Album) (ret graphql.Marshaler) {
@@ -7534,10 +7536,19 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob
 		case "parentAlbum":
 			out.Values[i] = ec._Album_parentAlbum(ctx, field, obj)
 		case "owner":
-			out.Values[i] = ec._Album_owner(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._Album_owner(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "filePath":
 			out.Values[i] = ec._Album_filePath(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
diff --git a/api/graphql/models/album.go b/api/graphql/models/album.go
index 62b6b8a..0e6149a 100644
--- a/api/graphql/models/album.go
+++ b/api/graphql/models/album.go
@@ -12,10 +12,11 @@ type Album struct {
 	Title         string `gorm:"not null"`
 	ParentAlbumID *int
 	ParentAlbum   *Album
-	OwnerID       int `gorm:"not null"`
-	Owner         User
-	Path          string `gorm:"not null"`
-	PathHash      string `gorm:"unique"`
+	// OwnerID       int `gorm:"not null"`
+	// Owner         User
+	Owners   []User `gorm:"many2many:user_albums"`
+	Path     string `gorm:"not null"`
+	PathHash string `gorm:"unique"`
 }
 
 func (a *Album) FilePath() string {
diff --git a/api/graphql/models/user.go b/api/graphql/models/user.go
index 94b1a1c..2d4505d 100644
--- a/api/graphql/models/user.go
+++ b/api/graphql/models/user.go
@@ -153,3 +153,30 @@ func VerifyTokenAndGetUser(db *gorm.DB, token string) (*User, error) {
 
 	return &user, nil
 }
+
+// FillAlbums fill user.Albums with albums from database
+func (user *User) FillAlbums(db *gorm.DB) error {
+	// Albums already present
+	if len(user.Albums) > 0 {
+		return nil
+	}
+
+	if err := db.Model(&user).Association("Albums").Find(&user.Albums); err != nil {
+		return errors.Wrap(err, "fill user albums")
+	}
+
+	return nil
+}
+
+func (user *User) OwnsAlbum(db *gorm.DB, album *Album) (bool, error) {
+
+	// user.QueryUserAlbums(db, db.Where("id = ?", album.ID))
+
+	// TODO: Implement this
+	return true, nil
+}
+
+func (user *User) OwnsMedia(db *gorm.DB, media *Media) (bool, error) {
+	// TODO: implement this
+	return true, nil
+}
diff --git a/api/graphql/resolvers/album.go b/api/graphql/resolvers/album.go
index b8313fb..ea8b5a7 100644
--- a/api/graphql/resolvers/album.go
+++ b/api/graphql/resolvers/album.go
@@ -2,10 +2,12 @@ package resolvers
 
 import (
 	"context"
+	"errors"
 
 	api "github.com/photoview/photoview/api/graphql"
 	"github.com/photoview/photoview/api/graphql/auth"
 	"github.com/photoview/photoview/api/graphql/models"
+	"gorm.io/gorm"
 )
 
 func (r *queryResolver) MyAlbums(ctx context.Context, filter *models.Filter, onlyRoot *bool, showEmpty *bool, onlyWithFavorites *bool) ([]*models.Album, error) {
@@ -14,10 +16,19 @@ func (r *queryResolver) MyAlbums(ctx context.Context, filter *models.Filter, onl
 		return nil, auth.ErrUnauthorized
 	}
 
-	query := r.Database.Where("owner_id = ?", user.ID)
+	if err := user.FillAlbums(r.Database); err != nil {
+		return nil, err
+	}
+
+	userAlbumIDs := make([]int, len(user.Albums))
+	for i, album := range user.Albums {
+		userAlbumIDs[i] = album.ID
+	}
+
+	query := r.Database.Model(models.Album{}).Where("id IN (?)", userAlbumIDs)
 
 	if onlyRoot != nil && *onlyRoot == true {
-		query = query.Where("parent_album_id = (?)", r.Database.Model(&models.Album{}).Select("id").Where("parent_album_id IS NULL AND owner_id = ?", user.ID))
+		query = query.Where("parent_album_id = (?)", r.Database.Model(&models.Album{}).Select("id").Where("parent_album_id IS NULL"))
 	}
 
 	if showEmpty == nil || *showEmpty == false {
@@ -33,7 +44,7 @@ func (r *queryResolver) MyAlbums(ctx context.Context, filter *models.Filter, onl
 	query = filter.FormatSQL(query)
 
 	var albums []*models.Album
-	if err := query.Find(&albums).Error; err != nil {
+	if err := query.Scan(&albums).Error; err != nil {
 		return nil, err
 	}
 
@@ -47,10 +58,22 @@ func (r *queryResolver) Album(ctx context.Context, id int) (*models.Album, error
 	}
 
 	var album models.Album
-	if err := r.Database.Where("owner_id = ?", user.ID).First(&album, id).Error; err != nil {
+	if err := r.Database.First(&album, id).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, errors.New("album not found")
+		}
 		return nil, err
 	}
 
+	ownsAlbum, err := user.OwnsAlbum(r.Database, &album)
+	if err != nil {
+		return nil, err
+	}
+
+	if !ownsAlbum {
+		return nil, errors.New("forbidden")
+	}
+
 	return &album, nil
 }
 
@@ -154,8 +177,24 @@ func (r *albumResolver) Path(ctx context.Context, obj *models.Album) ([]*models.
 			UNION
 			SELECT parent.* FROM path_albums child JOIN albums parent ON parent.id = child.parent_album_id
 		)
-		SELECT * FROM path_albums WHERE id != ? AND owner_id = ?
-	`, obj.ID, obj.ID, user.ID).Scan(&album_path).Error
+		SELECT * FROM path_albums WHERE id != ?
+	`, obj.ID, obj.ID).Scan(&album_path).Error
+
+	// Make sure to only return albums this user owns
+	for i := len(album_path) - 1; i >= 0; i-- {
+		album := album_path[i]
+
+		owns, err := user.OwnsAlbum(r.Database, album)
+		if err != nil {
+			return nil, err
+		}
+
+		if !owns {
+			album_path = album_path[i+1:]
+			break
+		}
+
+	}
 
 	if err != nil {
 		return nil, err
diff --git a/api/graphql/resolvers/media.go b/api/graphql/resolvers/media.go
index a6b8231..f1e3d66 100644
--- a/api/graphql/resolvers/media.go
+++ b/api/graphql/resolvers/media.go
@@ -18,11 +18,20 @@ func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*
 		return nil, errors.New("unauthorized")
 	}
 
+	if err := user.FillAlbums(r.Database); err != nil {
+		return nil, err
+	}
+
+	userAlbumIDs := make([]int, len(user.Albums))
+	for i, album := range user.Albums {
+		userAlbumIDs[i] = album.ID
+	}
+
 	var media []*models.Media
 
 	query := r.Database.
 		Joins("Album").
-		Where("albums.owner_id = ?", user.ID).
+		Where("albums.id IN (?)", userAlbumIDs).
 		Where("media.id IN (?)", r.Database.Model(&models.MediaURL{}).Select("id").Where("media_url.media_id = media.id"))
 
 	query = filter.FormatSQL(query)
diff --git a/api/graphql/resolvers/scanner.go b/api/graphql/resolvers/scanner.go
index 734eb47..179f1be 100644
--- a/api/graphql/resolvers/scanner.go
+++ b/api/graphql/resolvers/scanner.go
@@ -28,7 +28,6 @@ func (r *mutationResolver) ScanAll(ctx context.Context) (*models.ScannerResult,
 func (r *mutationResolver) ScanUser(ctx context.Context, userID int) (*models.ScannerResult, error) {
 
 	var user models.User
-
 	if err := r.Database.First(&user, userID).Error; err != nil {
 		return nil, errors.Wrap(err, "get user from database")
 	}
diff --git a/api/graphql/resolvers/user.go b/api/graphql/resolvers/user.go
index f6d594d..50dc24b 100644
--- a/api/graphql/resolvers/user.go
+++ b/api/graphql/resolvers/user.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/photoview/photoview/api/graphql/auth"
 	"github.com/photoview/photoview/api/graphql/models"
+	"github.com/photoview/photoview/api/scanner"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/bcrypt"
 	"gorm.io/gorm"
@@ -84,6 +85,11 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
 			return err
 		}
 
+		_, err = scanner.NewRootAlbum(tx, rootPath, user)
+		if err != nil {
+			return err
+		}
+
 		token, err = user.GenerateAccessToken(tx)
 		if err != nil {
 			return err
diff --git a/api/routes/authenticate_media.go b/api/routes/authenticate_media.go
index 8e42f41..e29d8cc 100644
--- a/api/routes/authenticate_media.go
+++ b/api/routes/authenticate_media.go
@@ -19,7 +19,12 @@ func authenticateMedia(media *models.Media, db *gorm.DB, r *http.Request) (succe
 			return false, "internal server error", http.StatusInternalServerError, err
 		}
 
-		if album.OwnerID != user.ID {
+		ownsAlbum, err := user.OwnsAlbum(db, &album)
+		if err != nil {
+			return false, "internal server error", http.StatusInternalServerError, err
+		}
+
+		if !ownsAlbum {
 			return false, "invalid credentials", http.StatusForbidden, nil
 		}
 	} else {
diff --git a/api/scanner/cleanup_media.go b/api/scanner/cleanup_media.go
index ddf0649..e89b2d1 100644
--- a/api/scanner/cleanup_media.go
+++ b/api/scanner/cleanup_media.go
@@ -4,7 +4,6 @@ import (
 	"os"
 	"path"
 	"strconv"
-	"strings"
 
 	"github.com/photoview/photoview/api/graphql/models"
 	"github.com/pkg/errors"
@@ -59,28 +58,32 @@ func deleteOldUserAlbums(db *gorm.DB, scannedAlbums []*models.Album, user *model
 		return nil
 	}
 
-	albumPaths := make([]interface{}, len(scannedAlbums))
+	scannedAlbumIDs := make([]interface{}, len(scannedAlbums))
 	for i, album := range scannedAlbums {
-		albumPaths[i] = album.Path
+		scannedAlbumIDs[i] = album.ID
 	}
 
 	// Delete old albums
-	album_args := make([]interface{}, 0)
-	album_args = append(album_args, user.ID)
-	album_args = append(album_args, albumPaths...)
-
 	var albums []models.Album
 
-	albums_questions := strings.Repeat("MD5(?),", len(albumPaths))[:len(albumPaths)*7-1]
-	if err := db.Where("owner_id = ? AND path_hash NOT IN ("+albums_questions+")", album_args...).Find(&albums).Error; err != nil {
+	userAlbumIDs := make([]int, len(user.Albums))
+	for i, album := range user.Albums {
+		userAlbumIDs[i] = album.ID
+	}
+
+	query := db.
+		Where("id IN (?)", userAlbumIDs).
+		Where("id NOT IN (?)", scannedAlbumIDs)
+
+	if err := query.Find(&albums).Error; err != nil {
 		return []error{errors.Wrap(err, "get albums to be deleted from database")}
 	}
 
 	deleteErrors := make([]error, 0)
 
-	albumIDs := make([]int, 0)
-	for _, album := range albums {
-		albumIDs = append(albumIDs, album.ID)
+	deleteAlbumIDs := make([]int, len(albums))
+	for i, album := range albums {
+		deleteAlbumIDs[i] = album.ID
 		cachePath := path.Join(PhotoCache(), strconv.Itoa(int(album.ID)))
 		err := os.RemoveAll(cachePath)
 		if err != nil {
@@ -88,7 +91,7 @@ func deleteOldUserAlbums(db *gorm.DB, scannedAlbums []*models.Album, user *model
 		}
 	}
 
-	if err := db.Where("id IN ?", albumIDs).Delete(models.Album{}).Error; err != nil {
+	if err := db.Where("id IN ?", deleteAlbumIDs).Delete(models.Album{}).Error; err != nil {
 		ScannerError("Could not delete old albums from database:\n%s\n", err)
 		deleteErrors = append(deleteErrors, errors.Wrap(err, "delete old albums from database"))
 	}
diff --git a/api/scanner/scanner_album.go b/api/scanner/scanner_album.go
index 38757da..11decfe 100644
--- a/api/scanner/scanner_album.go
+++ b/api/scanner/scanner_album.go
@@ -13,6 +13,25 @@ import (
 	"gorm.io/gorm"
 )
 
+func NewRootAlbum(db *gorm.DB, rootPath string, owner *models.User) (*models.Album, error) {
+
+	owners := []models.User{
+		*owner,
+	}
+
+	album := models.Album{
+		Title:  path.Base(rootPath),
+		Path:   rootPath,
+		Owners: owners,
+	}
+
+	if err := db.Create(&album).Error; err != nil {
+		return nil, err
+	}
+
+	return &album, nil
+}
+
 func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) {
 
 	album_notify_key := utils.GenerateToken()
diff --git a/api/scanner/scanner_user.go b/api/scanner/scanner_user.go
index ca1b157..eb4ba15 100644
--- a/api/scanner/scanner_user.go
+++ b/api/scanner/scanner_user.go
@@ -17,16 +17,25 @@ import (
 
 func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScannerCache) ([]*models.Album, []error) {
 
+	if err := user.FillAlbums(db); err != nil {
+		return nil, []error{err}
+	}
+
+	userAlbumIDs := make([]int, len(user.Albums))
+	for i, album := range user.Albums {
+		userAlbumIDs[i] = album.ID
+	}
+
 	var userRootAlbums []*models.Album
-	if err := db.Model(&user).Association("Albums").Find(&userRootAlbums); err != nil {
-		return nil, []error{errors.Wrapf(err, "get albums of user (%s)", user.Username)}
+	if err := db.Where("id IN (?)", userAlbumIDs).Where("parent_album_id IS NULL").Find(&userRootAlbums).Error; err != nil {
+		return nil, []error{err}
 	}
 
 	scanErrors := make([]error, 0)
 
 	type scanInfo struct {
-		path     string
-		parentID *int
+		path   string
+		parent *models.Album
 	}
 
 	scanQueue := list.New()
@@ -41,8 +50,8 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
 			}
 		} else {
 			scanQueue.PushBack(scanInfo{
-				path:     album.Path,
-				parentID: nil,
+				path:   album.Path,
+				parent: nil,
 			})
 		}
 	}
@@ -54,7 +63,7 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
 		scanQueue.Remove(scanQueue.Front())
 
 		albumPath := albumInfo.path
-		albumParentID := albumInfo.parentID
+		albumParent := albumInfo.parent
 
 		// Read path
 		dirContent, err := ioutil.ReadDir(albumPath)
@@ -64,26 +73,50 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
 		}
 
 		// Will become new album or album from db
-		var album models.Album
+		var album *models.Album
 
 		transErr := db.Transaction(func(tx *gorm.DB) error {
 			log.Printf("Scanning directory: %s", albumPath)
 
-			// Make album if not exists
-			albumTitle := path.Base(albumPath)
-
-			err = tx.FirstOrCreate(&album, models.Album{
-				Title:         albumTitle,
-				ParentAlbumID: albumParentID,
-				OwnerID:       user.ID,
-				Path:          albumPath,
-			}).Error
-
-			if err != nil {
-				return errors.Wrap(err, "insert album into database")
+			// check if album already exists
+			var albumResult []models.Album
+			result := tx.Where("path_hash = md5(?)", albumPath).Find(&albumResult)
+			if result.Error != nil {
+				return result.Error
 			}
 
-			userAlbums = append(userAlbums, &album)
+			// album does not exist, create new
+			if len(albumResult) == 0 {
+				albumTitle := path.Base(albumPath)
+
+				var albumParentID *int
+				parentOwners := make([]models.User, 0)
+				if albumParent != nil {
+					albumParentID = &albumParent.ID
+
+					if err := db.Model(&albumParent).Association("Owners").Find(&parentOwners); err != nil {
+						return err
+					}
+				}
+
+				album = &models.Album{
+					Title:         albumTitle,
+					ParentAlbumID: albumParentID,
+					Path:          albumPath,
+				}
+
+				if err := tx.Create(&album).Error; err != nil {
+					return errors.Wrap(err, "insert album into database")
+				}
+
+				if err := tx.Model(&album).Association("Owners").Append(parentOwners); err != nil {
+					return errors.Wrap(err, "add owners to album")
+				}
+			} else {
+				album = &albumResult[0]
+			}
+
+			userAlbums = append(userAlbums, album)
 
 			return nil
 		})
@@ -104,8 +137,8 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
 
 			if item.IsDir() && directoryContainsPhotos(subalbumPath, album_cache) {
 				scanQueue.PushBack(scanInfo{
-					path:     subalbumPath,
-					parentID: &album.ID,
+					path:   subalbumPath,
+					parent: album,
 				})
 			}
 		}
diff --git a/ui/src/Pages/SettingsPage/UsersTable.js b/ui/src/Pages/SettingsPage/UsersTable.js
index ea88ece..1dc187e 100644
--- a/ui/src/Pages/SettingsPage/UsersTable.js
+++ b/ui/src/Pages/SettingsPage/UsersTable.js
@@ -11,7 +11,7 @@ const USERS_QUERY = gql`
     user {
       id
       username
-      rootPath
+      # rootPath
       admin
     }
   }
