codemagic-ci-cd / cli-tools

Various utilities to managing Android and iOS app builds, code signing, and deployment.
https://codemagic.io/start/
GNU General Public License v3.0
235 stars 39 forks source link

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

Closed priitlatt closed 7 months ago

priitlatt commented 7 months ago

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 ```python $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