Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Provider] Garmin: oauth_verifier in params in getRequestAuthorization #4

Closed
matamalabs opened this issue May 30, 2024 · 15 comments
Closed
Labels
provider OAuth Provider support

Comments

@matamalabs
Copy link

To get the User Access Token from Garmin Connect Api, I've used the public method getAccessToken in OAuth1Provider.php. In the public method getRequestAuthorization I've added oauth_verifier in the params:

public function getRequestAuthorization(RequestInterface $request, AccessToken|null $token = null):RequestInterface{
		$token ??= $this->storage->getAccessToken($this->name);

		if($token->isExpired()){
			throw new InvalidAccessTokenException;
		}

		$params = [
			'oauth_consumer_key'     => $this->options->key,
			'oauth_nonce'            => $this->nonce(),
			'oauth_signature_method' => 'HMAC-SHA1',
			'oauth_timestamp'        => time(),
			'oauth_token'            => $token->accessToken,
			'oauth_version'          => '1.0',
                        'oauth_verifier'          => $verifier,
		];

		$params['oauth_signature'] = $this->getSignature(
			$request->getUri(),
			$params,
			$request->getMethod(),
			$token->accessTokenSecret,
		);

		return $request->withHeader('Authorization', sprintf('OAuth %s', QueryUtil::build($params, null, ', ', '"')));
}

Because without oauth_verifier I got an error in the response from Guzzle.

@matamalabs matamalabs added the bug Something isn't working label May 30, 2024
@codemasher
Copy link
Member

Hi, this is not a bug as this library does not offer a Garmin provider class (yet).
I'm going to re-label this issue in order to add support for it. Is there a public available API documentation and/or can you provide me with details?

@codemasher codemasher added provider OAuth Provider support and removed bug Something isn't working labels May 30, 2024
@codemasher codemasher changed the title [BUG] oauth_verifier in params in getRequestAuthorization [Provider] Garmin: oauth_verifier in params in getRequestAuthorization May 30, 2024
@codemasher
Copy link
Member

@matamalabs btw where does the method in your code snippet get the $verifierfrom? It seems that this parameter is undefined there, which will cause the underlying methods to remove it from the request.

@matamalabs
Copy link
Author

Hi, this is not a bug as this library does not offer a Garmin provider class (yet). I'm going to re-label this issue in order to add support for it. Is there a public available API documentation and/or can you provide me with details?

The Garmin Connect API is private so I'm not available to provide you. You can register on https://developer.garmin.com/gc-developer-program/health-api/

@matamalabs
Copy link
Author

matamalabs commented May 30, 2024

@matamalabs btw where does the method in your code snippet get the $verifierfrom? It seems that this parameter is undefined there, which will cause the underlying methods to remove it from the request.

Actually, I do:

public function getRequestAuthorization(RequestInterface $request, AccessToken|null $token = null):RequestInterface{
		$token ??= $this->storage->getAccessToken($this->name);

		if($token->isExpired()){
			throw new InvalidAccessTokenException;
		}

		$query = $request->getUri()->getQuery();
		$queryList = explode("=", $query);

		$params = [
			'oauth_consumer_key'     => $this->options->key,
			'oauth_nonce'            => $this->nonce(),
			'oauth_signature_method' => 'HMAC-SHA1',
			'oauth_timestamp'        => time(),
			'oauth_token'            => $token->accessToken,
			'oauth_version'          => '1.0',
			'oauth_verifier'         => $queryList[1],
		];

		$params['oauth_signature'] = $this->getSignature(
			$request->getUri(),
			$params,
			$request->getMethod(),
			$token->accessTokenSecret,
		);

		$header_auth = sprintf('OAuth %s', QueryUtil::build($params, null, ', ', '"'));

		return $request->withHeader('Authorization', $header_auth);
}

From $request param I can get the oauth_verifier, which is added to uri in sendAccessTokenRequest method.

@matamalabs
Copy link
Author

matamalabs commented May 30, 2024

$verifier could be an optional param in getRequestAuthorization method to make the $params with oauth_verifier?

@codemasher
Copy link
Member

Adding a non-interface parameter to this method is not advised as it is used in several places. This is a provider specific quirk that deviates from the RFC as the oauth_verifier parameter is usually only used during token requests - the getRequestAuthorization() method on the other hand is used for API requests with an existing token.

You can simplify fetching the parameter from the URI btw with the following:

$queryList = QueryUtil::parse($request->getUri()->getQuery());

@codemasher
Copy link
Member

Wait, I need to correct myself: the getRequestAuthorization() method is also used in the token request method, where the verifier is added from the incoming callback:

/**
* Sends the access token request
*
* @see \chillerlan\OAuth\Core\OAuth1Provider::getAccessToken()
*/
protected function sendAccessTokenRequest(string $verifier):ResponseInterface{
$request = $this->requestFactory
->createRequest('POST', QueryUtil::merge($this->accessTokenURL, ['oauth_verifier' => $verifier]))
->withHeader('Accept-Encoding', 'identity')
->withHeader('Content-Length', '0')
;
$request = $this->getRequestAuthorization($request);
return $this->http->sendRequest($request);
}

@matamalabs
Copy link
Author

matamalabs commented May 30, 2024

Wait, I need to correct myself: the getRequestAuthorization() method is also used in the token request method, where the verifier is added from the incoming callback:

/**
* Sends the access token request
*
* @see \chillerlan\OAuth\Core\OAuth1Provider::getAccessToken()
*/
protected function sendAccessTokenRequest(string $verifier):ResponseInterface{
$request = $this->requestFactory
->createRequest('POST', QueryUtil::merge($this->accessTokenURL, ['oauth_verifier' => $verifier]))
->withHeader('Accept-Encoding', 'identity')
->withHeader('Content-Length', '0')
;
$request = $this->getRequestAuthorization($request);
return $this->http->sendRequest($request);
}

In that getRequestAuthorization method from sendAccessTokenRequest method is where I'm needing the oauth_verifier to build the $params array.

In fact, getRequestAuthorization method is only used in sendAccessTokenRequest method.

@codemasher
Copy link
Member

codemasher commented May 30, 2024

In that getRequestAuthorization method from sendAccessTokenRequest method is where I'm needing the oauth_verifier to build the $params array

That is the $_GET parameter from the incoming callback then, which you need to add to the getAccessToken() method as shown in the example:

$token = $provider->getAccessToken($_GET['oauth_token'], $_GET['oauth_verifier']);

@matamalabs
Copy link
Author

In that getRequestAuthorization method from sendAccessTokenRequest method is where I'm needing the oauth_verifier to build the $params array

That is the $_GET parameter from the incoming request then, which you need to add to the getAccessToken() method as shown in the example:

$token = $provider->getAccessToken($_GET['oauth_token'], $_GET['oauth_verifier']);

I do that but I need the oauth_verifier in getRequestAuthorization method to build the $params aray such as:

public function getRequestAuthorization(RequestInterface $request, AccessToken|null $token = null):RequestInterface{
		$token ??= $this->storage->getAccessToken($this->name);

		if($token->isExpired()){
			throw new InvalidAccessTokenException;
		}

		$query = $request->getUri()->getQuery();
		$queryList = explode("=", $query);

		$params = [
			'oauth_consumer_key'     => $this->options->key,
			'oauth_nonce'            => $this->nonce(),
			'oauth_signature_method' => 'HMAC-SHA1',
			'oauth_timestamp'        => time(),
			'oauth_token'            => $token->accessToken,
			'oauth_version'          => '1.0',
			'oauth_verifier'         => $queryList[1],
		];

		$params['oauth_signature'] = $this->getSignature(
			$request->getUri(),
			$params,
			$request->getMethod(),
			$token->accessTokenSecret,
		);

		$header_auth = sprintf('OAuth %s', QueryUtil::build($params, null, ', ', '"'));

		return $request->withHeader('Authorization', $header_auth);
}

To get the User Access Token for Garmin Connect Api the header "Authorization OAuth..." needs the oauth_verifier to work fine.

@codemasher
Copy link
Member

Hmm, let me check what the other OAuth1 providers do with this parameter in the signature array, to see if this could be added in general (this might take me a moment).

@matamalabs
Copy link
Author

Hmm, let me check what the other OAuth1 providers do with this parameter in the signature array, to see if this could be added in general (this might take me a moment).

Sure. Thanks.

@codemasher
Copy link
Member

Ok that was a larger rework - I've added a separate method for the request headers, as the oauth_verifier is, in fact, part of the signature as per RFC. I can't recall why I still had it in the request URL instead - I think that's a remnant from earlier twitter implementations or something. Funny enough that all of the built-in providers seem to support both (or don't even check for the verifier... who knows).

Can you please check out from dev-main#06f74facb4bbc7167a71a7533bda46832dd660fd and let me know if the update works for you?

@matamalabs
Copy link
Author

Ok that was a larger rework - I've added a separate method for the request headers, as the oauth_verifier is, in fact, part of the signature as per RFC. I can't recall why I still had it in the request URL instead - I think that's a remnant from earlier twitter implementations or something. Funny enough that all of the built-in providers seem to support both (or don't even check for the verifier... who knows).

Can you please check out from dev-main#06f74facb4bbc7167a71a7533bda46832dd660fd and let me know if the update works for you?

Hi. Sorry for letting you know so late. The update is working for me.

Thank you so much and great job!

@codemasher
Copy link
Member

That's great news! I'm going to tag it as version 1.0.1 then, which you can use in your composer.json.
Would you mind sharing your Garmin provider class so that I can add it to the library? Unfortunately applying for API access there has several requirements I cannot fulfil.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
provider OAuth Provider support
Projects
None yet
Development

No branches or pull requests

2 participants