stellar / django-polaris

An extendable Django app for building modular Stellar services
https://django-polaris.readthedocs.io
Apache License 2.0
94 stars 70 forks source link

SEP10 JWT contains Server address and not Client address #8

Closed leighmcculloch closed 4 years ago

leighmcculloch commented 4 years ago

What version are you using?

v0.9.2

What did you do?

I haven't used polaris yet but I took a look at the SEP-10 implementation here: https://github.com/stellar/django-polaris/blob/1da3dd3ffd2e59dfbc19e27aceaeb097b5ad94f3/polaris/polaris/sep10auth/views.py#L99-L117

What did you expect to see?

I expected to see the address/public-key of the Client's Stellar account set as the sub field of the JWT. The Client's address is the source account of the challenge transaction's first manage data operation.

According to SEP-10's definition of what is in a token the sub field should contain:

the public key of the authenticating Stellar account (G...)

The authenticating Stellar account is the client.

What did you see instead?

The address/public-key of the distribution Stellar account set as the sub field of the JWT.

JakeUrban commented 4 years ago

Thanks Leigh,

This should do the trick:

def _generate_jwt(request, envelope_xdr):
    """
    Generates the JSON web token from the challenge transaction XDR.

    See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#token
    """
    issued_at = time.time()
    transaction_envelope = TransactionEnvelope.from_xdr(
        envelope_xdr,
        network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE
    )
    transaction = transaction_envelope.transaction
    hash_hex = binascii.hexlify(transaction_envelope.hash()).decode()
    jwt_dict = {
        "iss": request.build_absolute_uri("/auth"),
        "sub": transaction.source.public_key,
        "iat": issued_at,
        "exp": issued_at + 24 * 60 * 60,
        "jti": hash_hex,
    }
    encoded_jwt = jwt.encode(jwt_dict, settings.SERVER_JWT_KEY, algorithm="HS256")
    return encoded_jwt.decode("ascii")

We'll release this adjustment in v0.9.3

JakeUrban commented 4 years ago

Turns out the transaction's source is the anchor's account since its a challenge generated by the server. So instead of assigning sub to transaction.source.public_key, we'll use transaction.operations[0].source as defined in SEP-10:

  • transaction: an XDR-encoded Stellar transaction with the following:
    • source account set to server's signing account
    • invalid sequence number (set to 0) so the transaction cannot be run on the Stellar network
    • time bounds: {min: now(), max: now() + 300 } (we recommend expiration of 5 minutes to give user time to sign transaction)
    • operations: manage_data(source: client_account, key: '<anchor name> auth', value: random_nonce())
    • The value of key is not important, but can be the name of the anchor followed by auth. It can be at most 64 bytes.
    • The value must be 64 bytes long. It contains a 48 byte cryptographic-quality random string encoded using base64 (for a total of 64 bytes after encoding).
    • signature by the web service signing account

...

  • use operations's source account to determine the authenticating client and perform any additional service-specific validations.