feat: Add default HTTP/HTTPS configuration and enhance OAuth flow logging
- Introduced a default value for HTTP/HTTPS configuration in init_config.go. - Updated applyDefaultsToConfig to apply the default HTTPS setting if not explicitly defined. - Modified OAuth callback route to log the callback endpoint and query parameters. - Enhanced the OAuth flow initiation to log the generated proxy URL. - Updated login page template to reflect the correct OAuth link path.
- commit
- 0ea01e36ed84db2d2dee43723efb46a3b18694a8
- author
- bamen · · 2025-03-29 17:55 UTC
- parent
- 61106b4
- signature
- unsigned
7 files · +34 -13
M internal/common/init_config.go +10 -0
| @@ -103,6 +103,7 @@ var ( | |||
| 103 | 103 | renewBefore = 30 // days | |
| 104 | 104 | gracePeriod = 30 // seconds | |
| 105 | 105 | defaultLogLevel = "info" // Default log level | |
| 106 | + | defaultHttpHttps = true // Default to true | |
| 106 | 107 | disableRateLimit = false | |
| 107 | 108 | detectUpstreamProxy = false // Default to disabled | |
| 108 | 109 | skipCertificates = false // Default to disabled | |
| @@ -143,6 +144,15 @@ func applyDefaultsToConfig(config *Config) bool { | |||
| 143 | 144 | defaultsApplied = true | |
| 144 | 145 | } | |
| 145 | 146 | ||
| 147 | + | // Apply defaults to HttpConfig | |
| 148 | + | // Check if Https is NOT explicitly set to true (i.e., it's the zero value 'false') | |
| 149 | + | if !config.Http.Https { | |
| 150 | + | config.Http.Https = defaultHttpHttps // Use the defined default variable | |
| 151 | + | logger.Debug("Applied default value for Http.Https", "value", defaultHttpHttps) | |
| 152 | + | defaultsApplied = true | |
| 153 | + | } | |
| 154 | + | // No defaults for Http.Port, Http.Domain, Http.SubDomain as they require explicit config | |
| 155 | + | ||
| 146 | 156 | // Apply defaults to Admin config | |
| 147 | 157 | if config.Admin.Path == "" { | |
| 148 | 158 | config.Admin.Path = defaultAdminPath | |
| @@ -103,6 +103,7 @@ var ( | |||||
| 103 | renewBefore = 30 // days | 103 | renewBefore = 30 // days | ||
| 104 | gracePeriod = 30 // seconds | 104 | gracePeriod = 30 // seconds | ||
| 105 | defaultLogLevel = "info" // Default log level | 105 | defaultLogLevel = "info" // Default log level | ||
| 106 | + | defaultHttpHttps = true // Default to true | |||
| 106 | disableRateLimit = false | 107 | disableRateLimit = false | ||
| 107 | detectUpstreamProxy = false // Default to disabled | 108 | detectUpstreamProxy = false // Default to disabled | ||
| 108 | skipCertificates = false // Default to disabled | 109 | skipCertificates = false // Default to disabled | ||
| @@ -143,6 +144,15 @@ func applyDefaultsToConfig(config *Config) bool { | |||||
| 143 | defaultsApplied = true | 144 | defaultsApplied = true | ||
| 144 | } | 145 | } | ||
| 145 | 146 | ||||
| 147 | + | // Apply defaults to HttpConfig | |||
| 148 | + | // Check if Https is NOT explicitly set to true (i.e., it's the zero value 'false') | |||
| 149 | + | if !config.Http.Https { | |||
| 150 | + | config.Http.Https = defaultHttpHttps // Use the defined default variable | |||
| 151 | + | logger.Debug("Applied default value for Http.Https", "value", defaultHttpHttps) | |||
| 152 | + | defaultsApplied = true | |||
| 153 | + | } | |||
| 154 | + | // No defaults for Http.Port, Http.Domain, Http.SubDomain as they require explicit config | |||
| 155 | + | ||||
| 146 | // Apply defaults to Admin config | 156 | // Apply defaults to Admin config | ||
| 147 | if config.Admin.Path == "" { | 157 | if config.Admin.Path == "" { | ||
| 148 | config.Admin.Path = defaultAdminPath | 158 | config.Admin.Path = defaultAdminPath | ||
M internal/httpserve/handlers/login.go +11 -6
| @@ -128,18 +128,21 @@ func StartOAuthGithub(c echo.Context, a *server.App) error { | |||
| 128 | 128 | // initiateGithubOAuthFlow starts a new GitHub OAuth flow | |
| 129 | 129 | func initiateGithubOAuthFlow(c echo.Context, a *server.App) error { | |
| 130 | 130 | clientID := "" // IDK why it needs that even empty, but it does | |
| 131 | + | // Use the corrected generation function | |
| 131 | 132 | redirectDomain := a.GenerateOauthCallbackURL() | |
| 133 | + | ||
| 132 | 134 | encodedState := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | |
| 133 | 135 | ||
| 134 | - | // Redirect to GitHub's OAuth endpoint to grab the OAuth access | |
| 136 | + | // Redirect to GitHub's OAuth endpoint via the proxy | |
| 135 | 137 | proxyURL := a.Config.Build.ProxyURL | |
| 136 | 138 | oauthURL := fmt.Sprintf( | |
| 137 | 139 | "%s/github/authorize?client_id=%s&redirect_uri=%s&state=%s", | |
| 138 | 140 | proxyURL, | |
| 139 | 141 | clientID, | |
| 140 | - | redirectDomain, | |
| 142 | + | redirectDomain, // Pass the correctly generated public URL to the proxy | |
| 141 | 143 | encodedState, | |
| 142 | 144 | ) | |
| 145 | + | logger.Debug("Initiating OAuth flow, redirecting to proxy", "proxy_url", oauthURL) | |
| 143 | 146 | return c.Redirect(http.StatusFound, oauthURL) | |
| 144 | 147 | } | |
| 145 | 148 | ||
| @@ -162,7 +165,7 @@ func OAuthCallback(c echo.Context, a *server.App) error { | |||
| 162 | 165 | // update the struct with the new access token | |
| 163 | 166 | a.DBTables.Sessions.AccessToken = accessToken | |
| 164 | 167 | ||
| 165 | - | // Compare the state parameter with the redirect domain | |
| 168 | + | // Compare the state parameter with the correctly generated redirect domain | |
| 166 | 169 | redirectDomain := a.GenerateOauthCallbackURL() | |
| 167 | 170 | encodedRedirectDomain := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | |
| 168 | 171 | if encodedState != encodedRedirectDomain { | |
| @@ -196,10 +199,11 @@ func OAuthCallback(c echo.Context, a *server.App) error { | |||
| 196 | 199 | logger.Debug("Before setting session values", "account_id", accountID, "session", session) | |
| 197 | 200 | err = setSessionValues(c, sess, accountID, session) | |
| 198 | 201 | if err != nil { | |
| 199 | - | logger.Error("Failed to set session values", "error", err) | |
| 200 | - | return err | |
| 202 | + | // Error is already logged within setSessionValues | |
| 203 | + | return err // Return immediately if saving failed | |
| 201 | 204 | } | |
| 202 | - | logger.Info("Session values set successfully") | |
| 205 | + | // Log success ONLY after setSessionValues returns without error | |
| 206 | + | logger.Info("Session values set and saved successfully") | |
| 203 | 207 | ||
| 204 | 208 | logger.Info("OAuth callback successful, redirecting to", "path", redirectPath) | |
| 205 | 209 | return c.Redirect(http.StatusFound, redirectPath) | |
| @@ -346,6 +350,7 @@ func setSessionValues(c echo.Context, sess *sessions.Session, accountID string, | |||
| 346 | 350 | sess.Values["isOnline"] = session.IsOnline | |
| 347 | 351 | ||
| 348 | 352 | if err := sess.Save(c.Request(), c.Response()); err != nil { | |
| 353 | + | logger.Error("Failed to save session cookie", "error", err, "sessionID", session.ID) | |
| 349 | 354 | return fmt.Errorf("could not save session: %w", err) | |
| 350 | 355 | } | |
| 351 | 356 | return nil | |
| @@ -128,18 +128,21 @@ func StartOAuthGithub(c echo.Context, a *server.App) error { | |||||
| 128 | // initiateGithubOAuthFlow starts a new GitHub OAuth flow | 128 | // initiateGithubOAuthFlow starts a new GitHub OAuth flow | ||
| 129 | func initiateGithubOAuthFlow(c echo.Context, a *server.App) error { | 129 | func initiateGithubOAuthFlow(c echo.Context, a *server.App) error { | ||
| 130 | clientID := "" // IDK why it needs that even empty, but it does | 130 | clientID := "" // IDK why it needs that even empty, but it does | ||
| 131 | + | // Use the corrected generation function | |||
| 131 | redirectDomain := a.GenerateOauthCallbackURL() | 132 | redirectDomain := a.GenerateOauthCallbackURL() | ||
| 133 | + | ||||
| 132 | encodedState := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | 134 | encodedState := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | ||
| 133 | 135 | ||||
| 134 | - | // Redirect to GitHub's OAuth endpoint to grab the OAuth access | 136 | + | // Redirect to GitHub's OAuth endpoint via the proxy |
| 135 | proxyURL := a.Config.Build.ProxyURL | 137 | proxyURL := a.Config.Build.ProxyURL | ||
| 136 | oauthURL := fmt.Sprintf( | 138 | oauthURL := fmt.Sprintf( | ||
| 137 | "%s/github/authorize?client_id=%s&redirect_uri=%s&state=%s", | 139 | "%s/github/authorize?client_id=%s&redirect_uri=%s&state=%s", | ||
| 138 | proxyURL, | 140 | proxyURL, | ||
| 139 | clientID, | 141 | clientID, | ||
| 140 | - | redirectDomain, | 142 | + | redirectDomain, // Pass the correctly generated public URL to the proxy |
| 141 | encodedState, | 143 | encodedState, | ||
| 142 | ) | 144 | ) | ||
| 145 | + | logger.Debug("Initiating OAuth flow, redirecting to proxy", "proxy_url", oauthURL) | |||
| 143 | return c.Redirect(http.StatusFound, oauthURL) | 146 | return c.Redirect(http.StatusFound, oauthURL) | ||
| 144 | } | 147 | } | ||
| 145 | 148 | ||||
| @@ -162,7 +165,7 @@ func OAuthCallback(c echo.Context, a *server.App) error { | |||||
| 162 | // update the struct with the new access token | 165 | // update the struct with the new access token | ||
| 163 | a.DBTables.Sessions.AccessToken = accessToken | 166 | a.DBTables.Sessions.AccessToken = accessToken | ||
| 164 | 167 | ||||
| 165 | - | // Compare the state parameter with the redirect domain | 168 | + | // Compare the state parameter with the correctly generated redirect domain |
| 166 | redirectDomain := a.GenerateOauthCallbackURL() | 169 | redirectDomain := a.GenerateOauthCallbackURL() | ||
| 167 | encodedRedirectDomain := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | 170 | encodedRedirectDomain := base64.StdEncoding.EncodeToString([]byte("redirectDomain:" + redirectDomain)) | ||
| 168 | if encodedState != encodedRedirectDomain { | 171 | if encodedState != encodedRedirectDomain { | ||
| @@ -196,10 +199,11 @@ func OAuthCallback(c echo.Context, a *server.App) error { | |||||
| 196 | logger.Debug("Before setting session values", "account_id", accountID, "session", session) | 199 | logger.Debug("Before setting session values", "account_id", accountID, "session", session) | ||
| 197 | err = setSessionValues(c, sess, accountID, session) | 200 | err = setSessionValues(c, sess, accountID, session) | ||
| 198 | if err != nil { | 201 | if err != nil { | ||
| 199 | - | logger.Error("Failed to set session values", "error", err) | 202 | + | // Error is already logged within setSessionValues |
| 200 | - | return err | 203 | + | return err // Return immediately if saving failed |
| 201 | } | 204 | } | ||
| 202 | - | logger.Info("Session values set successfully") | 205 | + | // Log success ONLY after setSessionValues returns without error |
| 206 | + | logger.Info("Session values set and saved successfully") | |||
| 203 | 207 | ||||
| 204 | logger.Info("OAuth callback successful, redirecting to", "path", redirectPath) | 208 | logger.Info("OAuth callback successful, redirecting to", "path", redirectPath) | ||
| 205 | return c.Redirect(http.StatusFound, redirectPath) | 209 | return c.Redirect(http.StatusFound, redirectPath) | ||
| @@ -346,6 +350,7 @@ func setSessionValues(c echo.Context, sess *sessions.Session, accountID string, | |||||
| 346 | sess.Values["isOnline"] = session.IsOnline | 350 | sess.Values["isOnline"] = session.IsOnline | ||
| 347 | 351 | ||||
| 348 | if err := sess.Save(c.Request(), c.Response()); err != nil { | 352 | if err := sess.Save(c.Request(), c.Response()); err != nil { | ||
| 353 | + | logger.Error("Failed to save session cookie", "error", err, "sessionID", session.ID) | |||
| 349 | return fmt.Errorf("could not save session: %w", err) | 354 | return fmt.Errorf("could not save session: %w", err) | ||
| 350 | } | 355 | } | ||
| 351 | return nil | 356 | return nil | ||
M internal/httpserve/router.go +2 -1
| @@ -146,7 +146,8 @@ func bindLoginRoute(e *echo.Echo, a *server.App, adminPath string) { | |||
| 146 | 146 | e.GET(adminPath+"/login/oauth/github", func(c echo.Context) error { | |
| 147 | 147 | return handlers.StartOAuthGithub(c, a) | |
| 148 | 148 | }) | |
| 149 | - | e.GET(adminPath+"/login/oauth/callback", func(c echo.Context) error { | |
| 149 | + | e.GET("/callback", func(c echo.Context) error { | |
| 150 | + | log.Debug("OAuth callback endpoint hit", "path", c.Path(), "query", c.QueryString()) | |
| 150 | 151 | return handlers.OAuthCallback(c, a) | |
| 151 | 152 | }) | |
| 152 | 153 | e.GET(adminPath+"/logout", func(c echo.Context) error { | |
| @@ -146,7 +146,8 @@ func bindLoginRoute(e *echo.Echo, a *server.App, adminPath string) { | |||||
| 146 | e.GET(adminPath+"/login/oauth/github", func(c echo.Context) error { | 146 | e.GET(adminPath+"/login/oauth/github", func(c echo.Context) error { | ||
| 147 | return handlers.StartOAuthGithub(c, a) | 147 | return handlers.StartOAuthGithub(c, a) | ||
| 148 | }) | 148 | }) | ||
| 149 | - | e.GET(adminPath+"/login/oauth/callback", func(c echo.Context) error { | 149 | + | e.GET("/callback", func(c echo.Context) error { |
| 150 | + | log.Debug("OAuth callback endpoint hit", "path", c.Path(), "query", c.QueryString()) | |||
| 150 | return handlers.OAuthCallback(c, a) | 151 | return handlers.OAuthCallback(c, a) | ||
| 151 | }) | 152 | }) | ||
| 152 | e.GET(adminPath+"/logout", func(c echo.Context) error { | 153 | e.GET(adminPath+"/logout", func(c echo.Context) error { | ||
M internal/server/app_server.go +4 -4
| @@ -59,9 +59,7 @@ func (a *App) GenerateOauthCallbackURL() string { | |||
| 59 | 59 | if config.Http.Https { | |
| 60 | 60 | scheme = "https" | |
| 61 | 61 | port = "" | |
| 62 | - | } | |
| 63 | - | ||
| 64 | - | if !config.Http.Https { | |
| 62 | + | } else { | |
| 65 | 63 | scheme = "http" | |
| 66 | 64 | port = fmt.Sprintf(":%s", a.Config.Http.Port) | |
| 67 | 65 | } | |
| @@ -71,7 +69,9 @@ func (a *App) GenerateOauthCallbackURL() string { | |||
| 71 | 69 | domain = fmt.Sprintf("%s.%s", config.Http.SubDomain, config.Http.Domain) | |
| 72 | 70 | } | |
| 73 | 71 | ||
| 74 | - | return fmt.Sprintf("%s://%s%s%s/login/oauth/callback", scheme, domain, port, config.Admin.Path) | |
| 72 | + | callbackURL := fmt.Sprintf("%s://%s%s/callback", scheme, domain, port) | |
| 73 | + | log.Debug("Generated OAuth Callback URL", "url", callbackURL) | |
| 74 | + | return callbackURL | |
| 75 | 75 | } | |
| 76 | 76 | ||
| 77 | 77 | func (a *App) IsDevEnvironment() bool { | |
| @@ -59,9 +59,7 @@ func (a *App) GenerateOauthCallbackURL() string { | |||||
| 59 | if config.Http.Https { | 59 | if config.Http.Https { | ||
| 60 | scheme = "https" | 60 | scheme = "https" | ||
| 61 | port = "" | 61 | port = "" | ||
| 62 | - | } | 62 | + | } else { |
| 63 | - | ||||
| 64 | - | if !config.Http.Https { | |||
| 65 | scheme = "http" | 63 | scheme = "http" | ||
| 66 | port = fmt.Sprintf(":%s", a.Config.Http.Port) | 64 | port = fmt.Sprintf(":%s", a.Config.Http.Port) | ||
| 67 | } | 65 | } | ||
| @@ -71,7 +69,9 @@ func (a *App) GenerateOauthCallbackURL() string { | |||||
| 71 | domain = fmt.Sprintf("%s.%s", config.Http.SubDomain, config.Http.Domain) | 69 | domain = fmt.Sprintf("%s.%s", config.Http.SubDomain, config.Http.Domain) | ||
| 72 | } | 70 | } | ||
| 73 | 71 | ||||
| 74 | - | return fmt.Sprintf("%s://%s%s%s/login/oauth/callback", scheme, domain, port, config.Admin.Path) | 72 | + | callbackURL := fmt.Sprintf("%s://%s%s/callback", scheme, domain, port) |
| 73 | + | log.Debug("Generated OAuth Callback URL", "url", callbackURL) | |||
| 74 | + | return callbackURL | |||
| 75 | } | 75 | } | ||
| 76 | 76 | ||||
| 77 | func (a *App) IsDevEnvironment() bool { | 77 | func (a *App) IsDevEnvironment() bool { | ||
M internal/templating/models/templ/pages/login/index.templ +1 -1
| @@ -84,7 +84,7 @@ templ LoginPage(title string, buildVersion string, errorMsg string) { | |||
| 84 | 84 | <!-- GitHub OAuth --> | |
| 85 | 85 | <div class="w-full"> | |
| 86 | 86 | <a | |
| 87 | - | href="login/oauth/github" | |
| 87 | + | href="/admin/login/oauth/github" | |
| 88 | 88 | class="btn btn-secondary w-full" | |
| 89 | 89 | > | |
| 90 | 90 | <svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"> | |
| @@ -84,7 +84,7 @@ templ LoginPage(title string, buildVersion string, errorMsg string) { | |||||
| 84 | <!-- GitHub OAuth --> | 84 | <!-- GitHub OAuth --> | ||
| 85 | <div class="w-full"> | 85 | <div class="w-full"> | ||
| 86 | <a | 86 | <a | ||
| 87 | - | href="login/oauth/github" | 87 | + | href="/admin/login/oauth/github" |
| 88 | class="btn btn-secondary w-full" | 88 | class="btn btn-secondary w-full" | ||
| 89 | > | 89 | > | ||
| 90 | <svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"> | 90 | <svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"> | ||
A internal/templating/models/templ/pages/login/index.templ (lines 84-87) +5 -0
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <div class="w-full"> | |
| 2 | + | <a | |
| 3 | + | href="login/oauth/github" | |
| 4 | + | class="btn btn-secondary w-full" | |
| 5 | + | > ⏎̸ | |
| @@ -0,0 +1,5 @@ | |||||
| 1 | + | <div class="w-full"> | |||
| 2 | + | <a | |||
| 3 | + | href="login/oauth/github" | |||
| 4 | + | class="btn btn-secondary w-full" | |||
| 5 | + | > ⏎̸ | |||