123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- package logic
- import (
- "context"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/hex"
- "errors"
- "fmt"
- "strings"
- "time"
- "github.com/golang-jwt/jwt/v4"
- "github.com/gravitl/netmaker/db"
- "github.com/gravitl/netmaker/logger"
- "github.com/gravitl/netmaker/models"
- "github.com/gravitl/netmaker/schema"
- "github.com/gravitl/netmaker/servercfg"
- )
- var jwtSecretKey []byte
- // SetJWTSecret - sets the jwt secret on server startup
- func SetJWTSecret() {
- currentSecret, jwtErr := FetchJWTSecret()
- if jwtErr != nil {
- newValue := RandomString(64)
- jwtSecretKey = []byte(newValue) // 512 bit random password
- if err := StoreJWTSecret(string(jwtSecretKey)); err != nil {
- logger.FatalLog("something went wrong when configuring JWT authentication")
- }
- } else {
- jwtSecretKey = []byte(currentSecret)
- }
- }
- // CreateJWT func will used to create the JWT while signing in and signing out
- func CreateJWT(uuid string, macAddress string, network string) (response string, err error) {
- expirationTime := time.Now().Add(15 * time.Minute)
- claims := &models.Claims{
- ID: uuid,
- Network: network,
- MacAddress: macAddress,
- RegisteredClaims: jwt.RegisteredClaims{
- Issuer: "Netmaker",
- Subject: fmt.Sprintf("node|%s", uuid),
- IssuedAt: jwt.NewNumericDate(time.Now()),
- ExpiresAt: jwt.NewNumericDate(expirationTime),
- },
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- tokenString, err := token.SignedString(jwtSecretKey)
- if err == nil {
- return tokenString, nil
- }
- return "", err
- }
- // CreateUserJWT - creates a user jwt token
- func CreateUserAccessJwtToken(username string, role models.UserRoleID, d time.Time, tokenID string) (response string, err error) {
- claims := &models.UserClaims{
- UserName: username,
- Role: role,
- TokenType: models.AccessTokenType,
- Api: servercfg.GetAPIHost(),
- RegisteredClaims: jwt.RegisteredClaims{
- Issuer: "Netmaker",
- Subject: fmt.Sprintf("user|%s", username),
- IssuedAt: jwt.NewNumericDate(time.Now()),
- ExpiresAt: jwt.NewNumericDate(d),
- ID: tokenID,
- },
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- tokenString, err := token.SignedString(jwtSecretKey)
- if err == nil {
- return tokenString, nil
- }
- return "", err
- }
- // CreateUserJWT - creates a user jwt token
- func CreateUserJWT(username string, role models.UserRoleID, appName string) (response string, err error) {
- duration := GetJwtValidityDuration()
- if appName == NetclientApp || appName == NetmakerDesktopApp {
- duration = GetJwtValidityDurationForClients()
- }
- expirationTime := time.Now().Add(duration)
- claims := &models.UserClaims{
- UserName: username,
- Role: role,
- TokenType: models.UserIDTokenType,
- RegisteredClaims: jwt.RegisteredClaims{
- Issuer: "Netmaker",
- Subject: fmt.Sprintf("user|%s", username),
- IssuedAt: jwt.NewNumericDate(time.Now()),
- ExpiresAt: jwt.NewNumericDate(expirationTime),
- },
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- tokenString, err := token.SignedString(jwtSecretKey)
- if err == nil {
- return tokenString, nil
- }
- return "", err
- }
- // CreatePreAuthToken generate a jwt token to be used as intermediate
- // token after primary-factor authentication but before secondary-factor
- // authentication.
- func CreatePreAuthToken(username string) (string, error) {
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
- Issuer: "Netmaker",
- Subject: username,
- Audience: []string{"auth:mfa"},
- IssuedAt: jwt.NewNumericDate(time.Now()),
- ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
- })
- return token.SignedString(jwtSecretKey)
- }
- func GenerateOTPAuthURLSignature(url string) string {
- signer := hmac.New(sha256.New, jwtSecretKey)
- signer.Write([]byte(url))
- return hex.EncodeToString(signer.Sum(nil))
- }
- func VerifyOTPAuthURL(url, signature string) bool {
- signatureBytes, err := hex.DecodeString(signature)
- if err != nil {
- return false
- }
- signer := hmac.New(sha256.New, jwtSecretKey)
- signer.Write([]byte(url))
- return hmac.Equal(signatureBytes, signer.Sum(nil))
- }
- func GetUserNameFromToken(authtoken string) (username string, err error) {
- claims := &models.UserClaims{}
- var tokenSplit = strings.Split(authtoken, " ")
- var tokenString = ""
- if len(tokenSplit) < 2 {
- return "", Unauthorized_Err
- } else {
- tokenString = tokenSplit[1]
- }
- if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
- return MasterUser, nil
- }
- token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
- return jwtSecretKey, nil
- })
- if err != nil {
- return "", Unauthorized_Err
- }
- for _, aud := range claims.Audience {
- // token created for mfa cannot be used for
- // anything else.
- if aud == "auth:mfa" {
- return "", Unauthorized_Err
- }
- }
- if claims.TokenType == models.AccessTokenType {
- jti := claims.ID
- if jti != "" {
- a := schema.UserAccessToken{ID: jti}
- // check if access token is active
- err := a.Get(db.WithContext(context.TODO()))
- if err != nil {
- err = errors.New("token revoked")
- return "", err
- }
- a.LastUsed = time.Now().UTC()
- a.Update(db.WithContext(context.TODO()))
- }
- }
- if token != nil && token.Valid {
- var user *models.User
- // check that user exists
- user, err = GetUser(claims.UserName)
- if err != nil {
- return "", err
- }
- if user.UserName != "" {
- return user.UserName, nil
- }
- if user.PlatformRoleID != claims.Role {
- return "", Unauthorized_Err
- }
- err = errors.New("user does not exist")
- } else {
- err = Unauthorized_Err
- }
- return "", err
- }
- // VerifyUserToken func will used to Verify the JWT Token while using APIS
- func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin bool, err error) {
- claims := &models.UserClaims{}
- if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
- return MasterUser, true, true, nil
- }
- token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
- return jwtSecretKey, nil
- })
- if claims.TokenType == models.AccessTokenType {
- jti := claims.ID
- if jti != "" {
- a := schema.UserAccessToken{ID: jti}
- // check if access token is active
- err := a.Get(db.WithContext(context.TODO()))
- if err != nil {
- err = errors.New("token revoked")
- return "", false, false, err
- }
- a.LastUsed = time.Now().UTC()
- a.Update(db.WithContext(context.TODO()))
- }
- }
- if token != nil && token.Valid {
- var user *models.User
- // check that user exists
- user, err = GetUser(claims.UserName)
- if err != nil {
- return "", false, false, err
- }
- if user.UserName != "" {
- return user.UserName, user.PlatformRoleID == models.SuperAdminRole,
- user.PlatformRoleID == models.AdminRole, nil
- }
- err = errors.New("user does not exist")
- }
- return "", false, false, err
- }
- // VerifyHostToken - [hosts] Only
- func VerifyHostToken(tokenString string) (hostID string, mac string, network string, err error) {
- claims := &models.Claims{}
- // this may be a stupid way of serving up a master key
- // TODO: look into a different method. Encryption?
- if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
- return "mastermac", "", "", nil
- }
- token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
- return jwtSecretKey, nil
- })
- if token != nil {
- return claims.ID, claims.MacAddress, claims.Network, nil
- }
- return "", "", "", err
- }
|