Skip to content

Commit 721fd43

Browse files
committed
SDK-2584: Add support for digital identity match
1 parent 477b06a commit 721fd43

File tree

8 files changed

+421
-2
lines changed

8 files changed

+421
-2
lines changed

yoti-sdk-api/src/main/java/com/yoti/api/client/DigitalIdentityClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import java.security.KeyPair;
55
import java.security.Security;
66

7+
import com.yoti.api.client.identity.MatchRequest;
8+
import com.yoti.api.client.identity.MatchResult;
79
import com.yoti.api.client.identity.ShareSession;
810
import com.yoti.api.client.identity.ShareSessionQrCode;
911
import com.yoti.api.client.identity.ShareSessionRequest;
@@ -54,6 +56,10 @@ public Receipt fetchShareReceipt(String receiptId) throws DigitalIdentityExcepti
5456
return identityService.fetchShareReceipt(sdkId, keyPair, receiptId);
5557
}
5658

59+
public MatchResult fetchMatch(MatchRequest request) throws DigitalIdentityException {
60+
return identityService.fetchMatch(sdkId, keyPair, request);
61+
}
62+
5763
private KeyPair loadKeyPair(KeyPairSource keyPairSource) throws InitialisationException {
5864
try {
5965
return keyPairSource.getFromStream(new KeyStreamVisitor());
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.yoti.api.client.identity;
2+
3+
import static com.yoti.api.client.spi.remote.call.HttpMethod.SUPPORTED_HTTP_METHODS;
4+
5+
import java.net.URI;
6+
import java.util.HashMap;
7+
import java.util.Locale;
8+
import java.util.Map;
9+
10+
import com.yoti.validation.Validation;
11+
12+
import com.fasterxml.jackson.annotation.JsonProperty;
13+
14+
public final class MatchNotification {
15+
16+
@JsonProperty(Property.URL)
17+
private final String endpoint;
18+
19+
@JsonProperty(Property.METHOD)
20+
private final String method;
21+
22+
@JsonProperty(Property.VERIFY_TLS)
23+
private final boolean verifyTls;
24+
25+
@JsonProperty(Property.HEADERS)
26+
private final Map<String, String> headers;
27+
28+
private MatchNotification(Builder builder) {
29+
endpoint = builder.endpoint;
30+
method = builder.method;
31+
verifyTls = builder.verifyTls;
32+
headers = builder.headers;
33+
}
34+
35+
public String getEndpoint() {
36+
return endpoint;
37+
}
38+
39+
public String getMethod() {
40+
return method;
41+
}
42+
43+
public boolean isVerifyTls() {
44+
return verifyTls;
45+
}
46+
47+
public Map<String, String> getHeaders() {
48+
return headers;
49+
}
50+
51+
public static Builder forUrl(URI uri) {
52+
return new Builder(uri);
53+
}
54+
55+
public static final class Builder {
56+
57+
private final String endpoint;
58+
private final Map<String, String> headers;
59+
60+
private String method;
61+
private boolean verifyTls;
62+
63+
private Builder(URI uri) {
64+
Validation.notNull(uri, Property.URL);
65+
66+
endpoint = uri.toString();
67+
headers = new HashMap<>();
68+
}
69+
70+
public Builder withMethod(String method) {
71+
Validation.withinList(method.toUpperCase(Locale.ROOT), SUPPORTED_HTTP_METHODS);
72+
73+
this.method = method;
74+
return this;
75+
}
76+
77+
public Builder withVerifyTls(boolean verifyTls) {
78+
this.verifyTls = verifyTls;
79+
return this;
80+
}
81+
82+
public Builder withHeaders(Map<String, String> headers) {
83+
this.headers.putAll(headers);
84+
return this;
85+
}
86+
87+
public Builder withHeader(String key, String value) {
88+
headers.put(key, value);
89+
return this;
90+
}
91+
92+
public MatchNotification build() {
93+
return new MatchNotification(this);
94+
}
95+
96+
}
97+
98+
private static final class Property {
99+
100+
private static final String URL = "url";
101+
private static final String METHOD = "method";
102+
private static final String VERIFY_TLS = "verifyTls";
103+
private static final String HEADERS = "headers";
104+
105+
}
106+
107+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.yoti.api.client.identity;
2+
3+
import com.yoti.validation.Validation;
4+
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
public final class MatchRequest {
8+
9+
@JsonProperty(Property.VALUE)
10+
private final String value;
11+
12+
@JsonProperty(Property.NOTIFICATION)
13+
private final MatchNotification notification;
14+
15+
private MatchRequest(Builder builder) {
16+
value = builder.value;
17+
notification = builder.notification;
18+
}
19+
20+
public String getValue() {
21+
return value;
22+
}
23+
24+
public MatchNotification getNotification() {
25+
return notification;
26+
}
27+
28+
public static Builder builder(String value) {
29+
return new Builder(value);
30+
}
31+
32+
public static final class Builder {
33+
34+
private final String value;
35+
36+
private MatchNotification notification;
37+
38+
private Builder(String value) {
39+
Validation.notNullOrEmpty(value, Property.VALUE);
40+
41+
this.value = value;
42+
}
43+
44+
public Builder withNotification(MatchNotification notification) {
45+
this.notification = notification;
46+
return this;
47+
}
48+
49+
public MatchRequest build() {
50+
return new MatchRequest(this);
51+
}
52+
53+
}
54+
55+
private static final class Property {
56+
57+
private static final String VALUE = "value";
58+
private static final String NOTIFICATION = "notification";
59+
60+
}
61+
62+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.yoti.api.client.identity;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
public final class MatchResult {
6+
7+
@JsonProperty(Property.ID)
8+
private String id;
9+
10+
@JsonProperty(Property.RESULT)
11+
private String result;
12+
13+
public String getId() {
14+
return id;
15+
}
16+
17+
public String getResult() {
18+
return result;
19+
}
20+
21+
public void setId(String id) {
22+
this.id = id;
23+
}
24+
25+
public void setResult(String result) {
26+
this.result = result;
27+
}
28+
29+
private static final class Property {
30+
31+
private static final String ID = "id";
32+
private static final String RESULT = "result";
33+
34+
}
35+
36+
}

yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/factory/UnsignedPathFactory.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class UnsignedPathFactory {
1515
private static final String IDENTITY_SESSION_RECEIPT_RETRIEVAL = "/v2/receipts/%s";
1616
private static final String IDENTITY_SESSION_RECEIPT_KEY_RETRIEVAL = "/v2/wrapped-item-keys/%s";
1717

18+
// Match
19+
private static final String DIGITAL_ID_MATCH = "/v1/matches";
20+
1821
// Share V1
1922
private static final String PROFILE = "/profile/%s?appId=%s";
2023
private static final String QR_CODE = "/qrcodes/apps/%s";
@@ -66,6 +69,10 @@ private static String base64ToBase64url(String value) {
6669
return value.replace('+', '-').replace('/', '_');
6770
}
6871

72+
public String createIdentityMatchPath() {
73+
return DIGITAL_ID_MATCH;
74+
}
75+
6976
public String createProfilePath(String appId, String connectToken) {
7077
return format(PROFILE, connectToken, appId);
7178
}

yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/identity/DigitalIdentityService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import java.security.KeyPair;
1717
import java.util.Optional;
1818

19+
import com.yoti.api.client.identity.MatchRequest;
20+
import com.yoti.api.client.identity.MatchResult;
1921
import com.yoti.api.client.identity.ShareSession;
2022
import com.yoti.api.client.identity.ShareSessionQrCode;
2123
import com.yoti.api.client.identity.ShareSessionRequest;
@@ -189,6 +191,30 @@ private ReceiptItemKey fetchShareReceiptKey(String sdkId, KeyPair keyPair, Wrapp
189191
}
190192
}
191193

194+
public MatchResult fetchMatch(String sdkId, KeyPair keyPair, MatchRequest matchRequest)
195+
throws DigitalIdentityException {
196+
Validation.notNullOrEmpty(sdkId, "SDK ID");
197+
Validation.notNull(keyPair, "Application Key Pair");
198+
Validation.notNull(matchRequest, "DID Match request");
199+
200+
String path = pathFactory.createIdentityMatchPath();
201+
202+
LOG.debug("Requesting digital ID Match for SDK ID '{}' at '{}'", sdkId, path);
203+
204+
try {
205+
byte[] payload = ResourceMapper.writeValueAsString(matchRequest);
206+
return createSignedRequest(sdkId, keyPair, path, HTTP_POST, payload).execute(MatchResult.class);
207+
} catch (IOException ex) {
208+
throw new DigitalIdentityException("Error while parsing the DID Match request", ex);
209+
} catch (URISyntaxException ex) {
210+
throw new DigitalIdentityException("Error while building the DID Match request", ex);
211+
} catch (GeneralSecurityException ex) {
212+
throw new DigitalIdentityException("Error while signing the DID Match request", ex);
213+
} catch (ResourceException ex) {
214+
throw new DigitalIdentityException("Error while executing the DID Match request", ex);
215+
}
216+
}
217+
192218
SignedRequest createSignedRequest(String sdkId, KeyPair keyPair, String path)
193219
throws GeneralSecurityException, UnsupportedEncodingException, URISyntaxException {
194220
return createSignedRequest(sdkId, keyPair, path, HTTP_GET, null);

yoti-sdk-api/src/test/java/com/yoti/api/client/DigitalIdentityClientTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.InputStream;
1414
import java.security.KeyPair;
1515

16+
import com.yoti.api.client.identity.MatchRequest;
1617
import com.yoti.api.client.identity.ShareSessionRequest;
1718
import com.yoti.api.client.spi.remote.KeyStreamVisitor;
1819
import com.yoti.api.client.spi.remote.call.identity.DigitalIdentityException;
@@ -37,6 +38,7 @@ public class DigitalIdentityClientTest {
3738
@Mock(answer = RETURNS_DEEP_STUBS) KeyPair keyPair;
3839
@Mock DigitalIdentityService identityService;
3940
@Mock ShareSessionRequest shareSessionRequest;
41+
@Mock MatchRequest matchRequest;
4042

4143
private KeyPairSource validKeyPairSource;
4244

@@ -224,6 +226,28 @@ public void client_FetchShareReceiptException_DigitalIdentityException() throws
224226
assertThat(ex.getMessage(), equalTo(exMessage));
225227
}
226228

229+
@Test
230+
public void client_FetchDigitalIdMatchException_DigitalIdentityException() throws IOException {
231+
when(keyPairSource.getFromStream(any(KeyStreamVisitor.class))).thenReturn(keyPair);
232+
233+
DigitalIdentityClient identityClient = new DigitalIdentityClient(
234+
AN_SDK_ID,
235+
keyPairSource,
236+
identityService
237+
);
238+
239+
String exMessage = "Fetch digital identity match error";
240+
when(identityService.fetchMatch(AN_SDK_ID, keyPair, matchRequest))
241+
.thenThrow(new DigitalIdentityException(exMessage));
242+
243+
DigitalIdentityException ex = assertThrows(
244+
DigitalIdentityException.class,
245+
() -> identityClient.fetchMatch(matchRequest)
246+
);
247+
248+
assertThat(ex.getMessage(), equalTo(exMessage));
249+
}
250+
227251
private static class KeyPairSourceTest implements KeyPairSource {
228252

229253
private final String keyPair;

0 commit comments

Comments
 (0)