package auth import ( "fmt" "net/http" "strings" jwt "github.com/dgrijalva/jwt-go" "golang.org/x/crypto/bcrypt" ) var ( // ErrInvalidPassword returned when password and hash don't match ErrInvalidPassword = fmt.Errorf("Invalid password") // ErrInvalidSecret returned when cookie's secret is don't match ErrInvalidSecret = fmt.Errorf("Invalid secret") // ErrInvalidToken returned when the jwt token is invalid ErrInvalidToken = fmt.Errorf("Invalid token") ) // UserBackend interface for user backend type UserBackend interface { GetUser(username string) (User, error) } // User interface for user type User interface { GetName() string GetHash() string HasRole(string) bool IsAdmin() bool IsActivated() bool } // Authorizer handle sesssion type Authorizer struct { Params } // Params for Authorizer creation type Params struct { Backend UserBackend Pepper string Cost int Secret string } // New Authorizer pepper is like a salt but not stored in database, // cost is the bcrypt cost for hashing the password func New(params Params) *Authorizer { return &Authorizer{ Params: params, } } // GenHash generates a new hash from a password func (a *Authorizer) GenHash(password string) (string, error) { b, err := bcrypt.GenerateFromPassword([]byte(password+a.Pepper), a.Cost) if err != nil { return "", err } return string(b), nil } // Login cheks password and creates a jwt token func (a *Authorizer) Login(rw http.ResponseWriter, req *http.Request, username, password string) (User, error) { u, err := a.Backend.GetUser(username) if err != nil { return nil, err } // Compare the password err = bcrypt.CompareHashAndPassword([]byte(u.GetHash()), []byte(password+a.Pepper)) if err != nil { return nil, ErrInvalidPassword } return u, nil } // CurrentUser returns the logged in username from session and verifies the token func (a *Authorizer) CurrentUser(rw http.ResponseWriter, req *http.Request) (User, error) { var tokenStr string h := req.Header.Get("Authorization") if h != "" { // Get the token from the header tokenStr = strings.Replace(h, "Bearer ", "", -1) } // If the token string is still empty, check in the cookies if tokenStr == "" { tokenCookie, err := req.Cookie("token") if err != nil || tokenCookie == nil { return nil, nil } tokenStr = tokenCookie.Value } // No user logged if tokenStr == "" { return nil, nil } // Keyfunc to decode the token var keyfunc jwt.Keyfunc = func(token *jwt.Token) (interface{}, error) { return []byte(a.Secret), nil } var tokenClaims struct { Username string `json:"username"` jwt.StandardClaims } token, err := jwt.ParseWithClaims(tokenStr, &tokenClaims, keyfunc) if err != nil { return nil, err } // Check the token validity if !token.Valid { return nil, ErrInvalidToken } // Get the user u, err := a.Backend.GetUser(tokenClaims.Username) if err != nil { return nil, err } return u, nil }