feat(jans-auth-server): updates to Audience Values for OAuth 2.0 Auth… · JanssenProject/jans@e71c35b (original) (raw)

`@@ -13,6 +13,7 @@

`

13

13

`import io.jans.as.model.crypto.AbstractCryptoProvider;

`

14

14

`import io.jans.as.model.crypto.signature.AlgorithmFamily;

`

15

15

`import io.jans.as.model.crypto.signature.SignatureAlgorithm;

`

``

16

`+

import io.jans.as.model.exception.CryptoProviderException;

`

16

17

`import io.jans.as.model.exception.InvalidJwtException;

`

17

18

`import io.jans.as.model.jwt.Jwt;

`

18

19

`import io.jans.as.model.jwt.JwtClaimName;

`

`@@ -21,6 +22,7 @@

`

21

22

`import io.jans.as.model.token.ClientAssertionType;

`

22

23

`import io.jans.as.server.service.ClientService;

`

23

24

`import io.jans.service.cdi.util.CdiUtil;

`

``

25

`+

import io.jans.util.Pair;

`

24

26

`import io.jans.util.security.StringEncrypter;

`

25

27

`import org.apache.commons.lang3.StringUtils;

`

26

28

`import org.json.JSONObject;

`

`@@ -31,118 +33,181 @@

`

31

33

``

32

34

`/**

`

33

35

` * @author Javier Rojas Blum

`

``

36

`+

`

34

37

` * @version February 12, 2019

`

35

38

` */

`

36

39

`public class ClientAssertion {

`

37

40

``

``

41

`+

private final AppConfiguration appConfiguration;

`

``

42

`+

private final AbstractCryptoProvider cryptoProvider;

`

``

43

`+

private final String clientId;

`

``

44

`+

private final ClientAssertionType clientAssertionType;

`

``

45

`+

private final String encodedAssertion;

`

``

46

`+

private boolean verified;

`

``

47

+

38

48

`private Jwt jwt;

`

39

49

`private String clientSecret;

`

40

50

``

41

``

`-

public ClientAssertion(AppConfiguration appConfiguration, AbstractCryptoProvider cryptoProvider, String clientId, io.jans.as.model.token.ClientAssertionType clientAssertionType, String encodedAssertion)

`

42

``

`-

throws InvalidJwtException {

`

``

51

`+

public ClientAssertion(AppConfiguration appConfiguration, AbstractCryptoProvider cryptoProvider, String clientId, io.jans.as.model.token.ClientAssertionType clientAssertionType, String encodedAssertion) {

`

``

52

`+

this.appConfiguration = appConfiguration;

`

``

53

`+

this.cryptoProvider = cryptoProvider;

`

``

54

`+

this.clientId = clientId;

`

``

55

`+

this.clientAssertionType = clientAssertionType;

`

``

56

`+

this.encodedAssertion = encodedAssertion;

`

``

57

`+

this.verified = false;

`

``

58

`+

}

`

``

59

+

``

60

`+

public String getSubjectIdentifier() throws InvalidJwtException {

`

``

61

`+

assertVerified();

`

``

62

`+

return jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);

`

``

63

`+

}

`

``

64

+

``

65

`+

public String getJti() throws InvalidJwtException {

`

``

66

`+

assertVerified();

`

``

67

`+

return jwt.getClaims().getClaimAsString(JwtClaimName.JWT_ID);

`

``

68

`+

}

`

``

69

+

``

70

`+

public String getClientSecret() throws InvalidJwtException {

`

``

71

`+

assertVerified();

`

``

72

`+

return clientSecret;

`

``

73

`+

}

`

``

74

+

``

75

`+

public void assertVerified() throws InvalidJwtException {

`

``

76

`+

if (!verified) {

`

``

77

`+

throw new InvalidJwtException("Client Assertion is not verified");

`

``

78

`+

}

`

``

79

`+

}

`

``

80

+

``

81

`+

public void verifyClientAssertionType() throws InvalidJwtException {

`

``

82

`+

if (clientAssertionType != ClientAssertionType.JWT_BEARER) {

`

``

83

`+

throw new InvalidJwtException("Invalid Client Assertion Type");

`

``

84

`+

}

`

``

85

`+

}

`

``

86

+

``

87

`+

public void initAndVerify() throws InvalidJwtException {

`

43

88

`try {

`

44

``

`-

if (!load(appConfiguration, cryptoProvider, clientId, clientAssertionType, encodedAssertion)) {

`

45

``

`-

throw new InvalidJwtException("Cannot load the JWT");

`

46

``

`-

}

`

``

89

`+

initAndVerifyInternally();

`

``

90

`+

verified = true;

`

47

91

` } catch (StringEncrypter.EncryptionException e) {

`

48

92

`throw new InvalidJwtException(e.getMessage(), e);

`

49

93

` } catch (Exception e) {

`

50

94

`throw new InvalidJwtException("Cannot verify the JWT", e);

`

51

95

` }

`

52

96

` }

`

53

97

``

54

``

`-

public String getSubjectIdentifier() {

`

55

``

`-

return jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);

`

``

98

`+

public void verifyEncodedAssertion() throws InvalidJwtException {

`

``

99

`+

if (StringUtils.isBlank(encodedAssertion)) {

`

``

100

`+

throw new InvalidJwtException("The Client Assertion is null or empty");

`

``

101

`+

}

`

56

102

` }

`

57

103

``

58

``

`-

public String getClientSecret() {

`

59

``

`-

return clientSecret;

`

``

104

`+

public void verifyAudience(List audience) throws InvalidJwtException {

`

``

105

`+

if (audience == null || audience.isEmpty()) {

`

``

106

`+

throw new InvalidJwtException("Audience is null or blank.");

`

``

107

`+

}

`

``

108

+

``

109

`+

// relax strict 'aud' check against server issuer. By default this value is false which means server makes strict check.

`

``

110

`+

if (appConfiguration.getAllowClientAssertionAudWithoutStrictIssuerMatch()) {

`

``

111

`+

return;

`

``

112

`+

}

`

``

113

+

``

114

`+

final String serverIssuer = appConfiguration.getIssuer();

`

``

115

+

``

116

`+

// aud must be equals to server's issuer or otherwise start with it (e.g. point to particular endpoint)

`

``

117

`+

if (audience.stream().anyMatch(aud -> aud.equals(serverIssuer) || aud.startsWith(serverIssuer))) {

`

``

118

`+

return;

`

``

119

`+

}

`

``

120

+

``

121

`+

throw new InvalidJwtException("Invalid Audience. It must contain server issuer or server. Aud: " + audience);

`

60

122

` }

`

61

123

``

62

``

`-

private boolean load(AppConfiguration appConfiguration, AbstractCryptoProvider cryptoProvider, String clientId, io.jans.as.model.token.ClientAssertionType clientAssertionType, String encodedAssertion)

`

63

``

`-

throws Exception {

`

64

``

`-

boolean result;

`

65

``

-

66

``

`-

if (clientAssertionType == ClientAssertionType.JWT_BEARER) {

`

67

``

`-

if (StringUtils.isNotBlank(encodedAssertion)) {

`

68

``

`-

jwt = Jwt.parse(encodedAssertion);

`

69

``

-

70

``

`-

// TODO: Store jti this value to check for duplicates

`

71

``

-

72

``

`-

// Validate clientId

`

73

``

`-

String issuer = jwt.getClaims().getClaimAsString(JwtClaimName.ISSUER);

`

74

``

`-

String subject = jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);

`

75

``

`-

List audience = jwt.getClaims().getClaimAsStringList(JwtClaimName.AUDIENCE);

`

76

``

`-

Date expirationTime = jwt.getClaims().getClaimAsDate(JwtClaimName.EXPIRATION_TIME);

`

77

``

`-

//SignatureAlgorithm algorithm = SignatureAlgorithm.fromName(jwt.getHeader().getClaimAsString(JwtHeaderName.ALGORITHM));

`

78

``

`-

if ((clientId == null && StringUtils.isNotBlank(issuer) && StringUtils.isNotBlank(subject) && issuer.equals(subject))

`

79

``

`-

|| (StringUtils.isNotBlank(clientId) && StringUtils.isNotBlank(issuer)

`

80

``

`-

&& StringUtils.isNotBlank(subject) && clientId.equals(issuer) && issuer.equals(subject))) {

`

81

``

-

82

``

`-

// Validate audience

`

83

``

`-

String tokenUrl = appConfiguration.getTokenEndpoint();

`

84

``

`-

String parUrl = StringUtils.isNotBlank(appConfiguration.getParEndpoint()) ? appConfiguration.getParEndpoint() : "";

`

85

``

`-

String cibaAuthUrl = appConfiguration.getBackchannelAuthenticationEndpoint();

`

86

``

`-

if (audience != null && (audience.contains(appConfiguration.getIssuer()) || audience.contains(tokenUrl) || audience.contains(parUrl) || audience.contains(cibaAuthUrl))) {

`

87

``

-

88

``

`-

// Validate expiration

`

89

``

`-

if (expirationTime.after(new Date())) {

`

90

``

`-

ClientService clientService = CdiUtil.bean(ClientService.class);

`

91

``

`-

Client client = clientService.getClient(subject);

`

92

``

-

93

``

`-

// Validate client

`

94

``

`-

if (client != null) {

`

95

``

`-

JwtType jwtType = JwtType.fromString(jwt.getHeader().getClaimAsString(JwtHeaderName.TYPE));

`

96

``

`-

Set authenticationMethods = client.getAllAuthenticationMethods();

`

97

``

`-

SignatureAlgorithm signatureAlgorithm = jwt.getHeader().getSignatureAlgorithm();

`

98

``

-

99

``

`-

if (jwtType == null && signatureAlgorithm != null) {

`

100

``

`-

jwtType = signatureAlgorithm.getJwtType();

`

101

``

`-

}

`

102

``

-

103

``

`-

if (jwtType != null && signatureAlgorithm != null && signatureAlgorithm.getFamily() != null &&

`

104

``

`-

((authenticationMethods.contains(AuthenticationMethod.CLIENT_SECRET_JWT) && AlgorithmFamily.HMAC.equals(signatureAlgorithm.getFamily()))

`

105

``

`-

|| (authenticationMethods.contains(AuthenticationMethod.PRIVATE_KEY_JWT) && (AlgorithmFamily.RSA.equals(signatureAlgorithm.getFamily()) || AlgorithmFamily.EC.equals(signatureAlgorithm.getFamily()))))) {

`

106

``

`-

if (client.getTokenEndpointAuthSigningAlg() == null || SignatureAlgorithm.fromString(client.getTokenEndpointAuthSigningAlg()).equals(signatureAlgorithm)) {

`

107

``

`-

clientSecret = clientService.decryptSecret(client.getClientSecret());

`

108

``

-

109

``

`-

// Validate the crypto segment

`

110

``

`-

String keyId = jwt.getHeader().getKeyId();

`

111

``

`-

JSONObject jwks = CommonUtils.getJwks(client);

`

112

``

`-

String sharedSecret = clientService.decryptSecret(client.getClientSecret());

`

113

``

`-

boolean validSignature = cryptoProvider.verifySignature(jwt.getSigningInput(), jwt.getEncodedSignature(),

`

114

``

`-

keyId, jwks, sharedSecret, signatureAlgorithm);

`

115

``

-

116

``

`-

if (validSignature) {

`

117

``

`-

result = true;

`

118

``

`-

} else {

`

119

``

`-

throw new InvalidJwtException("Invalid cryptographic segment");

`

120

``

`-

}

`

121

``

`-

} else {

`

122

``

`-

throw new InvalidJwtException("Invalid signing algorithm");

`

123

``

`-

}

`

124

``

`-

} else {

`

125

``

`-

throw new InvalidJwtException("Invalid authentication method");

`

126

``

`-

}

`

127

``

`-

} else {

`

128

``

`-

throw new InvalidJwtException("Invalid client");

`

129

``

`-

}

`

130

``

`-

} else {

`

131

``

`-

throw new InvalidJwtException("JWT has expired");

`

132

``

`-

}

`

133

``

`-

} else {

`

134

``

`-

throw new InvalidJwtException("Invalid audience: " + audience);

`

135

``

`-

}

`

136

``

`-

} else {

`

137

``

`-

throw new InvalidJwtException("Invalid clientId");

`

138

``

`-

}

`

139

``

`-

} else {

`

140

``

`-

throw new InvalidJwtException("The Client Assertion is null or empty");

`

141

``

`-

}

`

142

``

`-

} else {

`

143

``

`-

throw new InvalidJwtException("Invalid Client Assertion Type");

`

``

124

`+

public void verifyExpiration(Date expirationTime) throws InvalidJwtException {

`

``

125

`+

if (expirationTime != null && expirationTime.after(new Date())) {

`

``

126

`+

return;

`

``

127

`+

}

`

``

128

`+

throw new InvalidJwtException("JWT has expired");

`

``

129

`+

}

`

``

130

+

``

131

`+

public Pair<Client, ClientService> verifyClient(String subject) throws InvalidJwtException {

`

``

132

`+

ClientService clientService = CdiUtil.bean(ClientService.class);

`

``

133

`+

Client client = clientService.getClient(subject);

`

``

134

`+

if (client != null) {

`

``

135

`+

return new Pair<>(client, clientService);

`

``

136

`+

}

`

``

137

`+

throw new InvalidJwtException("Invalid client");

`

``

138

`+

}

`

``

139

+

``

140

`+

public void verifyAuthenticationMethod(Client client) throws InvalidJwtException {

`

``

141

`+

JwtType jwtType = JwtType.fromString(jwt.getHeader().getClaimAsString(JwtHeaderName.TYPE));

`

``

142

`+

Set authenticationMethods = client.getAllAuthenticationMethods();

`

``

143

`+

SignatureAlgorithm signatureAlgorithm = jwt.getHeader().getSignatureAlgorithm();

`

``

144

+

``

145

`+

if (jwtType == null && signatureAlgorithm != null) {

`

``

146

`+

jwtType = signatureAlgorithm.getJwtType();

`

``

147

`+

}

`

``

148

+

``

149

`+

if (jwtType != null && signatureAlgorithm != null && signatureAlgorithm.getFamily() != null &&

`

``

150

`+

((authenticationMethods.contains(AuthenticationMethod.CLIENT_SECRET_JWT) && AlgorithmFamily.HMAC.equals(signatureAlgorithm.getFamily()))

`

``

151

`+

|| (authenticationMethods.contains(AuthenticationMethod.PRIVATE_KEY_JWT) && (AlgorithmFamily.RSA.equals(signatureAlgorithm.getFamily()) || AlgorithmFamily.EC.equals(signatureAlgorithm.getFamily()))))) {

`

``

152

`+

return;

`

``

153

`+

}

`

``

154

+

``

155

`+

throw new InvalidJwtException("Invalid authentication method");

`

``

156

`+

}

`

``

157

+

``

158

`+

private void initAndVerifyInternally() throws InvalidJwtException, StringEncrypter.EncryptionException, CryptoProviderException {

`

``

159

+

``

160

`+

verifyClientAssertionType();

`

``

161

`+

verifyEncodedAssertion();

`

``

162

+

``

163

`+

jwt = Jwt.parse(encodedAssertion);

`

``

164

+

``

165

`+

// Validate clientId

`

``

166

`+

String issuer = jwt.getClaims().getClaimAsString(JwtClaimName.ISSUER);

`

``

167

`+

String subject = jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);

`

``

168

`+

List audience = jwt.getClaims().getClaimAsStringList(JwtClaimName.AUDIENCE);

`

``

169

`+

Date expirationTime = jwt.getClaims().getClaimAsDate(JwtClaimName.EXPIRATION_TIME);

`

``

170

+

``

171

`+

final boolean issuerEqualsToSubject = StringUtils.isNotBlank(issuer) && StringUtils.isNotBlank(subject) && issuer.equals(subject);

`

``

172

`+

final boolean noClientAndIssuerEqualsToSubject = clientId == null && issuerEqualsToSubject;

`

``

173

`+

final boolean clientEqualsToSubjectAndToIssuer = StringUtils.isNotBlank(clientId) && clientId.equals(issuer) && issuerEqualsToSubject;

`

``

174

+

``

175

`+

if (!noClientAndIssuerEqualsToSubject && !clientEqualsToSubjectAndToIssuer) {

`

``

176

`+

throw new InvalidJwtException("Invalid clientId");

`

``

177

`+

}

`

``

178

+

``

179

`+

verifyAudience(audience);

`

``

180

`+

verifyExpiration(expirationTime);

`

``

181

+

``

182

`+

final Pair<Client, ClientService> verifiedClientPair = verifyClient(subject);

`

``

183

`+

Client client = verifiedClientPair.getFirst();

`

``

184

+

``

185

`+

verifyAuthenticationMethod(client);

`

``

186

`+

verifySignatureAlgorithm(client);

`

``

187

`+

verifySignature(verifiedClientPair.getSecond(), verifiedClientPair.getFirst());

`

``

188

`+

}

`

``

189

+

``

190

`+

public void verifySignature(ClientService clientService, Client client) throws StringEncrypter.EncryptionException, InvalidJwtException, CryptoProviderException {

`

``

191

`+

SignatureAlgorithm signatureAlgorithm = jwt.getHeader().getSignatureAlgorithm();

`

``

192

`+

clientSecret = clientService.decryptSecret(client.getClientSecret());

`

``

193

+

``

194

`+

// Validate the crypto segment

`

``

195

`+

String keyId = jwt.getHeader().getKeyId();

`

``

196

`+

JSONObject jwks = CommonUtils.getJwks(client);

`

``

197

`+

boolean validSignature = cryptoProvider.verifySignature(jwt.getSigningInput(), jwt.getEncodedSignature(),

`

``

198

`+

keyId, jwks, clientSecret, signatureAlgorithm);

`

``

199

+

``

200

`+

if (!validSignature) {

`

``

201

`+

throw new InvalidJwtException("Invalid cryptographic segment");

`

``

202

`+

}

`

``

203

`+

}

`

``

204

+

``

205

`+

public void verifySignatureAlgorithm(Client client) throws InvalidJwtException {

`

``

206

`+

SignatureAlgorithm signatureAlgorithm = jwt.getHeader().getSignatureAlgorithm();

`

``

207

`+

if (client.getTokenEndpointAuthSigningAlg() == null || SignatureAlgorithm.fromString(client.getTokenEndpointAuthSigningAlg()).equals(signatureAlgorithm)) {

`

``

208

`+

return;

`

144

209

` }

`

145

210

``

146

``

`-

return result;

`

``

211

`+

throw new InvalidJwtException("Invalid signing algorithm");

`

147

212

` }

`

148

213

`}

`