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"
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"
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"
2024-10-02 03:03:19 +03:00
oauth2_provider "code.gitea.io/gitea/services/oauth2_provider"
2022-09-02 22:18:23 +03:00
"code.gitea.io/gitea/tests"
2019-04-12 10:50:21 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError := new ( oauth2_provider . 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 , "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 )
2024-10-02 03:03:19 +03:00
parsedError := new ( oauth2_provider . AccessTokenError )
2022-10-12 07:22:43 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . 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 , "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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . 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 , "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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . 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 , "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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . AccessTokenError )
2022-10-12 07:22:43 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError := new ( oauth2_provider . 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 , "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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . AccessTokenError )
2022-10-12 07:22:43 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . AccessTokenError )
2022-10-12 07:22:43 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . AccessTokenError )
2022-10-12 07:22:43 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError := new ( oauth2_provider . AccessTokenError )
2022-10-23 08:28:46 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . AccessTokenError )
2022-10-23 08:28:46 +03:00
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 )
2024-10-02 03:03:19 +03:00
parsedError = new ( oauth2_provider . 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
}
2024-07-23 15:43:03 +03:00
func TestOAuthIntrospection ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
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" ,
"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 ) )
assert . True ( t , len ( parsed . AccessToken ) > 10 )
assert . True ( t , len ( parsed . RefreshToken ) > 10 )
// successful request with a valid client_id/client_secret and a valid token
req = NewRequestWithValues ( t , "POST" , "/login/oauth/introspect" , map [ string ] string {
"token" : parsed . AccessToken ,
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9" )
resp = MakeRequest ( t , req , http . StatusOK )
type introspectResponse struct {
2024-07-25 15:36:05 +03:00
Active bool ` json:"active" `
Scope string ` json:"scope,omitempty" `
Username string ` json:"username" `
2024-07-23 15:43:03 +03:00
}
introspectParsed := new ( introspectResponse )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , introspectParsed ) )
assert . True ( t , introspectParsed . Active )
2024-07-25 15:36:05 +03:00
assert . Equal ( t , "user1" , introspectParsed . Username )
2024-07-23 15:43:03 +03:00
// successful request with a valid client_id/client_secret, but an invalid token
req = NewRequestWithValues ( t , "POST" , "/login/oauth/introspect" , map [ string ] string {
"token" : "xyzzy" ,
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9" )
resp = MakeRequest ( t , req , http . StatusOK )
introspectParsed = new ( introspectResponse )
assert . NoError ( t , json . Unmarshal ( resp . Body . Bytes ( ) , introspectParsed ) )
assert . False ( t , introspectParsed . Active )
// unsuccessful request with an invalid client_id/client_secret
req = NewRequestWithValues ( t , "POST" , "/login/oauth/introspect" , map [ string ] string {
"token" : parsed . AccessToken ,
} )
req . Header . Add ( "Authorization" , "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpK" )
resp = MakeRequest ( t , req , http . StatusUnauthorized )
assert . Contains ( t , resp . Body . String ( ) , "no valid authorization" )
}