Skip to content

Commit fcbf03e

Browse files
committed
Merge branch 'release/0.2.0'
2 parents 9cb2213 + b846ee3 commit fcbf03e

File tree

15 files changed

+205
-80
lines changed

15 files changed

+205
-80
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ allprojects {
5252
In your application `build.gradle` file, add:
5353

5454
```groovy
55-
def did_jwt_version = "0.1.2"
55+
def did_jwt_version = "0.2.0"
5656
dependencies {
5757
//...
5858
implementation "com.github.uport-project.kotlin-did-jwt:jwt:$did_jwt_version"
@@ -119,11 +119,23 @@ key associated with the issuer DID (`iss` field).
119119
This association is resolved by a DID resolver, which can produce a `DIDDocument`
120120
listing various public keys and service endpoints for a given DID.
121121

122+
#### Audience verification
123+
124+
If the token contains a non null `aud` field, an additional soft-check is performed to
125+
match the verification against an intended audience.
126+
This same `aud` DID must be supplied to the `verify()` method for the token to be marked as valid
127+
(after passing all the cryptographic checks as well).
128+
129+
Generally your app will have its own DID which should always be passed to the `verify` method
130+
so that only tokens intended for your app are considered valid.
131+
122132

123133
## CHANGELOG
124134

135+
* 0.2.0
136+
- [breaking] add audience checking for JWT verification
137+
- add `jwt-test` module with helpers for testing
125138
* 0.1.2
126-
- add audience checking for JWT verification
127139
- fix crash when parsing legacy identity document
128140
* 0.1.1
129141
- initial stable release isolating the did-jwt implementation in kotlin along with resolvers

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ buildscript {
2222
spongycastle_version = "1.58.0.0"
2323
uport_kotlin_common_version = "0.1.1"
2424

25-
current_release_version = "0.1.2"
25+
current_release_version = "0.2.0"
2626
}
2727

2828
repositories {

ethr-did/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ dependencies {
2525
testImplementation "junit:junit:$junit_version"
2626
testImplementation "io.mockk:mockk:$mockk_version"
2727
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
28+
testImplementation project(":jwt-test")
2829
}

ethr-did/src/test/java/me/uport/sdk/ethrdid/EthrDIDResolverTest.kt

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import me.uport.sdk.core.Networks
1515
import me.uport.sdk.ethrdid.EthereumDIDRegistry.Events.DIDOwnerChanged
1616
import me.uport.sdk.jsonrpc.JsonRPC
1717
import me.uport.sdk.jsonrpc.JsonRpcLogItem
18+
import me.uport.sdk.jwt.test.EthrDIDTestHelpers
1819
import me.uport.sdk.signer.hexToBytes32
1920
import me.uport.sdk.signer.utf8
2021
import org.junit.Test
@@ -250,22 +251,7 @@ class EthrDIDResolverTest {
250251
fun `can resolve real did`() = runBlocking {
251252
val http = mockk<HttpClient>()
252253

253-
//language=JSON
254-
val referenceDDOString = """
255-
{
256-
"@context": "https://w3id.org/did/v1",
257-
"id": "did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a",
258-
"publicKey": [{
259-
"id": "did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a#owner",
260-
"type": "Secp256k1VerificationKey2018",
261-
"owner": "did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a",
262-
"ethereumAddress": "0xb9c5714089478a327f09197987f16f9e5d936e8a"}],
263-
"authentication": [{
264-
"type": "Secp256k1SignatureAuthentication2018",
265-
"publicKey": "did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a#owner"}]
266-
}
267-
""".trimIndent()
268-
val referenceDDO = EthrDIDDocument.fromJson(referenceDDOString)
254+
val referenceDDO = EthrDIDTestHelpers.mockDocForAddress("0xb9c5714089478a327f09197987f16f9e5d936e8a")
269255

270256
val addressHex = "b9c5714089478a327f09197987f16f9e5d936e8a"
271257

jwt-test/build.gradle

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apply plugin: "java-library"
2+
apply plugin: "kotlin"
3+
apply plugin: "maven"
4+
apply plugin: "com.jfrog.bintray"
5+
6+
project.ext.description = "tools to enable testing of the did-jwt library"
7+
8+
dependencies {
9+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
10+
11+
implementation project(":ethr-did")
12+
13+
testImplementation "junit:junit:$junit_version"
14+
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
15+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package me.uport.sdk.jwt.test
2+
3+
import me.uport.sdk.ethrdid.EthrDIDDocument
4+
5+
open class EthrDIDTestHelpers {
6+
7+
companion object {
8+
9+
fun mockDocForAddress(address: String): EthrDIDDocument {
10+
val did = "did:ethr:$address"
11+
return EthrDIDDocument.fromJson("""
12+
{
13+
"@context": "https://w3id.org/did/v1",
14+
"id": "$did",
15+
"publicKey": [{
16+
"id": "$did#owner",
17+
"type": "Secp256k1VerificationKey2018",
18+
"owner": "$did",
19+
"ethereumAddress": "$address"}],
20+
"authentication": [{
21+
"type": "Secp256k1SignatureAuthentication2018",
22+
"publicKey": "$did#owner"}]
23+
}
24+
""".trimIndent())
25+
}
26+
}
27+
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package me.uport.sdk.jwt.test
2+
3+
import assertk.assertThat
4+
import assertk.assertions.isEqualTo
5+
import me.uport.sdk.ethrdid.EthrDIDDocument
6+
import me.uport.sdk.universaldid.AuthenticationEntry
7+
import me.uport.sdk.universaldid.PublicKeyEntry
8+
import me.uport.sdk.universaldid.PublicKeyType
9+
import org.junit.Test
10+
11+
class EthrDIDTestHelpersTest {
12+
13+
@Test
14+
fun `can create mock ethr-did document from an address`() {
15+
val address = "0xb9c5714089478a327f09197987f16f9e5d936e8a"
16+
val did = "did:ethr:$address"
17+
18+
val referenceDDO = EthrDIDDocument(
19+
did,
20+
listOf(PublicKeyEntry(
21+
"$did#owner",
22+
PublicKeyType.Secp256k1VerificationKey2018,
23+
did,
24+
address
25+
)),
26+
listOf(AuthenticationEntry(
27+
PublicKeyType.Secp256k1SignatureAuthentication2018,
28+
"$did#owner"
29+
))
30+
)
31+
32+
val ddo = EthrDIDTestHelpers.mockDocForAddress(address)
33+
34+
assertThat(ddo).isEqualTo(referenceDDO)
35+
}
36+
}

jwt/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ dependencies {
2626
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
2727
testImplementation "com.github.uport-project.kotlin-common:test-helpers:$uport_kotlin_common_version"
2828
testImplementation "io.mockk:mockk:$mockk_version"
29+
testImplementation project(":jwt-test")
2930
}

jwt/src/main/java/me/uport/sdk/jwt/JWTTools.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import me.uport.sdk.ethrdid.EthrDIDResolver
88
import me.uport.sdk.httpsdid.HttpsDIDResolver
99
import me.uport.sdk.jsonrpc.JsonRPC
1010
import me.uport.sdk.jsonrpc.moshi
11+
import me.uport.sdk.jwt.JWTUtils.Companion.normalizeKnownDID
12+
import me.uport.sdk.jwt.JWTUtils.Companion.splitToken
1113
import me.uport.sdk.jwt.model.JwtHeader
1214
import me.uport.sdk.jwt.model.JwtHeader.Companion.ES256K
1315
import me.uport.sdk.jwt.model.JwtHeader.Companion.ES256K_R
@@ -220,7 +222,7 @@ class JWTTools(
220222

221223
if (payload.aud != null) {
222224

223-
val payloadAudience = normalize(payload.aud)
225+
val payloadAudience = normalizeKnownDID(payload.aud)
224226
if (UniversalDID.canResolve(payloadAudience)) {
225227

226228
if (aud == null) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package me.uport.sdk.jwt
2+
3+
import me.uport.mnid.MNID
4+
5+
/**
6+
* Utilities for dealing with known JWT and DID types and formats
7+
*/
8+
class JWTUtils {
9+
10+
companion object {
11+
12+
/**
13+
* convenience method used during token processing.
14+
* Splits JWT into parts.
15+
* @throws IllegalArgumentException if it can't split or if the number of parts != 3
16+
*/
17+
fun splitToken(token: String): Triple<String, String, String> {
18+
val parts: List<String>? = token.split('.', limit = 3)
19+
if (parts !== null && parts.size == 3) {
20+
return Triple(parts[0], parts[1], parts[2])
21+
} else {
22+
throw IllegalArgumentException("Token must have 3 parts: Header, Payload, and Signature")
23+
}
24+
}
25+
26+
/**
27+
* Attempts to normalize a [potentialDID] to a known format.
28+
*
29+
* @return This will transform an ethereum address into an ethr-did
30+
* and an MNID string into a uport-did
31+
* Other cases return the original string
32+
*/
33+
fun normalizeKnownDID(potentialDID: String): String {
34+
35+
//ignore if it's already a did
36+
if (potentialDID.matches("^did:(.*)?:.*".toRegex()))
37+
return potentialDID
38+
39+
//match an ethereum address
40+
"^(0[xX])*([0-9a-fA-F]{40})".toRegex().find(potentialDID)?.let {
41+
val (_, hexDigits) = it.destructured
42+
return "did:ethr:0x$hexDigits"
43+
}
44+
45+
//match an MNID
46+
if (MNID.isMNID(potentialDID)) {
47+
return "did:uport:$potentialDID"
48+
}
49+
50+
return potentialDID
51+
}
52+
53+
}
54+
}

0 commit comments

Comments
 (0)