uport-project / ethr-did

Create ethr DIDs
Apache License 2.0
260 stars 54 forks source link

Can NOT verify did:ether JWT token #22

Closed maitien2004 closed 5 years ago

maitien2004 commented 5 years ago

I try to use Ether-DID library to create new jwt token and after that i can NOT verify it. Here is example code i used.

`const EthrDID = require('ethr-did'); const ethrDid = new EthrDID({ provider: web3.currentProvider, address: 'address', privateKey: 'priv-key' });

var jwt = await ethrDid.signJWT({ claims: { name: 'Joe Lubin' } }); //Problem here const { payload, issuer } = ethrDid.verifyJWT(jwt); console.log(payload); // return undefined console.log(issuer); // return undefined`

froid1911 commented 5 years ago

same here :-(

Output:

undefined undefined (node:61714) UnhandledPromiseRejectionWarning: Error: Unsupported DID method: 'ethr' at /Users/user/projekt/node_modules/ethr-did/node_modules/did-resolver/lib/resolver.js:42:12

jmsofarelli commented 5 years ago

The error also happens in the method verifyJWT() of 'did-jwt' library.

froid1911 commented 5 years ago

Someone posted a solution in chat:

Rob helped me to find the solution and I will post here: "This can sometimes happen if you end up with multiple versions of the did-resolver package, and so ethr-did-resolver ends up being registered to the wrong copy of the root resolver. You can investigate further by running npm ls did-resolver and identify potential duplicates. Ensuring that all references are to the same version and deduped should resolve the issue — this may require updating one or more packages that depend on did-resolver." In my case the result of npm ls did-resolver was: uport-poc@1.0.0 /Users/sofarell/workspace/uport-poc

I downgraded my "did-jwt' to 0.0.8 and it worked fine!

hsnhrn commented 5 years ago

I am not able to sole it does anyone have any idea about this error while calling verifyJWT? (node:3310) UnhandledPromiseRejectionWarning: Error: Unsupported DID method: 'ethr'

thanks alot!!

mirceanis commented 5 years ago

The did-jwt lib is being refactored to use a local configuration instead of a global. (https://github.com/uport-project/did-jwt/pull/50) This should fix the error of being registered to the wrong copy of the root resolver.

That being said, you could also probably fix this by using did-jwt (or even uport-credentials) to create and verify JWT instead of ethr-did.

mirceanis commented 5 years ago

The root cause is that this library is not installing the ethr-did-registry on its own. The underlying did-jwt lib does not use a default set of DID resolvers. Devs have to install each one they plan to use. The upcoming changes in the way these resolvers are installed and used will make it possible for this library to use a local resolver configured with the same parameters.

m-yahya commented 5 years ago

@mirceanis I'm trying to verify jwt using ethrDid.verifyJWT(jwt) but getting the following error: UnhandledPromiseRejectionWarning: Error: Unsupported DID method: 'ethr' here is my sample code:

// network config
const HttpProvider = require('ethjs-provider-http')
const Web3 = require('web3')
Web3.providers.HttpProvider.prototype.sendAsync = Web3.providers.HttpProvider.prototype.send
let provider = new Web3.providers.HttpProvider('http://localhost:8546')

const EthrDID = require('ethr-did');
const resolve = require('did-resolver').default;
const registerResolver = require('ethr-did-resolver').default

const didJWT = require('did-jwt')
const { SimpleSigner } = require('did-jwt')

// get accounts
//const accounts = await provider.eth.getAccounts();
const signer = SimpleSigner('0x92dd08591c87f6b860fd2bb4895d593f9f240a6f289883a7d5d625494eaeda1d')
const registry = '0xd3a1647be4fe9fab57ba0c4a8215cb8a927e5e16';
const address = '0x07b3fae112f54be9c1bd4477565e0366c41fc2f3';

const keypair = EthrDID.createKeyPair()
//Generating Ethr DID
const ethrDid = new EthrDID({ provider, registry, address });

let test = async () => {

    const delegate = await ethrDid.createSigningDelegate()
    const jwt = await ethrDid.signJWT({ claims: { name: 'Joe Lubin' } });
    console.log(didJWT.decodeJWT(jwt)); // this is successful
    //const { payload, issuer } = ethrDid.verifyJWT(jwt); // this throws error

}

test()

Dependencies: "did-jwt": "0.0.8" "did-resolver": "0.0.7" "ethjs-provider-http": "^0.1.6" "ethr-did": "^1.1.0" "ethr-did-resolver": "^0.2.0" "web3": "^1.2.1"

mirceanis commented 5 years ago

you also need to register the ethr-did-resolver I see you are already importing it (const registerResolver = require('ethr-did-resolver').default). The missing step is to call registerResolver({provider : provider, registry : registry})

Also, this line seems to be unused: const keypair = EthrDID.createKeyPair() Unless you intend to sign something with that keypair, you should remove that line to avoid confusion.

m-yahya commented 5 years ago

@mirceanis thank you very much for your support. there is still one issue:

const { payload, issuer } = ethrDid.verifyJWT(jwt);
console.log(payload); // returns undefined
console.log(issuer); // returns undefined
m-yahya commented 5 years ago

it is fixed: we need to use async await method: const { payload, issuer } = await ethrDid.verifyJWT(jwt);

mirceanis commented 5 years ago

you may also want to upgrade to did-jwt@0.2.0 since it fixes an issue with global Buffer

m-yahya commented 5 years ago

@mirceanis thank you so much for your support. I upgraded to did-jwt@0.2.0 and ethrDid.signJWT and ethrDid.verifyJWT works fine. However, didJWT.verifyJWT is not working. here is the updated sample code:

....
const signer = didJWT.SimpleSigner('0x92dd08591c87f6b860fd2bb4895d593f9f240a6f289883a7d5d625494eaeda1d')
.....
// inside the async function

let jwt = '';
    didJWT.createJWT({ aud: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', exp: 1957463421, name: 'uPort Developer' },
        { issuer: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', signer }).then(response => {
            jwt = response
            console.log(jwt); // this works fine

            // this does not work
            let verifiedRespone = {};
            didJWT.verifyJWT(jwt, { audience: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3' }).then((response) => {
                verifiedRespone = response
                console.log(verifiedRespone);
            });
        });

The error is: UnhandledPromiseRejectionWarning: TypeError: Cannot read property '-1' of null at BN.bitLength

mirceanis commented 5 years ago

you are experiencing this bug: https://github.com/uport-project/did-jwt/issues/14

to fix it add alg : 'ES256K-R' to the options for didJWT.createJWT

//....
const signer = didJWT.SimpleSigner('0x92dd08591c87f6b860fd2bb4895d593f9f240a6f289883a7d5d625494eaeda1d')
//.....
// inside the async function

let jwt = '';
    didJWT.createJWT({ aud: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', exp: 1957463421, name: 'uPort Developer' },
        { alg: `ES256K-R`, issuer: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', signer }).then(jwt => {
            console.log(jwt); // this works fine

            didJWT.verifyJWT(jwt, { audience: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3' }).then((verifiedRespone) => {
                console.log(verifiedRespone);
            });
        });
m-yahya commented 5 years ago

it throws the error : UnhandledPromiseRejectionWarning: Error: Signature invalid for JWT the updated code is:

const { createJWT, verifyJWT, SimpleSigner } = require('did-jwt')
....
// with or without signer has same error
const ethrDid = new EthrDID({ provider, registry, address, signer });
....
let jwt = '';
    createJWT({ aud: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', exp: 1957463421, name: 'uPort Developer' },
        { alg: 'ES256K-R', issuer: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3', signer }).then(jwt => {
            console.log(jwt); // this works fine
            // this does not work
            verifyJWT(jwt, { audience: 'did:ethr:0x07b3fae112f54be9c1bd4477565e0366c41fc2f3' }).then((verifiedRespone) => {
                console.log(verifiedRespone);
            });
        });
mirceanis commented 5 years ago

what network are you on? is it a public network?

if you are using ganache, there seem to be some issues that prevent ethr-did-registry from working correctly (https://github.com/uport-project/ethr-did/issues/33#issuecomment-525726812)

m-yahya commented 5 years ago

yes, this is ganache.

m-yahya commented 5 years ago

I tested it with EWF Volta chain (https://energyweb.atlassian.net/wiki/spaces/EWF/pages/703201459/Volta+Connecting+to+Remote+RPC) running on my machine, but still the same error: UnhandledPromiseRejectionWarning: Error: Signature invalid for JWT at verifyRecoverableES256K

m-yahya commented 5 years ago

@mirceanis

// inside async function
const delegate = await ethrDid.createSigningDelegate()
    //console.log(delegate);
    registerResolver({ provider: provider, registry: registry })
    // sign and verify jwt
    const jwt = await ethrDid.signJWT({ claims: { name: 'Joe Lubin' } });
    console.log(decodeJWT(jwt));
    // this works fine with ganache
    // with volta, this throws error:
    // UnhandledPromiseRejectionWarning: Error: Signature invalid for JWT
    // at verifyRecoverableES256K
    const { payload, issuer } = await ethrDid.verifyJWT(jwt);
mirceanis commented 5 years ago

@m-yahya please post an example of an invalid JWT, as well as the registry address you are using on volta

mirceanis commented 5 years ago

Also, perhaps it is a timing issue, see #34

mirceanis commented 5 years ago

Closing this since the original error (Error: Unsupported DID method: 'ethr') was resolved

m-yahya commented 5 years ago

Also, perhaps it is a timing issue, see #34

we need a delay before executing verification. here is working example:

// inside async function
// ....
// sign and verify jwt
    const jwt = await ethrDid.signJWT({ claims: { name: 'uPort Developers' } });
    console.log(decodeJWT(jwt));
    setTimeout(async function () {
        const { payload, issuer } = await ethrDid.verifyJWT(jwt);
        console.log(issuer);
    }, 2000)
mirceanis commented 5 years ago

Just to be clear, the delay is needed somewhere between createSigningDelegate() and verifyJWT because createSigningDelegate() sends a transaction and that block needs to propagate to the node that responds to the calls made by verifyJWT (resolving the DID document).

So, if your flow requires some key delegation for signing but not immediately followed by verification, this delay will not be needed.

gdollard commented 4 years ago

I hate to drag this up again but when I call verifyJWT I still get: Error: Unsupported DID method: 'ethr'

I have created my EthrDID and registered my ethrDidResolver. I have even verified that the resolver does indeed resolve my DID string and produce a DID document.

const didResolver = new Resolver(ethrDidResolver)

The signJWT returns a JWT as expected:

const helloJWT = await ethrDid.signJWT({hello: 'world'})
    console.log("JWT: ", helloJWT)

But the line below fails: const {payload, issuer} = await ethrDid.verifyJWT(helloJWT).then(data => {console.log("Returned Data: ", data)}).catch(error => {console.log("Error when verifying JWT: ", error)})

This is doing my head in! Any ideas ??

gdollard commented 4 years ago

same here :-(

Output:

undefined undefined (node:61714) UnhandledPromiseRejectionWarning: Error: Unsupported DID method: 'ethr' at /Users/user/projekt/node_modules/ethr-did/node_modules/did-resolver/lib/resolver.js:42:12

Hi Frank, did you manage to resolve this? I'm currently having the same issue.

mirceanis commented 4 years ago

@gdollard try passing the resolver to the verify method

const didResolver = new Resolver(ethrDidResolver)
const {payload, issuer} = await ethrDid.verifyJWT(helloJWT, didResolver).then(...)
gdollard commented 4 years ago

@mirceanis thanks for the reply. I am now getting the following error: "mnidOrDid.match is not a function"

using the following code:

const {payload, issuer} = await ethrDid.verifyJWT(helloJWT, {resolver: didResolver, audience: keyPair.address }).catch(error => {
        console.log("Error from verifyJWT: ", error.message)
    })
gdollard commented 4 years ago

OK, I have modified my code to directly use did-jwt for JWT creation verification. I understand ethr-did provides a layer of abstraction from this but I was having so many problems, mainly due to the data I was passing it was easier to debug from this lower level. I discovered my problem may have been due to the keyPair object I was using. I was hoping to use a key pair from one of my accounts via my web3 instance but for some reason JWT doesn't like it. When I use the object returned by EtherDID.createKeyPair() it works! I'm going to investigate further because surely this should work with my own correctly created account keyPair (as exported from my MetaMask account). Thanks for your feedback @mirceanis

mirceanis commented 4 years ago

I was hoping to use a key pair from one of my accounts via my web3 instance but for some reason JWT doesn't like it.

web3 signatures are different from JWT signatures, even if the same private key is in use so it wouldn't work with only your web3 provider.

For reference, in pseudocode web3 signatures are computed as signHash(privKey, keccak(message)) while JWT signatures are computed as signHash(privKey, sha256(message))


As a side-note, unfortunately there's not enough bandwidth now to investigate the issues you are facing, not even to update docs for this library. PRs are still welcome, of course.

gdollard commented 4 years ago

I was hoping to use a key pair from one of my accounts via my web3 instance but for some reason JWT doesn't like it.

web3 signatures are different from JWT signatures, even if the same private key is in use so it wouldn't work with only your web3 provider.

For reference, in pseudocode web3 signatures are computed as signHash(privKey, keccak(message)) while JWT signatures are computed as signHash(privKey, sha256(message))

As a side-note, unfortunately there's not enough bandwidth now to investigate the issues you are facing, not even to update docs for this library. PRs are still welcome, of course.

@mirceanis thanks for that information, much appreciated.