Improve error handling in authentication
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Peter Stuifzand 2019-03-23 21:29:48 +01:00
parent 2bf3ce7aa4
commit ed90ebbdd3
Signed by: peter
GPG Key ID: 374322D56E5209E8
4 changed files with 55 additions and 32 deletions

View File

@ -9,90 +9,101 @@ import (
"time" "time"
"github.com/gomodule/redigo/redis" "github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
"p83.nl/go/ekster/pkg/auth" "p83.nl/go/ekster/pkg/auth"
) )
var authHeaderRegex = regexp.MustCompile("^Bearer (.+)$") var authHeaderRegex = regexp.MustCompile("^Bearer (.+)$")
func (b *memoryBackend) cachedCheckAuthToken(conn redis.Conn, header string, r *auth.TokenResponse) bool { func (b *memoryBackend) cachedCheckAuthToken(conn redis.Conn, header string, r *auth.TokenResponse) (bool, error) {
tokens := authHeaderRegex.FindStringSubmatch(header) tokens := authHeaderRegex.FindStringSubmatch(header)
if len(tokens) != 2 { if len(tokens) != 2 {
log.Println("No token found in the header") return false, fmt.Errorf("could not find token in header")
return false
} }
key := fmt.Sprintf("token:%s", tokens[1]) key := fmt.Sprintf("token:%s", tokens[1])
authorized, err := getCachedValue(conn, key, r) authorized, err := getCachedValue(conn, key, r)
if err != nil { if err != nil {
log.Println(err) log.Printf("could not get cached auth token value: %v", err)
} }
if authorized { if authorized {
return true return true, nil
}
authorized, err = b.checkAuthToken(header, r)
if err != nil {
return false, errors.Wrap(err, "could not check auth token")
} }
authorized = b.checkAuthToken(header, r)
if authorized { if authorized {
err = setCachedTokenResponseValue(conn, key, r) err = setCachedTokenResponseValue(conn, key, r)
if err != nil { if err != nil {
log.Println(err) log.Printf("could not set cached token response value: %v", err)
}
return true
} }
return authorized return true, nil
} }
func (b *memoryBackend) checkAuthToken(header string, token *auth.TokenResponse) bool { return authorized, nil
log.Println("Checking auth token") }
func (b *memoryBackend) checkAuthToken(header string, token *auth.TokenResponse) (bool, error) {
tokenEndpoint := b.TokenEndpoint tokenEndpoint := b.TokenEndpoint
req, err := buildValidateAuthTokenRequest(tokenEndpoint, header) req, err := buildValidateAuthTokenRequest(tokenEndpoint, header)
if err != nil { if err != nil {
return false return false, err
} }
client := http.Client{} client := http.Client{}
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
log.Println(err) return false, err
return false
} }
defer res.Body.Close() defer func() {
err := res.Body.Close()
if err != nil {
log.Printf("could not close http response body: %v", err)
}
}()
if res.StatusCode < 200 || res.StatusCode >= 300 { if res.StatusCode < 200 || res.StatusCode >= 300 {
log.Printf("HTTP StatusCode when verifying token: %d\n", res.StatusCode) return false, fmt.Errorf("got unsuccessfull http status code while verifying token: %d", res.StatusCode)
return false
} }
dec := json.NewDecoder(res.Body) dec := json.NewDecoder(res.Body)
err = dec.Decode(&token) err = dec.Decode(&token)
if err != nil { if err != nil {
log.Printf("Error in json object: %v", err) return false, errors.Wrap(err, "could not decode json body")
return false
} }
log.Println("Auth Token: Success") return true, nil
return true
} }
func buildValidateAuthTokenRequest(tokenEndpoint string, header string) (*http.Request, error) { func buildValidateAuthTokenRequest(tokenEndpoint string, header string) (*http.Request, error) {
req, err := http.NewRequest("GET", tokenEndpoint, nil) req, err := http.NewRequest("GET", tokenEndpoint, nil)
if err != nil {
return nil, errors.Wrap(err, "could not create a new request")
}
req.Header.Add("Authorization", header) req.Header.Add("Authorization", header)
req.Header.Add("Accept", "application/json") req.Header.Add("Accept", "application/json")
return req, err
return req, nil
} }
// setCachedTokenResponseValue remembers the value of the auth token response in redis // setCachedTokenResponseValue remembers the value of the auth token response in redis
func setCachedTokenResponseValue(conn redis.Conn, key string, r *auth.TokenResponse) error { func setCachedTokenResponseValue(conn redis.Conn, key string, r *auth.TokenResponse) error {
_, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(r)...) _, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(r)...)
if err != nil { if err != nil {
return fmt.Errorf("error while setting token: %v", err) return errors.Wrap(err, "could not remember token")
}
_, err = conn.Do("EXPIRE", key, uint64(10*time.Minute/time.Second))
if err != nil {
return errors.Wrap(err, "could not set expiration for token")
} }
conn.Do("EXPIRE", key, uint64(10*time.Minute/time.Second))
return nil return nil
} }
@ -100,7 +111,7 @@ func setCachedTokenResponseValue(conn redis.Conn, key string, r *auth.TokenRespo
func getCachedValue(conn redis.Conn, key string, r *auth.TokenResponse) (bool, error) { func getCachedValue(conn redis.Conn, key string, r *auth.TokenResponse) (bool, error) {
values, err := redis.Values(conn.Do("HGETALL", key)) values, err := redis.Values(conn.Do("HGETALL", key))
if err != nil { if err != nil {
return false, fmt.Errorf("error while getting value from backend: %v", err) return false, errors.Wrap(err, "could not get value from backend")
} }
if len(values) > 0 { if len(values) > 0 {

View File

@ -127,7 +127,7 @@ func getSessionCookie(w http.ResponseWriter, r *http.Request) string {
} }
http.SetCookie(w, newCookie) http.SetCookie(w, newCookie)
} else { } else if err == nil {
sessionVar = c.Value sessionVar = c.Value
} }
@ -295,6 +295,9 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err == http.ErrNoCookie { if err == http.ErrNoCookie {
http.Redirect(w, r, "/", 302) http.Redirect(w, r, "/", 302)
return return
} else if err != nil {
http.Error(w, "could not read cookie", 500)
return
} }
sessionVar := c.Value sessionVar := c.Value

View File

@ -74,7 +74,11 @@ func WithAuth(handler http.Handler, b *memoryBackend) http.Handler {
var token auth.TokenResponse var token auth.TokenResponse
if !b.AuthTokenAccepted(authorization, &token) { authorized, err := b.AuthTokenAccepted(authorization, &token)
if err != nil {
log.Printf("token not accepted: %v", err)
}
if !authorized {
log.Printf("Token could not be validated") log.Printf("Token could not be validated")
http.Error(w, "Can't validate token", 403) http.Error(w, "Can't validate token", 403)
return return

View File

@ -67,9 +67,14 @@ func (f *fetch2) Fetch(url string) (*http.Response, error) {
return Fetch2(url) return Fetch2(url)
} }
func (b *memoryBackend) AuthTokenAccepted(header string, r *auth.TokenResponse) bool { func (b *memoryBackend) AuthTokenAccepted(header string, r *auth.TokenResponse) (bool, error) {
conn := b.pool.Get() conn := b.pool.Get()
defer conn.Close() defer func() {
err := conn.Close()
if err != nil {
log.Printf("could not close redis connection: %v", err)
}
}()
return b.cachedCheckAuthToken(conn, header, r) return b.cachedCheckAuthToken(conn, header, r)
} }
@ -495,8 +500,8 @@ func (b *memoryBackend) PreviewURL(previewURL string) (microsub.Timeline, error)
} }
func (b *memoryBackend) MarkRead(channel string, uids []string) error { func (b *memoryBackend) MarkRead(channel string, uids []string) error {
timeline := b.getTimeline(channel) tl := b.getTimeline(channel)
err := timeline.MarkRead(uids) err := tl.MarkRead(uids)
if err != nil { if err != nil {
return err return err