Skip to content

Commit 69a9767

Browse files
committed
Adding http basic authentication middleware
Signed-off-by: Vishal Rana <vr@labstack.com>
1 parent 8ace8e2 commit 69a9767

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

echo.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const (
7979

8080
ApplicationJSON = "application/json"
8181
ApplicationProtobuf = "application/protobuf"
82+
ApplicationMsgpack = "application/msgpack"
8283
TextPlain = "text/plain"
8384
TextHTML = "text/html"
8485
ApplicationForm = "application/x-www-form-urlencoded"
@@ -92,6 +93,7 @@ const (
9293
ContentDisposition = "Content-Disposition"
9394
ContentLength = "Content-Length"
9495
ContentType = "Content-Type"
96+
Authorization = "Authorization"
9597
)
9698

9799
var (

middleware/auth.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package middleware
2+
3+
import (
4+
"encoding/base64"
5+
"github.com/labstack/echo"
6+
"net/http"
7+
)
8+
9+
type (
10+
AuthFunc func(string, string) bool
11+
)
12+
13+
const (
14+
Basic = "Basic"
15+
)
16+
17+
// BasicAuth provides HTTP basic authentication middleware.
18+
func BasicAuth(fn AuthFunc) echo.HandlerFunc {
19+
return func(c *echo.Context) (he *echo.HTTPError) {
20+
auth := c.Request.Header.Get(echo.Authorization)
21+
i := 0
22+
l := len(Basic)
23+
he = &echo.HTTPError{Code: http.StatusUnauthorized}
24+
25+
for ; i < len(auth); i++ {
26+
c := auth[i]
27+
// Ignore empty spaces
28+
if c == ' ' {
29+
continue
30+
}
31+
32+
// Check scheme
33+
if i < l {
34+
// Ignore case
35+
if i == 0 {
36+
if c != Basic[i] && c != 'b' {
37+
return
38+
}
39+
} else {
40+
if c != Basic[i] {
41+
return
42+
}
43+
}
44+
} else {
45+
// Extract credentials
46+
b, err := base64.StdEncoding.DecodeString(auth[i:])
47+
if err != nil {
48+
return
49+
}
50+
cred := string(b)
51+
for i := 0; i < len(cred); i++ {
52+
if cred[i] == ':' {
53+
// Verify credentials
54+
if !fn(cred[:i], cred[i+1:]) {
55+
return
56+
}
57+
return nil
58+
}
59+
}
60+
}
61+
}
62+
return nil
63+
}
64+
}

middleware/auth_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package middleware
2+
3+
import (
4+
"encoding/base64"
5+
"github.com/labstack/echo"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
)
10+
11+
func TestBasicAuth(t *testing.T) {
12+
req, _ := http.NewRequest(echo.POST, "/", nil)
13+
res := &echo.Response{Writer: httptest.NewRecorder()}
14+
c := echo.NewContext(req, res, echo.New())
15+
fn := func(u, p string) bool {
16+
if u == "joe" && p == "secret" {
17+
return true
18+
}
19+
return false
20+
}
21+
b := BasicAuth(fn)
22+
23+
//-------------------
24+
// Valid credentials
25+
//-------------------
26+
27+
auth := Basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:secret"))
28+
req.Header.Set(echo.Authorization, auth)
29+
if b(c) != nil {
30+
t.Error("basic auth should pass")
31+
}
32+
33+
// Case insensitive
34+
auth = "basic " + base64.StdEncoding.EncodeToString([]byte("joe:secret"))
35+
req.Header.Set(echo.Authorization, auth)
36+
if b(c) != nil {
37+
t.Error("basic auth should ignore case and pass")
38+
}
39+
40+
//---------------------
41+
// Invalid credentials
42+
//---------------------
43+
44+
auth = Basic + " " + base64.StdEncoding.EncodeToString([]byte(" joe: secret"))
45+
req.Header.Set(echo.Authorization, auth)
46+
b = BasicAuth(fn)
47+
if b(c) == nil {
48+
t.Error("basic auth should fail")
49+
}
50+
51+
// Invalid scheme
52+
auth = "foo " + base64.StdEncoding.EncodeToString([]byte(" joe: secret"))
53+
req.Header.Set(echo.Authorization, auth)
54+
b = BasicAuth(fn)
55+
if b(c) == nil {
56+
t.Error("basic auth should fail for invalid scheme")
57+
}
58+
}

middleware/middleware.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package middleware

0 commit comments

Comments
 (0)