Compare commits

...

3 Commits

Author SHA1 Message Date
Stavros b6df4b9f0a fix: coderabbit comments 2026-05-11 19:10:50 +03:00
Stavros c298d0b158 i18n: use friendlier tone in tailscale i18n 2026-05-11 18:48:46 +03:00
Stavros b8b0e45ec4 feat: add listener policy calculator 2026-05-11 18:47:37 +03:00
9 changed files with 58 additions and 24 deletions
+1 -1
View File
@@ -86,7 +86,7 @@
"addressScopeName": "Address",
"addressScopeDescription": "Allows the app to access your address.",
"loginTailscaleTitle": "Continue with Tailscale",
"loginTailscaleDescription": "We detected that you are accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
"loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
"loginTailscaleDeviceName": "Device name:",
"loginTailscaleSubmit": "Continue with Tailscale",
"loginTailscaleOtherMethod": "Login with another method",
+1 -1
View File
@@ -86,7 +86,7 @@
"addressScopeName": "Address",
"addressScopeDescription": "Allows the app to access your address.",
"loginTailscaleTitle": "Continue with Tailscale",
"loginTailscaleDescription": "We detected that you are accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
"loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
"loginTailscaleDeviceName": "Device name:",
"loginTailscaleSubmit": "Continue with Tailscale",
"loginTailscaleOtherMethod": "Login with another method",
+1 -15
View File
@@ -255,21 +255,7 @@ func (app *BootstrapApp) Setup() error {
}
// setup listeners
runUnix := app.config.Server.SocketPath != ""
runHTTP := app.config.Server.SocketPath == "" || app.config.Server.ConcurrentListenersEnabled
runTailscale := app.services.tailscaleService != nil
if runHTTP {
app.listeners = append(app.listeners, ListenerHTTP)
}
if runUnix {
app.listeners = append(app.listeners, ListenerUnix)
}
if runTailscale {
app.listeners = append(app.listeners, ListenerTailscale)
}
app.listeners = app.calculateListenerPolicy()
if app.config.Server.ConcurrentListenersEnabled {
app.log.App.Info().Msg("Concurrent listeners enabled, will run on all available listeners")
+46 -1
View File
@@ -1,14 +1,17 @@
package bootstrap
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"time"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/middleware"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/gin-gonic/gin"
)
@@ -85,6 +88,43 @@ func (app *BootstrapApp) runListeners() (chan error, error) {
return lec, nil
}
// The way we calculate listeners is as follows:
// If concurrent listeners are disabled, we pick the first available listener, so:
// 1. If tailscale is enabled, we use tailscale
// 2. If socket path is configured, we use unix socket
// 3. Finally if none is configured we use http
// If concurrent listeners are enabled, we add all available listeners in the following order
func (app *BootstrapApp) calculateListenerPolicy() []Listener {
l := []Listener{}
if !app.config.Server.ConcurrentListenersEnabled {
if app.config.Tailscale.Enabled {
l = append(l, ListenerTailscale)
return l
}
if app.config.Server.SocketPath != "" {
l = append(l, ListenerUnix)
return l
}
l = append(l, ListenerHTTP)
return l
}
if app.config.Server.SocketPath != "" {
l = append(l, ListenerUnix)
}
if app.config.Tailscale.Enabled {
l = append(l, ListenerTailscale)
}
l = append(l, ListenerHTTP)
return l
}
func (app *BootstrapApp) listenerFromType(listenerType Listener) (func() error, error) {
switch listenerType {
case ListenerHTTP:
@@ -162,7 +202,12 @@ func (app *BootstrapApp) serveTailscale() error {
func (app *BootstrapApp) serve(listener net.Listener, server *http.Server, name string) error {
shutdown := func() {
server.Shutdown(app.ctx)
ctx, cancel := context.WithTimeout(context.Background(), model.GracefulShutdownTimeout*time.Second)
defer cancel()
err := server.Shutdown(ctx)
if err != nil {
app.log.App.Error().Err(err).Msgf("Failed to shutdown %s listener gracefully", name)
}
listener.Close()
}
+1 -1
View File
@@ -424,7 +424,7 @@ func (controller *UserController) tailscaleHandler(c *gin.Context) {
cookie, err := controller.auth.CreateSession(c, sessionCookie)
if err != nil {
controller.log.App.Error().Err(err).Str("username", context.GetUsername()).Msg("Failed to create session cookie after successful TOTP verification")
controller.log.App.Error().Err(err).Str("username", context.GetUsername()).Msg("Failed to create session cookie after successful Tailscale login")
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
@@ -116,6 +116,7 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
if tailscaleContext != nil {
c.Set("context", &model.UserContext{
Authenticated: false,
Provider: model.ProviderTailscale,
Tailscale: tailscaleContext,
})
}
+2
View File
@@ -21,3 +21,5 @@ const SessionCookieName = "tinyauth-session"
const CSRFCookieName = "tinyauth-csrf"
const RedirectCookieName = "tinyauth-redirect"
const OAuthSessionCookieName = "tinyauth-oauth"
const GracefulShutdownTimeout = 5 // seconds
+4 -4
View File
@@ -292,6 +292,10 @@ func (auth *AuthService) IsEmailWhitelisted(email string) bool {
}
func (auth *AuthService) CreateSession(ctx context.Context, data repository.Session) (*http.Cookie, error) {
if data.Provider == "tailscale" && auth.tailscale == nil {
return nil, fmt.Errorf("tailscale service not configured, cannot create session for tailscale user")
}
uuid, err := uuid.NewRandom()
if err != nil {
@@ -329,10 +333,6 @@ func (auth *AuthService) CreateSession(ctx context.Context, data repository.Sess
}
if data.Provider == "tailscale" {
if auth.tailscale == nil {
return nil, fmt.Errorf("tailscale service not configured, cannot create session for tailscale user")
}
auth.log.App.Trace().Str("url", fmt.Sprintf("https://%s", auth.tailscale.GetHostname())).Msg("Extracting root domain from Tailscale hostname")
tsCookieDomain, err := utils.GetCookieDomain(fmt.Sprintf("https://%s", auth.tailscale.GetHostname()))
+1 -1
View File
@@ -64,7 +64,7 @@ func NewTailscaleService(log *logger.Logger, config model.Config, ctx context.Co
lc: lc,
}
connectCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
connectCtx, cancel := context.WithTimeout(ctx, 2*time.Minute) // large enough timeout to allow for user to manually authenticate with link if needed
defer cancel()
err = service.waitForConn(connectCtx)