gin icon indicating copy to clipboard operation
gin copied to clipboard

BasicAuth() asking for authorization on every route starting from subroute

Open benstigsen opened this issue 2 years ago • 1 comments

I have the following code:

authorized.GET("/api", func(ctx *gin.Context) {
	ctx.JSON(200, gin.H{"message": "this is the root!", "status": 200})
})

authorized.GET("/api/:first", func(ctx *gin.Context) {
	ctx.JSON(200, gin.H{"message": "this is a subroute!", "status": 200})
})

authorized.GET("/api/:first/:second", func(ctx *gin.Context) {
	ctx.JSON(200, gin.H{"message": "this is a deeper subroute!", "status": 200})
})

If I login to localhost:8080/api is asks for username and password as expected. Now I can go to localhost:8080/api/deep and localhost:8080/api/deep/deeper without issue.

[GIN] 2022/10/30 - 23:32:45 | 401 |       0s |  127.0.0.1 | GET "/api"  AUTHORIZATION
[GIN] 2022/10/30 - 23:32:47 | 200 |       0s |  127.0.0.1 | GET "/api"
[GIN] 2022/10/30 - 23:32:49 | 200 |  998.8µs |  127.0.0.1 | GET "/api/deep"
[GIN] 2022/10/30 - 23:32:53 | 200 | 1.0186ms |  127.0.0.1 | GET "/api/deep/deeper"

If however I start by logging in to to localhost:8080/api/deep/deeper, then it asks for username and password as expected. But if I now go to localhost:8080/api/deep/ it asks for login again. And then also at localhost:8080/api.

[GIN] 2022/10/30 - 23:32:27 | 401 |       0s |  127.0.0.1 | GET "/api/deep/deeper"  AUTHORIZATION
[GIN] 2022/10/30 - 23:32:30 | 200 |    991µs |  127.0.0.1 | GET "/api/deep/deeper"
[GIN] 2022/10/30 - 23:32:33 | 401 |       0s |  127.0.0.1 | GET "/api/deep"         AUTHORIZATION
[GIN] 2022/10/30 - 23:32:33 | 200 |  193.3µs |  127.0.0.1 | GET "/api/deep"
[GIN] 2022/10/30 - 23:32:35 | 401 |       0s |  127.0.0.1 | GET "/api"              AUTHORIZATION
[GIN] 2022/10/30 - 23:32:35 | 200 |       0s |  127.0.0.1 | GET "/api"

How to reproduce

Try logging into /api, then go to the subroutes. This should work as expected. Now try logging in from /api/first/second, then go to /api/first, then to /api, it should now show 401 in console.

package main

import "github.com/gin-gonic/gin"

func main() {
	router := gin.Default()

	authorized := router.Group("/", gin.BasicAuth(gin.Accounts{
		"foo": "bar",
	}))

	authorized.GET("/api", func(ctx *gin.Context) {
		ctx.JSON(200, gin.H{"message": "this is the root!", "status": 200})
	})
	
	authorized.GET("/api/:first", func(ctx *gin.Context) {
		ctx.JSON(200, gin.H{"message": "this is a subroute!", "status": 200})
	})
	
	authorized.GET("/api/:first/:second", func(ctx *gin.Context) {
		ctx.JSON(200, gin.H{"message": "this is a deeper subroute!", "status": 200})
	})

	router.Run("localhost:8080")
}

Other

While this does work in the browser for me (Firefox), this does not work for something like VLC Media Player or applications like that. They constantly have to authorize, which is very inconvenient if one is creating a file server with Gin, as this subroute permission mess can require one to log in every time a file is fetched.

Environment

  • go version: 1.19
  • gin version: v1.8.1
  • operating system: Windows 10

benstigsen avatar Oct 30 '22 22:10 benstigsen

Hey @BenStigsen, That is the regular behaviour. Maybe you can take a look this question: https://stackoverflow.com/questions/27818587/httpget-401-status-code-followed-by-200-status-code

If you don't want a redundant 401 response, maybe you can use other auth mode. I recommend you use JWT and token.

Cookiery avatar Nov 23 '22 16:11 Cookiery