auth_token.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package auth
  2. import (
  3. "crypto/sha256"
  4. "encoding/hex"
  5. "net/http"
  6. "net/url"
  7. "time"
  8. "github.com/grafana/grafana/pkg/bus"
  9. "github.com/grafana/grafana/pkg/log"
  10. "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/registry"
  12. "github.com/grafana/grafana/pkg/services/sqlstore"
  13. "github.com/grafana/grafana/pkg/setting"
  14. "github.com/grafana/grafana/pkg/util"
  15. )
  16. func init() {
  17. registry.RegisterService(&UserAuthTokenService{})
  18. }
  19. var (
  20. now = time.Now
  21. RotateTime = 1 * time.Minute // this should be read from [session] configuration.
  22. UrgentRotateTime = 30 * time.Second
  23. oneYearInSeconds = 31557600 //used as default maxage for session cookies. We validate/rotate them more often.
  24. )
  25. // UserAuthTokenService are used for generating and validating user auth tokens
  26. type UserAuthTokenService struct {
  27. SQLStore *sqlstore.SqlStore `inject:""`
  28. log log.Logger
  29. }
  30. // Init this service
  31. func (s *UserAuthTokenService) Init() error {
  32. s.log = log.New("auth")
  33. return nil
  34. }
  35. func (s *UserAuthTokenService) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool {
  36. //auth User
  37. unhashedToken := ctx.GetCookie(setting.SessionOptions.CookieName)
  38. if unhashedToken == "" {
  39. return false
  40. }
  41. user, err := s.LookupToken(unhashedToken)
  42. if err != nil {
  43. ctx.Logger.Info("failed to look up user based on cookie", "error", err)
  44. return false
  45. }
  46. query := models.GetSignedInUserQuery{UserId: user.UserId, OrgId: orgID}
  47. if err := bus.Dispatch(&query); err != nil {
  48. ctx.Logger.Error("Failed to get user with id", "userId", user.UserId, "error", err)
  49. return false
  50. }
  51. ctx.SignedInUser = query.Result
  52. ctx.IsSignedIn = true
  53. ctx.UserToken = user
  54. //rotate session token if needed.
  55. rotated, err := s.RefreshToken(ctx.UserToken, ctx.RemoteAddr(), ctx.Req.UserAgent())
  56. if err != nil {
  57. ctx.Logger.Error("failed to rotate token", "error", err, "user.id", user.UserId, "user_token.id", user.Id)
  58. return true
  59. }
  60. if rotated {
  61. s.writeSessionCookie(ctx, ctx.UserToken.UnhashedToken, oneYearInSeconds)
  62. }
  63. return true
  64. }
  65. func (s *UserAuthTokenService) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) {
  66. ctx.Logger.Info("new token", "unhashed token", value)
  67. ctx.Resp.Header().Del("Set-Cookie")
  68. cookie := http.Cookie{
  69. Name: setting.SessionOptions.CookieName,
  70. Value: url.QueryEscape(value),
  71. HttpOnly: true,
  72. Domain: setting.Domain,
  73. Path: setting.AppSubUrl + "/",
  74. Secure: setting.SessionOptions.Secure,
  75. }
  76. http.SetCookie(ctx.Resp, &cookie)
  77. }
  78. func (s *UserAuthTokenService) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error {
  79. userToken, err := s.CreateToken(user.Id, c.RemoteAddr(), c.Req.UserAgent())
  80. if err != nil {
  81. return err
  82. }
  83. c.UserToken = userToken
  84. s.writeSessionCookie(c, userToken.UnhashedToken, oneYearInSeconds)
  85. return nil
  86. }
  87. func (s *UserAuthTokenService) UserSignedOutHook(c *models.ReqContext) {
  88. s.writeSessionCookie(c, "", -1)
  89. }
  90. func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (*models.UserAuthToken, error) {
  91. clientIP = util.ParseIPAddress(clientIP)
  92. token, err := util.RandomHex(16)
  93. if err != nil {
  94. return nil, err
  95. }
  96. hashedToken := hashToken(token)
  97. userToken := models.UserAuthToken{
  98. UserId: userId,
  99. AuthToken: hashedToken,
  100. PrevAuthToken: hashedToken,
  101. ClientIp: clientIP,
  102. UserAgent: userAgent,
  103. RotatedAt: now().Unix(),
  104. CreatedAt: now().Unix(),
  105. UpdatedAt: now().Unix(),
  106. SeenAt: 0,
  107. AuthTokenSeen: false,
  108. }
  109. _, err = s.SQLStore.NewSession().Insert(&userToken)
  110. if err != nil {
  111. return nil, err
  112. }
  113. userToken.UnhashedToken = token
  114. return &userToken, nil
  115. }
  116. func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAuthToken, error) {
  117. hashedToken := hashToken(unhashedToken)
  118. var userToken models.UserAuthToken
  119. exists, err := s.SQLStore.NewSession().Where("auth_token = ? OR prev_auth_token = ?", hashedToken, hashedToken).Get(&userToken)
  120. if err != nil {
  121. return nil, err
  122. }
  123. if !exists {
  124. return nil, ErrAuthTokenNotFound
  125. }
  126. if userToken.AuthToken != hashedToken && userToken.PrevAuthToken == hashedToken && userToken.AuthTokenSeen {
  127. userToken.AuthTokenSeen = false
  128. expireBefore := now().Add(-RotateTime).Unix()
  129. affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND prev_auth_token = ? AND rotated_at < ?", userToken.Id, userToken.PrevAuthToken, expireBefore).AllCols().Update(&userToken)
  130. if err != nil {
  131. return nil, err
  132. }
  133. if affectedRows == 0 {
  134. s.log.Debug("prev seen token unchanged", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  135. } else {
  136. s.log.Debug("prev seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  137. }
  138. }
  139. if !userToken.AuthTokenSeen && userToken.AuthToken == hashedToken {
  140. userTokenCopy := userToken
  141. userTokenCopy.AuthTokenSeen = true
  142. userTokenCopy.SeenAt = now().Unix()
  143. affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND auth_token = ?", userTokenCopy.Id, userTokenCopy.AuthToken).AllCols().Update(&userTokenCopy)
  144. if err != nil {
  145. return nil, err
  146. }
  147. if affectedRows == 1 {
  148. userToken = userTokenCopy
  149. }
  150. if affectedRows == 0 {
  151. s.log.Debug("seen wrong token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  152. } else {
  153. s.log.Debug("seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  154. }
  155. }
  156. userToken.UnhashedToken = unhashedToken
  157. return &userToken, nil
  158. }
  159. func (s *UserAuthTokenService) RefreshToken(token *models.UserAuthToken, clientIP, userAgent string) (bool, error) {
  160. if token == nil {
  161. return false, nil
  162. }
  163. needsRotation := false
  164. rotatedAt := time.Unix(token.RotatedAt, 0)
  165. if token.AuthTokenSeen {
  166. needsRotation = rotatedAt.Before(now().Add(-RotateTime))
  167. } else {
  168. needsRotation = rotatedAt.Before(now().Add(-UrgentRotateTime))
  169. }
  170. s.log.Debug("refresh token", "needs rotation?", needsRotation, "auth_token_seen", token.AuthTokenSeen, "rotated_at", rotatedAt, "token.Id", token.Id)
  171. if !needsRotation {
  172. return false, nil
  173. }
  174. clientIP = util.ParseIPAddress(clientIP)
  175. newToken, _ := util.RandomHex(16)
  176. hashedToken := hashToken(newToken)
  177. sql := `
  178. UPDATE user_auth_token
  179. SET
  180. auth_token_seen = false,
  181. seen_at = null,
  182. user_agent = ?,
  183. client_ip = ?,
  184. prev_auth_token = case when auth_token_seen then auth_token else prev_auth_token end,
  185. auth_token = ?,
  186. rotated_at = ?
  187. WHERE id = ? AND (auth_token_seen or rotated_at < ?)`
  188. res, err := s.SQLStore.NewSession().Exec(sql, userAgent, clientIP, hashedToken, now().Unix(), token.Id, now().Add(-UrgentRotateTime))
  189. if err != nil {
  190. return false, err
  191. }
  192. affected, _ := res.RowsAffected()
  193. s.log.Debug("rotated", "affected", affected, "auth_token_id", token.Id, "userId", token.UserId, "user_agent", userAgent, "client_ip", clientIP)
  194. if affected > 0 {
  195. token.UnhashedToken = newToken
  196. return true, nil
  197. }
  198. return false, nil
  199. }
  200. func hashToken(token string) string {
  201. hashBytes := sha256.Sum256([]byte(token + setting.SecretKey))
  202. return hex.EncodeToString(hashBytes[:])
  203. }