Skip to content
Snippets Groups Projects
Select Git revision
  • 4068ac11908fe3e0868e85e1269af1a600baf79c
  • main default protected
  • v1.298.0
  • v1.297.0
  • v1.296.0
  • v1.295.0
  • v1.294.0
  • v1.293.0
  • v1.292.0
  • v1.291.0
  • v1.290.0
  • v1.289.0
  • v1.288.0
  • v1.287.0
  • v1.286.0
  • v1.285.0
  • v1.284.0
  • v1.283.0
  • v1.282.0
  • v1.281.0
  • v1.280.0
  • v1.279.0
22 results

jwt.go

Blame
  • jwt.go 4.90 KiB
    package auth
    
    import (
    	"encoding/json"
    	"github.com/golang-jwt/jwt/v4"
    	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/date_utils"
    	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
    	"net/http"
    	"time"
    )
    
    type JsonWebToken struct {
    	UserID     string    `json:"user_id"`
    	ProviderID int64     `json:"provider_id,omitempty"`
    	ExpiryDate time.Time `json:"expiry_date"`
    }
    
    // GenerateJWTWithSessionToken first signs the session token with the secret key, then takes the payload and generates a
    // signed JWT using the resulting signed session token
    func GenerateJWTWithSessionToken(payload JsonWebToken, secretKey string, sessionToken string) (string, error) {
    	signedSessionToken := SignSessionTokenWithKey(secretKey, sessionToken)
    	return GenerateJWT(payload, signedSessionToken)
    }
    
    // GenerateJWT takes the payload and generates a signed JWT using the provided secret
    func GenerateJWT(payload JsonWebToken, secretKey string) (string, error) {
    	// Convert the JsonWebToken to a map[string]interface{}
    	tokenBytes, err := json.Marshal(payload)
    	if err != nil {
    		return "", err
    	}
    
    	tokenMap := make(map[string]interface{})
    	err = json.Unmarshal(tokenBytes, &tokenMap)
    	if err != nil {
    		return "", err
    	}
    
    	// Create the signed token
    	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(tokenMap))
    	tokenString, err := token.SignedString([]byte(secretKey))
    	if err != nil {
    		return "", err
    	}
    
    	return tokenString, nil
    }
    
    func getJsonWebTokenFromTokenClaims(token *jwt.Token, checkValidity bool) (JsonWebToken, error) {
    	if token == nil {
    		return JsonWebToken{}, errors.Error("could not get token from token string")
    	}
    
    	claims, ok := token.Claims.(jwt.MapClaims)
    	if !ok || (checkValidity && token.Valid == false) {
    		return JsonWebToken{}, errors.Error("invalid token")
    	}
    
    	// Convert the MapClaims to a JsonWebToken
    	claimsBytes, err := json.Marshal(claims)
    	if err != nil {
    		return JsonWebToken{}, err
    	}
    
    	var jsonWebToken JsonWebToken
    	err = json.Unmarshal(claimsBytes, &jsonWebToken)
    	if err != nil {
    		return JsonWebToken{}, err
    	}
    
    	return jsonWebToken, nil
    }
    
    // ValidateJWTWithSessionToken first signs the session token using the secret key, then parses the JWT and validates
    // that it is signed correctly
    func ValidateJWTWithSessionToken(jsonWebTokenString string, secretKey string, sessionToken string) (JsonWebToken, error) {
    	// Sign the session token with the secret key - this prevents the JWT from being used by other sessions
    	signedSecret := SignSessionTokenWithKey(secretKey, sessionToken)
    	return ValidateJWT(jsonWebTokenString, signedSecret)
    }
    
    // ValidateJWT parses the JWT and validates that it is signed correctly
    func ValidateJWT(jsonWebTokenString string, secretKey string) (JsonWebToken, error) {
    	// Validate the
    	token, err := jwt.Parse(jsonWebTokenString, func(token *jwt.Token) (interface{}, error) {
    		// Validate the signing method
    		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
    			return nil, errors.Errorf("unexpected signing method: %v", token.Header["alg"])
    		}
    
    		return []byte(secretKey), nil
    	})
    	if err != nil {
    		return JsonWebToken{}, err
    	}
    
    	jsonWebToken, err := getJsonWebTokenFromTokenClaims(token, true)
    	if err != nil {
    		return JsonWebToken{}, err
    	}
    
    	// Validate the expiry date
    	if jsonWebToken.ExpiryDate.Before(date_utils.CurrentDate()) {
    		return jsonWebToken, errors.Error("token has expired")
    	}
    
    	return jsonWebToken, nil
    }
    
    // LoginWithPassword checks that the provided password is correct. If the password is correct, a signed JWT is returned
    // using the provided encryption key.
    func LoginWithPassword(password string, hashedPassword string, jsonWebToken JsonWebToken, jwtEncryptionKey string) (string, error) {
    	if PasswordIsCorrect(password, hashedPassword) {
    		return GenerateJWT(jsonWebToken, jwtEncryptionKey)
    	}
    
    	return "", errors.HTTPWithMsg(http.StatusBadRequest, "password is incorrect")
    }
    
    // LoginSessionWithPassword checks that the provided password is correct. If the password is correct, the session token
    // is signed using the secret key, and a JWT is returned using the signed session token
    func LoginSessionWithPassword(password string, hashedPassword string, jsonWebToken JsonWebToken, secretKey string, sessionToken string) (string, error) {
    	if PasswordIsCorrect(password, hashedPassword) {
    		return GenerateJWTWithSessionToken(jsonWebToken, secretKey, sessionToken)
    	}
    
    	return "", errors.HTTPWithMsg(http.StatusBadRequest, "password is incorrect")
    }
    
    // GetUserIDFromJWTWithoutValidation gets the userID from the jsonWebTokenString without validating the signature.
    // Successful execution of this function DOES NOT indicate that the JWT is valid in any way.
    func GetUserIDFromJWTWithoutValidation(jsonWebTokenString string) string {
    	token, _, err := jwt.NewParser().ParseUnverified(jsonWebTokenString, jwt.MapClaims{})
    	if err != nil {
    		return ""
    	}
    
    	jsonWebToken, err := getJsonWebTokenFromTokenClaims(token, false)
    	if err != nil {
    		return ""
    	}
    	return jsonWebToken.UserID
    }