saloonphp / saloon

🤠 Build beautiful API integrations and SDKs with Saloon
https://docs.saloon.dev
MIT License
2.03k stars 105 forks source link

OAuth2 - Method not allowed (405) when getting the access Token #386

Closed labomatik closed 5 months ago

labomatik commented 5 months ago

Hello,

I'm trying to implement a new API connection using OAuth2 but i'm facing a 405 error when trying to get the access token. The counter part of the API is correctly sending the data:

~/Desktop ⮀ curl 'https://auth.acc.connect.easypost.eu/oauth2/token' \
   --data-raw 'grant_type=client_credentials'

{"error":"invalid_client"}%

The code is the following:

$easypost = new EasyPostConnector("123", "secretPass");

        $authenticator = $easypost->getAccessToken([
            "connect/read:jobs",
            "connect/submit:jobs",
            "connect/read:sending-events"
        ]);

        $easypost->authenticate($authenticator);

The connector is containing the defaultOauthConfig

protected function defaultOauthConfig(): OAuthConfig
    {
        return OAuthConfig::make()
            ->setDefaultScopes(['connect/read:jobs', 'connect/submit:jobs', 'connect/read:sending-events'])
            ->setTokenEndpoint('https://auth.acc.connect.easypost.eu/oauth2/token');

    }

The error thrown:

Saloon\Exceptions\Request\Statuses\MethodNotAllowedException 

Method Not Allowed (  405) Response:

at vendor/saloonphp/saloon/src/Helpers/RequestExceptionHelper.php:51
  47▕             $response->clientError() => ClientException::class,
  48▕             default => RequestException::class,
  49▕         };
  50▕ 
➜ 51▕         return new $requestException($response, null,   0, $previous);
  52▕     }
  53▕ }
  54▕

Do you have any idea why the result is 405?

Sammyjo20 commented 5 months ago

Hey @labomatik I'm sorry for the delay in getting back to you and thank you very much for the GitHub Sponsor! It means a lot. 405 usually means that the HTTP status code is wrong - the getAccessToken method usually uses a POST request. Does the API you are integrating with require a different HTTP status code? Maybe PUT/PATCH?

labomatik commented 5 months ago

Hello, No problem for the delay :-) It's an open source package

I've tried this already in postman to make sure the API is not using another HTTP request

POST https://auth.acc.connect.easypost.eu/oauth2/token?grant_type=client_credentials&scope=connect%2Fread%3Ajobs+connect%2Fsubmit%3Ajobs+connect%2Fread%3Asending-events

The result is a correct json {"error":"invalid_client"} .

This is also available in the API doc: https://documentation.acc.connect.easypost.eu/static/index.html

Somehow it's not working with the request i'm doing with saloon. I guess it's something stupid i didn't configured...

labomatik commented 5 months ago

I've tried the new ->debug() with no luck... The result is the same, seems like saloon didn't execute the request.

juse-less commented 5 months ago

@labomatik Hi!

Have you tried wrapping the getAccessToken() in try-catch for the RequestException, and then dump the Request, and PendingRequest? It'd be interesting to see which Methods they show, since HTTP 405 means that the HTTP method used is not allowed. Also do dump the headers on the Response, because they must return an Allow header with the HTTP methods allowed.

Would it also be possible to get some more of the Connector code, as well as a full stack trace? I can't see anything immediately obvious in the Saloon code that'd cause this. So any code and details you can provide would help.

labomatik commented 5 months ago

This is the trace:


App\Http\Integrations\EasyPost\EasyPostConnector#1
(
    [*:authenticator] => null
    [*:headers] => Saloon\Repositories\ArrayStore#2
    (
        [*:data] => [        
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ]
    )
    [*:query] => Saloon\Repositories\ArrayStore#3
    (
        [*:data] => [],
    )
    [*:config] => Saloon\Repositories\ArrayStore#4
    (
        [*:data] => [],
    )
    [*:middlewarePipeline] => Saloon\Helpers\MiddlewarePipeline#5
    (
        [*:requestPipeline] => Saloon\Helpers\Pipeline#6
        (
            [*:pipes] => [],
        )
        [*:responsePipeline] => Saloon\Helpers\Pipeline#7
        (
            [*:pipes] => [],
        )
    )
    [*:delay] => Saloon\Repositories\IntegerStore#8
    (
        [*:data] => null
    )
    [*:response] => null
    [*:mockClient] => null
    [*:defaultSender] => ''
    [*:sender] => Saloon\Http\Senders\GuzzleSender#9
    (
        [*:client] => GuzzleHttp\Client#10
        (
            [GuzzleHttp\Client:config] => [            
                'crypto_method' => 33,
                'connect_timeout' => 10,
                'timeout' => 30,
                'http_errors' => true,
                'handler' => GuzzleHttp\HandlerStack#11
                (
                    [GuzzleHttp\HandlerStack:handler] => Closure(...)
                    [GuzzleHttp\HandlerStack:stack] => [...],
                    [GuzzleHttp\HandlerStack:cached] => Closure(...)
                ),
                'allow_redirects' => [                
                    'max' => 5,
                    'protocols' => [...],,
                    'strict' => false,
                    'referer' => false,
                    'track_redirects' => false,
                ],
                'decode_content' => true,
                'verify' => true,
                'cookies' => false,
                'idn_conversion' => false,
                'headers' => [                
                    'User-Agent' => 'GuzzleHttp/7',
                ],
            ]
        )
        [*:handlerStack] => GuzzleHttp\HandlerStack#11(...)
    )
    [tries] => null
    [retryInterval] => null
    [useExponentialBackoff] => null
    [throwOnMaxTries] => null
    [*:connectTimeout] => 60
    [*:requestTimeout] => 120
    [*:oauthConfig] => Saloon\Helpers\OAuth2\OAuthConfig#12
    (
        [*:clientId] => 'qsd'
        [*:clientSecret] => 'sd'
        [*:redirectUri] => ''
        [*:authorizeEndpoint] => 'authorize'
        [*:tokenEndpoint] => 'https://auth.acc.connect.easypost.eu/oauth2/token'
        [*:userEndpoint] => 'user'
        [*:requestModifier] => null
        [*:defaultScopes] => [        
            0 => 'connect/read:jobs',
            1 => 'connect/submit:jobs',
            2 => 'connect/read:sending-events',
        ]
    )
)
Saloon\Exceptions\Request\Statuses\MethodNotAllowedException#1
(
    [*:message] => 'Method Not Allowed (405) Response: '
    [Exception:string] => ''
    [*:code] => 0
    [*:file] => 'vendor/saloonphp/saloon/src/Helpers/RequestExceptionHelper.php'
    [*:line] => 51
    [Exception:trace] => [    
        0 => [        
            'file' => 'vendor/saloonphp/saloon/src/Http/Response.php',
            'line' => 450,
            'function' => 'create',
            'class' => 'Saloon\\Helpers\\RequestExceptionHelper',
            'type' => '::',
            'args' => [            
                0 => Saloon\Http\Response#2
                (
                    [*:psrRequest] => GuzzleHttp\Psr7\Request(...)
                    [*:psrResponse] => GuzzleHttp\Psr7\Response(...)
                    [*:pendingRequest] => Saloon\Http\PendingRequest(...)
                    [*:senderException] => GuzzleHttp\Exception\ClientException(...)
                    [*:mocked] => false
                    [*:cached] => false
                    [*:fakeResponse] => null
                ),
                1 => GuzzleHttp\Exception\ClientException#3
                (
                    [*:message] => 'Client error: `POST https://auth.acc.connect.easypost.eu/oauth2/token` resulted in a `405 Method Not Allowed` response'
                    [Exception:string] => ''
                    [*:code] => 405
                    [*:file] => 'vendor/guzzlehttp/guzzle/src/Exception/RequestException.php'
                    [*:line] => 113
                    [Exception:trace] => [...],
                    [Exception:previous] => null
                    [GuzzleHttp\Exception\RequestException:request] => GuzzleHttp\Psr7\Request(...)
                    [GuzzleHttp\Exception\RequestException:response] => GuzzleHttp\Psr7\Response(...)
                    [GuzzleHttp\Exception\RequestException:handlerContext] => [...],
                ),
            ],
        ],
        1 => [        
            'file' => 'vendor/saloonphp/saloon/src/Http/Response.php',
            'line' => 428,
            'function' => 'createException',
            'class' => 'Saloon\\Http\\Response',
            'type' => '->',
            'args' => [],,
        ],
        2 => [        
            'file' => 'vendor/saloonphp/saloon/src/Http/Response.php',
            'line' => 462,
            'function' => 'toException',
            'class' => 'Saloon\\Http\\Response',
            'type' => '->',
            'args' => [],,
        ],
        3 => [        
            'file' => 'vendor/saloonphp/saloon/src/Traits/Plugins/AlwaysThrowOnErrors.php',
            'line' => 23,
            'function' => 'throw',
            'class' => 'Saloon\\Http\\Response',
            'type' => '->',
            'args' => [],,
        ],
        4 => [        
            'file' => 'vendor/saloonphp/saloon/src/Helpers/MiddlewarePipeline.php',
            'line' => 87,
            'function' => 'Saloon\\Traits\\Plugins\\{closure}',
            'class' => 'App\\Http\\Integrations\\EasyPost\\EasyPostConnector',
            'type' => '::',
            'args' => [            
                0 => Saloon\Http\Response#2(...),
            ],
        ],
        5 => [        
            'function' => 'Saloon\\Helpers\\{closure}',
            'class' => 'Saloon\\Helpers\\MiddlewarePipeline',
            'type' => '::',
            'args' => [            
                0 => Saloon\Http\Response#2(...),
            ],
        ],
        6 => [        
            'file' => 'vendor/saloonphp/saloon/src/Helpers/Pipeline.php',
            'line' => 45,
            'function' => 'call_user_func',
            'args' => [            
                0 => Closure#4
                (
                    [0] => Closure#4(...)
                ),
                1 => Saloon\Http\Response#2(...),
            ],
        ],
        7 => [        
            'file' => 'vendor/saloonphp/saloon/src/Helpers/MiddlewarePipeline.php',
            'line' => 108,
            'function' => 'process',
            'class' => 'Saloon\\Helpers\\Pipeline',
            'type' => '->',
            'args' => [            
                0 => Saloon\Http\Response#2(...),
            ],
        ],
        8 => [        
            'file' => 'vendor/saloonphp/saloon/src/Http/PendingRequest.php',
            'line' => 152,
            'function' => 'executeResponsePipeline',
            'class' => 'Saloon\\Helpers\\MiddlewarePipeline',
            'type' => '->',
            'args' => [            
                0 => Saloon\Http\Response#2(...),
            ],
        ],
        9 => [        
            'file' => 'vendor/saloonphp/saloon/src/Traits/Connector/SendsRequests.php',
            'line' => 78,
            'function' => 'executeResponsePipeline',
            'class' => 'Saloon\\Http\\PendingRequest',
            'type' => '->',
            'args' => [            
                0 => Saloon\Http\Response#2(...),
            ],
        ],
        10 => [        
            'file' => 'vendor/saloonphp/saloon/src/Traits/OAuth2/ClientCredentialsGrant.php',
            'line' => 40,
            'function' => 'send',
            'class' => 'Saloon\\Http\\Connector',
            'type' => '->',
            'args' => [            
                0 => Saloon\Http\OAuth2\GetClientCredentialsTokenRequest#5
                (
                    [*:method] => Saloon\Enums\Method(...)
                    [*:authenticator] => null
                    [*:headers] => Saloon\Repositories\ArrayStore(...)
                    [*:query] => Saloon\Repositories\ArrayStore(...)
                    [*:config] => Saloon\Repositories\ArrayStore(...)
                    [*:middlewarePipeline] => Saloon\Helpers\MiddlewarePipeline(...)
                    [*:delay] => Saloon\Repositories\IntegerStore(...)
                    [*:response] => null
                    [*:mockClient] => null
                    [tries] => null
                    [retryInterval] => null
                    [useExponentialBackoff] => null
                    [throwOnMaxTries] => null
                    [*:oauthConfig] => Saloon\Helpers\OAuth2\OAuthConfig(...)
                    [*:scopes] => [...],
                    [*:scopeSeparator] => ' '
                    [*:body] => Saloon\Repositories\Body\FormBodyRepository(...)
                ),
            ],
        ],
        11 => [        
            'file' => 'app/Console/Commands/TEMPEasyPostApi.php',
            'line' => 37,
            'function' => 'getAccessToken',
            'class' => 'App\\Http\\Integrations\\EasyPost\\EasyPostConnector',
            'type' => '->',
            'args' => [            
                0 => [                
                    0 => 'connect/read:jobs',
                    1 => 'connect/submit:jobs',
                    2 => 'connect/read:sending-events',
                ],
            ],
        ],
        12 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php',
            'line' => 36,
            'function' => 'handle',
            'class' => 'App\\Console\\Commands\\TEMPEasyPostApi',
            'type' => '->',
            'args' => [],,
        ],
        13 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Container/Util.php',
            'line' => 41,
            'function' => 'Illuminate\\Container\\{closure}',
            'class' => 'Illuminate\\Container\\BoundMethod',
            'type' => '::',
            'args' => [],,
        ],
        14 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php',
            'line' => 93,
            'function' => 'unwrapIfClosure',
            'class' => 'Illuminate\\Container\\Util',
            'type' => '::',
            'args' => [            
                0 => Closure#6
                (
                    [0] => Closure#6(...)
                ),
            ],
        ],
        15 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php',
            'line' => 37,
            'function' => 'callBoundMethod',
            'class' => 'Illuminate\\Container\\BoundMethod',
            'type' => '::',
            'args' => [            
                0 => Illuminate\Foundation\Application#7
                (
                    [*:resolved] => [...],
                    [*:bindings] => [...],
                    [*:methodBindings] => [...],
                    [*:instances] => [...],
                    [*:scopedInstances] => [...],
                    [*:aliases] => [...],
                    [*:abstractAliases] => [...],
                    [*:extenders] => [...],
                    [*:tags] => [...],
                    [*:buildStack] => [...],
                    [*:with] => [...],
                    [contextual] => [...],
                    [*:reboundCallbacks] => [...],
                    [*:globalBeforeResolvingCallbacks] => [...],
                    [*:globalResolvingCallbacks] => [...],
                    [*:globalAfterResolvingCallbacks] => [...],
                    [*:beforeResolvingCallbacks] => [...],
                    [*:resolvingCallbacks] => [...],
                    [*:afterResolvingCallbacks] => [...],
                    [*:basePath] => ''
                    [*:hasBeenBootstrapped] => true
                    [*:booted] => true
                    [*:bootingCallbacks] => [...],
                    [*:bootedCallbacks] => [...],
                    [*:terminatingCallbacks] => [...],
                    [*:serviceProviders] => [...],
                    [*:loadedProviders] => [...],
                    [*:deferredServices] => [...],
                    [*:bootstrapPath] => 'bootstrap'
                    [*:appPath] => null
                    [*:configPath] => null
                    [*:databasePath] => null
                    [*:langPath] => 'lang'
                    [*:publicPath] => null
                    [*:storagePath] => null
                    [*:environmentPath] => null
                    [*:environmentFile] => '.env'
                    [*:isRunningInConsole] => true
                    [*:namespace] => 'App\\'
                    [*:absoluteCachePathPrefixes] => [...],
                ),
                1 => [                
                    0 => App\Console\Commands\TEMPEasyPostApi(...),
                    1 => 'handle',
                ],
                2 => Closure#6(...),
            ],
        ],
        16 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Container/Container.php',
            'line' => 662,
            'function' => 'call',
            'class' => 'Illuminate\\Container\\BoundMethod',
            'type' => '::',
            'args' => [            
                0 => Illuminate\Foundation\Application#7(...),
                1 => [                
                    0 => App\Console\Commands\TEMPEasyPostApi(...),
                    1 => 'handle',
                ],
                2 => [],,
                3 => null,
            ],
        ],
        17 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Console/Command.php',
            'line' => 211,
            'function' => 'call',
            'class' => 'Illuminate\\Container\\Container',
            'type' => '->',
            'args' => [            
                0 => [                
                    0 => App\Console\Commands\TEMPEasyPostApi(...),
                    1 => 'handle',
                ],
            ],
        ],
        18 => [        
            'file' => 'vendor/symfony/console/Command/Command.php',
            'line' => 326,
            'function' => 'execute',
            'class' => 'Illuminate\\Console\\Command',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8
                (
                    [*:definition] => Symfony\Component\Console\Input\InputDefinition(...)
                    [*:stream] => null
                    [*:options] => [...],
                    [*:arguments] => [...],
                    [*:interactive] => true
                    [Symfony\Component\Console\Input\ArgvInput:tokens] => [...],
                    [Symfony\Component\Console\Input\ArgvInput:parsed] => [...],
                ),
                1 => Illuminate\Console\OutputStyle#9
                (
                    [Symfony\Component\Console\Style\OutputStyle:output] => Symfony\Component\Console\Output\ConsoleOutput(...)
                    [Symfony\Component\Console\Style\SymfonyStyle:input] => Symfony\Component\Console\Input\ArgvInput#8(...)
                    [Symfony\Component\Console\Style\SymfonyStyle:output] => Symfony\Component\Console\Output\ConsoleOutput(...)
                    [Symfony\Component\Console\Style\SymfonyStyle:lineLength] => 120
                    [Symfony\Component\Console\Style\SymfonyStyle:bufferedOutput] => Symfony\Component\Console\Output\TrimmedBufferOutput(...)
                    [Illuminate\Console\OutputStyle:output] => Symfony\Component\Console\Output\ConsoleOutput(...)
                    [*:newLinesWritten] => 1
                    [*:newLineWritten] => false
                ),
            ],
        ],
        19 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Console/Command.php',
            'line' => 181,
            'function' => 'run',
            'class' => 'Symfony\\Component\\Console\\Command\\Command',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8(...),
                1 => Illuminate\Console\OutputStyle#9(...),
            ],
        ],
        20 => [        
            'file' => 'vendor/symfony/console/Application.php',
            'line' => 1096,
            'function' => 'run',
            'class' => 'Illuminate\\Console\\Command',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8(...),
                1 => Symfony\Component\Console\Output\ConsoleOutput#10
                (
                    [Symfony\Component\Console\Output\Output:verbosity] => 32
                    [Symfony\Component\Console\Output\Output:formatter] => Symfony\Component\Console\Formatter\OutputFormatter(...)
                    [Symfony\Component\Console\Output\StreamOutput:stream] => {resource}
                    [Symfony\Component\Console\Output\ConsoleOutput:stderr] => Symfony\Component\Console\Output\StreamOutput(...)
                    [Symfony\Component\Console\Output\ConsoleOutput:consoleSectionOutputs] => [...],
                ),
            ],
        ],
        21 => [        
            'file' => 'vendor/symfony/console/Application.php',
            'line' => 324,
            'function' => 'doRunCommand',
            'class' => 'Symfony\\Component\\Console\\Application',
            'type' => '->',
            'args' => [            
                0 => App\Console\Commands\TEMPEasyPostApi#11
                (
                    [Symfony\Component\Console\Command\Command:application] => Illuminate\Console\Application(...)
                    [Symfony\Component\Console\Command\Command:name] => 'easypost:test'
                    [Symfony\Component\Console\Command\Command:processTitle] => null
                    [Symfony\Component\Console\Command\Command:aliases] => [...],
                    [Symfony\Component\Console\Command\Command:definition] => Symfony\Component\Console\Input\InputDefinition(...)
                    [Symfony\Component\Console\Command\Command:hidden] => false
                    [Symfony\Component\Console\Command\Command:help] => ''
                    [Symfony\Component\Console\Command\Command:description] => 'Command description'
                    [Symfony\Component\Console\Command\Command:fullDefinition] => Symfony\Component\Console\Input\InputDefinition(...)
                    [Symfony\Component\Console\Command\Command:ignoreValidationErrors] => false
                    [Symfony\Component\Console\Command\Command:code] => null
                    [Symfony\Component\Console\Command\Command:synopsis] => [...],
                    [Symfony\Component\Console\Command\Command:usages] => [...],
                    [Symfony\Component\Console\Command\Command:helperSet] => Symfony\Component\Console\Helper\HelperSet(...)
                    [*:laravel] => Illuminate\Foundation\Application#7(...)
                    [*:signature] => 'easypost:test'
                    [*:name] => 'easypost:test'
                    [*:description] => 'Command description'
                    [*:help] => null
                    [*:hidden] => false
                    [*:isolated] => false
                    [*:isolatedExitCode] => 0
                    [*:aliases] => null
                    [*:components] => Illuminate\Console\View\Components\Factory(...)
                    [*:input] => Symfony\Component\Console\Input\ArgvInput#8(...)
                    [*:output] => Illuminate\Console\OutputStyle#9(...)
                    [*:verbosity] => 32
                    [*:verbosityMap] => [...],
                    [*:signals] => null
                ),
                1 => Symfony\Component\Console\Input\ArgvInput#8(...),
                2 => Symfony\Component\Console\Output\ConsoleOutput#10(...),
            ],
        ],
        22 => [        
            'file' => 'vendor/symfony/console/Application.php',
            'line' => 175,
            'function' => 'doRun',
            'class' => 'Symfony\\Component\\Console\\Application',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8(...),
                1 => Symfony\Component\Console\Output\ConsoleOutput#10(...),
            ],
        ],
        23 => [        
            'file' => 'vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php',
            'line' => 201,
            'function' => 'run',
            'class' => 'Symfony\\Component\\Console\\Application',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8(...),
                1 => Symfony\Component\Console\Output\ConsoleOutput#10(...),
            ],
        ],
        24 => [        
            'file' => 'artisan',
            'line' => 37,
            'function' => 'handle',
            'class' => 'Illuminate\\Foundation\\Console\\Kernel',
            'type' => '->',
            'args' => [            
                0 => Symfony\Component\Console\Input\ArgvInput#8(...),
                1 => Symfony\Component\Console\Output\ConsoleOutput#10(...),
            ],
        ],
    ]
    [Exception:previous] => GuzzleHttp\Exception\ClientException#3(...)
    [*:response] => Saloon\Http\Response#2(...)
    [*:maxBodyLength] => 200
)

And the connector:

<?php

namespace App\Http\Integrations\EasyPost;

use Saloon\Helpers\OAuth2\OAuthConfig;
use Saloon\Http\Connector;
use Saloon\Traits\OAuth2\ClientCredentialsGrant;
use Saloon\Traits\Plugins\AcceptsJson;
use Saloon\Traits\Plugins\AlwaysThrowOnErrors;
use Saloon\Traits\Plugins\HasTimeout;

class EasyPostConnector extends Connector
{
    use AcceptsJson;
    use HasTimeout;
    use ClientCredentialsGrant;
    use AlwaysThrowOnErrors;

    protected int $connectTimeout = 60;

    protected int $requestTimeout = 120;

    public function __construct(string $clientId, string $clientSecret)
    {
        $this->oauthConfig()->setClientId($clientId);
        $this->oauthConfig()->setClientSecret($clientSecret);
    }

    /**
     * The Base URL of the API
     */
    public function resolveBaseUrl(): string
    {
        return config('services.easypost.base_url');
    }

    /**
     * Default headers for every request
     */
    protected function defaultHeaders(): array
    {
        return [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ];
    }

    /**
     * Default HTTP client options
     */
    protected function defaultConfig(): array
    {
        return [];
    }

    protected function defaultOauthConfig(): OAuthConfig
    {
        return OAuthConfig::make()
            ->setDefaultScopes(['connect/read:jobs', 'connect/submit:jobs', 'connect/read:sending-events'])
            ->setTokenEndpoint('https://auth.acc.connect.easypost.eu/oauth2/token');

    }

}
juse-less commented 5 months ago

Hmm. Very odd. The cURL command you use will implicitly use POST. But the error message from Guzzle states that it's also using POST. And the URLs look identical to me.

I can't really see anything wrong. Could you maybe remove the default headers in the Connector to test if that changes anything? It's a long shot, and technically has nothing to do with the error, but worth a shot.

I'm currently on my phone, but can test some stuff a bit later today. 🙂

labomatik commented 5 months ago

Oh yes! removing the header did the trick, strange that the json is causing a 405 MethodNotAllowedException ...

juse-less commented 5 months ago

Very strange! I'd expect it to result in a HTTP 400 since the endpoint itself can handle POST.

I guess you could create a custom OAuth Request that uses the expected headers.

I don't remember off the top of my head, but if the OAuth specification (or at least Client Credentials Grant) states that form data has to be used, then Saloon could enforce it. But with the possibility to override, since some APIs don't follow various specifications.