Skip to content

Commit 89be89c

Browse files
authored
Merge pull request #44 from appleboy/patch-3
Support fetch token from cookie, query or header
2 parents 40c6c8b + 73a41de commit 89be89c

File tree

7 files changed

+199
-18
lines changed

7 files changed

+199
-18
lines changed

.drone.yml

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
pipeline:
22
build:
3-
image: appleboy/golang-testing
3+
image: golang:${GO_VERSION}
44
environment:
55
- GOPATH=/go
66
commands:
7-
- make install
8-
- coverage all
7+
- go get -d -t -v
8+
- go test -v
99

1010
workspace:
1111
path: /go/src/github.com/appleboy/gin-jwt
12+
13+
matrix:
14+
GO_VERSION:
15+
- 1.6.3
16+
- 1.7.1

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ update:
99
glide up
1010

1111
test:
12-
go test -v -cover .
12+
go test -v -cover -coverprofile=.cover/coverage.txt
1313

1414
html:
1515
go tool cover -html=.cover/coverage.txt

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ func main() {
9393
"message": message,
9494
})
9595
},
96+
// TokenLookup is a string in the form of "<source>:<name>" that is used
97+
// to extract token from the request.
98+
// Optional. Default value "header:Authorization".
99+
// Possible values:
100+
// - "header:<name>"
101+
// - "query:<name>"
102+
// - "cookie:<name>"
103+
TokenLookup: "header:Authorization",
104+
// TokenLookup: "query:token",
105+
// TokenLookup: "cookie:token",
96106
}
97107

98108
r.POST("/login", authMiddleware.LoginHandler)

auth_jwt.go

+59-5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ type GinJWTMiddleware struct {
5454

5555
// User can define own Unauthorized func.
5656
Unauthorized func(*gin.Context, int, string)
57+
58+
// TokenLookup is a string in the form of "<source>:<name>" that is used
59+
// to extract token from the request.
60+
// Optional. Default value "header:Authorization".
61+
// Possible values:
62+
// - "header:<name>"
63+
// - "query:<name>"
64+
// - "cookie:<name>"
65+
TokenLookup string
5766
}
5867

5968
// Login form structure.
@@ -65,6 +74,10 @@ type Login struct {
6574
// MiddlewareInit initialize jwt configs.
6675
func (mw *GinJWTMiddleware) MiddlewareInit() error {
6776

77+
if mw.TokenLookup == "" {
78+
mw.TokenLookup = "header:Authorization"
79+
}
80+
6881
if mw.SigningAlgorithm == "" {
6982
mw.SigningAlgorithm = "HS256"
7083
}
@@ -267,19 +280,60 @@ func (mw *GinJWTMiddleware) TokenGenerator(userID string) string {
267280
return tokenString
268281
}
269282

270-
func (mw *GinJWTMiddleware) parseToken(c *gin.Context) (*jwt.Token, error) {
271-
authHeader := c.Request.Header.Get("Authorization")
283+
func (mw *GinJWTMiddleware) jwtFromHeader(c *gin.Context, key string) (string, error) {
284+
authHeader := c.Request.Header.Get(key)
272285

273286
if authHeader == "" {
274-
return nil, errors.New("auth header empty")
287+
return "", errors.New("auth header empty")
275288
}
276289

277290
parts := strings.SplitN(authHeader, " ", 2)
278291
if !(len(parts) == 2 && parts[0] == "Bearer") {
279-
return nil, errors.New("invalid auth header")
292+
return "", errors.New("invalid auth header")
293+
}
294+
295+
return parts[1], nil
296+
}
297+
298+
func (mw *GinJWTMiddleware) jwtFromQuery(c *gin.Context, key string) (string, error) {
299+
token := c.Query(key)
300+
301+
if token == "" {
302+
return "", errors.New("Query token empty")
303+
}
304+
305+
return token, nil
306+
}
307+
308+
func (mw *GinJWTMiddleware) jwtFromCookie(c *gin.Context, key string) (string, error) {
309+
cookie, _ := c.Cookie(key)
310+
311+
if cookie == "" {
312+
return "", errors.New("Cookie token empty")
313+
}
314+
315+
return cookie, nil
316+
}
317+
318+
func (mw *GinJWTMiddleware) parseToken(c *gin.Context) (*jwt.Token, error) {
319+
var token string
320+
var err error
321+
322+
parts := strings.Split(mw.TokenLookup, ":")
323+
switch parts[0] {
324+
case "header":
325+
token, err = mw.jwtFromHeader(c, parts[1])
326+
case "query":
327+
token, err = mw.jwtFromQuery(c, parts[1])
328+
case "cookie":
329+
token, err = mw.jwtFromCookie(c, parts[1])
330+
}
331+
332+
if err != nil {
333+
return nil, err
280334
}
281335

282-
return jwt.Parse(parts[1], func(token *jwt.Token) (interface{}, error) {
336+
return jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
283337
if jwt.GetSigningMethod(mw.SigningAlgorithm) != token.Method {
284338
return nil, errors.New("invalid signing algorithm")
285339
}

auth_jwt_test.go

+101
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ func TestMissingTimeOut(t *testing.T) {
108108
assert.Equal(t, time.Hour, authMiddleware.Timeout)
109109
}
110110

111+
func TestMissingTokenLookup(t *testing.T) {
112+
113+
authMiddleware := &GinJWTMiddleware{
114+
Realm: "test zone",
115+
Key: key,
116+
Authenticator: func(userId string, password string, c *gin.Context) (string, bool) {
117+
if userId == "admin" && password == "admin" {
118+
return "", true
119+
}
120+
121+
return "", false
122+
},
123+
}
124+
125+
authMiddleware.MiddlewareInit()
126+
127+
assert.Equal(t, "header:Authorization", authMiddleware.TokenLookup)
128+
}
129+
111130
func helloHandler(c *gin.Context) {
112131
c.JSON(200, gin.H{
113132
"text": "Hello World.",
@@ -605,3 +624,85 @@ func TestTokenExpire(t *testing.T) {
605624
assert.Equal(t, http.StatusUnauthorized, r.Code)
606625
})
607626
}
627+
628+
func TestTokenFromQueryString(t *testing.T) {
629+
// the middleware to test
630+
authMiddleware := &GinJWTMiddleware{
631+
Realm: "test zone",
632+
Key: key,
633+
Timeout: time.Hour,
634+
Authenticator: func(userId string, password string, c *gin.Context) (string, bool) {
635+
if userId == "admin" && password == "admin" {
636+
return userId, true
637+
}
638+
return userId, false
639+
},
640+
Unauthorized: func(c *gin.Context, code int, message string) {
641+
c.String(code, message)
642+
},
643+
TokenLookup: "query:token",
644+
}
645+
646+
handler := ginHandler(authMiddleware)
647+
648+
r := gofight.New()
649+
650+
userToken := authMiddleware.TokenGenerator("admin")
651+
652+
r.GET("/auth/refresh_token").
653+
SetHeader(gofight.H{
654+
"Authorization": "Bearer " + userToken,
655+
}).
656+
Run(handler, func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
657+
assert.Equal(t, http.StatusUnauthorized, r.Code)
658+
})
659+
660+
r.GET("/auth/refresh_token?token="+userToken).
661+
SetHeader(gofight.H{
662+
"Authorization": "Bearer " + userToken,
663+
}).
664+
Run(handler, func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
665+
assert.Equal(t, http.StatusOK, r.Code)
666+
})
667+
}
668+
669+
func TestTokenFromCookieString(t *testing.T) {
670+
// the middleware to test
671+
authMiddleware := &GinJWTMiddleware{
672+
Realm: "test zone",
673+
Key: key,
674+
Timeout: time.Hour,
675+
Authenticator: func(userId string, password string, c *gin.Context) (string, bool) {
676+
if userId == "admin" && password == "admin" {
677+
return userId, true
678+
}
679+
return userId, false
680+
},
681+
Unauthorized: func(c *gin.Context, code int, message string) {
682+
c.String(code, message)
683+
},
684+
TokenLookup: "cookie:token",
685+
}
686+
687+
handler := ginHandler(authMiddleware)
688+
689+
r := gofight.New()
690+
691+
userToken := authMiddleware.TokenGenerator("admin")
692+
693+
r.GET("/auth/refresh_token").
694+
SetHeader(gofight.H{
695+
"Authorization": "Bearer " + userToken,
696+
}).
697+
Run(handler, func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
698+
assert.Equal(t, http.StatusUnauthorized, r.Code)
699+
})
700+
701+
r.GET("/auth/refresh_token").
702+
SetCookie(gofight.H{
703+
"token": userToken,
704+
}).
705+
Run(handler, func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
706+
assert.Equal(t, http.StatusOK, r.Code)
707+
})
708+
}

example/server.go

+10
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ func main() {
5050
"message": message,
5151
})
5252
},
53+
// TokenLookup is a string in the form of "<source>:<name>" that is used
54+
// to extract token from the request.
55+
// Optional. Default value "header:Authorization".
56+
// Possible values:
57+
// - "header:<name>"
58+
// - "query:<name>"
59+
// - "cookie:<name>"
60+
TokenLookup: "header:Authorization",
61+
// TokenLookup: "query:token",
62+
// TokenLookup: "cookie:token",
5363
}
5464

5565
r.POST("/login", authMiddleware.LoginHandler)

glide.lock

+10-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)