Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions sdk/src/main/java/io/opentdf/platform/sdk/TokenSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* timeouts and creating OIDC calls. It is thread-safe.
*/
class TokenSource {
private final DefaultDPoPProofFactory dpopProofFactory;
private Instant tokenExpiryTime;
private AccessToken token;
private final ClientAuthentication clientAuth;
Expand All @@ -51,6 +52,11 @@ public TokenSource(ClientAuthentication clientAuth, RSAKey rsaKey, URI tokenEndp
this.tokenEndpointURI = tokenEndpointURI;
this.sslFactory = sslFactory;
this.authzGrant = authzGrant;
try {
this.dpopProofFactory = new DefaultDPoPProofFactory(rsaKey, JWSAlgorithm.RS256);
} catch (JOSEException e) {
throw new SDKException("Error creating DPoP proof factory", e);
}
}

class AuthHeaders {
Expand All @@ -74,22 +80,28 @@ public String getDpopHeader() {
public AuthHeaders getAuthHeaders(URL url, String method) {
// Get the access token
AccessToken t = getToken();

// Build the DPoP proof for each request
String dpopProof;
String dpopProof = getDPoPProof(url, method, dpopProofFactory, t);

return new AuthHeaders(
"DPoP " + t.getValue(),
dpopProof);
}

// package-private for testing
static String getDPoPProof(URL url, String method, DPoPProofFactory dpopFactory, AccessToken t) {
URI onlyPath;
try {
DPoPProofFactory dpopFactory = new DefaultDPoPProofFactory(rsaKey, JWSAlgorithm.RS256);
SignedJWT proof = dpopFactory.createDPoPJWT(method, url.toURI(), t);
dpopProof = proof.serialize();
onlyPath = new URI(url.getPath());
} catch (URISyntaxException e) {
throw new SDKException("Invalid URI syntax for DPoP proof creation", e);
throw new SDKException("cannot create URL containing only path", e);
}
try {
SignedJWT proof = dpopFactory.createDPoPJWT(method, onlyPath, t);
return proof.serialize();
} catch (JOSEException e) {
throw new SDKException("Error creating DPoP proof", e);
}

return new AuthHeaders(
"DPoP " + t.getValue(),
dpopProof);
}

/**
Expand Down
35 changes: 35 additions & 0 deletions sdk/src/test/java/io/opentdf/platform/sdk/TokenSourceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.opentdf.platform.sdk;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.oauth2.sdk.dpop.DefaultDPoPProofFactory;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import org.junit.jupiter.api.Test;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;

import static org.assertj.core.api.Assertions.assertThat;

class TokenSourceTest {

@Test
void getDPoPProof() throws URISyntaxException, JOSEException, MalformedURLException, ParseException {
var fakeToken = new BearerAccessToken("this is a fake token");
var keypair = CryptoUtils.generateRSAKeypair();
var dpopKey = new RSAKey.Builder((RSAPublicKey) keypair.getPublic()).privateKey(keypair.getPrivate()).build();
var dpopProofFactory = new DefaultDPoPProofFactory(dpopKey, JWSAlgorithm.RS256) ;

var dpop = TokenSource.getDPoPProof(new URI("http://example.org/path/to/the/resource").toURL(), "POST", dpopProofFactory, fakeToken);

var jws = JWSObject.parse(dpop);
assertThat(jws.getPayload().toJSONObject())
.extracting("htu")
.containsOnly("/path/to/the/resource");
}
}
Loading