Last active
August 29, 2015 14:15
-
-
Save dahernan/f7593c5b83073e99c0a2 to your computer and use it in GitHub Desktop.
Basic auth login route
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// example for Minimal Auth | |
// it does not compile is just as a guide for rolling your own | |
// some code for login by http post | |
func (a *AuthRoute) Login(w http.ResponseWriter, req *http.Request) { | |
var authForm map[string]string | |
err := RequestToJsonObject(req, &authForm) | |
if err != nil { | |
Render().JSON(w, http.StatusUnauthorized, nil) | |
return | |
} | |
email := authForm["email"] | |
pass := authForm["password"] | |
userId, err := a.userStore.Login(email, pass) | |
if err != nil { | |
Render().JSON(w, http.StatusUnauthorized, "Username or Password Invalid") | |
log.Printf("INFO: Failed attempt to login '%s' with error '%v'\n", email, err) | |
return | |
} | |
log.Println("INFO: user login", email, userId) | |
token, err := jwt.GenerateJWTToken(userId) | |
if err != nil { | |
Render().JSON(w, http.StatusInternalServerError, "Error while Signing Token :S") | |
log.Printf("ERROR: Token Signing error: %v\n", err) | |
return | |
} | |
authredis.SetSessionToken(token, userId) | |
Render().JSON(w, http.StatusOK, map[string]string{"token": token}) | |
} | |
// some code to validate the login in Redis | |
func (r *RedisRepository) Login(email, pass string) (string, error) { | |
id, err := r.UserByEmail(email) | |
if err != nil { | |
return "", err | |
} | |
userId := string(id) | |
if userId == "" { | |
return "", ErrUserNotFound | |
} | |
data, err := r.client.HMGet(userKey+userId, "Password", "Salt") | |
if err != nil { | |
return "", err | |
} | |
if string(data[0]) == "" { | |
return "", ErrUserNotFound | |
} | |
passStored := data[0] | |
salt := data[1] | |
hpass, err := HashPassword(pass, salt) | |
if err != nil { | |
return "", err | |
} | |
passOk := SecureCompare(hpass, passStored) | |
if !passOk { | |
return "", ErrWrongPassword | |
} | |
r.client.HSet(userKey+userId, "Lastlogin", strconv.FormatInt(time.Now().Unix(), 10)) | |
log.Println("INFO: User Login", userId, email) | |
return userId, nil | |
} | |
// "golang.org/x/crypto/scrypt" | |
func HashPassword(pass string, salt []byte) ([]byte, error) { | |
return scrypt.Key([]byte(pass), salt, 16384, 8, 1, 32) | |
} | |
//"crypto/subtle" | |
func SecureCompare(given, actual []byte) bool { | |
return subtle.ConstantTimeCompare(given, actual) == 1 | |
} | |
// auth middleware for negroni | |
func AuthMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | |
var userId, token string | |
var err error | |
auth := r.Header.Get("Authorization") | |
if auth == "" { | |
w.WriteHeader(http.StatusUnauthorized) | |
fmt.Fprintln(w, "Error while Validation Token!") | |
return | |
} | |
// API Auth "Basic " | |
if strings.HasPrefix(auth, "Basic ") { | |
userId, token, err = validateAPIToken(auth) | |
} else { | |
// Web Auth with JWT "Bearer " | |
userId, token, err = jwt.ValidateToken(r) | |
} | |
if err != nil { | |
w.WriteHeader(http.StatusUnauthorized) | |
fmt.Fprintln(w, err.Error()) | |
log.Printf("ERROR: Token Validation Error: %+v\n", err.Error()) | |
return | |
} | |
context.Set(r, TokenKey, token) | |
context.Set(r, UserKey, userId) | |
// gorilla context Clear | |
defer context.Clear(r) | |
next(w, r) | |
} | |
// Generate and Validate JWT with | |
// jwt "github.com/dgrijalva/jwt-go" | |
func GenerateJWTToken(userId string) (string, error) { | |
// create a signer for rsa 256 | |
t := jwt.New(jwt.GetSigningMethod("RS256")) | |
// set claims | |
t.Claims["exp"] = time.Now().Add(time.Minute * 60).Unix() | |
t.Claims["iat"] = time.Now().Unix() | |
t.Claims["sub"] = userId | |
tokenString, err := t.SignedString([]byte(keys.Private)) | |
return tokenString, err | |
} | |
func ValidateToken(r *http.Request) (string, string, error) { | |
token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) { | |
return []byte(keys.Public), nil | |
}) | |
if err != nil { | |
switch err.(type) { | |
case *jwt.ValidationError: | |
vErr := err.(*jwt.ValidationError) | |
switch vErr.Errors { | |
case jwt.ValidationErrorExpired: | |
log.Printf("ERROR: JWT Token Expired: %+v\n", vErr.Errors) | |
return "", "", ErrTokenExpired | |
default: | |
log.Printf("ERROR: JWT Token ValidationError: %+v\n", vErr.Errors) | |
return "", "", ErrTokenValidation | |
} | |
} | |
log.Printf("ERROR: Token parse error: %v\n", err) | |
return "", "", ErrTokenParse | |
} | |
if !token.Valid { | |
log.Printf("ERROR: Token Invalid trying to access: %v\n", err) | |
return "", "", ErrTokenInvalid | |
} | |
// otherwise is a valid token | |
userId := token.Claims["sub"].(string) | |
//log.Printf("INFO: user '%s' accesed with a Valid token", userId) | |
return userId, token.Raw, nil | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment