Compare commits

..

7 Commits

Author SHA1 Message Date
tonyaellie
37890c2a22 docs: update OIDC configuration details 2025-12-22 11:19:49 +00:00
Tonya
096b682f0a Improve oidc docs and fix attachment issue (#1153)
* fix: sort auth issues for oidc

* feat: improve oidc docs
2025-12-21 22:11:38 +00:00
Tonya
e4d8bb2ada chore: use example.com for example
better safe than sorry
2025-12-20 21:50:44 +00:00
Katos
3becf046e6 Merge pull request #1147 from sysadminsmedia/katos/docs-variable
Update max file upload environment variable
2025-12-20 16:01:04 +00:00
Katos
a21b3257d4 Update max file upload environment variable 2025-12-20 15:57:14 +00:00
Robert Eggl
5f9ab577bb fix: request camera permission in ScannerModal (#1113)
* feat: request camera permission in ScannerModal

* chore: simplify source code
2025-12-19 21:47:37 +00:00
Robert Eggl
0a969bb64d fix(sidebar): prevent dropdown menu layout shift on hover (#1116) 2025-12-19 21:38:06 +00:00
11 changed files with 175 additions and 227 deletions

View File

@@ -1,14 +0,0 @@
entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd h1:QULUJSgHc4rSlTjb2qYT6FIgwDWFCqEpnYqc/ltsrkk=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd/go.mod h1:jB+tPmHtPDM1VnAjah0gvcRfP/s7c+rtQwpA8cvZD/U=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -124,7 +124,7 @@ func (ctrl *V1Controller) HandleAuthLogin(ps ...AuthProvider) errchain.HandlerFu
return validate.NewUnauthorizedError() return validate.NewUnauthorizedError()
} }
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true) ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true, newToken.AttachmentToken)
return server.JSON(w, http.StatusOK, TokenResponse{ return server.JSON(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw, Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt, ExpiresAt: newToken.ExpiresAt,
@@ -178,7 +178,7 @@ func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
return validate.NewUnauthorizedError() return validate.NewUnauthorizedError()
} }
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false) ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false, newToken.AttachmentToken)
return server.JSON(w, http.StatusOK, newToken) return server.JSON(w, http.StatusOK, newToken)
} }
} }
@@ -187,7 +187,7 @@ func noPort(host string) string {
return strings.Split(host, ":")[0] return strings.Split(host, ":")[0]
} }
func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool) { func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool, attachmentToken string) {
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: cookieNameRemember, Name: cookieNameRemember,
Value: strconv.FormatBool(remember), Value: strconv.FormatBool(remember),
@@ -219,6 +219,19 @@ func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string
HttpOnly: false, HttpOnly: false,
Path: "/", Path: "/",
}) })
// Set attachment token cookie (accessible to frontend, not HttpOnly)
if attachmentToken != "" {
http.SetCookie(w, &http.Cookie{
Name: "hb.auth.attachment_token",
Value: attachmentToken,
Expires: expires,
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: false,
Path: "/",
})
}
} }
func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) { func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
@@ -252,6 +265,17 @@ func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
HttpOnly: false, HttpOnly: false,
Path: "/", Path: "/",
}) })
// Unset attachment token cookie
http.SetCookie(w, &http.Cookie{
Name: "hb.auth.attachment_token",
Value: "",
Expires: time.Unix(0, 0),
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: false,
Path: "/",
})
} }
// HandleOIDCLogin godoc // HandleOIDCLogin godoc
@@ -310,7 +334,7 @@ func (ctrl *V1Controller) HandleOIDCCallback() errchain.HandlerFunc {
} }
// Set cookies and redirect to home // Set cookies and redirect to home
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true) ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true, newToken.AttachmentToken)
http.Redirect(w, r, "/home", http.StatusFound) http.Redirect(w, r, "/home", http.StatusFound)
return nil return nil
} }

View File

@@ -325,6 +325,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
@@ -347,6 +349,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/olahol/melody v1.4.0 h1:Pa5SdeZL/zXPi1tJuMAPDbl4n3gQOThSL6G1p4qZ4SI= github.com/olahol/melody v1.4.0 h1:Pa5SdeZL/zXPi1tJuMAPDbl4n3gQOThSL6G1p4qZ4SI=
github.com/olahol/melody v1.4.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4= github.com/olahol/melody v1.4.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
@@ -389,6 +393,10 @@ github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAX
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU= github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@@ -97,35 +97,12 @@ func ToItemAttachment(attachment *ent.Attachment) ItemAttachment {
} }
} }
// normalizePath converts backslashes to forward slashes and trims slashes from both ends
// This ensures consistent path separators for blob storage which expects forward slashes
func normalizePath(path string) string {
path = strings.ReplaceAll(path, "\\", "/")
return strings.Trim(path, "/")
}
func (r *AttachmentRepo) path(gid uuid.UUID, hash string) string { func (r *AttachmentRepo) path(gid uuid.UUID, hash string) string {
// Always use forward slashes for consistency across platforms return filepath.Join(gid.String(), "documents", hash)
// This ensures paths are stored in the database with forward slashes
return fmt.Sprintf("%s/documents/%s", gid.String(), hash)
} }
func (r *AttachmentRepo) fullPath(relativePath string) string { func (r *AttachmentRepo) fullPath(relativePath string) string {
// Normalize path separators to forward slashes for blob storage return filepath.Join(r.storage.PrefixPath, relativePath)
// The blob library expects forward slashes in keys regardless of OS
normalizedRelativePath := normalizePath(relativePath)
// Always use forward slashes when joining paths for blob storage
if r.storage.PrefixPath == "" {
return normalizedRelativePath
}
normalizedPrefix := normalizePath(r.storage.PrefixPath)
if normalizedPrefix == "" {
return normalizedRelativePath
}
return fmt.Sprintf("%s/%s", normalizedPrefix, normalizedRelativePath)
} }
func (r *AttachmentRepo) GetFullPath(relativePath string) string { func (r *AttachmentRepo) GetFullPath(relativePath string) string {

View File

@@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/sysadminsmedia/homebox/backend/internal/data/ent" "github.com/sysadminsmedia/homebox/backend/internal/data/ent"
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment" "github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
) )
func TestAttachmentRepo_Create(t *testing.T) { func TestAttachmentRepo_Create(t *testing.T) {
@@ -282,58 +281,3 @@ func TestAttachmentRepo_SettingPhotoPrimaryStillWorks(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.False(t, photo1.Primary, "Photo 1 should no longer be primary after setting Photo 2 as primary") assert.False(t, photo1.Primary, "Photo 1 should no longer be primary after setting Photo 2 as primary")
} }
func TestAttachmentRepo_PathNormalization(t *testing.T) {
// Test that paths always use forward slashes
repo := &AttachmentRepo{
storage: config.Storage{
PrefixPath: ".data",
},
}
testGUID := uuid.MustParse("eb6bf410-a1a8-478d-a803-ca3948368a0c")
testHash := "f295eb01-18a9-4631-a797-70bd9623edd4.png"
// Test path() method - should always return forward slashes
relativePath := repo.path(testGUID, testHash)
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", relativePath)
assert.NotContains(t, relativePath, "\\", "path() should not contain backslashes")
// Test fullPath() with forward slash input (from database)
fullPath := repo.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
assert.Equal(t, ".data/eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPath)
assert.NotContains(t, fullPath, "\\", "fullPath() should not contain backslashes")
// Test fullPath() with backslash input (legacy Windows paths from old database)
fullPathWithBackslash := repo.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c\\documents\\f295eb01-18a9-4631-a797-70bd9623edd4.png")
assert.Equal(t, ".data/eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathWithBackslash)
assert.NotContains(t, fullPathWithBackslash, "\\", "fullPath() should normalize backslashes to forward slashes")
// Test with Windows-style prefix path
repoWindows := &AttachmentRepo{
storage: config.Storage{
PrefixPath: ".data",
},
}
fullPathWindows := repoWindows.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
assert.NotContains(t, fullPathWindows, "\\", "fullPath() should normalize Windows paths")
// Test empty prefix
repoNoPrefix := &AttachmentRepo{
storage: config.Storage{
PrefixPath: "",
},
}
fullPathNoPrefix := repoNoPrefix.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathNoPrefix)
// Test with single slash prefix (like in tests)
repoSlashPrefix := &AttachmentRepo{
storage: config.Storage{
PrefixPath: "/",
},
}
fullPathSlashPrefix := repoSlashPrefix.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathSlashPrefix)
assert.NotContains(t, fullPathSlashPrefix, "//", "fullPath() should not have double slashes")
}

View File

@@ -6,6 +6,7 @@ export default [
{text: 'Installation', link: '/en/installation'}, {text: 'Installation', link: '/en/installation'},
{text: 'Configure', link: '/en/configure'}, {text: 'Configure', link: '/en/configure'},
{text: 'Storage', link: '/en/configure/storage'}, {text: 'Storage', link: '/en/configure/storage'},
{text: 'OIDC', link: '/en/configure/oidc'},
{text: 'Upgrade Guide', link: '/en/upgrade'}, {text: 'Upgrade Guide', link: '/en/upgrade'},
{text: 'Migration Guide', link: '/en/migration'}, {text: 'Migration Guide', link: '/en/migration'},
] ]

View File

@@ -73,6 +73,83 @@ aside: false
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels | | HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
| HBOX_BARCODE_TOKEN_BARCODESPIDER | | API token for BarcodeSpider.com service used for barcode product lookups. If not set, barcode product lookups will not be performed. | | HBOX_BARCODE_TOKEN_BARCODESPIDER | | API token for BarcodeSpider.com service used for barcode product lookups. If not set, barcode product lookups will not be performed. |
```sh
Options:
--barcode-token-barcodespider <string>
--database-database <string>
--database-driver <string> (default: sqlite3)
--database-host <string>
--database-password <string>
--database-port <string>
--database-pub-sub-conn-string <string> (default: mem://{{ .Topic }})
--database-sqlite-path <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
--database-ssl-cert <string>
--database-ssl-key <string>
--database-ssl-mode <string> (default: require)
--database-ssl-root-cert <string>
--database-username <string>
--debug-enabled <bool> (default: false)
--debug-port <string> (default: 4000)
--demo <bool>
-h, --help display this help message
--label-maker-additional-information <string>
--label-maker-bold-font-path <string>
--label-maker-dynamic-length <bool> (default: true)
--label-maker-font-size <float> (default: 32.0)
--label-maker-height <int> (default: 200)
--label-maker-label-service-timeout <int>
--label-maker-label-service-url <string>
--label-maker-margin <int> (default: 32)
--label-maker-padding <int> (default: 32)
--label-maker-print-command <string>
--label-maker-regular-font-path <string>
--label-maker-width <int> (default: 526)
--log-format <string> (default: text)
--log-level <string> (default: info)
--mailer-from <string>
--mailer-host <string>
--mailer-password <string>
--mailer-port <int>
--mailer-username <string>
--mode <string> (default: development)
--oidc-allowed-groups <string>
--oidc-auto-redirect <bool> (default: false)
--oidc-button-text <string> (default: Sign in with OIDC)
--oidc-client-id <string>
--oidc-client-secret <string>
--oidc-email-claim <string> (default: email)
--oidc-email-verified-claim <string> (default: email_verified)
--oidc-enabled <bool> (default: false)
--oidc-group-claim <string> (default: groups)
--oidc-issuer-url <string>
--oidc-name-claim <string> (default: name)
--oidc-request-timeout <duration> (default: 30s)
--oidc-scope <string> (default: openid profile email)
--oidc-state-expiry <duration> (default: 10m)
--oidc-verify-email <bool> (default: false)
--options-allow-analytics <bool> (default: false)
--options-allow-local-login <bool> (default: true)
--options-allow-registration <bool> (default: true)
--options-auto-increment-asset-id <bool> (default: true)
--options-currency-config <string>
--options-github-release-check <bool> (default: true)
--options-hostname <string>
--options-trust-proxy <bool> (default: false)
--storage-conn-string <string> (default: file:///./)
--storage-prefix-path <string> (default: .data)
--thumbnail-enabled <bool> (default: true)
--thumbnail-height <int> (default: 500)
--thumbnail-width <int> (default: 500)
-v, --version display version
--web-host <string>
--web-idle-timeout <duration> (default: 30s)
--web-max-upload-size <int> (default: 10)
--web-port <string> (default: 7745)
--web-read-timeout <duration> (default: 10s)
--web-write-timeout <duration> (default: 10s)
```
:::
### HBOX_WEB_HOST examples ### HBOX_WEB_HOST examples
| Value | Notes | | Value | Notes |
@@ -170,114 +247,4 @@ For SQLite in production:
## OIDC Configuration ## OIDC Configuration
HomeBox supports OpenID Connect (OIDC) authentication, allowing users to login using external identity providers like Keycloak, Authentik, Google, Microsoft, etc. For configuring OpenID Connect (OIDC) authentication, refer to the [OIDC Configuration Guide](/en/configure/oidc).
### Basic OIDC Setup
1. **Enable OIDC**: Set `HBOX_OIDC_ENABLED=true`
2. **Provider Configuration**: Set the required provider details:
- `HBOX_OIDC_ISSUER_URL`: Your OIDC provider's issuer URL
- `HBOX_OIDC_CLIENT_ID`: Client ID from your OIDC provider
- `HBOX_OIDC_CLIENT_SECRET`: Client secret from your OIDC provider
3. **Configure Redirect URI**: In your OIDC provider, set the redirect URI to:
`https://your-homebox-domain.com/api/v1/users/login/oidc/callback`
### Advanced OIDC Configuration
- **Group Authorization**: Use `HBOX_OIDC_ALLOWED_GROUPS` to restrict access to specific groups
- **Custom Claims**: Configure `HBOX_OIDC_GROUP_CLAIM`, `HBOX_OIDC_EMAIL_CLAIM`, and `HBOX_OIDC_NAME_CLAIM` if your provider uses different claim names
- **Auto Redirect to OIDC**: Set `HBOX_OIDC_AUTO_REDIRECT=true` to automatically redirect users directly to OIDC
- **Local Login**: Set `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false` to completely disable username/password login
- **Email Verification**: Set `HBOX_OIDC_VERIFY_EMAIL=true` to require email verification from the OIDC provider
### Security Considerations
::: warning OIDC Security
- Store `HBOX_OIDC_CLIENT_SECRET` securely (use environment variables, not config files)
- Use HTTPS for production deployments
- Configure proper redirect URIs in your OIDC provider
- Consider setting `HBOX_OIDC_ALLOWED_GROUPS` for group-based access control
:::
::: tip CLI Arguments
If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help`
for more information.
```sh
Options:
--barcode-token-barcodespider <string>
--database-database <string>
--database-driver <string> (default: sqlite3)
--database-host <string>
--database-password <string>
--database-port <string>
--database-pub-sub-conn-string <string> (default: mem://{{ .Topic }})
--database-sqlite-path <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
--database-ssl-cert <string>
--database-ssl-key <string>
--database-ssl-mode <string> (default: require)
--database-ssl-root-cert <string>
--database-username <string>
--debug-enabled <bool> (default: false)
--debug-port <string> (default: 4000)
--demo <bool>
-h, --help display this help message
--label-maker-additional-information <string>
--label-maker-bold-font-path <string>
--label-maker-dynamic-length <bool> (default: true)
--label-maker-font-size <float> (default: 32.0)
--label-maker-height <int> (default: 200)
--label-maker-label-service-timeout <int>
--label-maker-label-service-url <string>
--label-maker-margin <int> (default: 32)
--label-maker-padding <int> (default: 32)
--label-maker-print-command <string>
--label-maker-regular-font-path <string>
--label-maker-width <int> (default: 526)
--log-format <string> (default: text)
--log-level <string> (default: info)
--mailer-from <string>
--mailer-host <string>
--mailer-password <string>
--mailer-port <int>
--mailer-username <string>
--mode <string> (default: development)
--oidc-allowed-groups <string>
--oidc-auto-redirect <bool> (default: false)
--oidc-button-text <string> (default: Sign in with OIDC)
--oidc-client-id <string>
--oidc-client-secret <string>
--oidc-email-claim <string> (default: email)
--oidc-email-verified-claim <string> (default: email_verified)
--oidc-enabled <bool> (default: false)
--oidc-group-claim <string> (default: groups)
--oidc-issuer-url <string>
--oidc-name-claim <string> (default: name)
--oidc-request-timeout <duration> (default: 30s)
--oidc-scope <string> (default: openid profile email)
--oidc-state-expiry <duration> (default: 10m)
--oidc-verify-email <bool> (default: false)
--options-allow-analytics <bool> (default: false)
--options-allow-local-login <bool> (default: true)
--options-allow-registration <bool> (default: true)
--options-auto-increment-asset-id <bool> (default: true)
--options-currency-config <string>
--options-github-release-check <bool> (default: true)
--options-hostname <string>
--options-trust-proxy <bool> (default: false)
--storage-conn-string <string> (default: file:///./)
--storage-prefix-path <string> (default: .data)
--thumbnail-enabled <bool> (default: true)
--thumbnail-height <int> (default: 500)
--thumbnail-width <int> (default: 500)
-v, --version display version
--web-host <string>
--web-idle-timeout <duration> (default: 30s)
--web-max-upload-size <int> (default: 10)
--web-port <string> (default: 7745)
--web-read-timeout <duration> (default: 10s)
--web-write-timeout <duration> (default: 10s)
```
:::

44
docs/en/configure/oidc.md Normal file
View File

@@ -0,0 +1,44 @@
# Configure OIDC
HomeBox supports OpenID Connect (OIDC) authentication, allowing users to login using external identity providers like Keycloak, Authentik, Authelia, Google, Microsoft, etc.
::: tip OIDC Provider Documentation
When configuring OIDC, always refer to the documentation provided by your identity provider for specific details and requirements.
:::
## Basic OIDC Setup
1. **Enable OIDC**: Set `HBOX_OIDC_ENABLED=true`.
2. **Provider Configuration**: Set the required provider details:
- `HBOX_OIDC_ISSUER_URL`: Your OIDC provider's issuer URL.
- Generally this URL should not have a trailing slash, though it may be required for some providers.
- `HBOX_OIDC_CLIENT_ID`: Client ID from your OIDC provider.
- `HBOX_OIDC_CLIENT_SECRET`: Client secret from your OIDC provider.
- If you are using a reverse proxy, it may be necessary to set `HBOX_OPTIONS_TRUST_PROXY=true` to ensure `https` is correctly detected.
- If you have set `HBOX_OPTIONS_HOSTNAME` make sure it is just the hostname and does not include `https://` or `http://`.
3. **Configure Redirect URI**: In your OIDC provider, set the redirect URI to:
`https://your-homebox-domain.example.com/api/v1/users/login/oidc/callback`.
## Advanced OIDC Configuration
- **Group Authorization**: Use `HBOX_OIDC_ALLOWED_GROUPS` to restrict access to specific groups, e.g. `HBOX_OIDC_ALLOWED_GROUPS=admin,homebox`.
- Some providers require the `groups` scope to return group claims, include it in `HBOX_OIDC_SCOPE` (e.g. `openid profile email groups`) or configure the provider to release the claim.
- **Custom Claims**: Configure `HBOX_OIDC_GROUP_CLAIM`, `HBOX_OIDC_EMAIL_CLAIM`, and `HBOX_OIDC_NAME_CLAIM` if your provider uses different claim names.
- These default to `HBOX_OIDC_GROUP_CLAIM=groups`, `HBOX_OIDC_EMAIL_CLAIM=email` and `HBOX_OIDC_NAME_CLAIM=name`.
- **Auto Redirect to OIDC**: Set `HBOX_OIDC_AUTO_REDIRECT=true` to automatically redirect users directly to OIDC.
- **Local Login**: Set `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false` to completely disable username/password login.
- **Email Verification**: Set `HBOX_OIDC_VERIFY_EMAIL=true` to require email verification from the OIDC provider.
## Security Considerations
::: warning OIDC Security
- Store `HBOX_OIDC_CLIENT_SECRET` securely (use environment variables, not config files).
- Use HTTPS for production deployments.
- Configure proper redirect URIs in your OIDC provider.
- Consider setting `HBOX_OIDC_ALLOWED_GROUPS` for group-based access control.
:::
::: tip CLI Arguments
If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help` for more information.
:::

View File

@@ -52,7 +52,7 @@ services:
environment: environment:
- HBOX_LOG_LEVEL=info - HBOX_LOG_LEVEL=info
- HBOX_LOG_FORMAT=text - HBOX_LOG_FORMAT=text
- HBOX_WEB_MAX_FILE_UPLOAD=10 - HBOX_WEB_MAX_UPLOAD_SIZE=10
# Please consider allowing analytics to help us improve Homebox (basic computer information, no personal data) # Please consider allowing analytics to help us improve Homebox (basic computer information, no personal data)
- HBOX_OPTIONS_ALLOW_ANALYTICS=false - HBOX_OPTIONS_ALLOW_ANALYTICS=false
volumes: volumes:

View File

@@ -81,17 +81,6 @@
errorMessage.value = t("scanner.error"); errorMessage.value = t("scanner.error");
}; };
const checkPermissionsError = async () => {
if (navigator.permissions) {
const permissionStatus = await navigator.permissions.query({ name: "camera" as PermissionName });
if (permissionStatus.state === "denied") {
errorMessage.value = t("scanner.permission_denied");
console.error("Camera permission denied");
return true;
}
}
};
const handleButtonClick = () => { const handleButtonClick = () => {
openDialog(DialogID.ProductImport, { params: { barcode: detectedBarcode.value } }); openDialog(DialogID.ProductImport, { params: { barcode: detectedBarcode.value } });
}; };
@@ -103,11 +92,19 @@
return; return;
} }
if (await checkPermissionsError()) { try {
// Request camera permission first
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
stream.getTracks().forEach(track => track.stop());
} catch (err: unknown) {
if (err instanceof Error && err.name === "NotAllowedError") {
errorMessage.value = t("scanner.permission_denied");
return; return;
} }
throw err;
}
try {
const devices = await codeReader.listVideoInputDevices(); const devices = await codeReader.listVideoInputDevices();
sources.value = devices; sources.value = devices;

View File

@@ -47,7 +47,7 @@
{{ btn.name.value }} {{ btn.name.value }}
<Shortcut <Shortcut
v-if="btn.shortcut" v-if="btn.shortcut"
class="ml-auto hidden group-hover:inline" class="invisible ml-auto group-hover:visible"
:keys="btn.shortcut.replace('Shift', '').split('+')" :keys="btn.shortcut.replace('Shift', '').split('+')"
/> />
</DropdownMenuItem> </DropdownMenuItem>