-
Notifications
You must be signed in to change notification settings - Fork 25.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support 3rd party initiated login for OpenID Connect #38474
Changes from 28 commits
6448271
92f63ef
19034ce
aaa5426
6614057
b70526a
422319d
05910d9
22e8bb7
b8aa5be
e3204f5
ad493a9
b3cbd8d
0a0ae0e
bc1f552
baadcdb
09413b5
59bc122
b368ffc
81a1770
c9c1cf7
3450541
8fd7f31
354f7fd
5937ec8
d6a8474
afaf2e9
4e0788e
0fbe3c7
82fa677
af22291
00baca1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -20,7 +20,17 @@ | |||
*/ | ||||
public class OpenIdConnectPrepareAuthenticationRequest extends ActionRequest { | ||||
|
||||
/** | ||||
* The name of the OpenID Connect realm in the configuration that should be used for authentication | ||||
*/ | ||||
private String realmName; | ||||
/** | ||||
* In case of a | ||||
* <a href="https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin">3rd party initiated authentication</a>, the | ||||
* issuer to the UA needs to be redirected for authentication | ||||
*/ | ||||
private String issuer; | ||||
private String loginHint; | ||||
private String state; | ||||
private String nonce; | ||||
|
||||
|
@@ -36,10 +46,23 @@ public String getNonce() { | |||
return nonce; | ||||
} | ||||
|
||||
public String getIssuer() { | ||||
return issuer; | ||||
} | ||||
|
||||
public String getLoginHint() { | ||||
return loginHint; | ||||
} | ||||
|
||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
public void setRealmName(String realmName) { | ||||
this.realmName = realmName; | ||||
} | ||||
|
||||
public void setIssuer(String issuer) { | ||||
this.issuer = issuer; | ||||
} | ||||
|
||||
public void setState(String state) { | ||||
this.state = state; | ||||
} | ||||
|
@@ -48,29 +71,40 @@ public void setNonce(String nonce) { | |||
this.nonce = nonce; | ||||
} | ||||
|
||||
public void setLoginHint(String loginHint) { | ||||
this.loginHint = loginHint; | ||||
} | ||||
|
||||
public OpenIdConnectPrepareAuthenticationRequest() { | ||||
} | ||||
|
||||
public OpenIdConnectPrepareAuthenticationRequest(StreamInput in) throws IOException { | ||||
super.readFrom(in); | ||||
realmName = in.readString(); | ||||
realmName = in.readOptionalString(); | ||||
issuer = in.readOptionalString(); | ||||
loginHint = in.readOptionalString(); | ||||
state = in.readOptionalString(); | ||||
nonce = in.readOptionalString(); | ||||
} | ||||
|
||||
@Override | ||||
public ActionRequestValidationException validate() { | ||||
ActionRequestValidationException validationException = null; | ||||
if (Strings.hasText(realmName) == false) { | ||||
validationException = addValidationError("realm name must be provided", null); | ||||
if (Strings.hasText(realmName) == false && Strings.hasText(issuer) == false) { | ||||
validationException = addValidationError("one of [realm, issuer] must be provided", null); | ||||
} | ||||
if (Strings.hasText(realmName) && Strings.hasText(issuer)) { | ||||
validationException = addValidationError("only one of [realm, issuer] can be provided in the same request", null); | ||||
} | ||||
return validationException; | ||||
} | ||||
|
||||
@Override | ||||
public void writeTo(StreamOutput out) throws IOException { | ||||
super.writeTo(out); | ||||
out.writeString(realmName); | ||||
out.writeOptionalString(realmName); | ||||
out.writeOptionalString(issuer); | ||||
out.writeOptionalString(loginHint); | ||||
out.writeOptionalString(state); | ||||
out.writeOptionalString(nonce); | ||||
} | ||||
|
@@ -81,7 +115,8 @@ public void readFrom(StreamInput in) { | |||
} | ||||
|
||||
public String toString() { | ||||
return "{realmName=" + realmName + ", state=" + state + ", nonce=" + nonce + "}"; | ||||
return "{realmName=" + realmName + ", issuer=" + issuer + ", login_hint=" + | ||||
loginHint + ", state=" + state + ", nonce=" + nonce + "}"; | ||||
} | ||||
|
||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
import org.elasticsearch.action.ActionListener; | ||
import org.elasticsearch.action.support.ActionFilters; | ||
import org.elasticsearch.action.support.HandledTransportAction; | ||
import org.elasticsearch.common.Strings; | ||
import org.elasticsearch.common.inject.Inject; | ||
import org.elasticsearch.common.io.stream.Writeable; | ||
import org.elasticsearch.tasks.Task; | ||
|
@@ -21,6 +22,8 @@ | |
import org.elasticsearch.xpack.security.authc.Realms; | ||
import org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class TransportOpenIdConnectPrepareAuthenticationAction extends HandledTransportAction<OpenIdConnectPrepareAuthenticationRequest, | ||
OpenIdConnectPrepareAuthenticationResponse> { | ||
|
@@ -38,19 +41,39 @@ public TransportOpenIdConnectPrepareAuthenticationAction(TransportService transp | |
@Override | ||
protected void doExecute(Task task, OpenIdConnectPrepareAuthenticationRequest request, | ||
ActionListener<OpenIdConnectPrepareAuthenticationResponse> listener) { | ||
final Realm realm = this.realms.realm(request.getRealmName()); | ||
if (null == realm || realm instanceof OpenIdConnectRealm == false) { | ||
Realm realm = null; | ||
if (Strings.hasText(request.getIssuer())) { | ||
List<OpenIdConnectRealm> matchingRealms = this.realms.stream().filter(r -> r instanceof OpenIdConnectRealm) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we use a single filter?
|
||
.map(r -> (OpenIdConnectRealm) r) | ||
.filter(r -> r.isIssuerValid(request.getIssuer())) | ||
.collect(Collectors.toList()); | ||
if (matchingRealms.isEmpty()) { | ||
listener.onFailure( | ||
new ElasticsearchSecurityException("Cannot find OpenID Connect realm with issuer [{}]", request.getIssuer())); | ||
} else if (matchingRealms.size() > 1) { | ||
listener.onFailure( | ||
new ElasticsearchSecurityException("Found multiple OpenID Connect realm with issuer [{}]", request.getIssuer())); | ||
} else { | ||
realm = matchingRealms.get(0); | ||
} | ||
} else if (Strings.hasText(request.getRealmName())) { | ||
realm = this.realms.realm(request.getRealmName()); | ||
} | ||
|
||
if (realm instanceof OpenIdConnectRealm) { | ||
prepareAuthenticationResponse((OpenIdConnectRealm) realm, request.getState(), request.getNonce(), request.getLoginHint(), | ||
listener); | ||
} else { | ||
listener.onFailure( | ||
new ElasticsearchSecurityException("Cannot find OpenID Connect realm with name [{}]", request.getRealmName())); | ||
} else { | ||
prepareAuthenticationResponse((OpenIdConnectRealm) realm, request.getState(), request.getNonce(), listener); | ||
} | ||
} | ||
|
||
private void prepareAuthenticationResponse(OpenIdConnectRealm realm, String state, String nonce, | ||
private void prepareAuthenticationResponse(OpenIdConnectRealm realm, String state, String nonce, String loginHint, | ||
ActionListener<OpenIdConnectPrepareAuthenticationResponse> listener) { | ||
try { | ||
final OpenIdConnectPrepareAuthenticationResponse authenticationResponse = realm.buildAuthenticationRequestUri(state, nonce); | ||
final OpenIdConnectPrepareAuthenticationResponse authenticationResponse = | ||
realm.buildAuthenticationRequestUri(state, nonce, loginHint); | ||
listener.onResponse(authenticationResponse); | ||
} catch (ElasticsearchException e) { | ||
listener.onFailure(e); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ public class RestOpenIdConnectPrepareAuthenticationAction extends OpenIdConnectB | |
|
||
static { | ||
PARSER.declareString(OpenIdConnectPrepareAuthenticationRequest::setRealmName, new ParseField("realm")); | ||
PARSER.declareString(OpenIdConnectPrepareAuthenticationRequest::setIssuer, new ParseField("iss")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦♂️ |
||
PARSER.declareString(OpenIdConnectPrepareAuthenticationRequest::setState, new ParseField("state")); | ||
PARSER.declareString(OpenIdConnectPrepareAuthenticationRequest::setNonce, new ParseField("nonce")); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.