2020-03-07 14:06:57 +03:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2020-03-17 23:04:16 +03:00
|
|
|
"fmt"
|
2020-03-07 14:06:57 +03:00
|
|
|
"net/http"
|
2020-03-17 23:04:16 +03:00
|
|
|
"net/url"
|
2020-03-07 14:06:57 +03:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/micro/go-micro/v2/auth"
|
|
|
|
)
|
|
|
|
|
2020-03-17 19:03:49 +03:00
|
|
|
var (
|
|
|
|
// DefaultExcludes is the paths which are allowed by default
|
|
|
|
DefaultExcludes = []string{"/favicon.ico"}
|
|
|
|
)
|
|
|
|
|
2020-03-07 14:06:57 +03:00
|
|
|
// CombinedAuthHandler wraps a server and authenticates requests
|
|
|
|
func CombinedAuthHandler(h http.Handler) http.Handler {
|
|
|
|
return authHandler{
|
|
|
|
handler: h,
|
|
|
|
auth: auth.DefaultAuth,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type authHandler struct {
|
|
|
|
handler http.Handler
|
|
|
|
auth auth.Auth
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// BearerScheme is the prefix in the auth header
|
|
|
|
BearerScheme = "Bearer "
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
2020-03-17 22:24:10 +03:00
|
|
|
// Extract the token from the request
|
|
|
|
var token string
|
|
|
|
if header := req.Header.Get("Authorization"); len(header) > 0 {
|
|
|
|
// Extract the auth token from the request
|
|
|
|
if strings.HasPrefix(header, BearerScheme) {
|
|
|
|
token = header[len(BearerScheme):]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Get the token out the cookies if not provided in headers
|
|
|
|
if c, err := req.Cookie("micro-token"); err == nil && c != nil {
|
2020-03-23 19:19:30 +03:00
|
|
|
token = strings.TrimPrefix(c.Value, auth.TokenCookieName+"=")
|
2020-03-17 22:24:10 +03:00
|
|
|
req.Header.Set("Authorization", BearerScheme+token)
|
|
|
|
}
|
|
|
|
}
|
2020-03-07 14:06:57 +03:00
|
|
|
|
2020-03-23 19:19:30 +03:00
|
|
|
// Get the account using the token, fallback to a blank account
|
|
|
|
// since some endpoints can be unauthenticated, so the lack of an
|
|
|
|
// account doesn't necesserially mean a forbidden request
|
|
|
|
acc, err := h.auth.Inspect(token)
|
|
|
|
if err != nil {
|
|
|
|
acc = &auth.Account{}
|
2020-03-07 14:06:57 +03:00
|
|
|
}
|
2020-03-23 19:19:30 +03:00
|
|
|
err = h.auth.Verify(acc, &auth.Resource{
|
|
|
|
Type: "service",
|
|
|
|
Name: "go.micro.web",
|
|
|
|
Endpoint: req.URL.Path,
|
|
|
|
})
|
2020-03-17 19:03:49 +03:00
|
|
|
|
2020-03-23 19:19:30 +03:00
|
|
|
// The account has the necessary permissions to access the
|
|
|
|
// resource
|
|
|
|
if err == nil {
|
|
|
|
h.handler.ServeHTTP(w, req)
|
|
|
|
return
|
2020-03-07 14:06:57 +03:00
|
|
|
}
|
|
|
|
|
2020-03-23 19:19:30 +03:00
|
|
|
// The account is set, but they don't have enough permissions,
|
|
|
|
// hence we 403.
|
|
|
|
if len(acc.ID) > 0 {
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
2020-03-07 14:06:57 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is no auth login url set, 401
|
2020-03-23 19:19:30 +03:00
|
|
|
loginURL := h.auth.Options().LoginURL
|
2020-03-07 14:06:57 +03:00
|
|
|
if loginURL == "" {
|
2020-03-23 19:19:30 +03:00
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
2020-03-16 13:30:56 +03:00
|
|
|
return
|
2020-03-07 14:06:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Redirect to the login path
|
2020-03-17 23:04:16 +03:00
|
|
|
params := url.Values{"redirect_to": {req.URL.Path}}
|
|
|
|
loginWithRedirect := fmt.Sprintf("%v?%v", loginURL, params.Encode())
|
|
|
|
http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect)
|
2020-03-07 14:06:57 +03:00
|
|
|
}
|