jwt.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package otjwt
  2. import (
  3. "context"
  4. "crypto/rsa"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "strings"
  9. "sync"
  10. "time"
  11. "github.com/MicahParks/keyfunc/v3"
  12. authTypes "github.com/OliveTin/OliveTin/internal/auth/authpublic"
  13. "github.com/OliveTin/OliveTin/internal/config"
  14. "github.com/golang-jwt/jwt/v5"
  15. log "github.com/sirupsen/logrus"
  16. )
  17. func parseJwtToken(cfg *config.Config, jwtString string) (*jwt.Token, error) {
  18. if cfg.AuthJwtCertsURL != "" {
  19. return parseJwtTokenWithRemoteKey(cfg, jwtString)
  20. }
  21. if cfg.AuthJwtPubKeyPath != "" {
  22. return parseJwtTokenWithLocalKey(cfg, jwtString)
  23. }
  24. if cfg.AuthJwtHmacSecret == "" {
  25. return nil, errors.New("no JWT authentication method configured")
  26. }
  27. return parseJwtTokenWithHMAC(cfg, jwtString)
  28. }
  29. func getClaimsFromJwtToken(cfg *config.Config, jwtString string) (jwt.MapClaims, error) {
  30. token, err := parseJwtToken(cfg, jwtString)
  31. if err != nil {
  32. log.Errorf("jwt parse failure: %v", err)
  33. return nil, errors.New("jwt parse failure")
  34. }
  35. if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
  36. return claims, nil
  37. } else {
  38. return nil, errors.New("jwt token isn't valid")
  39. }
  40. }
  41. func parseJwtTokenWithRemoteKey(cfg *config.Config, jwtToken string) (*jwt.Token, error) {
  42. err := initJwks(cfg)
  43. if err != nil {
  44. log.Errorf("jwt init JWKS failure: %v", err)
  45. return nil, err
  46. }
  47. return jwt.Parse(jwtToken, jwksVerifier.Keyfunc, jwt.WithAudience(cfg.AuthJwtAud))
  48. }
  49. var (
  50. pubKeyBytes []byte = nil
  51. pubKey *rsa.PublicKey
  52. loadedKeyPath string
  53. jwksVerifier keyfunc.Keyfunc
  54. jwksOnce sync.Once
  55. jwksInitErr error
  56. localKeyMutex sync.RWMutex
  57. localKeyInitErr error
  58. )
  59. func initJwks(cfg *config.Config) error {
  60. jwksOnce.Do(func() {
  61. if cfg.AuthJwtCertsURL != "" {
  62. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  63. defer cancel()
  64. var err error
  65. jwksVerifier, err = keyfunc.NewDefaultCtx(ctx, []string{
  66. cfg.AuthJwtCertsURL,
  67. })
  68. if err != nil {
  69. log.Errorf("Init JWKS Failure: %v", err)
  70. jwksInitErr = err
  71. }
  72. }
  73. })
  74. return jwksInitErr
  75. }
  76. func loadPublicKeyFromFile(keyPath string) error {
  77. keyBytes, err := os.ReadFile(keyPath)
  78. if err != nil {
  79. return fmt.Errorf("couldn't read public key from file %s", keyPath)
  80. }
  81. parsedKey, err := jwt.ParseRSAPublicKeyFromPEM(keyBytes)
  82. if err != nil {
  83. return fmt.Errorf("error parsing public key object (from %s)", keyPath)
  84. }
  85. pubKeyBytes = keyBytes
  86. pubKey = parsedKey
  87. loadedKeyPath = keyPath
  88. localKeyInitErr = nil
  89. return nil
  90. }
  91. func isKeyLoadedForPath(keyPath string) bool {
  92. return pubKeyBytes != nil && loadedKeyPath == keyPath
  93. }
  94. func readLocalPublicKeyWithLock(keyPath string) error {
  95. localKeyMutex.RLock()
  96. alreadyLoaded := isKeyLoadedForPath(keyPath)
  97. localKeyMutex.RUnlock()
  98. if alreadyLoaded {
  99. return nil
  100. }
  101. localKeyMutex.Lock()
  102. defer localKeyMutex.Unlock()
  103. if isKeyLoadedForPath(keyPath) {
  104. return nil
  105. }
  106. localKeyInitErr = loadPublicKeyFromFile(keyPath)
  107. return localKeyInitErr
  108. }
  109. func readLocalPublicKey(cfg *config.Config) error {
  110. if cfg.AuthJwtPubKeyPath == "" {
  111. return errors.New("no JWT public key path configured")
  112. }
  113. return readLocalPublicKeyWithLock(cfg.AuthJwtPubKeyPath)
  114. }
  115. func parseJwtTokenWithLocalKey(cfg *config.Config, jwtString string) (*jwt.Token, error) {
  116. err := readLocalPublicKey(cfg)
  117. if err != nil {
  118. return nil, err
  119. }
  120. return jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
  121. if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
  122. return nil, fmt.Errorf("parseJwt expected token algorithm RSA but got: %v", token.Header["alg"])
  123. }
  124. return pubKey, nil
  125. })
  126. }
  127. // Hash-based Message Authentication Code
  128. func parseJwtTokenWithHMAC(cfg *config.Config, jwtString string) (*jwt.Token, error) {
  129. return jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
  130. if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
  131. return nil, fmt.Errorf("parseJwt expected token algorithm HMAC but got: %v", token.Header["alg"])
  132. }
  133. return []byte(cfg.AuthJwtHmacSecret), nil
  134. })
  135. }
  136. func lookupClaimValueOrDefault(claims jwt.MapClaims, key string, def string) string {
  137. if val, ok := claims[key]; ok {
  138. return fmt.Sprintf("%s", val)
  139. } else {
  140. return def
  141. }
  142. }
  143. func CheckUserFromJwtCookie(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser {
  144. cookie, err := context.Request.Cookie(context.Config.AuthJwtCookieName)
  145. if err != nil {
  146. log.Debugf("jwt cookie check %v name: %v", err, context.Config.AuthJwtCookieName)
  147. return nil
  148. }
  149. return parseJwt(context.Config, cookie.Value)
  150. }
  151. func CheckUserFromJwtHeader(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser {
  152. header := context.Request.Header.Get(context.Config.AuthJwtHeader)
  153. if header == "" {
  154. return nil
  155. }
  156. token := strings.TrimPrefix(header, "Bearer ")
  157. token = strings.TrimSpace(token)
  158. return parseJwt(context.Config, token)
  159. }
  160. func parseJwt(cfg *config.Config, token string) *authTypes.AuthenticatedUser {
  161. claims, err := getClaimsFromJwtToken(cfg, token)
  162. if err != nil {
  163. log.Warnf("jwt claim error: %+v", err)
  164. return nil
  165. }
  166. if cfg.InsecureAllowDumpJwtClaims {
  167. log.Debugf("JWT Claims %+v", claims)
  168. }
  169. user := &authTypes.AuthenticatedUser{
  170. Username: lookupClaimValueOrDefault(claims, cfg.AuthJwtClaimUsername, ""),
  171. UsergroupLine: parseGroupClaim(cfg.AuthJwtClaimUserGroup, claims),
  172. Provider: "jwt",
  173. }
  174. return user
  175. }
  176. func parseGroupClaim(groupClaim string, claims jwt.MapClaims) string {
  177. usergroup := ""
  178. if val, ok := claims[groupClaim]; ok {
  179. if array, ok := val.([]interface{}); ok {
  180. groups := make([]string, len(array))
  181. for i, v := range array {
  182. groups[i] = fmt.Sprintf("%s", v)
  183. }
  184. usergroup = strings.Join(groups, " ")
  185. } else {
  186. usergroup = fmt.Sprintf("%s", val)
  187. }
  188. }
  189. return usergroup
  190. }