From f9ad463eabe9e3e39695e2dd2f5f1367550e2be7 Mon Sep 17 00:00:00 2001
From: jan-olaveide <80393418+jan-olaveide@users.noreply.github.com>
Date: Mon, 17 May 2021 08:53:24 +0200
Subject: [PATCH] Token x (#808)
---
.../pdl/OnBehalfOfJerseyPdlKlient.java | 9 ++-
.../integrasjon/pdl/TestJerseyPdlClient.java | 12 ++-
integrasjon/rest-klient/pom.xml | 17 +++-
.../integrasjon/graphql/GraphQLException.java | 3 +-
.../jersey/OnBehalfOfTokenRequestFilter.java | 27 -------
.../jersey/StsAccessTokenJerseyClient.java | 5 +-
.../tokenx/TokenXAssertionGenerator.java | 57 +++++++++++++
.../rest/jersey/tokenx/TokenXAudience.java | 23 ++++++
.../tokenx/TokenXAudienceGenerator.java | 37 +++++++++
.../rest/jersey/tokenx/TokenXClient.java | 7 ++
.../rest/jersey/tokenx/TokenXConfig.java | 32 ++++++++
.../jersey/tokenx/TokenXConfigMetadata.java | 8 ++
.../jersey/tokenx/TokenXJerseyClient.java | 61 ++++++++++++++
.../jersey/tokenx/TokenXRequestFilter.java | 81 +++++++++++++++++++
.../rest/jersey/tokenx/TokenXResponse.java | 10 +++
.../rest/jersey/tokenx/TestTokenX.java | 19 +++++
16 files changed, 371 insertions(+), 37 deletions(-)
delete mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java
create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java
create mode 100644 integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java
diff --git a/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java b/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java
index 689f7bbd0..939587cf9 100644
--- a/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java
+++ b/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java
@@ -7,7 +7,7 @@
import no.nav.foreldrepenger.konfig.KonfigVerdi;
import no.nav.vedtak.felles.integrasjon.rest.jersey.Jersey;
-import no.nav.vedtak.felles.integrasjon.rest.jersey.OnBehalfOfTokenRequestFilter;
+import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXRequestFilter;
@Dependent
@Jersey("onbehalf")
@@ -17,6 +17,11 @@ public class OnBehalfOfJerseyPdlKlient extends AbstractJerseyPdlKlient {
public OnBehalfOfJerseyPdlKlient(
@KonfigVerdi(value = "pdl.base.url", defaultVerdi = HTTP_PDL_API_DEFAULT_GRAPHQL) URI endpoint,
@KonfigVerdi(value = "pdl.tema", defaultVerdi = FOR) String tema) {
- super(endpoint, new OnBehalfOfTokenRequestFilter(tema));
+ this(endpoint, new TokenXRequestFilter(tema));
}
+
+ OnBehalfOfJerseyPdlKlient(URI endpoint, TokenXRequestFilter tokenXTokenRequestFilter) {
+ super(endpoint, tokenXTokenRequestFilter);
+ }
+
}
diff --git a/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java b/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java
index 0013705c3..ac19e5716 100644
--- a/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java
+++ b/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java
@@ -46,6 +46,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@@ -68,6 +69,8 @@
import no.nav.vedtak.exception.TekniskException;
import no.nav.vedtak.felles.integrasjon.rest.jersey.StsAccessTokenClientRequestFilter;
import no.nav.vedtak.felles.integrasjon.rest.jersey.StsAccessTokenJerseyClient;
+import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXClient;
+import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXRequestFilter;
import no.nav.vedtak.sikkerhet.context.SubjectHandler;
import no.nav.vedtak.sikkerhet.domene.SAMLAssertionCredential;
@@ -84,6 +87,9 @@ class TestJerseyPdlClient {
private Pdl legacyClient;
private Pdl tokenXClient;
+ @Mock
+ TokenXClient client;
+
@Mock
private StsAccessTokenJerseyClient sts;
@Mock
@@ -112,9 +118,8 @@ static void stopServer() {
@BeforeEach
void beforeEach() throws Exception {
- when(sts.getUsername()).thenReturn(USERNAME);
legacyClient = new JerseyPdlKlient(URI, new StsAccessTokenClientRequestFilter(sts, FOR, cache));
- tokenXClient = new OnBehalfOfJerseyPdlKlient(URI, FOR);
+ tokenXClient = new OnBehalfOfJerseyPdlKlient(URI, new TokenXRequestFilter(FOR, client));
}
@Test
@@ -135,9 +140,9 @@ void testErrorHandler() throws Exception {
@Test
@DisplayName("Test at kun Authorization og Tema blir satt for tokenX token")
void testPersonAuthWithUserToken() throws Exception {
- when(sts.accessToken()).thenReturn(SYSTEMTOKEN);
doReturn(TOKENXTOKEN).when(subjectHandler).getInternSsoToken();
try (var s = mockStatic(SubjectHandler.class)) {
+ when(client.exchange(Mockito.any(), Mockito.any())).thenReturn(TOKENXTOKEN);
s.when(SubjectHandler::getSubjectHandler).thenReturn(subjectHandler);
stubFor(post(urlPathEqualTo(GRAPHQL))
.withHeader(ACCEPT, equalTo(APPLICATION_JSON))
@@ -152,7 +157,6 @@ void testPersonAuthWithUserToken() throws Exception {
res = legacyClient.hentPerson(pq(), pp());
assertNotNull(res.getNavn());
assertNotNull(res.getNavn().get(0).getFornavn());
- verify(sts).accessToken();
}
}
diff --git a/integrasjon/rest-klient/pom.xml b/integrasjon/rest-klient/pom.xml
index e3074880e..3fe6dbf47 100644
--- a/integrasjon/rest-klient/pom.xml
+++ b/integrasjon/rest-klient/pom.xml
@@ -18,10 +18,15 @@
${jersey.version}
pom
import
-
+
+
+ no.nav.foreldrepenger
+ konfig
+ 1.1.2
+
io.github.kobylynskyi
graphql-java-codegen
@@ -103,5 +108,15 @@
org.slf4j
jul-to-slf4j
+
+ no.nav.security
+ token-client-core
+ 1.3.7
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+ 9.9.3
+
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java
index 1586fe74e..a1ca0e79c 100644
--- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java
@@ -1,5 +1,6 @@
package no.nav.vedtak.felles.integrasjon.graphql;
+import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
import java.net.URI;
@@ -12,7 +13,7 @@
public class GraphQLException extends IntegrasjonException {
public GraphQLException(String kode, List errors, URI uri) {
- super(kode, String.format("Feil %s ved GraphQL oppslag mot %s", errors.stream()
+ super(kode, format("Feil %s ved GraphQL oppslag mot %s", errors.stream()
.map(GraphQLError::getMessage)
.collect(joining(",")), uri));
}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java
deleted file mode 100644
index bfd17ed60..000000000
--- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package no.nav.vedtak.felles.integrasjon.rest.jersey;
-
-import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
-import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.OIDC_AUTH_HEADER_PREFIX;
-import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.TEMA;
-
-import javax.ws.rs.client.ClientRequestContext;
-
-/**
- * Dette filteret skal brukes når man vet man mottar et token som støtter
- * on-behalf-of, og når mottakende system krever kun dette
- *
- */
-public class OnBehalfOfTokenRequestFilter extends OidcTokenRequestFilter {
-
- private final String tema;
-
- public OnBehalfOfTokenRequestFilter(String tema) {
- this.tema = tema;
- }
-
- @Override
- public void filter(ClientRequestContext ctx) {
- ctx.getHeaders().add(TEMA, tema);
- ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + accessToken());
- }
-}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java
index a8fb76a42..01ab0782b 100644
--- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java
@@ -35,7 +35,8 @@ public String accessToken() {
}).get("access_token");
}
- public String getUsername() {
- return cfg.getUsername();
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [cfg=" + cfg + "]";
}
}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java
new file mode 100644
index 000000000..25e8ebf35
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java
@@ -0,0 +1,57 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import static com.nimbusds.jose.JOSEObjectType.JWT;
+import static com.nimbusds.jose.JWSAlgorithm.RS256;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.UUID;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+class TokenXAssertionGenerator {
+
+ private final TokenXConfig cfg;
+ private final TokenXConfigMetadata metadata;
+
+ TokenXAssertionGenerator(TokenXConfig cfg, TokenXConfigMetadata metadata) {
+ this.cfg = cfg;
+ this.metadata = metadata;
+ }
+
+ public String assertion() {
+ var now = Date.from(Instant.now());
+ try {
+ return sign(new JWTClaimsSet.Builder()
+ .subject(cfg.clientId())
+ .issuer(cfg.clientId())
+ .audience(metadata.tokenEndpoint().toString())
+ .issueTime(now)
+ .notBeforeTime(now)
+ .expirationTime(Date.from(Instant.now().plusSeconds(60)))
+ .jwtID(UUID.randomUUID().toString())
+ .build(), cfg.rsaKey())
+ .serialize();
+ } catch (JOSEException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static SignedJWT sign(JWTClaimsSet claimsSet, RSAKey rsaKey) throws JOSEException {
+ var signedJWT = new SignedJWT(new JWSHeader.Builder(RS256)
+ .keyID(rsaKey.getKeyID())
+ .type(JWT).build(), claimsSet);
+ signedJWT.sign(new RSASSASigner(rsaKey.toPrivateKey()));
+ return signedJWT;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [cfg=" + cfg + ", metadata=" + metadata + "]";
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java
new file mode 100644
index 000000000..9635576ac
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java
@@ -0,0 +1,23 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import java.util.StringJoiner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import no.nav.foreldrepenger.konfig.Cluster;
+import no.nav.foreldrepenger.konfig.Namespace;
+
+public record TokenXAudience(Cluster cluster, Namespace namespace, String app) {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TokenXAudience.class);
+ public String asAudience() {
+ var joiner = new StringJoiner(":");
+ joiner.add(cluster.clusterName());
+ joiner.add(namespace.getName());
+ joiner.add(app);
+ var audience = joiner.toString();
+ LOG.trace("Audience er {}", audience);
+ return audience;
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java
new file mode 100644
index 000000000..f840269ed
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java
@@ -0,0 +1,37 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import java.net.URI;
+import java.util.Optional;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import lombok.val;
+import no.nav.foreldrepenger.konfig.Cluster;
+import no.nav.foreldrepenger.konfig.Environment;
+import no.nav.foreldrepenger.konfig.Namespace;
+
+public class TokenXAudienceGenerator {
+ private static final Logger LOG = LoggerFactory.getLogger(TokenXAudienceGenerator.class);
+ private static final Environment ENV = Environment.current();
+
+ public TokenXAudience audience(URI uri) {
+ LOG.trace("Utleder audience for {}", uri);
+ String host = uri.getHost();
+ val elems = host.split("\\.");
+
+ if (elems.length == 1) {
+ return new TokenXAudience(cluster(host), ENV.getNamespace(), elems[0]);
+ }
+ if (elems.length == 2) {
+ return new TokenXAudience(cluster(host), Namespace.of(elems[1]), elems[0]);
+ }
+ throw new IllegalArgumentException("Kan ikke analysere " + host + "(" + elems.length + ")");
+ }
+
+ private Cluster cluster(String key) {
+ return Optional.ofNullable(ENV.getProperty(key))
+ .map(Cluster::of)
+ .orElseGet(() -> ENV.getCluster());
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java
new file mode 100644
index 000000000..6c61780ac
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java
@@ -0,0 +1,7 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+public interface TokenXClient {
+
+ String exchange(String token, TokenXAudience audience);
+
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java
new file mode 100644
index 000000000..5fab180a8
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java
@@ -0,0 +1,32 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import java.net.URI;
+import java.text.ParseException;
+
+import com.nimbusds.jose.jwk.RSAKey;
+
+import no.nav.foreldrepenger.konfig.Environment;
+
+public record TokenXConfig(URI wellKnownUrl, String clientId, String privateJwk) {
+
+ private static final Environment ENV = Environment.current();
+
+ static TokenXConfig fraEnv() {
+ return new TokenXConfig(ENV.getRequiredProperty("token.x.well.known.url", URI.class),
+ ENV.getRequiredProperty("token.x.client.id"),
+ ENV.getRequiredProperty("token.x.private.jwk"));
+ }
+
+ public RSAKey rsaKey() {
+ try {
+ return RSAKey.parse(privateJwk);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [wellKnownUrl=" + wellKnownUrl + ",clientId=" + clientId + "]";
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java
new file mode 100644
index 000000000..d01595eea
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java
@@ -0,0 +1,8 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+record TokenXConfigMetadata(String issuer,
+ @JsonProperty("token_endpoint") String tokenEndpoint,
+ @JsonProperty("jwks_uri") String jwksUri) {
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java
new file mode 100644
index 000000000..86a16825c
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java
@@ -0,0 +1,61 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import static com.nimbusds.oauth2.sdk.auth.JWTAuthentication.CLIENT_ASSERTION_TYPE;
+import static javax.ws.rs.client.Entity.form;
+import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Form;
+
+import no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient;
+
+public class TokenXJerseyClient extends AbstractJerseyRestClient implements TokenXClient {
+
+ private final TokenXAssertionGenerator assertionGenerator;
+ private final TokenXConfigMetadata metadata;
+
+ public TokenXJerseyClient() {
+ this(TokenXConfig.fraEnv());
+ }
+
+ public TokenXJerseyClient(TokenXConfig cfg) {
+ this.metadata = metadataFra(cfg.wellKnownUrl());
+ this.assertionGenerator = new TokenXAssertionGenerator(cfg, metadata);
+ }
+
+ TokenXJerseyClient(TokenXConfigMetadata metdata, TokenXAssertionGenerator assertionGenerator) {
+ this.metadata = metdata;
+ this.assertionGenerator = assertionGenerator;
+ }
+
+ @Override
+ public String exchange(String token, TokenXAudience audience) {
+ var form = new Form()
+ .param("subject_token", token)
+ .param("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ .param("client_assertion_type", CLIENT_ASSERTION_TYPE)
+ .param("client_assertion", assertionGenerator.assertion())
+ .param("subject_token_type", "urn:ietf:params:oauth:token-type:jwt")
+ .param("audience", audience.asAudience());
+
+ return client
+ .target(metadata.tokenEndpoint())
+ .request(APPLICATION_FORM_URLENCODED_TYPE)
+ .post(form(form), TokenXResponse.class).accessToken();
+ }
+
+ private TokenXConfigMetadata metadataFra(URI uri) {
+ return client
+ .target(uri)
+ .request(APPLICATION_JSON_TYPE)
+ .get(TokenXConfigMetadata.class);
+
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [assertionGenerator=" + assertionGenerator + ", metadata=" + metadata + "]";
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java
new file mode 100644
index 000000000..b999687fc
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java
@@ -0,0 +1,81 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.OIDC_AUTH_HEADER_PREFIX;
+import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.TEMA;
+import static no.nav.vedtak.sikkerhet.context.SubjectHandler.getSubjectHandler;
+
+import java.net.URI;
+import java.util.Optional;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.SignedJWT;
+
+/**
+ * Dette filteret skal brukes når man vet man mottar et token som støtter
+ * on-behalf-of, og når mottakende system krever kun dette
+ *
+ */
+public class TokenXRequestFilter implements ClientRequestFilter {
+ private static final Logger LOG = LoggerFactory.getLogger(TokenXRequestFilter.class);
+ private final String tema;
+ private final TokenXClient client;
+ private final TokenXAudienceGenerator audienceGenerator;
+
+ public TokenXRequestFilter() {
+ this("FOR");
+ }
+
+ public TokenXRequestFilter(String tema) {
+ this(tema, new TokenXJerseyClient());
+ }
+
+ public TokenXRequestFilter(String tema, TokenXClient client) {
+ this(tema, client, new TokenXAudienceGenerator());
+ }
+
+ public TokenXRequestFilter(String tema, TokenXClient client, TokenXAudienceGenerator audienceGenerator) {
+ this.tema = tema;
+ this.client = client;
+ this.audienceGenerator = audienceGenerator;
+ }
+
+ @Override
+ public void filter(ClientRequestContext ctx) {
+ String token = originalToken();
+ ctx.getHeaders().add(TEMA, tema);
+ if (isTokenXToken(token)) {
+ LOG.trace("Veksler tokenX token for {}", ctx.getUri());
+ ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + client.exchange(token, audienceGenerator.audience(ctx.getUri())));
+ } else {
+ LOG.warn("Dette er intet tokenX token, sender originalt token videre til {}", ctx.getUri());
+ ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + token);
+
+ }
+ }
+
+ private String originalToken() {
+ return Optional.ofNullable(getSubjectHandler().getInternSsoToken()).orElseThrow();
+ }
+
+ private boolean isTokenXToken(String token) {
+ try {
+ return URI.create(SignedJWT.parse(token)
+ .getJWTClaimsSet().getIssuer()).getHost().contains("tokendings");
+
+ } catch (Exception e) {
+ LOG.warn("Kunne ikke sjekke token type", e);
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [tema=" + tema + ", client=" + client + ", audienceGenerator=" + audienceGenerator + "]";
+ }
+}
diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java
new file mode 100644
index 000000000..a8609b6e0
--- /dev/null
+++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java
@@ -0,0 +1,10 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+record TokenXResponse(
+ @JsonProperty("access_token") String accessToken,
+ @JsonProperty("issued_token_type") String issuedTokenType,
+ @JsonProperty("token_type") String tokenType,
+ @JsonProperty("expires_in") String expiresIn) {
+}
diff --git a/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java b/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java
new file mode 100644
index 000000000..f3672a37f
--- /dev/null
+++ b/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java
@@ -0,0 +1,19 @@
+package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.net.URI;
+
+import org.junit.jupiter.api.Test;
+
+class TestTokenX {
+
+ @Test
+ void testAudience() {
+ System.setProperty("pdl-api.default", "dev-fss");
+ var uri = URI.create("http://pdl-api.default/graphql");
+ var aud = new TokenXAudienceGenerator().audience(uri);
+ assertEquals("dev-fss:default:pdl-api", aud.asAudience());
+ }
+
+}