Compare commits

..

8 Commits

Author SHA1 Message Date
Stavros 5aeb886523 Merge branch 'main' into single-cookie-domain 2026-05-07 15:50:55 +03:00
Stavros 1382ab41e7 refactor: rework user context handling throughout tinyauth (#829)
* wip

* fix: fix util imports

* fix: fix bootstrap import issues

* fix: fix cli imports

* fix: context controller

* fix: use new context in user controller

* fix: fix imports and context in proxy controller

* fix: fix oauth and oidc controller imports and context

* feat: finalize context functionality

* refactor: simplify acls checking logic by passing the entire acl struct

* chore: rename get basic auth to encode basic auth for clarity

* fix: fix controller tests

* tests: fix service tests

* tests: fix utils tests

* tests: move to testify for testing in utils

* fix: fix config reference generator

* tests: add tests for context parsing

* tests: add tests for context middleware

* tests: remove error wrapper from context tests

* tests: fix log wrapper tests

* fix: fix verion setting in cd and dockerfiles

* fix: review comments batch 1

* fix: review comments batch 2

* fix: review comments batch 3

* fix: delete totp pending session cookie on totp success

* tests: fix user controller tests

* fix: don't audit login too early

* fix: own comments
2026-05-07 15:41:07 +03:00
dependabot[bot] 24f2da4e58 chore(deps): bump github/codeql-action from 4.35.2 to 4.35.3 (#837)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.2 to 4.35.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/95e58e9a2cdfd71adc6e0353d5c52f41a045d225...e46ed2cbd01164d986452f91f178727624ae40d7)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-06 18:49:37 +03:00
Stavros b06b60150f Merge branch 'main' into single-cookie-domain 2026-05-04 15:59:22 +03:00
Stavros 4077bacfdf chore: rabbit feedback 2026-04-29 16:00:59 +03:00
Stavros 4c0181c5e2 Merge branch 'main' into single-cookie-domain 2026-04-29 15:50:52 +03:00
Stavros 44a7cbf41b fix: inform services and controllers if subdomain cookie domain is enabled 2026-04-29 15:49:45 +03:00
Jacek Kowalski d90e3d652d Add TINYAUTH_AUTH_SUBDOMAINSENABLED option
Setting it to false allows to use Tinyauth on top-level domain only,
but forbids automatic cross-app authentication using Traefik/Nginx.
2026-04-19 22:17:10 +02:00
8 changed files with 45 additions and 4 deletions
+1 -1
View File
@@ -38,6 +38,6 @@ jobs:
retention-days: 5 retention-days: 5
- name: Upload to code-scanning - name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4
with: with:
sarif_file: results.sarif sarif_file: results.sarif
+7 -1
View File
@@ -104,7 +104,13 @@ func (app *BootstrapApp) Setup() error {
} }
// Get cookie domain // Get cookie domain
cookieDomain, err := utils.GetCookieDomain(app.context.appUrl) cookieDomainResolver := utils.GetCookieDomain
if !app.config.Auth.SubdomainsEnabled {
tlog.App.Info().Msg("Subdomains disabled, automatic authentication for proxied apps will not work")
cookieDomainResolver = utils.GetStandaloneCookieDomain
}
cookieDomain, err := cookieDomainResolver(app.context.appUrl)
if err != nil { if err != nil {
return err return err
+1
View File
@@ -84,6 +84,7 @@ func (app *BootstrapApp) setupRouter() (*gin.Engine, error) {
RedirectCookieName: app.context.redirectCookieName, RedirectCookieName: app.context.redirectCookieName,
CookieDomain: app.context.cookieDomain, CookieDomain: app.context.cookieDomain,
OAuthSessionCookieName: app.context.oauthSessionCookieName, OAuthSessionCookieName: app.context.oauthSessionCookieName,
SubdomainsEnabled: app.config.Auth.SubdomainsEnabled,
}, apiRouter, app.services.authService) }, apiRouter, app.services.authService)
oauthController.SetupRoutes() oauthController.SetupRoutes()
+1
View File
@@ -100,6 +100,7 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er
SessionCookieName: app.context.sessionCookieName, SessionCookieName: app.context.sessionCookieName,
IP: app.config.Auth.IP, IP: app.config.Auth.IP,
LDAPGroupsCacheTTL: app.config.LDAP.GroupCacheTTL, LDAPGroupsCacheTTL: app.config.LDAP.GroupCacheTTL,
SubdomainsEnabled: app.config.Auth.SubdomainsEnabled,
}, services.ldapService, queries, services.oauthBrokerService) }, services.ldapService, queries, services.oauthBrokerService)
err = authService.Init() err = authService.Init()
+10 -2
View File
@@ -26,6 +26,7 @@ type OAuthControllerConfig struct {
SecureCookie bool SecureCookie bool
AppURL string AppURL string
CookieDomain string CookieDomain string
SubdomainsEnabled bool
} }
type OAuthController struct { type OAuthController struct {
@@ -105,7 +106,7 @@ func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
return return
} }
c.SetCookie(controller.config.OAuthSessionCookieName, sessionId, int(time.Hour.Seconds()), "/", fmt.Sprintf(".%s", controller.config.CookieDomain), controller.config.SecureCookie, true) c.SetCookie(controller.config.OAuthSessionCookieName, sessionId, int(time.Hour.Seconds()), "/", controller.getCookieDomain(), controller.config.SecureCookie, true)
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": 200, "status": 200,
@@ -135,7 +136,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
return return
} }
c.SetCookie(controller.config.OAuthSessionCookieName, "", -1, "/", fmt.Sprintf(".%s", controller.config.CookieDomain), controller.config.SecureCookie, true) c.SetCookie(controller.config.OAuthSessionCookieName, "", -1, "/", controller.getCookieDomain(), controller.config.SecureCookie, true)
oauthPendingSession, err := controller.auth.GetOAuthPendingSession(sessionIdCookie) oauthPendingSession, err := controller.auth.GetOAuthPendingSession(sessionIdCookie)
@@ -283,3 +284,10 @@ func (controller *OAuthController) isOidcRequest(params service.OAuthURLParams)
params.ClientID != "" && params.ClientID != "" &&
params.RedirectURI != "" params.RedirectURI != ""
} }
func (controller *OAuthController) getCookieDomain() string {
if controller.config.SubdomainsEnabled {
return "." + controller.config.CookieDomain
}
return controller.config.CookieDomain
}
+2
View File
@@ -18,6 +18,7 @@ func NewDefaultConfiguration() *Config {
Address: "0.0.0.0", Address: "0.0.0.0",
}, },
Auth: AuthConfig{ Auth: AuthConfig{
SubdomainsEnabled: true,
SessionExpiry: 86400, // 1 day SessionExpiry: 86400, // 1 day
SessionMaxLifetime: 0, // disabled SessionMaxLifetime: 0, // disabled
LoginTimeout: 300, // 5 minutes LoginTimeout: 300, // 5 minutes
@@ -102,6 +103,7 @@ type ServerConfig struct {
type AuthConfig struct { type AuthConfig struct {
IP IPConfig `description:"IP whitelisting config options." yaml:"ip"` IP IPConfig `description:"IP whitelisting config options." yaml:"ip"`
Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"` Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
SubdomainsEnabled bool `description:"Enable subdomains support." yaml:"subdomainsEnabled"`
UserAttributes map[string]UserAttributes `description:"Map of per-user OIDC attributes (username -> attributes)." yaml:"userAttributes"` UserAttributes map[string]UserAttributes `description:"Map of per-user OIDC attributes (username -> attributes)." yaml:"userAttributes"`
UsersFile string `description:"Path to the users file." yaml:"usersFile"` UsersFile string `description:"Path to the users file." yaml:"usersFile"`
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"` SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
+14
View File
@@ -84,6 +84,7 @@ type AuthServiceConfig struct {
SessionCookieName string SessionCookieName string
IP model.IPConfig IP model.IPConfig
LDAPGroupsCacheTTL int LDAPGroupsCacheTTL int
SubdomainsEnabled bool
} }
type AuthService struct { type AuthService struct {
@@ -397,6 +398,12 @@ func (auth *AuthService) DeleteSession(ctx context.Context, uuid string) (*http.
tlog.App.Warn().Err(err).Msg("Failed to delete session from database, proceeding to clear cookie anyway") tlog.App.Warn().Err(err).Msg("Failed to delete session from database, proceeding to clear cookie anyway")
} }
err = auth.queries.DeleteSession(ctx, uuid)
if err != nil {
return nil, err
}
return &http.Cookie{ return &http.Cookie{
Name: auth.config.SessionCookieName, Name: auth.config.SessionCookieName,
Value: "", Value: "",
@@ -838,3 +845,10 @@ func (auth *AuthService) ClearRateLimitsTestingOnly() {
} }
auth.loginMutex.Unlock() auth.loginMutex.Unlock()
} }
func (auth *AuthService) getCookieDomain() string {
if auth.config.SubdomainsEnabled {
return "." + auth.config.CookieDomain
}
return auth.config.CookieDomain
}
+9
View File
@@ -47,6 +47,15 @@ func GetCookieDomain(u string) (string, error) {
return domain, nil return domain, nil
} }
func GetStandaloneCookieDomain(u string) (string, error) {
parsed, err := url.Parse(u)
if err != nil {
return "", err
}
return parsed.Hostname(), nil
}
func ParseFileToLine(content string) string { func ParseFileToLine(content string) string {
lines := strings.Split(content, "\n") lines := strings.Split(content, "\n")
users := make([]string, 0) users := make([]string, 0)