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

New provider: Garmin Connect #3609

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

New provider: Garmin Connect #3609

wants to merge 10 commits into from

Conversation

soh55
Copy link

@soh55 soh55 commented Jan 22, 2024

Submitting Pull Requests

General

  • Make sure you use semantic commit messages.
    Examples: "fix(google): Fixed foobar bug", "feat(accounts): Added foobar feature".
  • All Python code must formatted using Black, and clean from pep8 and isort issues.
  • JavaScript code should adhere to StandardJS.
  • If your changes are significant, please update ChangeLog.rst.
  • If your change is substantial, feel free to add yourself to AUTHORS.

Provider Specifics

In case you add a new provider:

  • Make sure unit tests are available.
  • Add an entry of your provider in test_settings.py::INSTALLED_APPS and docs/installation.rst::INSTALLED_APPS.
  • Add documentation to docs/providers/<provider name>.rst and docs/providers/index.rst Provider Specifics toctree.
  • Add an entry to the list of supported providers over at docs/overview.rst. -> Couldn't find this file

@coveralls
Copy link

coveralls commented Jan 23, 2024

Coverage Status

coverage: 95.82% (+0.02%) from 95.804%
when pulling 382e9af on soh55:main
into 0d67699 on pennersr:main.


class GarminConnectOAuthClient(OAuthClient):

def get_access_token(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an almost exact copy of OAuthClient.get_access_token() -- can't we reuse that, if not, can't this be made more DRY?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm not 100% familiar with OAuth1 api, but the major change I made was to explicitly pass verifier param when constructing oauth object. I can hop on a debugger and see if we can use vanilla OAuthClient class later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah verified that using the original implementation results in 400 response from the Garmin server as it expects verifier in the header. Please let me know if you'd like extra data to review.

Copy link
Owner

@pennersr pennersr Jan 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This likely benefits other providers as well.The base class is already checking for the oauth_verifier here:

oauth_verifier = get_request_param(self.request, "oauth_verifier")

Could you change that so that it passes that verifier to the OAuth1 client, if available? Then, you don't need to override the get_access_token() method here...

Copy link
Author

@soh55 soh55 Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does modifying the base OAuthClient's get_access_token method like this work for you?

    def get_access_token(self):
        """
        Obtain the access token to access private resources at the API
        endpoint.
        """
        if self.access_token is None:
            request_token = self._get_rt_from_session()
            at_url = self.access_token_url

            # Passing along oauth_verifier is required according to:
            # http://groups.google.com/group/twitter-development-talk/browse_frm/thread/472500cfe9e7cdb9#
            # Though, the custom oauth_callback seems to work without it?
            oauth_verifier = get_request_param(self.request, "oauth_verifier")
            if oauth_verifier:
                at_url = at_url + "?" + urlencode({"oauth_verifier": oauth_verifier})

            oauth = OAuth1(
                self.consumer_key,
                client_secret=self.consumer_secret,
                resource_owner_key=request_token["oauth_token"],
                resource_owner_secret=request_token["oauth_token_secret"],
                verifier=oauth_verifier,
            )

            response = get_adapter().get_requests_session().post(url=at_url, auth=oauth)
            if response.status_code not in [200, 201]:
                raise OAuthError(
                    _("Invalid response while obtaining access token" ' from "%s".')
                    % get_token_prefix(self.request_token_url)
                )
            self.access_token = dict(parse_qsl(response.text))

            self.request.session[
                "oauth_%s_access_token" % get_token_prefix(self.request_token_url)
            ] = self.access_token
        return self.access_token

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, seems like Garmin throws a 400 error because of below section. Would be grateful for some help here- would we want to implement an option to not pass this as a URL param?

      if oauth_verifier:
          at_url = (
              at_url + "?" + urlencode({"oauth_verifier": oauth_verifier})
          )

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pennersr Sorry to bother you, but would be grateful for some clarification here for implementing get_access_token.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to add a access_token_requires_oauth_verifier=True parameter to the OAuthClient.__init__, and use this in the code that builds at_url. Then, alter _get_client() to copy a similarly named access_token_requires_oauth_verifier attribute over from the adapter to the client. That way, this functionality is encapsulated in the base class and you do not need to alter _get_client() yourself.

allauth/socialaccount/providers/garminconnect/provider.py Outdated Show resolved Hide resolved
allauth/socialaccount/providers/garminconnect/provider.py Outdated Show resolved Hide resolved
allauth/socialaccount/providers/garminconnect/provider.py Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants