Skip to content

feat: initial gnap and http signatures implementation #10

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

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

Conversation

elijah0kello
Copy link
Contributor

@elijah0kello elijah0kello commented Jun 17, 2025

Changes in this PR

  • added classes for key management
  • added http signature header support for requests to auth server and resource server
  • added unit tests
  • added github actions file to run tests

Things to note

  • integration tests are still failing since when I was making requests to https://auth.interledger-test.dev, I didn't have any public keys registered.
  • I therefore need some help understanding how to upload public keys to the AS.

@Tymmmy @johngian please review

Authors

Run unit tests

poetry run pytest -s test/unit

@elijah0kello
Copy link
Contributor Author

@Tymmmy I have added some usage documentation in the README.md. Still adding more docs though.

The current docs should give an idea of how I envisioned the client to be used.

@elijah0kello
Copy link
Contributor Author

The integration test for requesting a grant is now passing.

to run integration tests.

poetry run pytest -s tests/integration/test_grants.py

I registered a wallet , created keys and loaded it in privkey.pem.example for testing

Copy link
Member

@sidvishnoi sidvishnoi left a comment

Choose a reason for hiding this comment

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

Looking good!
Did a quick first pass, will try setting up locally for next round of review.


strategy:
matrix:
python-version: [3.11, 3.12, 3.13]
Copy link
Member

Choose a reason for hiding this comment

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

Can we aim from Python 3.9 as it's still a supported version?
One old version, one "most used supported" version, and one latest (even beta)?

@@ -0,0 +1,44 @@
name: Test Python open payments sdk f
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
name: Test Python open payments sdk f
name: Test Python Open Payments SDK


jobs:
test:
runs-on: ubuntu-latest
Copy link
Member

Choose a reason for hiding this comment

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

Can we test on Windows as well?

Comment on lines +28 to +35
- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python3 -
echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Configure Poetry
run: |
poetry config virtualenvs.create false
Copy link
Member

Choose a reason for hiding this comment

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

Can we merge these two into "Set up Poetry"?

Comment on lines 40 to 42
```
> poetry install
```
Copy link
Member

Choose a reason for hiding this comment

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

So the code becomes part of Step 2:

Suggested change
```
> poetry install
```
```
> poetry install
```

request = self.set_content_digest(request=request)
request = self.sign_request(request,("content-type","content-digest","content-length",*self.get_default_covered_components()))
response = self.http_client.send(request=request)
return response.json()
Copy link
Member

Choose a reason for hiding this comment

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

Skipping model_validate? for this response?

)
return message

def get_default_headers(self) -> dict:
Copy link
Member

Choose a reason for hiding this comment

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

Should these methods be made @staticmethod or move moved out of the class? They're essentially constants.

Comment on lines +46 to +51
def load_ed25519_private_key_from_pem(self,pem_str: Union[str, bytes]) -> Ed25519PrivateKey:
"""
Read private key from str or bytes string
"""
if isinstance(pem_str,str):
pem_str = pem_str.encode("utf-8")
Copy link
Member

Choose a reason for hiding this comment

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

Nit: given we'll be using encoded bytes, better to name this variable pem_bytes:

Suggested change
def load_ed25519_private_key_from_pem(self,pem_str: Union[str, bytes]) -> Ed25519PrivateKey:
"""
Read private key from str or bytes string
"""
if isinstance(pem_str,str):
pem_str = pem_str.encode("utf-8")
def load_ed25519_private_key_from_pem(self,pem_bytes: Union[str, bytes]) -> Ed25519PrivateKey:
"""
Read private key from str or bytes string
"""
if isinstance(pem_bytes,str):
pem_bytes = pem_bytes.encode("utf-8")

method: str,
url: str,
headers = None,
data = None,
Copy link
Member

Choose a reason for hiding this comment

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

Should this be typed as dict as well?

data = None,
json: dict = None,
params: dict = None
) -> httpx.Request:
Copy link
Member

Choose a reason for hiding this comment

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

Should we expose this httpx dependency like this to other modules? Seems fine from perspective of better control at call-site, but makes dependency harder to switch when/if needed.

@elijah0kello
Copy link
Contributor Author

thanks @sidvishnoi for these comments. I will address them ASAP.

@johngian
Copy link
Contributor

Some overall comments about this PR:

  • I think 33 commits are not very manageable/readable to review. I suggest the commits to be more atomic.
  • I am not sure what's the policy for 3rd party libraries but I would advise against building on top of non established authentication related libraries

@elijah0kello
Copy link
Contributor Author

thanks @johngian for these comments.

  • I am happy to take you through the codebase if it is overwhelming. Also it is just a lot of commits but the actual code changes are not so many.
  • In regard to the third party lib, I read through the code base for the http signatures lib and also made a PR to fix something that was wrong. That being said if there is a policy, let's hear it and I'll address that specific issue with a custom implementation of http signatures.

@johngian
Copy link
Contributor

johngian commented Jul 14, 2025

@elijah0kello its not about having an overwhelming codebase. There is a purpose and value on having atomic commits. The PR at its current state (from a quick look) has 4+ commits that are about running unit tests and 3-4 commits only about updating docs, on top of that there is a bunch of merge commits from other repositories which don't bring much value in the context of the commit history. Also there is a commit that fixes a typo of a change introduced in this changeset.

Regarding the http signatures lib i would defer to the folks from the organization to decide but if it was my decision i would rather have an implementation that is audited/vetted by the org or built internally just for the purpose of ILF projects.

Install it in your project

```bash
pip install </path/to/>open-payments-python-sdk/dist/open_payments_sdk-0.1.0-py3-none-any.whl
Copy link
Contributor

Choose a reason for hiding this comment

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

We do have a poetry based setup, there is no need to point to a pinned version of the wheel file.

To use this sdk, you will first need to install it in your project. Currently you will need to build from source but once it is hosted on pypi you will be able to install it with pip

```bash
python3 -m pip install open-payments-python-sdk #currently not setup
Copy link
Contributor

Choose a reason for hiding this comment

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

If you are running poetry install why do you need to explicitly install the module?

@koekiebox
Copy link

@elijah0kello its not about having an overwhelming codebase. There is a purpose and value on having atomic commits. The PR at its current state (from a quick look) has 4+ commits that are about running unit tests and 3-4 commits only about updating docs, on top of that there is a bunch of merge commits from other repositories which don't bring much value in the context of the commit history. Also there is a commit that fixes a typo of a change introduced in this changeset.

Regarding the http signatures lib i would defer to the folks from the organization to decide but if it was my decision i would rather have an implementation that is audited/vetted by the org or built internally just for the purpose of ILF projects.

I am happy, as long as we introduce CVE scanning to ensure the libraries are safe to use. I have left other comments on the PR. 🙇

Copy link

@koekiebox koekiebox left a comment

Choose a reason for hiding this comment

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

Left a couple of comments, mostly regarding pip-audit.

@@ -170,3 +170,4 @@ poetry.toml
pyrightconfig.json

# End of https://www.toptal.com/developers/gitignore/api/python
privkey.pem

Choose a reason for hiding this comment

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

add new line.

{name = "Yiannis Giannelos",email = "johngiannelos@gmail.com"}
{name = "Yiannis Giannelos",email = "johngiannelos@gmail.com"},
{name = "Elijah Okello", email = "elijahokello90@gmail.com"},
{name = "Kasweka Michael Mukoko", email = "kasweka.mukoko@izyane.com"}
]
license = {text = "Apache-2.0"}
requires-python = ">=3.11"
dependencies = [
"httpx (>=0.28.1,<0.29.0)",

Choose a reason for hiding this comment

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

There doesn't seem to be any vulnerabilities with these dependencies, but could we also run the pip-audit as part of the workflow?

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install dependencies
        run: |
          python3 -m pip install .  # assumes dependencies declared in pyproject.toml
          python3 -m pip install pip-audit
      - name: Run pip-audit
        run: pip-audit .


jobs:
test:
runs-on: ubuntu-latest

Choose a reason for hiding this comment

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

Could you also run an audit as part of the flow?

image

@elijah0kello
Copy link
Contributor Author

thanks @koekiebox for comments.

I will address as advised. Especially the dependency auditing.

elijah0kello and others added 2 commits July 17, 2025 20:03
Co-authored-by: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com>
Co-authored-by: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com>
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.

5 participants