Awesome
kotlin-did-jwt
This is the Kotlin implementation of the basic JWT methods for DID-JWTs
did-jwt
The kotlin-did-JWT library allows you to sign and verify JSON Web Tokens (JWT) using ES256K, and ES256K-R algorithms.
Public keys are resolved using the
Decentralized ID (DID)
of the signing identity of the claim, which is passed as the iss
attribute of the encoded JWT.
DID methods
We currently support the following DID methods:
Defaults are automatically installed but you can customize to fit your needs.
Support for other DID methods should be simple.
Write a DID resolver supporting the
DIDResolver
interface.
Install it using
val resolver : DIDResolver = DIDResolver.Builder
.addResolver(ethrDidResolver)
.addResolver(/*...*/)
.build()
Once you've verified that it works, feel free to advertise it in the list above so people can find it.
If your DID method requires a different signing algorithm than what is already supported, please create a PR.
Installation
The libraries built here are distributed through jitpack
In your main build.gradle
file, add:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
//...
}
}
In your application build.gradle
file, add:
def did_jwt_version = "0.4.0"
dependencies {
//...
implementation "com.github.uport-project.kotlin-did-jwt:jwt:$did_jwt_version"
}
Example
1. Create a did-JWT
In practice you should secure the key passed to KPSigner. The key provided in code below is for informational purposes.
val jwt = JWTTools()
//...
val payload = mapOf(
"claims" to mapOf("name" to "R Daneel Olivaw")
)
val signer = KPSigner("0x54ece214d38fe6b46110a21c69fd55230f09688bf85b95fc7c1e4e160441ece1")
val issuerDID = "did:ethr:${signer.getAddress()}"
val token = jwt.createJWT(payload, issuerDID, signer)
2. Decode a did-JWT
Try decoding the JWT. You can also do this using jwt.io
val (header, payload, sig) = jwt.decodeRaw("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJjbGFpbXMiOnsibmFtZSI6IlIgRGFuZWVsIE9saXZhdyJ9LCJpYXQiOjEyMzQ1Njc4LCJleHAiOjEyMzQ1OTc4LCJpc3MiOiJkaWQ6ZXRocjoweDQxMjNjYmQxNDNiNTVjMDZlNDUxZmYyNTNhZjA5Mjg2YjY4N2E5NTAifQ.o6eDKYjHJnak1ylkpe9g8krxvK9UEhKf-1T0EYhH8pGyb8MjOEepRJi8DYlVEnZno0DkVYXQCf3u1i_HThBKtAA")
The decoded payload resembles:
mapOf(
"claims" to mapOf("name" to "R Daneel Olivaw"),
"iat" to 1.2345678E7,
"exp" to 1.2345978E7,
"iss" to "did:ethr:0x4123cbd143b55c06e451ff253af09286b687a950"
)
You can also use jwt.decode("<token>")
to get a JwtPayload
object instead of a map
but that is a more rigid structure and will be phased away in future releases.
3. Verify a did-JWT
val resolver = EthrDIDResolver.Builder()
.addNetwork(EthrDIDNetwork("<name>", "<registryAddress>", "<JsonRPC>"))
.build()
val payload : JwtPayload = JWTTools().verify("<token>", resolver)
If the token is valid, the method returns the decoded payload,
otherwise throws a InvalidJWTException
or JWTEncodingException
This behavior is subject to change in an upcoming release The verify() method will return a higher level abstraction that will contain the payload and more.
The function requires a DIDResolver which will be used to resolve DIDs during the verification
Verifying a token means checking that the signature was produced by a
key associated with the issuer DID (iss
field).
This association is resolved by a DID resolver, which can produce a DIDDocument
listing various public keys and service endpoints for a given DID.
Audience verification
If the token contains a non null aud
field, an additional soft-check is performed to
match the verification against an intended audience.
This same aud
DID must be supplied to the verify()
method for the token to be marked as valid
(after passing all the cryptographic checks as well).
Generally your app will have its own DID which should always be passed to the verify
method
so that only tokens intended for your app are considered valid.
CHANGELOG
- 0.4.0
- removed deprecated components ( #46 )
- 0.3.6
- fix: okhttp dependency issue (#43)
- docs: mark UportDIDResolver as deprecated (f5ffad34)
- refactor: code cleanup, enforcing detekt on PRs (#45)
- 0.3.5
- feat: add credential status / revocation support (#35)(#42)
- support: bump dependencies (kotlin 1.3.70, kethereum 0.81.4) (#44)
- 0.3.4
- feat: deprecate UniversalResolver singleton (#31)(#34)(#37)
- 0.3.3
- refactor: use kethereum 0.76.2 ( 1f730e39 )
- bugfix: resolve publicKey entries with null chars in their names ( #27 )
- 0.3.2
- feat: support multi-network ethr-did ( #20 )
- bugfix: crash in ethr-did-resolver when nodes don't reply with logs ( #21 )
- refactor: use kethereum 0.76.1 and kotlin-common 0.3.1 ( #22 )
- refactor: use lowercase coordinates for kethereum libs ( #25 )
- 0.3.1
- allow override or removal of
iat
field when creating JWTs (#17)
- allow override or removal of
- 0.3.0
- [breaking] remove deprecated
https-did
module, now replaced byweb-did
(#14) - add test coverage metrics (#10)
- add support for arbitrary maps with
@Serializer
s as JWT payloads (#16) - remove moshi dependency (#16)
- [breaking] remove deprecated
- 0.2.1
- add support for web DID, deprecating https DID (#5)
- allow creation of JWTs with no expiry (#6)
- fallback to ES256K-R style verification if ES256K algorithm fails because of missing key encoding (#7)
- [bugfix] delegate keys in ethr-did documents were not being resolved properly (#9)
- 0.2.0
- [breaking] add audience checking for JWT verification (#2)
- add
jwt-test
module with helpers for testing
- 0.1.2
- fix crash when parsing legacy identity document
- 0.1.1
- initial stable release isolating the did-jwt implementation in kotlin along with resolvers