Skip to content

Commit ee87b4e

Browse files
GiteaBotCaiCandongwolfogre
authored
Fix the error message when the token is incorrect (#25701) (#25834)
Backport #25701 by @CaiCandong we refactored `userIDFromToken` for the token parsing part into a new function `parseToken`. `parseToken` returns the string `token` from request, and a boolean `ok` representing whether the token exists or not. So we can distinguish between token non-existence and token inconsistency in the `verfity` function, thus solving the problem of no proper error message when the token is inconsistent. close #24439 related #22119 Co-authored-by: caicandong <50507092+CaiCandong@users.noreply.github.com> Co-authored-by: Jason Song <i@wolfogre.com>
1 parent bd1946e commit ee87b4e

File tree

3 files changed

+52
-24
lines changed

3 files changed

+52
-24
lines changed

services/auth/group.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,22 @@ func (b *Group) Free() error {
8181
// Verify extracts and validates
8282
func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
8383
// Try to sign in with each of the enabled plugins
84+
var retErr error
8485
for _, ssoMethod := range b.methods {
8586
user, err := ssoMethod.Verify(req, w, store, sess)
8687
if err != nil {
87-
return nil, err
88+
if retErr == nil {
89+
retErr = err
90+
}
91+
// Try other methods if this one failed.
92+
// Some methods may share the same protocol to detect if they are matched.
93+
// For example, OAuth2 and conan.Auth both read token from "Authorization: Bearer <token>" header,
94+
// If OAuth2 returns error, we should give conan.Auth a chance to try.
95+
continue
8896
}
8997

98+
// If any method returns a user, we can stop trying.
99+
// Return the user and ignore any error returned by previous methods.
90100
if user != nil {
91101
if store.GetData()["AuthedMethod"] == nil {
92102
if named, ok := ssoMethod.(Named); ok {
@@ -97,5 +107,6 @@ func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore
97107
}
98108
}
99109

100-
return nil, nil
110+
// If no method returns a user, return the error returned by the first method.
111+
return nil, retErr
101112
}

services/auth/oauth2.go

+28-22
Original file line numberDiff line numberDiff line change
@@ -59,31 +59,32 @@ func (o *OAuth2) Name() string {
5959
return "oauth2"
6060
}
6161

62-
// userIDFromToken returns the user id corresponding to the OAuth token.
63-
// It will set 'IsApiToken' to true if the token is an API token and
64-
// set 'ApiTokenScope' to the scope of the access token
65-
func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
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) {
6665
_ = req.ParseForm()
67-
66+
// Check token.
67+
if token := req.Form.Get("token"); token != "" {
68+
return token, true
69+
}
6870
// Check access token.
69-
tokenSHA := req.Form.Get("token")
70-
if len(tokenSHA) == 0 {
71-
tokenSHA = req.Form.Get("access_token")
72-
}
73-
if len(tokenSHA) == 0 {
74-
// Well, check with header again.
75-
auHead := req.Header.Get("Authorization")
76-
if len(auHead) > 0 {
77-
auths := strings.Fields(auHead)
78-
if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
79-
tokenSHA = auths[1]
80-
}
81-
}
71+
if token := req.Form.Get("access_token"); token != "" {
72+
return token, true
8273
}
83-
if len(tokenSHA) == 0 {
84-
return 0
74+
// check header token
75+
if auHead := req.Header.Get("Authorization"); auHead != "" {
76+
auths := strings.Fields(auHead)
77+
if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
78+
return auths[1], true
79+
}
8580
}
81+
return "", false
82+
}
8683

84+
// userIDFromToken returns the user id corresponding to the OAuth token.
85+
// It will set 'IsApiToken' to true if the token is an API token and
86+
// set 'ApiTokenScope' to the scope of the access token
87+
func (o *OAuth2) userIDFromToken(tokenSHA string, store DataStore) int64 {
8788
// Let's see if token is valid.
8889
if strings.Contains(tokenSHA, ".") {
8990
uid := CheckOAuthAccessToken(tokenSHA)
@@ -129,10 +130,15 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor
129130
return nil, nil
130131
}
131132

132-
id := o.userIDFromToken(req, store)
133+
token, ok := parseToken(req)
134+
if !ok {
135+
return nil, nil
136+
}
137+
138+
id := o.userIDFromToken(token, store)
133139

134140
if id <= 0 && id != -2 { // -2 means actions, so we need to allow it.
135-
return nil, nil
141+
return nil, user_model.ErrUserNotExist{}
136142
}
137143
log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
138144

tests/integration/api_repo_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ func TestAPIUserReposNotLogin(t *testing.T) {
4141
}
4242
}
4343

44+
func TestAPIUserReposWithWrongToken(t *testing.T) {
45+
defer tests.PrepareTestEnv(t)()
46+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
47+
wrongToken := fmt.Sprintf("Bearer %s", "wrong_token")
48+
req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name)
49+
req = addTokenAuthHeader(req, wrongToken)
50+
resp := MakeRequest(t, req, http.StatusUnauthorized)
51+
52+
assert.Contains(t, resp.Body.String(), "user does not exist")
53+
}
54+
4455
func TestAPISearchRepo(t *testing.T) {
4556
defer tests.PrepareTestEnv(t)()
4657
const keyword = "test"

0 commit comments

Comments
 (0)