Skip to content

Commit

Permalink
fix: add capability to encode kid header in request to Oauth2 server (
Browse files Browse the repository at this point in the history
#4627)

fix: oauth2 kid header
  • Loading branch information
bscholtes1A authored Nov 18, 2024
1 parent 304b3c5 commit e5aa246
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
/**
* Provides OAuth2 client credentials flow support.
*/
@Provides({ IdentityService.class })
@Provides({IdentityService.class})
@Extension(value = Oauth2ServiceExtension.NAME)
public class Oauth2ServiceExtension implements ServiceExtension {

Expand Down Expand Up @@ -125,7 +125,12 @@ public void initialize(ServiceExtensionContext context) {

var certificate = Optional.ofNullable(certificateResolver.resolveCertificate(configuration.getPublicCertificateAlias()))
.orElseThrow(() -> new EdcException("Public certificate not found: " + configuration.getPublicCertificateAlias()));
jwtDecoratorRegistry.register(OAUTH2_TOKEN_CONTEXT, new Oauth2AssertionDecorator(configuration.getProviderAudience(), configuration.getClientId(), clock, configuration.getTokenExpiration()));
jwtDecoratorRegistry.register(OAUTH2_TOKEN_CONTEXT, Oauth2AssertionDecorator.Builder.newInstance()
.audience(configuration.getProviderAudience())
.clientId(configuration.getClientId())
.clock(clock)
.validity(configuration.getTokenExpiration())
.build());
jwtDecoratorRegistry.register(OAUTH2_TOKEN_CONTEXT, new X509CertificateDecorator(certificate));

providerKeyResolver = identityProviderKeyResolver(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.CLIENT_ID;
import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.CLIENT_SECRET_KEY;
import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.KEY_ID;
import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.PRIVATE_KEY_NAME;
import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.SCOPE;
import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.TOKEN_URL;
Expand Down Expand Up @@ -104,7 +105,13 @@ private Result<TokenRepresentation> createAssertion(String pkSecret, DataAddress
var validity = Optional.ofNullable(dataAddress.getStringProperty(VALIDITY))
.map(this::parseLong)
.orElse(DEFAULT_TOKEN_VALIDITY);
var decorator = new Oauth2AssertionDecorator(dataAddress.getStringProperty(TOKEN_URL), dataAddress.getStringProperty(CLIENT_ID), clock, validity);
var decorator = Oauth2AssertionDecorator.Builder.newInstance()
.audience(dataAddress.getStringProperty(TOKEN_URL))
.clientId(dataAddress.getStringProperty(CLIENT_ID))
.clock(clock)
.validity(validity)
.kid(dataAddress.getStringProperty(KEY_ID))
.build();
var service = new JwtGenerationService(jwsSignerProvider);

return service.generate(pkSecret, decorator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@

package org.eclipse.edc.iam.oauth2.spi;

import com.fasterxml.jackson.annotation.JsonCreator;
import org.eclipse.edc.spi.iam.TokenParameters;
import org.eclipse.edc.token.spi.KeyIdDecorator;
import org.eclipse.edc.token.spi.TokenDecorator;

import java.time.Clock;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
Expand All @@ -32,25 +35,72 @@

public class Oauth2AssertionDecorator implements TokenDecorator {

private final String audience;
private final String clientId;
private final Clock clock;
private final long validity;
private String audience;
private String clientId;
private Clock clock;
private long validity;
private String kid;

public Oauth2AssertionDecorator(String audience, String clientId, Clock clock, long validity) {
this.audience = audience;
this.clientId = clientId;
this.clock = clock;
this.validity = validity;
private Oauth2AssertionDecorator() {
}

@Override
public TokenParameters.Builder decorate(TokenParameters.Builder tokenParameters) {
new KeyIdDecorator(kid).decorate(tokenParameters);
return tokenParameters.claims(AUDIENCE, List.of(audience))
.claims(ISSUER, clientId)
.claims(SUBJECT, clientId)
.claims(JWT_ID, UUID.randomUUID().toString())
.claims(ISSUED_AT, Date.from(clock.instant()))
.claims(EXPIRATION_TIME, Date.from(clock.instant().plusSeconds(validity)));
}

public static class Builder {

private final Oauth2AssertionDecorator decorator;

private Builder() {
decorator = new Oauth2AssertionDecorator();
}

@JsonCreator
public static Oauth2AssertionDecorator.Builder newInstance() {
return new Oauth2AssertionDecorator.Builder();
}

public Builder audience(String audience) {
decorator.audience = audience;
return this;
}

public Builder clientId(String clientId) {
decorator.clientId = clientId;
return this;
}

public Builder clock(Clock clock) {
decorator.clock = clock;
return this;
}

public Builder validity(long validity) {
decorator.validity = validity;
return this;
}

public Builder kid(String kid) {
decorator.kid = kid;
return this;
}

public Oauth2AssertionDecorator build() {
Objects.requireNonNull(decorator.audience, "Audience must be set");
Objects.requireNonNull(decorator.clientId, "Client ID must be set");
if (decorator.validity <= 0) {
throw new IllegalArgumentException("Validity must be greater than 0");
}
decorator.clock = Objects.requireNonNullElse(decorator.clock, Clock.systemUTC());
return decorator;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
public interface Oauth2DataAddressSchema {
String CLIENT_ID = "oauth2:clientId";
String CLIENT_SECRET_KEY = "oauth2:clientSecretKey";
String KEY_ID = "oauth2:kid";
String TOKEN_URL = "oauth2:tokenUrl";
String VALIDITY = "oauth2:validity";
String PRIVATE_KEY_NAME = "oauth2:privateKeyName";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package org.eclipse.edc.iam.oauth2.spi;

import org.eclipse.edc.spi.iam.TokenParameters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.time.Clock;
import java.time.Instant;
Expand All @@ -34,27 +36,32 @@
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT;

class Oauth2AssertionDecoratorTest {

private static final long TOKEN_EXPIRATION = 500;

private final Instant now = Instant.now();
private String audience;
private String clientId;
private Oauth2AssertionDecorator decorator;
private final Clock clock = Clock.fixed(now, UTC);
private final String audience = "test-audience";
private final String clientId = UUID.randomUUID().toString();

@BeforeEach
void setUp() {
audience = "test-audience";
clientId = UUID.randomUUID().toString();
var clock = Clock.fixed(now, UTC);
decorator = new Oauth2AssertionDecorator(audience, clientId, clock, TOKEN_EXPIRATION);
}
@ParameterizedTest
@EmptySource
@NullSource
@ValueSource(strings = {"test-kid", " ", " "})
void verifyDecorate(String kid) {
var decorator = Oauth2AssertionDecorator.Builder.newInstance()
.audience(audience)
.clientId(clientId)
.clock(clock)
.validity(TOKEN_EXPIRATION)
.kid(kid)
.build();

@Test
void verifyDecorate() {
var b = TokenParameters.Builder.newInstance();
decorator.decorate(b);

var t = b.build();
assertThat(t.getHeaders()).isEmpty();
assertThat(t.getHeaders().get("kid")).isEqualTo(kid);
assertThat(t.getClaims())
.hasEntrySatisfying(AUDIENCE, o -> assertThat(o).asInstanceOf(list(String.class)).contains(audience))
.hasFieldOrPropertyWithValue(ISSUER, clientId)
Expand All @@ -63,4 +70,5 @@ void verifyDecorate() {
.hasEntrySatisfying(ISSUED_AT, issueDate -> assertThat((Date) issueDate).isEqualTo(now))
.hasEntrySatisfying(EXPIRATION_TIME, expiration -> assertThat((Date) expiration).isEqualTo(now.plusSeconds(TOKEN_EXPIRATION)));
}

}

0 comments on commit e5aa246

Please sign in to comment.