2019-03-08 19:42:50 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2019-03-08 19:42:50 +03:00
2022-09-02 22:18:23 +03:00
package integration
2019-03-08 19:42:50 +03:00
import (
2020-11-13 15:51:07 +03:00
"bytes"
2023-06-27 16:00:15 +03:00
"context"
"fmt"
2021-09-22 08:38:34 +03:00
"io"
2022-03-23 07:54:07 +03:00
"net/http"
2019-03-08 19:42:50 +03:00
"testing"
2023-06-27 16:00:15 +03:00
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2019-04-12 10:50:21 +03:00
"code.gitea.io/gitea/modules/setting"
2023-06-27 16:00:15 +03:00
"code.gitea.io/gitea/modules/test"
2022-10-12 07:22:43 +03:00
"code.gitea.io/gitea/routers/web/auth"
2022-09-02 22:18:23 +03:00
"code.gitea.io/gitea/tests"
2019-04-12 10:50:21 +03:00
2023-06-27 16:00:15 +03:00
"github.com/markbates/goth"
2019-03-08 19:42:50 +03:00
"github.com/stretchr/testify/assert"
)
2022-10-12 07:22:43 +03:00
func TestAuthorizeNoClientID ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-03-08 19:42:50 +03:00
req := NewRequest ( t , "GET" , "/login/oauth/authorize" )
ctx := loginUser ( t , "user2" )
2022-10-12 07:22:43 +03:00
resp := ctx . MakeRequest ( t , req , http . StatusBadRequest )
assert . Contains ( t , resp . Body . String ( ) , "Client ID not registered" )
}
func TestAuthorizeUnregisteredRedirect ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=UNREGISTERED&response_type=code&state=thestate" )
ctx := loginUser ( t , "user1" )
resp := ctx . MakeRequest ( t , req , http . StatusBadRequest )
assert . Contains ( t , resp . Body . String ( ) , "Unregistered Redirect URI" )
}
func TestAuthorizeUnsupportedResponseType ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=UNEXPECTED&state=thestate" )
ctx := loginUser ( t , "user1" )
resp := ctx . MakeRequest ( t , req , http . StatusSeeOther )
u , err := resp . Result ( ) . Location ( )
assert . NoError ( t , err )
assert . Equal ( t , "unsupported_response_type" , u . Query ( ) . Get ( "error" ) )
assert . Equal ( t , "Only code response type is supported." , u . Query ( ) . Get ( "error_description" ) )
}
func TestAuthorizeUnsupportedCodeChallengeMethod ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate&code_challenge_method=UNEXPECTED" )
ctx := loginUser ( t , "user1" )
resp := ctx . MakeRequest ( t , req , http . StatusSeeOther )
u , err := resp . Result ( ) . Location ( )
assert . NoError ( t , err )
assert . Equal ( t , "invalid_request" , u . Query ( ) . Get ( "error" ) )
assert . Equal ( t , "unsupported code challenge method" , u . Query ( ) . Get ( "error_description" ) )
2019-03-08 19:42:50 +03:00
}
2022-10-12 07:22:43 +03:00
func TestAuthorizeLoginRedirect ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-03-08 19:42:50 +03:00
req := NewRequest ( t , "GET" , "/login/oauth/authorize" )
2022-03-23 07:54:07 +03:00
assert . Contains ( t , MakeRequest ( t , req , http . StatusSeeOther ) . Body . String ( ) , "/user/login" )
2019-03-08 19:42:50 +03:00
}
2022-10-12 07:22:43 +03:00
func TestAuthorizeShow ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-10-12 07:22:43 +03:00
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate" )
2019-03-08 19:42:50 +03:00
ctx := loginUser ( t , "user4" )
2022-03-23 07:54:07 +03:00
resp := ctx . MakeRequest ( t , req , http . StatusOK )
2019-03-08 19:42:50 +03:00
htmlDoc := NewHTMLParser ( t , resp . Body )
htmlDoc . AssertElement ( t , "#authorize-app" , true )
htmlDoc . GetCSRF ( )
}
2022-10-12 07:22:43 +03:00
func TestAuthorizeRedirectWithExistingGrant ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-10-12 07:22:43 +03:00
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=https%3A%2F%2Fexample.com%2Fxyzzy&response_type=code&state=thestate" )
2019-03-08 19:42:50 +03:00
ctx := loginUser ( t , "user1" )
2022-03-23 07:54:07 +03:00
resp := ctx . MakeRequest ( t , req , http . StatusSeeOther )
2019-03-08 19:42:50 +03:00
u , err := resp . Result ( ) . Location ( )
assert . NoError ( t , err )
assert . Equal ( t , "thestate" , u . Query ( ) . Get ( "state" ) )
assert . Truef ( t , len ( u . Query ( ) . Get ( "code" ) ) > 30 , "authorization code '%s' should be longer then 30" , u . Query ( ) . Get ( "code" ) )
2022-10-12 07:22:43 +03:00
u . RawQuery = ""
assert . Equal ( t , "https://example.com/xyzzy" , u . String ( ) )
2019-03-08 19:42:50 +03:00
}
2022-10-24 10:59:24 +03:00
func TestAuthorizePKCERequiredForPublicClient ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
req := NewRequest ( t , "GET" , "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=http%3A%2F%2F127.0.0.1&response_type=code&state=thestate" )
ctx := loginUser ( t , "user1" )
resp := ctx . MakeRequest ( t , req , http . StatusSeeOther )
u , err := resp . Result ( ) . Location ( )
assert . NoError ( t , err )
assert . Equal ( t , "invalid_request" , u . Query ( ) . Get ( "error" ) )
assert . Equal ( t , "PKCE is required for public clients" , u . Query ( ) . Get ( "error_description" ) )
}
2019-03-08 19:42:50 +03:00
func TestAccessTokenExchange ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-03-08 19:42:50 +03:00
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-03-23 07:54:07 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2019-03-08 19:42:50 +03:00
type response struct {
AccessToken string ` json:"access_token" `
TokenType string ` json:"token_type" `
ExpiresIn int64 ` json:"expires_in" `
RefreshToken string ` json:"refresh_token" `
}
parsed := new ( response )
2021-03-02 00:08:10 +03:00
2019-03-08 19:42:50 +03:00
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsed ) )
2023-06-03 06:59:28 +03:00
assert . True ( t , len ( parsed . AccessToken ) > 10 )
assert . True ( t , len ( parsed . RefreshToken ) > 10 )
}
func TestAccessTokenExchangeWithPublicClient ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "ce5a1322-42a7-11ed-b878-0242ac120002" ,
"redirect_uri" : "http://127.0.0.1" ,
"code" : "authcodepublic" ,
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
} )
resp := MakeRequest ( t , req , http . StatusOK )
type response struct {
AccessToken string ` json:"access_token" `
TokenType string ` json:"token_type" `
ExpiresIn int64 ` json:"expires_in" `
RefreshToken string ` json:"refresh_token" `
}
parsed := new ( response )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsed ) )
2019-03-08 19:42:50 +03:00
assert . True ( t , len ( parsed . AccessToken ) > 10 )
assert . True ( t , len ( parsed . RefreshToken ) > 10 )
}
2022-10-12 07:22:43 +03:00
func TestAccessTokenExchangeJSON ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-04-15 18:54:50 +03:00
req := NewRequestWithJSON ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-04-15 18:54:50 +03:00
} )
2022-03-23 07:54:07 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2019-04-15 18:54:50 +03:00
type response struct {
AccessToken string ` json:"access_token" `
TokenType string ` json:"token_type" `
ExpiresIn int64 ` json:"expires_in" `
RefreshToken string ` json:"refresh_token" `
}
parsed := new ( response )
2021-03-02 00:08:10 +03:00
2019-04-15 18:54:50 +03:00
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsed ) )
assert . True ( t , len ( parsed . AccessToken ) > 10 )
assert . True ( t , len ( parsed . RefreshToken ) > 10 )
}
2022-10-12 07:22:43 +03:00
func TestAccessTokenExchangeWithoutPKCE ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-10-12 07:22:43 +03:00
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
2019-03-08 19:42:50 +03:00
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
} )
2022-10-12 07:22:43 +03:00
resp := MakeRequest ( t , req , http . StatusBadRequest )
parsedError := new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "failed PKCE code challenge" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
}
func TestAccessTokenExchangeWithInvalidCredentials ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-03-08 19:42:50 +03:00
// invalid client id
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "???" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-10-12 07:22:43 +03:00
resp := MakeRequest ( t , req , http . StatusBadRequest )
parsedError := new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "invalid_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "cannot load client with client id: '???'" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
// invalid client secret
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "???" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-10-12 07:22:43 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "invalid client secret" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
// invalid redirect uri
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "???" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-10-12 07:22:43 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "unexpected redirect URI" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
// invalid authorization code
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "???" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-10-12 07:22:43 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "client is not authorized" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
// invalid grant_type
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "???" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-08 19:42:50 +03:00
} )
2022-10-12 07:22:43 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unsupported_grant_type" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "Only refresh_token or authorization_code grant type is supported" , parsedError . ErrorDescription )
2019-03-08 19:42:50 +03:00
}
2019-03-11 05:54:59 +03:00
func TestAccessTokenExchangeWithBasicAuth ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-03-11 05:54:59 +03:00
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-11 05:54:59 +03:00
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9" )
2022-03-23 07:54:07 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2019-03-11 05:54:59 +03:00
type response struct {
AccessToken string ` json:"access_token" `
TokenType string ` json:"token_type" `
ExpiresIn int64 ` json:"expires_in" `
RefreshToken string ` json:"refresh_token" `
}
parsed := new ( response )
2021-03-02 00:08:10 +03:00
2019-03-11 05:54:59 +03:00
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsed ) )
assert . True ( t , len ( parsed . AccessToken ) > 10 )
assert . True ( t , len ( parsed . RefreshToken ) > 10 )
// use wrong client_secret
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-03-11 05:54:59 +03:00
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OmJsYWJsYQ==" )
2022-10-12 07:22:43 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError := new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "invalid client secret" , parsedError . ErrorDescription )
2019-03-11 05:54:59 +03:00
// missing header
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
} )
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "invalid_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "cannot load client with client id: ''" , parsedError . ErrorDescription )
// client_id inconsistent with Authorization header
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
"client_id" : "inconsistent" ,
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9" )
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "invalid_request" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "client_id in request body inconsistent with Authorization header" , parsedError . ErrorDescription )
// client_secret inconsistent with Authorization header
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
"client_secret" : "inconsistent" ,
2019-03-11 05:54:59 +03:00
} )
2022-10-12 07:22:43 +03:00
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9" )
2022-10-23 08:28:46 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
2022-10-12 07:22:43 +03:00
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "invalid_request" , string ( parsedError . ErrorCode ) )
2022-10-23 08:28:46 +03:00
assert . Equal ( t , "client_secret in request body inconsistent with Authorization header" , parsedError . ErrorDescription )
2019-03-11 05:54:59 +03:00
}
2019-04-12 10:50:21 +03:00
func TestRefreshTokenInvalidation ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2019-04-12 10:50:21 +03:00
req := NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "authorization_code" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"code" : "authcode" ,
2022-10-12 07:22:43 +03:00
"code_verifier" : "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt" ,
2019-04-12 10:50:21 +03:00
} )
2022-03-23 07:54:07 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2019-04-12 10:50:21 +03:00
type response struct {
AccessToken string ` json:"access_token" `
TokenType string ` json:"token_type" `
ExpiresIn int64 ` json:"expires_in" `
RefreshToken string ` json:"refresh_token" `
}
parsed := new ( response )
2021-03-02 00:08:10 +03:00
2019-04-12 10:50:21 +03:00
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsed ) )
// test without invalidation
setting . OAuth2 . InvalidateRefreshTokens = false
2022-10-23 08:28:46 +03:00
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "refresh_token" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
// omit secret
"redirect_uri" : "a" ,
"refresh_token" : parsed . RefreshToken ,
} )
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError := new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "invalid_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "invalid empty client secret" , parsedError . ErrorDescription )
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
"grant_type" : "refresh_token" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"refresh_token" : "UNEXPECTED" ,
} )
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "unable to parse refresh token" , parsedError . ErrorDescription )
req = NewRequestWithValues ( t , "POST" , "/login/oauth/access_token" , map [ string ] string {
2019-04-12 10:50:21 +03:00
"grant_type" : "refresh_token" ,
"client_id" : "da7da3ba-9a13-4167-856f-3899de0b0138" ,
"client_secret" : "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=" ,
"redirect_uri" : "a" ,
"refresh_token" : parsed . RefreshToken ,
} )
2021-01-29 18:35:30 +03:00
2022-10-23 08:28:46 +03:00
bs , err := io . ReadAll ( req . Body )
2020-11-13 15:51:07 +03:00
assert . NoError ( t , err )
2022-10-23 08:28:46 +03:00
req . Body = io . NopCloser ( bytes . NewReader ( bs ) )
MakeRequest ( t , req , http . StatusOK )
2020-11-13 15:51:07 +03:00
2022-10-23 08:28:46 +03:00
req . Body = io . NopCloser ( bytes . NewReader ( bs ) )
MakeRequest ( t , req , http . StatusOK )
2019-04-12 10:50:21 +03:00
// test with invalidation
setting . OAuth2 . InvalidateRefreshTokens = true
2022-10-23 08:28:46 +03:00
req . Body = io . NopCloser ( bytes . NewReader ( bs ) )
MakeRequest ( t , req , http . StatusOK )
2020-11-13 15:51:07 +03:00
2022-10-12 07:22:43 +03:00
// repeat request should fail
2022-10-23 08:28:46 +03:00
req . Body = io . NopCloser ( bytes . NewReader ( bs ) )
resp = MakeRequest ( t , req , http . StatusBadRequest )
parsedError = new ( auth . AccessTokenError )
2022-10-12 07:22:43 +03:00
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , parsedError ) )
assert . Equal ( t , "unauthorized_client" , string ( parsedError . ErrorCode ) )
assert . Equal ( t , "token was already used" , parsedError . ErrorDescription )
2019-04-12 10:50:21 +03:00
}
2023-06-27 16:00:15 +03:00
func TestSignInOAuthCallbackSignIn ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
//
// OAuth2 authentication source GitLab
//
gitlabName := "gitlab"
gitlab := addAuthSource ( t , authSourcePayloadGitLabCustom ( gitlabName ) )
//
// Create a user as if it had been previously been created by the GitLab
// authentication source.
//
userGitLabUserID := "5678"
userGitLab := & user_model . User {
Name : "gitlabuser" ,
Email : "gitlabuser@example.com" ,
Passwd : "gitlabuserpassword" ,
Type : user_model . UserTypeIndividual ,
LoginType : auth_model . OAuth2 ,
LoginSource : gitlab . ID ,
LoginName : userGitLabUserID ,
}
defer createUser ( context . Background ( ) , t , userGitLab ) ( )
//
// A request for user information sent to Goth will return a
// goth.User exactly matching the user created above.
//
defer mockCompleteUserAuth ( func ( res http . ResponseWriter , req * http . Request ) ( goth . User , error ) {
return goth . User {
Provider : gitlabName ,
UserID : userGitLabUserID ,
Email : userGitLab . Email ,
} , nil
} ) ( )
req := NewRequest ( t , "GET" , fmt . Sprintf ( "/user/oauth2/%s/callback?code=XYZ&state=XYZ" , gitlabName ) )
resp := MakeRequest ( t , req , http . StatusSeeOther )
assert . Equal ( t , test . RedirectURL ( resp ) , "/" )
userAfterLogin := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : userGitLab . ID } )
assert . Greater ( t , userAfterLogin . LastLoginUnix , userGitLab . LastLoginUnix )
}
2023-11-19 14:50:05 +03:00
2024-03-07 12:50:04 +03:00
func TestSignInOAuthCallbackRedirectToEscaping ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
//
// OAuth2 authentication source GitLab
//
gitlabName := "gitlab"
gitlab := addAuthSource ( t , authSourcePayloadGitLabCustom ( gitlabName ) )
//
// Create a user as if it had been previously been created by the GitLab
// authentication source.
//
userGitLabUserID := "5678"
userGitLab := & user_model . User {
Name : "gitlabuser" ,
Email : "gitlabuser@example.com" ,
Passwd : "gitlabuserpassword" ,
Type : user_model . UserTypeIndividual ,
LoginType : auth_model . OAuth2 ,
LoginSource : gitlab . ID ,
LoginName : userGitLabUserID ,
}
defer createUser ( context . Background ( ) , t , userGitLab ) ( )
//
// A request for user information sent to Goth will return a
// goth.User exactly matching the user created above.
//
defer mockCompleteUserAuth ( func ( res http . ResponseWriter , req * http . Request ) ( goth . User , error ) {
return goth . User {
Provider : gitlabName ,
UserID : userGitLabUserID ,
Email : userGitLab . Email ,
} , nil
} ) ( )
req := NewRequest ( t , "GET" , fmt . Sprintf ( "/user/oauth2/%s/callback?code=XYZ&state=XYZ" , gitlabName ) )
req . AddCookie ( & http . Cookie {
Name : "redirect_to" ,
Value : "/login/oauth/authorize?redirect_uri=https%3A%2F%2Ftranslate.example.org" ,
Path : "/" ,
} )
resp := MakeRequest ( t , req , http . StatusSeeOther )
hasNewSessionCookie := false
sessionCookieName := setting . SessionConfig . CookieName
for _ , c := range resp . Result ( ) . Cookies ( ) {
if c . Name == sessionCookieName {
hasNewSessionCookie = true
break
}
t . Log ( "Got cookie" , c . Name )
}
assert . True ( t , hasNewSessionCookie , "Session cookie %q is missing" , sessionCookieName )
assert . Equal ( t , "/login/oauth/authorize?redirect_uri=https://translate.example.org" , test . RedirectURL ( resp ) )
}
2023-11-19 14:50:05 +03:00
func TestSignUpViaOAuthWithMissingFields ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
// enable auto-creation of accounts via OAuth2
enableAutoRegistration := setting . OAuth2Client . EnableAutoRegistration
setting . OAuth2Client . EnableAutoRegistration = true
defer func ( ) {
setting . OAuth2Client . EnableAutoRegistration = enableAutoRegistration
} ( )
// OAuth2 authentication source GitLab
gitlabName := "gitlab"
addAuthSource ( t , authSourcePayloadGitLabCustom ( gitlabName ) )
userGitLabUserID := "5678"
// The Goth User returned by the oauth2 integration is missing
// an email address, so we won't be able to automatically create a local account for it.
defer mockCompleteUserAuth ( func ( res http . ResponseWriter , req * http . Request ) ( goth . User , error ) {
return goth . User {
Provider : gitlabName ,
UserID : userGitLabUserID ,
} , nil
} ) ( )
req := NewRequest ( t , "GET" , fmt . Sprintf ( "/user/oauth2/%s/callback?code=XYZ&state=XYZ" , gitlabName ) )
resp := MakeRequest ( t , req , http . StatusSeeOther )
assert . Equal ( t , test . RedirectURL ( resp ) , "/user/link_account" )
}