Skip to content

Commit 51dbd9e

Browse files
committed
Adding authentication information to access token create APIs
Adding authentication object to following APIs: /_security/oauth2/token /_security/delegate_pki /_security/saml/authenticate /_security/oidc/authenticate Resolves: elastic#59685
1 parent 5075e83 commit 51dbd9e

File tree

15 files changed

+76
-37
lines changed

15 files changed

+76
-37
lines changed

client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class DelegatePkiAuthenticationResponseTests extends
3737
protected org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse createServerTestInstance(
3838
XContentType xContentType) {
3939
return new org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse(randomAlphaOfLength(6),
40-
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"));
40+
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"), null);
4141
}
4242

4343
@Override

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/DelegatePkiAuthenticationResponse.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
1515
import org.elasticsearch.common.xcontent.ToXContentObject;
1616
import org.elasticsearch.common.xcontent.XContentBuilder;
17+
import org.elasticsearch.xpack.core.security.authc.Authentication;
1718

1819
import java.io.IOException;
1920
import java.util.Objects;
@@ -26,6 +27,7 @@ public final class DelegatePkiAuthenticationResponse extends ActionResponse impl
2627
private static final ParseField ACCESS_TOKEN_FIELD = new ParseField("access_token");
2728
private static final ParseField TYPE_FIELD = new ParseField("type");
2829
private static final ParseField EXPIRES_IN_FIELD = new ParseField("expires_in");
30+
private static final ParseField AUTHENTICATION = new ParseField("authentication");
2931

3032
public static final ConstructingObjectParser<DelegatePkiAuthenticationResponse, Void> PARSER = new ConstructingObjectParser<>(
3133
"delegate_pki_response", true, a -> {
@@ -35,30 +37,35 @@ public final class DelegatePkiAuthenticationResponse extends ActionResponse impl
3537
throw new IllegalArgumentException("Unknown token type [" + type + "], only [Bearer] type permitted");
3638
}
3739
final Long expiresIn = (Long) a[2];
38-
return new DelegatePkiAuthenticationResponse(accessToken, TimeValue.timeValueSeconds(expiresIn));
40+
final Authentication authentication = (Authentication) a[3];
41+
return new DelegatePkiAuthenticationResponse(accessToken, TimeValue.timeValueSeconds(expiresIn), authentication);
3942
});
4043

4144
static {
4245
PARSER.declareString(ConstructingObjectParser.constructorArg(), ACCESS_TOKEN_FIELD);
4346
PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD);
4447
PARSER.declareLong(ConstructingObjectParser.constructorArg(), EXPIRES_IN_FIELD);
48+
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), AUTHENTICATION);
4549
}
4650

4751
private String accessToken;
4852
private TimeValue expiresIn;
53+
private Authentication authentication;
4954

5055
DelegatePkiAuthenticationResponse() { }
5156

52-
public DelegatePkiAuthenticationResponse(String accessToken, TimeValue expiresIn) {
57+
public DelegatePkiAuthenticationResponse(String accessToken, TimeValue expiresIn, Authentication authentication) {
5358
this.accessToken = Objects.requireNonNull(accessToken);
5459
// always store expiration in seconds because this is how we "serialize" to JSON and we need to parse back
5560
this.expiresIn = TimeValue.timeValueSeconds(Objects.requireNonNull(expiresIn).getSeconds());
61+
this.authentication = authentication;
5662
}
5763

5864
public DelegatePkiAuthenticationResponse(StreamInput input) throws IOException {
5965
super(input);
6066
accessToken = input.readString();
6167
expiresIn = input.readTimeValue();
68+
authentication = null;
6269
}
6370

6471
public String getAccessToken() {
@@ -91,10 +98,13 @@ public int hashCode() {
9198

9299
@Override
93100
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
94-
builder.startObject()
95-
.field(ACCESS_TOKEN_FIELD.getPreferredName(), accessToken)
96-
.field(TYPE_FIELD.getPreferredName(), "Bearer")
97-
.field(EXPIRES_IN_FIELD.getPreferredName(), expiresIn.getSeconds());
101+
builder.startObject();
102+
builder.field(ACCESS_TOKEN_FIELD.getPreferredName(), accessToken);
103+
builder.field(TYPE_FIELD.getPreferredName(), "Bearer");
104+
builder.field(EXPIRES_IN_FIELD.getPreferredName(), expiresIn.getSeconds());
105+
if (authentication != null) {
106+
builder.field(AUTHENTICATION.getPreferredName(), authentication);
107+
}
98108
return builder.endObject();
99109
}
100110
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/oidc/OpenIdConnectAuthenticateResponse.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.common.io.stream.StreamInput;
1010
import org.elasticsearch.common.io.stream.StreamOutput;
1111
import org.elasticsearch.common.unit.TimeValue;
12+
import org.elasticsearch.xpack.core.security.authc.Authentication;
1213

1314
import java.io.IOException;
1415

@@ -17,12 +18,15 @@ public class OpenIdConnectAuthenticateResponse extends ActionResponse {
1718
private String accessTokenString;
1819
private String refreshTokenString;
1920
private TimeValue expiresIn;
21+
private Authentication authentication;
2022

21-
public OpenIdConnectAuthenticateResponse(String principal, String accessTokenString, String refreshTokenString, TimeValue expiresIn) {
23+
public OpenIdConnectAuthenticateResponse(Authentication authentication, String accessTokenString, String refreshTokenString,
24+
TimeValue expiresIn) {
2225
this.principal = principal;
2326
this.accessTokenString = accessTokenString;
2427
this.refreshTokenString = refreshTokenString;
2528
this.expiresIn = expiresIn;
29+
this.authentication = authentication;
2630
}
2731

2832
public OpenIdConnectAuthenticateResponse(StreamInput in) throws IOException {
@@ -31,6 +35,7 @@ public OpenIdConnectAuthenticateResponse(StreamInput in) throws IOException {
3135
accessTokenString = in.readString();
3236
refreshTokenString = in.readString();
3337
expiresIn = in.readTimeValue();
38+
authentication = null;
3439
}
3540

3641
public String getPrincipal() {
@@ -49,6 +54,8 @@ public TimeValue getExpiresIn() {
4954
return expiresIn;
5055
}
5156

57+
public Authentication getAuthentication() { return authentication; }
58+
5259
@Override
5360
public void writeTo(StreamOutput out) throws IOException {
5461
out.writeString(principal);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/saml/SamlAuthenticateResponse.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.common.io.stream.StreamInput;
1111
import org.elasticsearch.common.io.stream.StreamOutput;
1212
import org.elasticsearch.common.unit.TimeValue;
13+
import org.elasticsearch.xpack.core.security.authc.Authentication;
1314

1415
import java.io.IOException;
1516

@@ -24,6 +25,7 @@ public final class SamlAuthenticateResponse extends ActionResponse {
2425
private String refreshToken;
2526
private String realm;
2627
private TimeValue expiresIn;
28+
private Authentication authentication;
2729

2830
public SamlAuthenticateResponse(StreamInput in) throws IOException {
2931
super(in);
@@ -34,14 +36,16 @@ public SamlAuthenticateResponse(StreamInput in) throws IOException {
3436
tokenString = in.readString();
3537
refreshToken = in.readString();
3638
expiresIn = in.readTimeValue();
39+
authentication = null;
3740
}
3841

39-
public SamlAuthenticateResponse(String principal, String realm, String tokenString, String refreshToken, TimeValue expiresIn) {
40-
this.principal = principal;
41-
this.realm = realm;
42+
public SamlAuthenticateResponse(Authentication authentication, String tokenString, String refreshToken, TimeValue expiresIn) {
43+
this.principal = authentication.getUser().principal();
44+
this.realm = authentication.getAuthenticatedBy().getName();
4245
this.tokenString = tokenString;
4346
this.refreshToken = refreshToken;
4447
this.expiresIn = expiresIn;
48+
this.authentication = authentication;
4549
}
4650

4751
public String getPrincipal() {
@@ -64,6 +68,8 @@ public TimeValue getExpiresIn() {
6468
return expiresIn;
6569
}
6670

71+
public Authentication getAuthentication() { return authentication; }
72+
6773
@Override
6874
public void writeTo(StreamOutput out) throws IOException {
6975
out.writeString(principal);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/token/CreateTokenResponse.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.common.unit.TimeValue;
1212
import org.elasticsearch.common.xcontent.ToXContentObject;
1313
import org.elasticsearch.common.xcontent.XContentBuilder;
14+
import org.elasticsearch.xpack.core.security.authc.Authentication;
1415

1516
import java.io.IOException;
1617
import java.util.Objects;
@@ -27,6 +28,7 @@ public final class CreateTokenResponse extends ActionResponse implements ToXCont
2728
private String scope;
2829
private String refreshToken;
2930
private String kerberosAuthenticationResponseToken;
31+
private Authentication authentication;
3032

3133
CreateTokenResponse() {}
3234

@@ -40,12 +42,13 @@ public CreateTokenResponse(StreamInput in) throws IOException {
4042
}
4143

4244
public CreateTokenResponse(String tokenString, TimeValue expiresIn, String scope, String refreshToken,
43-
String kerberosAuthenticationResponseToken) {
45+
String kerberosAuthenticationResponseToken, Authentication authentication) {
4446
this.tokenString = Objects.requireNonNull(tokenString);
4547
this.expiresIn = Objects.requireNonNull(expiresIn);
4648
this.scope = scope;
4749
this.refreshToken = refreshToken;
4850
this.kerberosAuthenticationResponseToken = kerberosAuthenticationResponseToken;
51+
this.authentication = authentication;
4952
}
5053

5154
public String getTokenString() {
@@ -93,6 +96,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
9396
if (kerberosAuthenticationResponseToken != null) {
9497
builder.field("kerberos_authentication_response_token", kerberosAuthenticationResponseToken);
9598
}
99+
if (authentication != null) {
100+
builder.field("authentication", authentication);
101+
}
96102
return builder.endObject();
97103
}
98104

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/DelegatePkiAuthenticationResponseTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void testSerialization() throws Exception {
3535
@Override
3636
protected DelegatePkiAuthenticationResponse createTestInstance() {
3737
return new DelegatePkiAuthenticationResponse(randomAlphaOfLengthBetween(0, 10),
38-
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"));
38+
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"), null);
3939
}
4040

4141
@Override

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/token/CreateTokenResponseTests.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class CreateTokenResponseTests extends ESTestCase {
1414

1515
public void testSerialization() throws Exception {
1616
CreateTokenResponse response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
17-
randomBoolean() ? null : "FULL", randomAlphaOfLengthBetween(1, 10), randomBoolean() ? null :randomAlphaOfLengthBetween(1, 10));
17+
randomBoolean() ? null : "FULL", randomAlphaOfLengthBetween(1, 10), randomBoolean() ? null :randomAlphaOfLengthBetween(1, 10),
18+
null);
1819
try (BytesStreamOutput output = new BytesStreamOutput()) {
1920
response.writeTo(output);
2021
try (StreamInput input = output.bytes().streamInput()) {
@@ -24,7 +25,7 @@ public void testSerialization() throws Exception {
2425
}
2526

2627
response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
27-
randomBoolean() ? null : "FULL", null, null);
28+
randomBoolean() ? null : "FULL", null, null, null);
2829
try (BytesStreamOutput output = new BytesStreamOutput()) {
2930
response.writeTo(output);
3031
try (StreamInput input = output.bytes().streamInput()) {

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/TransportDelegatePkiAuthenticationAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ protected void doExecute(Task task, DelegatePkiAuthenticationRequest request,
8484
tokenService.createOAuth2Tokens(authentication, delegateeAuthentication, Map.of(), false,
8585
ActionListener.wrap(tuple -> {
8686
final TimeValue expiresIn = tokenService.getExpirationDelay();
87-
listener.onResponse(new DelegatePkiAuthenticationResponse(tuple.v1(), expiresIn));
87+
listener.onResponse(new DelegatePkiAuthenticationResponse(tuple.v1(), expiresIn, authentication));
8888
}, listener::onFailure));
8989
}, e -> {
9090
logger.debug((Supplier<?>) () -> new ParameterizedMessage("Delegated x509Token [{}] could not be authenticated",

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/oidc/TransportOpenIdConnectAuthenticateAction.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ protected void doExecute(Task task, OpenIdConnectAuthenticateRequest request,
7474
tokenService.createOAuth2Tokens(authentication, originatingAuthentication, tokenMetadata, true,
7575
ActionListener.wrap(tuple -> {
7676
final TimeValue expiresIn = tokenService.getExpirationDelay();
77-
listener.onResponse(new OpenIdConnectAuthenticateResponse(authentication.getUser().principal(), tuple.v1(),
78-
tuple.v2(), expiresIn));
77+
listener.onResponse(new OpenIdConnectAuthenticateResponse(authentication, tuple.v1(), tuple.v2(), expiresIn));
7978
}, listener::onFailure));
8079
}, e -> {
8180
logger.debug(() -> new ParameterizedMessage("OpenIDConnectToken [{}] could not be authenticated", token), e);

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlAuthenticateAction.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ protected void doExecute(Task task, SamlAuthenticateRequest request, ActionListe
6868
tokenMeta, true, ActionListener.wrap(tuple -> {
6969
final TimeValue expiresIn = tokenService.getExpirationDelay();
7070
listener.onResponse(
71-
new SamlAuthenticateResponse(authentication.getUser().principal(),
72-
authentication.getAuthenticatedBy().getName(), tuple.v1(), tuple.v2(), expiresIn));
71+
new SamlAuthenticateResponse(authentication, tuple.v1(), tuple.v2(), expiresIn));
7372
}, listener::onFailure));
7473
}, e -> {
7574
logger.debug(() -> new ParameterizedMessage("SamlToken [{}] could not be authenticated", saml), e);

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/token/TransportCreateTokenAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ private void createToken(GrantType grantType, CreateTokenRequest request, Authen
135135
final String scope = getResponseScopeValue(request.getScope());
136136
final String base64AuthenticateResponse = (grantType == GrantType.KERBEROS) ? extractOutToken() : null;
137137
final CreateTokenResponse response = new CreateTokenResponse(tuple.v1(), tokenService.getExpirationDelay(), scope,
138-
tuple.v2(), base64AuthenticateResponse);
138+
tuple.v2(), base64AuthenticateResponse, authentication);
139139
listener.onResponse(response);
140140
}, listener::onFailure));
141141
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/token/TransportRefreshTokenAction.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.common.inject.Inject;
1212
import org.elasticsearch.tasks.Task;
1313
import org.elasticsearch.transport.TransportService;
14+
import org.elasticsearch.xpack.core.security.SecurityContext;
1415
import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequest;
1516
import org.elasticsearch.xpack.core.security.action.token.CreateTokenResponse;
1617
import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction;
@@ -21,19 +22,23 @@
2122
public class TransportRefreshTokenAction extends HandledTransportAction<CreateTokenRequest, CreateTokenResponse> {
2223

2324
private final TokenService tokenService;
25+
private final SecurityContext securityContext;
2426

2527
@Inject
26-
public TransportRefreshTokenAction(TransportService transportService, ActionFilters actionFilters, TokenService tokenService) {
28+
public TransportRefreshTokenAction(TransportService transportService, ActionFilters actionFilters, TokenService tokenService,
29+
SecurityContext securityContext) {
2730
super(RefreshTokenAction.NAME, transportService, actionFilters, CreateTokenRequest::new);
2831
this.tokenService = tokenService;
32+
this.securityContext = securityContext;
2933
}
3034

3135
@Override
3236
protected void doExecute(Task task, CreateTokenRequest request, ActionListener<CreateTokenResponse> listener) {
3337
tokenService.refreshToken(request.getRefreshToken(), ActionListener.wrap(tuple -> {
3438
final String scope = getResponseScopeValue(request.getScope());
3539
final CreateTokenResponse response =
36-
new CreateTokenResponse(tuple.v1(), tokenService.getExpirationDelay(), scope, tuple.v2(), null);
40+
new CreateTokenResponse(tuple.v1(), tokenService.getExpirationDelay(), scope, tuple.v2(), null,
41+
securityContext.getAuthentication());
3742
listener.onResponse(response);
3843
}, listener::onFailure));
3944
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/oidc/RestOpenIdConnectAuthenticateAction.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,15 @@ protected RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClien
6363
@Override
6464
public RestResponse buildResponse(OpenIdConnectAuthenticateResponse response, XContentBuilder builder)
6565
throws Exception {
66-
builder.startObject()
67-
.field("username", response.getPrincipal())
68-
.field("access_token", response.getAccessTokenString())
69-
.field("refresh_token", response.getRefreshTokenString())
70-
.field("expires_in", response.getExpiresIn().seconds())
71-
.endObject();
66+
builder.startObject();
67+
builder.field("username", response.getPrincipal());
68+
builder.field("access_token", response.getAccessTokenString());
69+
builder.field("refresh_token", response.getRefreshTokenString());
70+
builder.field("expires_in", response.getExpiresIn().seconds());
71+
if (response.getAuthentication() != null){
72+
builder.field("authentication", response.getAuthentication());
73+
}
74+
builder.endObject();
7275
return new BytesRestResponse(RestStatus.OK, builder);
7376
}
7477
});

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlAuthenticateAction.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,16 @@ public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient c
9595
requestBuilder.execute(new RestBuilderListener<>(channel) {
9696
@Override
9797
public RestResponse buildResponse(SamlAuthenticateResponse response, XContentBuilder builder) throws Exception {
98-
builder.startObject()
99-
.field("username", response.getPrincipal())
100-
.field("realm", response.getRealm())
101-
.field("access_token", response.getTokenString())
102-
.field("refresh_token", response.getRefreshToken())
103-
.field("expires_in", response.getExpiresIn().seconds())
104-
.endObject();
98+
builder.startObject();
99+
builder.field("username", response.getPrincipal());
100+
builder.field("realm", response.getRealm());
101+
builder.field("access_token", response.getTokenString());
102+
builder.field("refresh_token", response.getRefreshToken());
103+
builder.field("expires_in", response.getExpiresIn().seconds());
104+
if (response.getAuthentication() != null){
105+
builder.field("authentication", response.getAuthentication());
106+
}
107+
builder.endObject();
105108
return new BytesRestResponse(RestStatus.OK, builder);
106109
}
107110
});

0 commit comments

Comments
 (0)