@@ -10,6 +10,7 @@ import (
1010 "context"
1111 "encoding/json"
1212 "fmt"
13+ "net"
1314 "net/http"
1415 "sync"
1516 "sync/atomic"
@@ -784,6 +785,178 @@ func TestProviderConcurrency(t *testing.T) {
784785 })
785786}
786787
788+ func TestProvider_OpenIDConfigurationErrors (t * testing.T ) {
789+ const retryAfterValue = "120"
790+
791+ t .Run ("error, openid config returns 503" , func (t * testing.T ) {
792+ // Create a test server that returns 503 for OpenID configuration endpoint
793+ testServer := http .NewServeMux ()
794+ testServer .HandleFunc (idptest .OpenIDConfigurationPath , func (w http.ResponseWriter , r * http.Request ) {
795+ w .Header ().Set ("Retry-After" , retryAfterValue )
796+ w .WriteHeader (http .StatusServiceUnavailable )
797+ })
798+ server := & http.Server {Addr : "127.0.0.1:0" , Handler : testServer }
799+ listener , err := net .Listen ("tcp" , server .Addr )
800+ require .NoError (t , err )
801+ defer func () { _ = listener .Close () }()
802+
803+ go func () { _ = server .Serve (listener ) }()
804+ defer func () { _ = server .Shutdown (context .Background ()) }()
805+
806+ serverURL := fmt .Sprintf ("http://%s" , listener .Addr ().String ())
807+
808+ credentials := []idptoken.Source {
809+ {
810+ ClientID : testClientID ,
811+ ClientSecret : "test-secret" ,
812+ URL : serverURL ,
813+ },
814+ }
815+ // Use a custom HTTP client with minimal timeout and no retries
816+ httpClient := & http.Client {Timeout : 2 * time .Second }
817+ opts := idptoken.ProviderOpts {
818+ HTTPClient : httpClient ,
819+ }
820+ provider := idptoken .NewMultiSourceProviderWithOpts (credentials , opts )
821+
822+ _ , err = provider .GetToken (context .Background (), testClientID , serverURL )
823+ var svcUnavailableErr * idptoken.ServiceUnavailableError
824+ require .ErrorAs (t , err , & svcUnavailableErr )
825+ require .Equal (t , retryAfterValue , svcUnavailableErr .RetryAfter )
826+ })
827+
828+ t .Run ("error, openid config returns 429" , func (t * testing.T ) {
829+ // Create a test server that returns 429 for OpenID configuration endpoint
830+ testServer := http .NewServeMux ()
831+ testServer .HandleFunc (idptest .OpenIDConfigurationPath , func (w http.ResponseWriter , r * http.Request ) {
832+ w .Header ().Set ("Retry-After" , retryAfterValue )
833+ w .WriteHeader (http .StatusTooManyRequests )
834+ })
835+ server := & http.Server {Addr : "127.0.0.1:0" , Handler : testServer }
836+ listener , err := net .Listen ("tcp" , server .Addr )
837+ require .NoError (t , err )
838+ defer func () { _ = listener .Close () }()
839+
840+ go func () { _ = server .Serve (listener ) }()
841+ defer func () { _ = server .Shutdown (context .Background ()) }()
842+
843+ serverURL := fmt .Sprintf ("http://%s" , listener .Addr ().String ())
844+
845+ credentials := []idptoken.Source {
846+ {
847+ ClientID : testClientID ,
848+ ClientSecret : "test-secret" ,
849+ URL : serverURL ,
850+ },
851+ }
852+ // Use a custom HTTP client with minimal timeout and no retries
853+ httpClient := & http.Client {Timeout : 2 * time .Second }
854+ opts := idptoken.ProviderOpts {
855+ HTTPClient : httpClient ,
856+ }
857+ provider := idptoken .NewMultiSourceProviderWithOpts (credentials , opts )
858+
859+ _ , err = provider .GetToken (context .Background (), testClientID , serverURL )
860+ var throttledErr * idptoken.ThrottledError
861+ require .ErrorAs (t , err , & throttledErr )
862+ require .Equal (t , retryAfterValue , throttledErr .RetryAfter )
863+ })
864+ }
865+
866+ func TestProvider_TokenEndpointErrors (t * testing.T ) {
867+ const retryAfterValue = "120"
868+
869+ t .Run ("error, token endpoint returns 503" , func (t * testing.T ) {
870+ // Create a test server that returns 503 for token endpoint
871+ testServer := http .NewServeMux ()
872+ // OpenID configuration needs to be served to get the token URL
873+ testServer .HandleFunc (idptest .OpenIDConfigurationPath , func (w http.ResponseWriter , r * http.Request ) {
874+ w .Header ().Set ("Content-Type" , "application/json" )
875+ resp := map [string ]string {
876+ "token_endpoint" : fmt .Sprintf ("http://%s%s" , r .Host , idptest .TokenEndpointPath ),
877+ }
878+ _ = json .NewEncoder (w ).Encode (resp )
879+ })
880+ testServer .HandleFunc (idptest .TokenEndpointPath , func (w http.ResponseWriter , r * http.Request ) {
881+ w .Header ().Set ("Retry-After" , retryAfterValue )
882+ w .WriteHeader (http .StatusServiceUnavailable )
883+ })
884+ server := & http.Server {Addr : "127.0.0.1:0" , Handler : testServer }
885+ listener , err := net .Listen ("tcp" , server .Addr )
886+ require .NoError (t , err )
887+ defer func () { _ = listener .Close () }()
888+
889+ go func () { _ = server .Serve (listener ) }()
890+ defer func () { _ = server .Shutdown (context .Background ()) }()
891+
892+ serverURL := fmt .Sprintf ("http://%s" , listener .Addr ().String ())
893+
894+ credentials := []idptoken.Source {
895+ {
896+ ClientID : testClientID ,
897+ ClientSecret : "test-secret" ,
898+ URL : serverURL ,
899+ },
900+ }
901+ // Use a custom HTTP client with minimal timeout and no retries
902+ httpClient := & http.Client {Timeout : 2 * time .Second }
903+ opts := idptoken.ProviderOpts {
904+ HTTPClient : httpClient ,
905+ }
906+ provider := idptoken .NewMultiSourceProviderWithOpts (credentials , opts )
907+
908+ _ , err = provider .GetToken (context .Background (), testClientID , serverURL )
909+ var svcUnavailableErr * idptoken.ServiceUnavailableError
910+ require .ErrorAs (t , err , & svcUnavailableErr )
911+ require .Equal (t , retryAfterValue , svcUnavailableErr .RetryAfter )
912+ })
913+
914+ t .Run ("error, token endpoint returns 429" , func (t * testing.T ) {
915+ // Create a test server that returns 429 for token endpoint
916+ testServer := http .NewServeMux ()
917+ // OpenID configuration needs to be served to get the token URL
918+ testServer .HandleFunc (idptest .OpenIDConfigurationPath , func (w http.ResponseWriter , r * http.Request ) {
919+ w .Header ().Set ("Content-Type" , "application/json" )
920+ resp := map [string ]string {
921+ "token_endpoint" : fmt .Sprintf ("http://%s%s" , r .Host , idptest .TokenEndpointPath ),
922+ }
923+ _ = json .NewEncoder (w ).Encode (resp )
924+ })
925+ testServer .HandleFunc (idptest .TokenEndpointPath , func (w http.ResponseWriter , r * http.Request ) {
926+ w .Header ().Set ("Retry-After" , retryAfterValue )
927+ w .WriteHeader (http .StatusTooManyRequests )
928+ })
929+ server := & http.Server {Addr : "127.0.0.1:0" , Handler : testServer }
930+ listener , err := net .Listen ("tcp" , server .Addr )
931+ require .NoError (t , err )
932+ defer func () { _ = listener .Close () }()
933+
934+ go func () { _ = server .Serve (listener ) }()
935+ defer func () { _ = server .Shutdown (context .Background ()) }()
936+
937+ serverURL := fmt .Sprintf ("http://%s" , listener .Addr ().String ())
938+
939+ credentials := []idptoken.Source {
940+ {
941+ ClientID : testClientID ,
942+ ClientSecret : "test-secret" ,
943+ URL : serverURL ,
944+ },
945+ }
946+ // Use a custom HTTP client with minimal timeout and no retries
947+ httpClient := & http.Client {Timeout : 2 * time .Second }
948+ opts := idptoken.ProviderOpts {
949+ HTTPClient : httpClient ,
950+ }
951+ provider := idptoken .NewMultiSourceProviderWithOpts (credentials , opts )
952+
953+ _ , err = provider .GetToken (context .Background (), testClientID , serverURL )
954+ var throttledErr * idptoken.ThrottledError
955+ require .ErrorAs (t , err , & throttledErr )
956+ require .Equal (t , retryAfterValue , throttledErr .RetryAfter )
957+ })
958+ }
959+
787960type claimsProviderWithExpiration struct {
788961 ExpTime time.Duration
789962}
0 commit comments