You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

oauth2.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package auth
  5. import (
  6. "net/http"
  7. "strings"
  8. "time"
  9. actions_model "code.gitea.io/gitea/models/actions"
  10. auth_model "code.gitea.io/gitea/models/auth"
  11. "code.gitea.io/gitea/models/db"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/timeutil"
  15. "code.gitea.io/gitea/modules/web/middleware"
  16. "code.gitea.io/gitea/services/auth/source/oauth2"
  17. )
  18. // Ensure the struct implements the interface.
  19. var (
  20. _ Method = &OAuth2{}
  21. _ Named = &OAuth2{}
  22. )
  23. // CheckOAuthAccessToken returns uid of user from oauth token
  24. func CheckOAuthAccessToken(accessToken string) int64 {
  25. // JWT tokens require a "."
  26. if !strings.Contains(accessToken, ".") {
  27. return 0
  28. }
  29. token, err := oauth2.ParseToken(accessToken, oauth2.DefaultSigningKey)
  30. if err != nil {
  31. log.Trace("oauth2.ParseToken: %v", err)
  32. return 0
  33. }
  34. var grant *auth_model.OAuth2Grant
  35. if grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, token.GrantID); err != nil || grant == nil {
  36. return 0
  37. }
  38. if token.Type != oauth2.TypeAccessToken {
  39. return 0
  40. }
  41. if token.ExpiresAt.Before(time.Now()) || token.IssuedAt.After(time.Now()) {
  42. return 0
  43. }
  44. return grant.UserID
  45. }
  46. // OAuth2 implements the Auth interface and authenticates requests
  47. // (API requests only) by looking for an OAuth token in query parameters or the
  48. // "Authorization" header.
  49. type OAuth2 struct{}
  50. // Name represents the name of auth method
  51. func (o *OAuth2) Name() string {
  52. return "oauth2"
  53. }
  54. // userIDFromToken returns the user id corresponding to the OAuth token.
  55. // It will set 'IsApiToken' to true if the token is an API token and
  56. // set 'ApiTokenScope' to the scope of the access token
  57. func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
  58. _ = req.ParseForm()
  59. // Check access token.
  60. tokenSHA := req.Form.Get("token")
  61. if len(tokenSHA) == 0 {
  62. tokenSHA = req.Form.Get("access_token")
  63. }
  64. if len(tokenSHA) == 0 {
  65. // Well, check with header again.
  66. auHead := req.Header.Get("Authorization")
  67. if len(auHead) > 0 {
  68. auths := strings.Fields(auHead)
  69. if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
  70. tokenSHA = auths[1]
  71. }
  72. }
  73. }
  74. if len(tokenSHA) == 0 {
  75. return 0
  76. }
  77. // Let's see if token is valid.
  78. if strings.Contains(tokenSHA, ".") {
  79. uid := CheckOAuthAccessToken(tokenSHA)
  80. if uid != 0 {
  81. store.GetData()["IsApiToken"] = true
  82. store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all
  83. }
  84. return uid
  85. }
  86. t, err := auth_model.GetAccessTokenBySHA(tokenSHA)
  87. if err != nil {
  88. if auth_model.IsErrAccessTokenNotExist(err) {
  89. // check task token
  90. task, err := actions_model.GetRunningTaskByToken(db.DefaultContext, tokenSHA)
  91. if err == nil && task != nil {
  92. log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
  93. store.GetData()["IsActionsToken"] = true
  94. store.GetData()["ActionsTaskID"] = task.ID
  95. return user_model.ActionsUserID
  96. }
  97. } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
  98. log.Error("GetAccessTokenBySHA: %v", err)
  99. }
  100. return 0
  101. }
  102. t.UpdatedUnix = timeutil.TimeStampNow()
  103. if err = auth_model.UpdateAccessToken(t); err != nil {
  104. log.Error("UpdateAccessToken: %v", err)
  105. }
  106. store.GetData()["IsApiToken"] = true
  107. store.GetData()["ApiTokenScope"] = t.Scope
  108. return t.UID
  109. }
  110. // Verify extracts the user ID from the OAuth token in the query parameters
  111. // or the "Authorization" header and returns the corresponding user object for that ID.
  112. // If verification is successful returns an existing user object.
  113. // Returns nil if verification fails.
  114. func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
  115. if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) {
  116. return nil, nil
  117. }
  118. id := o.userIDFromToken(req, store)
  119. if id <= 0 && id != -2 { // -2 means actions, so we need to allow it.
  120. return nil, nil
  121. }
  122. log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
  123. user, err := user_model.GetPossibleUserByID(req.Context(), id)
  124. if err != nil {
  125. if !user_model.IsErrUserNotExist(err) {
  126. log.Error("GetUserByName: %v", err)
  127. }
  128. return nil, err
  129. }
  130. log.Trace("OAuth2 Authorization: Logged in user %-v", user)
  131. return user, nil
  132. }
  133. func isAuthenticatedTokenRequest(req *http.Request) bool {
  134. switch req.URL.Path {
  135. case "/login/oauth/userinfo":
  136. fallthrough
  137. case "/login/oauth/introspect":
  138. return true
  139. }
  140. return false
  141. }