Skip to content

Commit 2ff3804

Browse files
authored
feat: improve error handling with custom error template (#47)
- Add custom error template for better user experience - Replace c.Error() calls with proper error rendering - Add error handling for session.Save() operations - Remove default session options from main.go
1 parent cd28916 commit 2ff3804

3 files changed

Lines changed: 123 additions & 15 deletions

File tree

pkg/auth/auth.go

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type AuthRouter struct {
2020
providers []Provider
2121
loginTemplate *template.Template
2222
unauthorizedTemplate *template.Template
23+
errorTemplate *template.Template
2324
}
2425

2526
func NewAuthRouter(passwordHash []string, providers ...Provider) (*AuthRouter, error) {
@@ -33,11 +34,17 @@ func NewAuthRouter(passwordHash []string, providers ...Provider) (*AuthRouter, e
3334
return nil, err
3435
}
3536

37+
errorTmpl, err := template.ParseFS(templateFS, "templates/error.html")
38+
if err != nil {
39+
return nil, err
40+
}
41+
3642
return &AuthRouter{
3743
passwordHash: passwordHash,
3844
providers: providers,
3945
loginTemplate: tmpl,
4046
unauthorizedTemplate: unauthorizedTmpl,
47+
errorTemplate: errorTmpl,
4148
}, nil
4249
}
4350

@@ -69,22 +76,22 @@ func (a *AuthRouter) SetupRoutes(router gin.IRouter) {
6976
session := sessions.Default(c)
7077
state := session.Get(SessionKeyOAuthState)
7178
if state == nil {
72-
c.Error(errors.New("OAuth state is missing"))
79+
a.renderError(c, errors.New("OAuth state is missing"))
7380
return
7481
}
7582
token, err := provider.Exchange(c, state.(string))
7683
if err != nil {
77-
c.Error(err)
84+
a.renderError(c, err)
7885
return
7986
}
8087
userID, err := provider.GetUserID(c, token)
8188
if err != nil {
82-
c.Error(err)
89+
a.renderError(c, err)
8390
return
8491
}
8592
ok, err := provider.Authorization(userID)
8693
if err != nil {
87-
c.Error(err)
94+
a.renderError(c, err)
8895
return
8996
}
9097
if !ok {
@@ -97,7 +104,10 @@ func (a *AuthRouter) SetupRoutes(router gin.IRouter) {
97104
if redirectURL != nil {
98105
session.Delete(SessionKeyRedirectURL)
99106
}
100-
session.Save()
107+
if err := session.Save(); err != nil {
108+
a.renderError(c, err)
109+
return
110+
}
101111

102112
if redirectURL == nil {
103113
c.Redirect(http.StatusFound, "/")
@@ -111,16 +121,19 @@ func (a *AuthRouter) SetupRoutes(router gin.IRouter) {
111121

112122
state, err := utils.GenerateState()
113123
if err != nil {
114-
c.Error(err)
124+
a.renderError(c, err)
115125
return
116126
}
117127
url, err := provider.AuthCodeURL(c, state)
118128
if err != nil {
119-
c.Error(err)
129+
a.renderError(c, err)
120130
return
121131
}
122132
session.Set(SessionKeyOAuthState, state)
123-
session.Save()
133+
if err := session.Save(); err != nil {
134+
a.renderError(c, err)
135+
return
136+
}
124137
c.Redirect(http.StatusFound, url)
125138
})
126139
}
@@ -176,7 +189,10 @@ func (a *AuthRouter) handleLoginPost(c *gin.Context) {
176189
if redirectURL != nil {
177190
session.Delete(SessionKeyRedirectURL)
178191
}
179-
session.Save()
192+
if err := session.Save(); err != nil {
193+
a.renderError(c, err)
194+
return
195+
}
180196

181197
if redirectURL == nil {
182198
c.Redirect(http.StatusFound, "/")
@@ -189,7 +205,10 @@ func (a *AuthRouter) handleLogout(c *gin.Context) {
189205
session := sessions.Default(c)
190206
session.Delete(SessionKeyProvider)
191207
session.Delete(SessionKeyUserID)
192-
session.Save()
208+
if err := session.Save(); err != nil {
209+
a.renderError(c, err)
210+
return
211+
}
193212
c.Redirect(http.StatusFound, LoginEndpoint)
194213
}
195214

@@ -200,7 +219,10 @@ func (a *AuthRouter) RequireAuth() gin.HandlerFunc {
200219
userID := session.Get(SessionKeyUserID)
201220
if providerName == nil || userID == nil {
202221
session.Set(SessionKeyRedirectURL, c.Request.URL.String())
203-
session.Save()
222+
if err := session.Save(); err != nil {
223+
a.renderError(c, err)
224+
return
225+
}
204226
c.Redirect(http.StatusFound, LoginEndpoint)
205227
return
206228
}
@@ -241,6 +263,10 @@ type unauthorizedTemplateData struct {
241263
Provider string
242264
}
243265

266+
type errorTemplateData struct {
267+
ErrorMessage string
268+
}
269+
244270
func (a *AuthRouter) renderLogin(c *gin.Context, passwordError string) {
245271
data := loginTemplateData{
246272
Providers: a.providers,
@@ -271,3 +297,16 @@ func (a *AuthRouter) renderUnauthorized(c *gin.Context, userID, providerName str
271297
return
272298
}
273299
}
300+
301+
func (a *AuthRouter) renderError(c *gin.Context, err error) {
302+
data := errorTemplateData{
303+
ErrorMessage: err.Error(),
304+
}
305+
c.Header("Content-Type", "text/html; charset=utf-8")
306+
c.Status(http.StatusInternalServerError)
307+
if templateErr := a.errorTemplate.Execute(c.Writer, data); templateErr != nil {
308+
c.AbortWithError(http.StatusInternalServerError, templateErr)
309+
return
310+
}
311+
c.Abort()
312+
}

pkg/auth/templates/error.html

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Error - MCP Auth Proxy</title>
7+
<style>
8+
body {
9+
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
10+
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
11+
margin: 0;
12+
padding: 0;
13+
min-height: 100vh;
14+
display: flex;
15+
align-items: center;
16+
justify-content: center;
17+
}
18+
.error-container {
19+
background: white;
20+
padding: 2.5rem;
21+
border-radius: 12px;
22+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
23+
text-align: center;
24+
min-width: 350px;
25+
max-width: 500px;
26+
width: 100%;
27+
}
28+
.error-icon {
29+
font-size: 4rem;
30+
color: #e74c3c;
31+
margin-bottom: 1rem;
32+
}
33+
h1 {
34+
color: #333;
35+
margin-bottom: 1.5rem;
36+
font-size: 2rem;
37+
font-weight: 600;
38+
}
39+
.error-message {
40+
color: #666;
41+
margin-bottom: 2rem;
42+
font-size: 1.1rem;
43+
line-height: 1.6;
44+
}
45+
.error-details {
46+
background: #f8f9fa;
47+
border: 1px solid #e9ecef;
48+
border-radius: 8px;
49+
padding: 1rem;
50+
margin-bottom: 2rem;
51+
font-family: monospace;
52+
color: #e74c3c;
53+
font-size: 0.9rem;
54+
text-align: left;
55+
word-wrap: break-word;
56+
}
57+
</style>
58+
</head>
59+
<body>
60+
<div class="error-container">
61+
<div class="error-icon">⚠️</div>
62+
<h1>Error</h1>
63+
<div class="error-message">
64+
An error occurred while processing your request.
65+
</div>
66+
{{if .ErrorMessage}}
67+
<div class="error-details">
68+
{{.ErrorMessage}}
69+
</div>
70+
{{end}}
71+
</div>
72+
</body>
73+
</html>

pkg/mcp-proxy/main.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,6 @@ func Run(
207207
router.Use(ginzap.Ginzap(logger, time.RFC3339, true))
208208
router.Use(ginzap.RecoveryWithZap(logger, true))
209209
store := cookie.NewStore(secret)
210-
store.Options(sessions.Options{
211-
MaxAge: 3600,
212-
HttpOnly: true,
213-
})
214210
router.Use(sessions.Sessions("session", store))
215211
authRouter.SetupRoutes(router)
216212
idpRouter.SetupRoutes(router)

0 commit comments

Comments
 (0)