This repository has been archived by the owner on Dec 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathmiddleware_auth.go
151 lines (124 loc) · 3.29 KB
/
middleware_auth.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package rye
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"strings"
jwt "github.com/dgrijalva/jwt-go"
)
/*
NewMiddlewareAuth creates a new middleware to extract the Authorization header
from a request and validate it. It accepts a func of type AuthFunc which is
used to do the credential validation.
An AuthFuncs for Basic auth and JWT are provided here.
Example usage:
routes.Handle("/some/route", myMWHandler.Handle(
[]rye.Handler{
rye.NewMiddlewareAuth(rye.NewBasicAuthFunc(map[string]string{
"user1": "my_password",
})),
yourHandler,
})).Methods("POST")
*/
type AuthFunc func(context.Context, string) *Response
func NewMiddlewareAuth(authFunc AuthFunc) func(rw http.ResponseWriter, req *http.Request) *Response {
return func(rw http.ResponseWriter, r *http.Request) *Response {
auth := r.Header.Get("Authorization")
if auth == "" {
return &Response{
Err: errors.New("unauthorized: no authentication provided"),
StatusCode: http.StatusUnauthorized,
}
}
return authFunc(r.Context(), auth)
}
}
/***********
Basic Auth
***********/
func NewBasicAuthFunc(userPass map[string]string) AuthFunc {
return basicAuth(userPass).authenticate
}
type basicAuth map[string]string
const AUTH_USERNAME_KEY = "request-username"
// basicAuth.authenticate meets the AuthFunc type
func (b basicAuth) authenticate(ctx context.Context, auth string) *Response {
errResp := &Response{
Err: errors.New("unauthorized: invalid authentication provided"),
StatusCode: http.StatusUnauthorized,
}
// parse the Authorization header
u, p, ok := parseBasicAuth(auth)
if !ok {
return errResp
}
// get the password
pass, ok := b[u]
if !ok {
return errResp
}
// compare the password
if pass != p {
return errResp
}
// add username to the context
return &Response{
Context: context.WithValue(ctx, AUTH_USERNAME_KEY, u),
}
}
const basicPrefix = "Basic "
// parseBasicAuth parses an HTTP Basic Authentication string.
// taken from net/http/request.go
func parseBasicAuth(auth string) (username, password string, ok bool) {
if !strings.HasPrefix(auth, basicPrefix) {
return
}
c, err := base64.StdEncoding.DecodeString(auth[len(basicPrefix):])
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}
/****
JWT
****/
type jwtAuth struct {
secret string
}
func NewJWTAuthFunc(secret string) AuthFunc {
j := &jwtAuth{secret: secret}
return j.authenticate
}
const bearerPrefix = "Bearer "
func (j *jwtAuth) authenticate(ctx context.Context, auth string) *Response {
// Remove 'Bearer' prefix
if !strings.HasPrefix(auth, bearerPrefix) && !strings.HasPrefix(auth, strings.ToLower(bearerPrefix)) {
return &Response{
Err: errors.New("unauthorized: invalid authentication provided"),
StatusCode: http.StatusUnauthorized,
}
}
token := auth[len(bearerPrefix):]
_, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method")
}
return []byte(j.secret), nil
})
if err != nil {
return &Response{
Err: err,
StatusCode: http.StatusUnauthorized,
}
}
return &Response{
Context: context.WithValue(ctx, CONTEXT_JWT, token),
}
}