add additional validation, renaming and cleanups

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2019-08-29 17:47:37 +10:00
parent d33b96f411
commit 19369636e3
7 changed files with 52 additions and 40 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<New id="OpenIdConfiguration" class="org.eclipse.jetty.security.openid.OpenIdConfiguration">
<Arg><Property name="jetty.openid.identityProvider"/></Arg>
<Arg><Property name="jetty.openid.openIdProvider"/></Arg>
<Arg><Property name="jetty.openid.clientId"/></Arg>
<Arg><Property name="jetty.openid.clientSecret"/></Arg>
<Call name="addScopes">

View File

@ -19,7 +19,7 @@ etc/jetty-openid.xml
[ini-template]
## Identity Provider
# jetty.openid.identityProvider=https://accounts.google.com/
# jetty.openid.openIdProvider=https://accounts.google.com/
## Client ID
# jetty.openid.clientId=1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="BaseLoginService">
<!-- Optional code to configure the base LoginService used by the OpenIdLoginService
<New id="BaseLoginService" class="org.eclipse.jetty.security.HashLoginService">

View File

@ -31,7 +31,7 @@ public class OpenIdConfiguration
{
private static String CONFIG_PATH = "/.well-known/openid-configuration";
private final String identityProvider;
private final String openIdProvider;
private final String authEndpoint;
private final String tokenEndpoint;
private final String clientId;
@ -42,7 +42,7 @@ public class OpenIdConfiguration
public OpenIdConfiguration(String provider, String clientId, String clientSecret)
{
this.identityProvider = provider;
this.openIdProvider = provider;
this.clientId = clientId;
this.clientSecret = clientSecret;
@ -93,9 +93,9 @@ public class OpenIdConfiguration
return clientSecret;
}
public String getIdentityProvider()
public String getOpenIdProvider()
{
return identityProvider;
return openIdProvider;
}
public String getTokenEndpoint()

View File

@ -73,11 +73,25 @@ public class OpenIdCredentials
{
try
{
String jwt = getJWT();
decodeJWT(jwt);
response = claimAuthCode(authCode);
if (LOG.isDebugEnabled())
LOG.debug("userInfo {}", claims);
LOG.debug("response: {}", response);
String idToken = (String)response.get("id_token");
if (idToken == null)
throw new IllegalArgumentException("no id_token");
String accessToken = (String)response.get("access_token");
if (accessToken == null)
throw new IllegalArgumentException("no access_token");
String tokenType = (String)response.get("token_type");
if (!"Bearer".equalsIgnoreCase(tokenType))
throw new IllegalArgumentException("invalid token_type");
claims = decodeJWT(idToken);
if (LOG.isDebugEnabled())
LOG.debug("userClaims {}", claims);
}
finally
{
@ -92,18 +106,20 @@ public class OpenIdCredentials
if (authCode != null)
return false;
// Check audience should be clientId
// The aud (audience) Claim MUST contain the client_id value.
String audience = (String)claims.get("aud");
if (!configuration.getIdentityProvider().equals(audience))
if (!configuration.getClientId().equals(audience))
{
LOG.warn("Audience claim MUST contain the value of the Issuer Identifier for the OP", this);
//return false;
LOG.warn("Audience claim MUST contain the value of the Issuer Identifier for the OP");
return false;
}
// TODO: this does not work for microsoft
// Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim.
String issuer = (String)claims.get("iss");
if (!configuration.getClientId().equals(issuer))
if (!configuration.getOpenIdProvider().equals(issuer))
{
LOG.warn("Issuer claim MUST be the client_id of the OAuth Client {}", this);
//LOG.warn("");
//return false;
}
@ -120,7 +136,7 @@ public class OpenIdCredentials
return true;
}
private void decodeJWT(String jwt) throws IOException
protected Map<String, Object> decodeJWT(String jwt) throws IOException
{
if (LOG.isDebugEnabled())
LOG.debug("decodeJWT {}", jwt);
@ -136,46 +152,41 @@ public class OpenIdCredentials
Map<String, Object> jwtHeader = (Map)JSON.parse(jwtHeaderString);
LOG.debug("JWT Header: {}", jwtHeader);
// validate signature
LOG.warn("Signature NOT validated {}", jwtSignature);
/* If the ID Token is received via direct communication between the Client
and the Token Endpoint (which it is in this flow), the TLS server validation
MAY be used to validate the issuer in place of checking the token signature. */
if (LOG.isDebugEnabled())
LOG.debug("JWT signature not validated {}", jwtSignature);
// response should be a set of name/value pairs
claims = (Map)JSON.parse(jwtClaimString);
return (Map)JSON.parse(jwtClaimString);
}
private String getJWT() throws IOException
private Map<String, Object> claimAuthCode(String authCode) throws IOException
{
if (LOG.isDebugEnabled())
LOG.debug("getJWT {}", authCode);
LOG.debug("claimAuthCode {}", authCode);
// Use the auth code to get the id_token from the OpenID Provider
// Use the authorization code to get the id_token from the OpenID Provider
String urlParameters = "code=" + authCode +
"&client_id=" + configuration.getClientId() +
"&client_secret=" + configuration.getClientSecret() +
"&redirect_uri=" + redirectUri +
"&grant_type=authorization_code";
byte[] payload = urlParameters.getBytes(StandardCharsets.UTF_8);
URL url = new URL(configuration.getTokenEndpoint());
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Host", configuration.getIdentityProvider());
connection.setRequestProperty("Host", configuration.getOpenIdProvider());
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("charset", "utf-8");
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream()))
{
wr.write(payload);
wr.write(urlParameters.getBytes(StandardCharsets.UTF_8));
}
// get response and extract id_token jwt
InputStream content = (InputStream)connection.getContent();
response = (Map)JSON.parse(IO.toString(content));
if (LOG.isDebugEnabled())
LOG.debug("responseMap: {}", response);
return (String)response.get("id_token");
return (Map)JSON.parse(IO.toString(content));
}
}

View File

@ -53,7 +53,7 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi
@Override
public String getName()
{
return _configuration.getIdentityProvider();
return _configuration.getOpenIdProvider();
}
public OpenIdConfiguration getConfiguration()
@ -131,4 +131,4 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi
public void logout(UserIdentity user)
{
}
}
}

View File

@ -161,13 +161,14 @@ public class OpenIdAuthenticationDemo
securityHandler.addConstraintMapping(loginMapping);
securityHandler.addConstraintMapping(adminMapping);
/**/
// Google Authentication
OpenIdConfiguration configuration = new OpenIdConfiguration(
"https://accounts.google.com/",
"1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com",
"XT_MIsSv_aUCGollauCaJY8S");
configuration.addScopes("email", "profile");
/**/
/*
// Microsoft Authentication
@ -175,7 +176,7 @@ public class OpenIdAuthenticationDemo
"https://login.microsoftonline.com/common/v2.0",
"5f05dea8-2bd9-45de-b30f-cf5c102b8784",
"IfhQJKi-5[vxhh_=ldqt0y4PkV3z_1ca");
*/
*/
/*
// Yahoo Authentication