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
`+
- @author Yuriy Z
`
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
`}
`