auth_utils.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/go-chi/jwtauth/v5"
  8. "github.com/lestrrat-go/jwx/jwt"
  9. "github.com/rs/xid"
  10. "github.com/drakkan/sftpgo/dataprovider"
  11. "github.com/drakkan/sftpgo/logger"
  12. "github.com/drakkan/sftpgo/utils"
  13. )
  14. type tokenAudience = string
  15. const (
  16. tokenAudienceWebAdmin tokenAudience = "WebAdmin"
  17. tokenAudienceWebClient tokenAudience = "WebClient"
  18. tokenAudienceAPI tokenAudience = "API"
  19. tokenAudienceCSRF tokenAudience = "CSRF"
  20. )
  21. const (
  22. claimUsernameKey = "username"
  23. claimPermissionsKey = "permissions"
  24. basicRealm = "Basic realm=\"SFTPGo\""
  25. )
  26. var (
  27. tokenDuration = 10 * time.Minute
  28. tokenRefreshMin = 5 * time.Minute
  29. )
  30. type jwtTokenClaims struct {
  31. Username string
  32. Permissions []string
  33. Signature string
  34. }
  35. func (c *jwtTokenClaims) asMap() map[string]interface{} {
  36. claims := make(map[string]interface{})
  37. claims[claimUsernameKey] = c.Username
  38. claims[claimPermissionsKey] = c.Permissions
  39. claims[jwt.SubjectKey] = c.Signature
  40. return claims
  41. }
  42. func (c *jwtTokenClaims) Decode(token map[string]interface{}) {
  43. username := token[claimUsernameKey]
  44. switch v := username.(type) {
  45. case string:
  46. c.Username = v
  47. }
  48. signature := token[jwt.SubjectKey]
  49. switch v := signature.(type) {
  50. case string:
  51. c.Signature = v
  52. }
  53. permissions := token[claimPermissionsKey]
  54. switch v := permissions.(type) {
  55. case []interface{}:
  56. for _, elem := range v {
  57. switch elemValue := elem.(type) {
  58. case string:
  59. c.Permissions = append(c.Permissions, elemValue)
  60. }
  61. }
  62. }
  63. }
  64. func (c *jwtTokenClaims) isCriticalPermRemoved(permissions []string) bool {
  65. if utils.IsStringInSlice(dataprovider.PermAdminAny, permissions) {
  66. return false
  67. }
  68. if (utils.IsStringInSlice(dataprovider.PermAdminManageAdmins, c.Permissions) ||
  69. utils.IsStringInSlice(dataprovider.PermAdminAny, c.Permissions)) &&
  70. !utils.IsStringInSlice(dataprovider.PermAdminManageAdmins, permissions) &&
  71. !utils.IsStringInSlice(dataprovider.PermAdminAny, permissions) {
  72. return true
  73. }
  74. return false
  75. }
  76. func (c *jwtTokenClaims) hasPerm(perm string) bool {
  77. if utils.IsStringInSlice(dataprovider.PermAdminAny, c.Permissions) {
  78. return true
  79. }
  80. return utils.IsStringInSlice(perm, c.Permissions)
  81. }
  82. func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth, audience tokenAudience) (map[string]interface{}, error) {
  83. claims := c.asMap()
  84. now := time.Now().UTC()
  85. claims[jwt.JwtIDKey] = xid.New().String()
  86. claims[jwt.NotBeforeKey] = now.Add(-30 * time.Second)
  87. claims[jwt.ExpirationKey] = now.Add(tokenDuration)
  88. claims[jwt.AudienceKey] = audience
  89. token, tokenString, err := tokenAuth.Encode(claims)
  90. if err != nil {
  91. return nil, err
  92. }
  93. response := make(map[string]interface{})
  94. response["access_token"] = tokenString
  95. response["expires_at"] = token.Expiration().Format(time.RFC3339)
  96. return response, nil
  97. }
  98. func (c *jwtTokenClaims) createAndSetCookie(w http.ResponseWriter, r *http.Request, tokenAuth *jwtauth.JWTAuth, audience tokenAudience) error {
  99. resp, err := c.createTokenResponse(tokenAuth, audience)
  100. if err != nil {
  101. return err
  102. }
  103. var basePath string
  104. if audience == tokenAudienceWebAdmin {
  105. basePath = webBaseAdminPath
  106. } else {
  107. basePath = webBaseClientPath
  108. }
  109. http.SetCookie(w, &http.Cookie{
  110. Name: "jwt",
  111. Value: resp["access_token"].(string),
  112. Path: basePath,
  113. Expires: time.Now().Add(tokenDuration),
  114. HttpOnly: true,
  115. Secure: r.TLS != nil,
  116. })
  117. return nil
  118. }
  119. func (c *jwtTokenClaims) removeCookie(w http.ResponseWriter, r *http.Request) {
  120. http.SetCookie(w, &http.Cookie{
  121. Name: "jwt",
  122. Value: "",
  123. Path: webBasePath,
  124. MaxAge: -1,
  125. HttpOnly: true,
  126. Secure: r.TLS != nil,
  127. })
  128. invalidateToken(r)
  129. }
  130. func isTokenInvalidated(r *http.Request) bool {
  131. isTokenFound := false
  132. token := jwtauth.TokenFromHeader(r)
  133. if token != "" {
  134. isTokenFound = true
  135. if _, ok := invalidatedJWTTokens.Load(token); ok {
  136. return true
  137. }
  138. }
  139. token = jwtauth.TokenFromCookie(r)
  140. if token != "" {
  141. isTokenFound = true
  142. if _, ok := invalidatedJWTTokens.Load(token); ok {
  143. return true
  144. }
  145. }
  146. return !isTokenFound
  147. }
  148. func invalidateToken(r *http.Request) {
  149. tokenString := jwtauth.TokenFromHeader(r)
  150. if tokenString != "" {
  151. invalidatedJWTTokens.Store(tokenString, time.Now().UTC().Add(tokenDuration))
  152. }
  153. tokenString = jwtauth.TokenFromCookie(r)
  154. if tokenString != "" {
  155. invalidatedJWTTokens.Store(tokenString, time.Now().UTC().Add(tokenDuration))
  156. }
  157. }
  158. func getUserFromToken(r *http.Request) *dataprovider.User {
  159. user := &dataprovider.User{}
  160. _, claims, err := jwtauth.FromContext(r.Context())
  161. if err != nil {
  162. return user
  163. }
  164. tokenClaims := jwtTokenClaims{}
  165. tokenClaims.Decode(claims)
  166. user.Username = tokenClaims.Username
  167. user.Filters.WebClient = tokenClaims.Permissions
  168. return user
  169. }
  170. func getAdminFromToken(r *http.Request) *dataprovider.Admin {
  171. admin := &dataprovider.Admin{}
  172. _, claims, err := jwtauth.FromContext(r.Context())
  173. if err != nil {
  174. return admin
  175. }
  176. tokenClaims := jwtTokenClaims{}
  177. tokenClaims.Decode(claims)
  178. admin.Username = tokenClaims.Username
  179. admin.Permissions = tokenClaims.Permissions
  180. return admin
  181. }
  182. func createCSRFToken() string {
  183. claims := make(map[string]interface{})
  184. now := time.Now().UTC()
  185. claims[jwt.JwtIDKey] = xid.New().String()
  186. claims[jwt.NotBeforeKey] = now.Add(-30 * time.Second)
  187. claims[jwt.ExpirationKey] = now.Add(tokenDuration)
  188. claims[jwt.AudienceKey] = tokenAudienceCSRF
  189. _, tokenString, err := csrfTokenAuth.Encode(claims)
  190. if err != nil {
  191. logger.Debug(logSender, "", "unable to create CSRF token: %v", err)
  192. return ""
  193. }
  194. return tokenString
  195. }
  196. func verifyCSRFToken(tokenString string) error {
  197. token, err := jwtauth.VerifyToken(csrfTokenAuth, tokenString)
  198. if err != nil || token == nil {
  199. logger.Debug(logSender, "", "error validating CSRF: %v", err)
  200. return fmt.Errorf("unable to verify form token: %v", err)
  201. }
  202. if !utils.IsStringInSlice(tokenAudienceCSRF, token.Audience()) {
  203. logger.Debug(logSender, "", "error validating CSRF token audience")
  204. return errors.New("the form token is not valid")
  205. }
  206. return nil
  207. }