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