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

Improve error handling for invalid App Store Connect API keys #381

Merged
merged 2 commits into from
Dec 14, 2023

Conversation

priitlatt
Copy link
Contributor

@priitlatt priitlatt commented Dec 13, 2023

When running app-store-connect actions with private key that is not issued by App Store Connect (i.e. a valid PEM encoded private key that is not elliptic curve private key) then generating JSON web token for API requests fails and consequently the whole action fails unexpectedly:

$  APP_STORE_CONNECT_PRIVATE_KEY=@file:/tmp/invalid-key.pk app-store-connect apps get 1496105355
Get App 1496105355
Executing "app-store-connect apps get" failed unexpectedly. Detailed logs are available at "/var/folders/wr/c44p23x10f302_kfbj32z0p80000gn/T/codemagic-13-12-23.log". To see more details about the error, add "--verbose" command line option.
Output with --verbose option
$APP_STORE_CONNECT_PRIVATE_KEY=@file:/tmp/invalid-key.pk app-store-connect apps get 1496105355 --verbose
[14:15:23] INFO  > Get App 1496105355
[14:15:23] DEBUG > Load JWT for App Store Connect key 'KXCJ7SZ6L7' from disk cache
[14:15:23] WARNING > Executing "app-store-connect apps get" failed unexpectedly. Detailed logs are available at "/var/folders/wr/c44p23x10f302_kfbj32z0p80000gn/T/codemagic-13-12-23.log".
[14:15:23] ERROR > Exception traceback:
Traceback (most recent call last):
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/cli/cli_app.py", line 243, in invoke_cli
    CliApp._running_app._invoke_action(args)
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/cli/cli_app.py", line 184, in _invoke_action
    return cli_action(**action_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/cli/cli_app.py", line 496, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/tools/_app_store_connect/action_groups/apps_action_group.py", line 39, in get_app
    return self._get_resource(application_id, self.api_client.apps, should_print)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/tools/_app_store_connect/resource_manager_mixin.py", line 62, in _get_resource
    resource = read_resource(resource_id)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/apps/apps.py", line 67, in read
    response = self.client.session.get(f"{self.client.API_URL}/apps/{app_id}").json()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/requests/sessions.py", line 602, in get
    return self.request("GET", url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/api_session.py", line 97, in request
    return self._do_request(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/api_session.py", line 68, in _do_request
    headers.update(self._auth_headers_factory())
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/api_client.py", line 76, in generate_auth_headers
    return {"Authorization": f"Bearer {self.jwt}"}
                                       ^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/api_client.py", line 72, in jwt
    jwt = self._jwt_manager.get_jwt()
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/json_web_token_manager.py", line 148, in get_jwt
    self._jwt = self._load_jwt_from_disk()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/json_web_token_manager.py", line 110, in _load_jwt_from_disk
    payload = self._decode_payload(token)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/codemagic/apple/app_store_connect/json_web_token_manager.py", line 93, in _decode_payload
    return jwt.decode(
           ^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jwt/api_jwt.py", line 210, in decode
    decoded = self.decode_complete(
              ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jwt/api_jwt.py", line 151, in decode_complete
    decoded = api_jws.decode_complete(
              ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jwt/api_jws.py", line 209, in decode_complete
    self._verify_signature(signing_input, header, signature, key, algorithms)
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jwt/api_jws.py", line 307, in _verify_signature
    prepared_key = alg_obj.prepare_key(key)
                   ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/priit/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jwt/algorithms.py", line 525, in prepare_key
    raise InvalidKeyError(
jwt.exceptions.InvalidKeyError: Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for ECDSA algorithms

None of it is particularly useful for the users. To avoid that from happening, validate that the provided key can be used for JWT generation beforehand when action is invoked from command line (not via Python API), and issue a proper actionable error message:

$ APP_STORE_CONNECT_PRIVATE_KEY=@file:/tmp/invalid-key.pk app-store-connect apps get 1496105355
usage: app-store-connect [-h] [--log-stream {stderr,stdout}] [--no-color] [--version] [-s] [-v]
                              {app-store-version-localizations,app-store-version-submissions,app-store-versions,apps,beta-app-review-submissions,beta-build-localizations,beta-groups,builds,create-bundle-id,create-certificate,create-profile,delete-bundle-id,delete-certificate,delete-profile,fetch-signing-files,get-bundle-id,get-certificate,get-latest-app-store-build-number,get-latest-build-number,get-latest-testflight-build-number,get-profile,list-builds,list-bundle-id-profiles,list-bundle-ids,list-certificates,list-devices,list-profiles,publish,register-device,review-submission-items,review-submissions}
                              ...
app-store-connect: error: argument --private-key: Invalid App Store Connect API key. Make sure to use the private API key downloaded from App Store Connect. Read more about creating App Store Connect API keys from https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api

@priitlatt priitlatt marked this pull request as ready for review December 13, 2023 12:31
@priitlatt priitlatt merged commit 8987087 into master Dec 14, 2023
7 checks passed
@priitlatt priitlatt deleted the improvement/invalid-asc-keys-handling branch December 14, 2023 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants