-
Notifications
You must be signed in to change notification settings - Fork 7
/
midware.go
109 lines (87 loc) · 3.48 KB
/
midware.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package auth
import (
"errors"
"fmt"
"github.com/ardanlabs/kit/log"
"github.com/ardanlabs/kit/web"
"github.com/coralproject/shelf/internal/platform/auth"
jwt "github.com/dgrijalva/jwt-go"
)
// ErrInvalidToken is returned when the token provided is not valid.
var ErrInvalidToken = errors.New("invalid token")
// MidwareOpts describes the options for configuring the Midware.
type MidwareOpts struct {
// AllowQueryString is true when we want to allow accessing the tokenString
// from the query string as a fallback.
AllowQueryString bool
}
// Midware handles token authentication for external authentication
// sources.
func Midware(publicKeyBase64Str string, config MidwareOpts) (web.Middleware, error) {
publicKey, err := auth.DecodePublicKey(publicKeyBase64Str)
if err != nil {
log.Error("startup", "auth : Midware", err, "Can not decode the public key base64 encoding")
return nil, err
}
// Create the middleware to actually return.
m := func(h web.Handler) web.Handler {
// Create the handler that we should return as a part of the middleware
// chain.
f := func(c *web.Context) error {
log.Dev(c.SessionID, "auth : Midware", "Started")
// Extract the token from the Authorization header provided on the request.
tokenString := c.Request.Header.Get("Authorization")
// In the event that the request does not have a header key for the
// Authorization header, and we are allowed to check the query string, then
// we need to try and access it from the URL query parameters.
if tokenString == "" && config.AllowQueryString {
tokenString = c.Request.URL.Query().Get("access_token")
}
if tokenString == "" {
log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "No token on request")
return web.ErrNotAuthorized
}
// This describes the key validation function to provide the certificate
// to validate the signature on the passed in JWT.
keyValidation := func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect.
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// Return with the public key that was provided in the config.
return publicKey, nil
}
// Here we actually parse/verify the signature on the JWT and extract the
// claims.
token, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, keyValidation)
if err != nil {
log.Error(c.SessionID, "auth : Midware", err, "Token could not be parsed")
return web.ErrNotAuthorized
}
// Return with an error if the token is not valid.
if !token.Valid {
log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "Token not valid")
return web.ErrNotAuthorized
}
// Ensure that the claims that are inside the token are indeed the MapClaims
// that we expect.
claims, ok := token.Claims.(*jwt.MapClaims)
if !ok {
log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "Claims not valid")
return web.ErrNotAuthorized
}
// Validate that all the parameters we expect are correct, noteably, the
// expiry date, and not before claims should be verified.
if err := claims.Valid(); err != nil {
log.Error(c.SessionID, "auth : Midware", err, "Claims not valid")
return web.ErrNotAuthorized
}
// Add the claims to the context.
c.Ctx["claims"] = claims
log.Dev(c.SessionID, "auth : Midware", "Completed : Valid")
return h(c)
}
return f
}
return m, nil
}