Skip to content

Commit cc94294

Browse files
authored
feat: add x-tinyauth-location to nginx response (#783)
* feat: add x-tinyauth-location to nginx response Solves #773. Normally you let Nginx handle the login URL creation but with this "hack" we can set an arbitary header with where Tinyauth wants the user to go to. Later the Nginx error page can get this header and redirect accordingly. * tests: fix assert.Equal order
1 parent b44dc75 commit cc94294

2 files changed

Lines changed: 64 additions & 49 deletions

File tree

internal/controller/proxy_controller.go

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -131,26 +131,29 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
131131
}
132132

133133
if !controller.auth.CheckIP(acls.IP, clientIP) {
134-
if !controller.useBrowserResponse(proxyCtx) {
135-
c.JSON(401, gin.H{
136-
"status": 401,
137-
"message": "Unauthorized",
138-
})
139-
return
140-
}
141-
142134
queries, err := query.Values(config.UnauthorizedQuery{
143135
Resource: strings.Split(proxyCtx.Host, ".")[0],
144136
IP: clientIP,
145137
})
146138

147139
if err != nil {
148140
tlog.App.Error().Err(err).Msg("Failed to encode unauthorized query")
149-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
141+
controller.handleError(c, proxyCtx)
142+
return
143+
}
144+
145+
redirectURL := fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode())
146+
147+
if !controller.useBrowserResponse(proxyCtx) {
148+
c.Header("x-tinyauth-location", redirectURL)
149+
c.JSON(401, gin.H{
150+
"status": 401,
151+
"message": "Unauthorized",
152+
})
150153
return
151154
}
152155

153-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode()))
156+
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
154157
return
155158
}
156159

@@ -175,21 +178,13 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
175178
if !userAllowed {
176179
tlog.App.Warn().Str("user", userContext.Username).Str("resource", strings.Split(proxyCtx.Host, ".")[0]).Msg("User not allowed to access resource")
177180

178-
if !controller.useBrowserResponse(proxyCtx) {
179-
c.JSON(403, gin.H{
180-
"status": 403,
181-
"message": "Forbidden",
182-
})
183-
return
184-
}
185-
186181
queries, err := query.Values(config.UnauthorizedQuery{
187182
Resource: strings.Split(proxyCtx.Host, ".")[0],
188183
})
189184

190185
if err != nil {
191186
tlog.App.Error().Err(err).Msg("Failed to encode unauthorized query")
192-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
187+
controller.handleError(c, proxyCtx)
193188
return
194189
}
195190

@@ -199,7 +194,18 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
199194
queries.Set("username", userContext.Username)
200195
}
201196

202-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode()))
197+
redirectURL := fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode())
198+
199+
if !controller.useBrowserResponse(proxyCtx) {
200+
c.Header("x-tinyauth-location", redirectURL)
201+
c.JSON(403, gin.H{
202+
"status": 403,
203+
"message": "Forbidden",
204+
})
205+
return
206+
}
207+
208+
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
203209
return
204210
}
205211

@@ -215,22 +221,14 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
215221
if !groupOK {
216222
tlog.App.Warn().Str("user", userContext.Username).Str("resource", strings.Split(proxyCtx.Host, ".")[0]).Msg("User groups do not match resource requirements")
217223

218-
if !controller.useBrowserResponse(proxyCtx) {
219-
c.JSON(403, gin.H{
220-
"status": 403,
221-
"message": "Forbidden",
222-
})
223-
return
224-
}
225-
226224
queries, err := query.Values(config.UnauthorizedQuery{
227225
Resource: strings.Split(proxyCtx.Host, ".")[0],
228226
GroupErr: true,
229227
})
230228

231229
if err != nil {
232230
tlog.App.Error().Err(err).Msg("Failed to encode unauthorized query")
233-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
231+
controller.handleError(c, proxyCtx)
234232
return
235233
}
236234

@@ -240,7 +238,18 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
240238
queries.Set("username", userContext.Username)
241239
}
242240

243-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode()))
241+
redirectURL := fmt.Sprintf("%s/unauthorized?%s", controller.config.AppURL, queries.Encode())
242+
243+
if !controller.useBrowserResponse(proxyCtx) {
244+
c.Header("x-tinyauth-location", redirectURL)
245+
c.JSON(403, gin.H{
246+
"status": 403,
247+
"message": "Forbidden",
248+
})
249+
return
250+
}
251+
252+
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
244253
return
245254
}
246255
}
@@ -266,25 +275,28 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
266275
return
267276
}
268277

269-
if !controller.useBrowserResponse(proxyCtx) {
270-
c.JSON(401, gin.H{
271-
"status": 401,
272-
"message": "Unauthorized",
273-
})
274-
return
275-
}
276-
277278
queries, err := query.Values(config.RedirectQuery{
278279
RedirectURI: fmt.Sprintf("%s://%s%s", proxyCtx.Proto, proxyCtx.Host, proxyCtx.Path),
279280
})
280281

281282
if err != nil {
282283
tlog.App.Error().Err(err).Msg("Failed to encode redirect URI query")
283-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
284+
controller.handleError(c, proxyCtx)
284285
return
285286
}
286287

287-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/login?%s", controller.config.AppURL, queries.Encode()))
288+
redirectURL := fmt.Sprintf("%s/login?%s", controller.config.AppURL, queries.Encode())
289+
290+
if !controller.useBrowserResponse(proxyCtx) {
291+
c.Header("x-tinyauth-location", redirectURL)
292+
c.JSON(401, gin.H{
293+
"status": 401,
294+
"message": "Unauthorized",
295+
})
296+
return
297+
}
298+
299+
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
288300
}
289301

290302
func (controller *ProxyController) setHeaders(c *gin.Context, acls config.App) {
@@ -306,15 +318,18 @@ func (controller *ProxyController) setHeaders(c *gin.Context, acls config.App) {
306318
}
307319

308320
func (controller *ProxyController) handleError(c *gin.Context, proxyCtx ProxyContext) {
321+
redirectURL := fmt.Sprintf("%s/error", controller.config.AppURL)
322+
309323
if !controller.useBrowserResponse(proxyCtx) {
324+
c.Header("x-tinyauth-location", redirectURL)
310325
c.JSON(500, gin.H{
311326
"status": 500,
312327
"message": "Internal Server Error",
313328
})
314329
return
315330
}
316331

317-
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
332+
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
318333
}
319334

320335
func (controller *ProxyController) getHeader(c *gin.Context, header string) (string, bool) {

internal/controller/proxy_controller_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ func TestProxyController(t *testing.T) {
116116

117117
assert.Equal(t, 307, recorder.Code)
118118
location := recorder.Header().Get("Location")
119-
assert.Contains(t, location, "https://tinyauth.example.com/login?redirect_uri=")
120-
assert.Contains(t, location, "https%3A%2F%2Ftest.example.com%2F")
119+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2F", location)
121120
},
122121
},
123122
{
@@ -129,6 +128,8 @@ func TestProxyController(t *testing.T) {
129128
req.Header.Set("user-agent", browserUserAgent)
130129
router.ServeHTTP(recorder, req)
131130
assert.Equal(t, 401, recorder.Code)
131+
location := recorder.Header().Get("x-tinyauth-location")
132+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2F", location)
132133
},
133134
},
134135
{
@@ -142,8 +143,7 @@ func TestProxyController(t *testing.T) {
142143
router.ServeHTTP(recorder, req)
143144
assert.Equal(t, 307, recorder.Code)
144145
location := recorder.Header().Get("Location")
145-
assert.Contains(t, location, "https://tinyauth.example.com/login?redirect_uri=")
146-
assert.Contains(t, location, "https%3A%2F%2Ftest.example.com%2Fhello")
146+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2Fhello", location)
147147
},
148148
},
149149
{
@@ -159,8 +159,7 @@ func TestProxyController(t *testing.T) {
159159

160160
assert.Equal(t, 307, recorder.Code)
161161
location := recorder.Header().Get("Location")
162-
assert.Contains(t, location, "https://tinyauth.example.com/login?redirect_uri=")
163-
assert.Contains(t, location, "https%3A%2F%2Ftest.example.com%2F")
162+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2F", location)
164163
},
165164
},
166165
{
@@ -174,6 +173,8 @@ func TestProxyController(t *testing.T) {
174173
req.Header.Set("user-agent", browserUserAgent)
175174
router.ServeHTTP(recorder, req)
176175
assert.Equal(t, 401, recorder.Code)
176+
location := recorder.Header().Get("x-tinyauth-location")
177+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2F", location)
177178
},
178179
},
179180
{
@@ -189,8 +190,7 @@ func TestProxyController(t *testing.T) {
189190
router.ServeHTTP(recorder, req)
190191
assert.Equal(t, 307, recorder.Code)
191192
location := recorder.Header().Get("Location")
192-
assert.Contains(t, location, "https://tinyauth.example.com/login?redirect_uri=")
193-
assert.Contains(t, location, "https%3A%2F%2Ftest.example.com%2Fhello")
193+
assert.Equal(t, "https://tinyauth.example.com/login?redirect_uri=https%3A%2F%2Ftest.example.com%2Fhello", location)
194194
},
195195
},
196196
{

0 commit comments

Comments
 (0)