-
Notifications
You must be signed in to change notification settings - Fork 137
/
ConfidentialClientApplication.java
224 lines (183 loc) · 8.87 KB
/
ConfidentialClientApplication.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.aad.msal4j;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.auth.*;
import com.nimbusds.oauth2.sdk.id.ClientID;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import static com.microsoft.aad.msal4j.ParameterValidationUtils.validateNotNull;
/**
* Class to be used to acquire tokens for confidential client applications (Web Apps, Web APIs,
* and daemon applications).
* For details see {@link IConfidentialClientApplication}
* <p>
* Conditionally thread-safe
*/
public class ConfidentialClientApplication extends AbstractClientApplicationBase implements IConfidentialClientApplication {
private ClientAuthentication clientAuthentication;
private boolean clientCertAuthentication = false;
private ClientCertificate clientCertificate;
/** AppTokenProvider creates a Credential from a function that provides access tokens. The function
must be concurrency safe. This is intended only to allow the Azure SDK to cache MSI tokens. It isn't
useful to applications in general because the token provider must implement all authentication logic. */
public Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider;
@Accessors(fluent = true)
@Getter
private boolean sendX5c;
@Override
public CompletableFuture<IAuthenticationResult> acquireToken(ClientCredentialParameters parameters) {
validateNotNull("parameters", parameters);
RequestContext context = new RequestContext(
this,
PublicApi.ACQUIRE_TOKEN_FOR_CLIENT,
parameters);
ClientCredentialRequest clientCredentialRequest =
new ClientCredentialRequest(
parameters,
this,
context,
appTokenProvider);
return this.executeRequest(clientCredentialRequest);
}
@Override
public CompletableFuture<IAuthenticationResult> acquireToken(OnBehalfOfParameters parameters) {
validateNotNull("parameters", parameters);
RequestContext context = new RequestContext(
this,
PublicApi.ACQUIRE_TOKEN_ON_BEHALF_OF,
parameters);
OnBehalfOfRequest oboRequest = new OnBehalfOfRequest(
parameters,
this,
context);
return this.executeRequest(oboRequest);
}
private ConfidentialClientApplication(Builder builder) {
super(builder);
sendX5c = builder.sendX5c;
appTokenProvider = builder.appTokenProvider;
log = LoggerFactory.getLogger(ConfidentialClientApplication.class);
initClientAuthentication(builder.clientCredential);
this.tenant = this.authenticationAuthority.tenant;
}
private void initClientAuthentication(IClientCredential clientCredential) {
validateNotNull("clientCredential", clientCredential);
if (clientCredential instanceof ClientSecret) {
clientAuthentication = new ClientSecretPost(
new ClientID(clientId()),
new Secret(((ClientSecret) clientCredential).clientSecret()));
} else if (clientCredential instanceof ClientCertificate) {
this.clientCertAuthentication = true;
this.clientCertificate = (ClientCertificate) clientCredential;
clientAuthentication = buildValidClientCertificateAuthority();
} else if (clientCredential instanceof ClientAssertion) {
clientAuthentication = createClientAuthFromClientAssertion((ClientAssertion) clientCredential);
} else {
throw new IllegalArgumentException("Unsupported client credential");
}
}
@Override
protected ClientAuthentication clientAuthentication() {
if (clientCertAuthentication) {
final Date currentDateTime = new Date(System.currentTimeMillis());
final Date expirationTime = ((PrivateKeyJWT) clientAuthentication).getJWTAuthenticationClaimsSet().getExpirationTime();
if (expirationTime.before(currentDateTime)) {
//The asserted private jwt with the client certificate can expire so rebuild it when the
clientAuthentication = buildValidClientCertificateAuthority();
}
}
return clientAuthentication;
}
private ClientAuthentication buildValidClientCertificateAuthority() {
ClientAssertion clientAssertion = JwtHelper.buildJwt(
clientId(),
clientCertificate,
this.authenticationAuthority.selfSignedJwtAudience(),
sendX5c);
return createClientAuthFromClientAssertion(clientAssertion);
}
protected ClientAuthentication createClientAuthFromClientAssertion(
final ClientAssertion clientAssertion) {
final Map<String, List<String>> map = new HashMap<>();
try {
map.put("client_assertion_type", Collections.singletonList(ClientAssertion.assertionType));
map.put("client_assertion", Collections.singletonList(clientAssertion.assertion()));
return PrivateKeyJWT.parse(map);
} catch (final ParseException e) {
//This library is not supposed to validate Issuer and subject values.
//The next lines of code ensures that exception is not thrown.
if (e.getMessage().contains("Issuer and subject in client JWT assertion must designate the same client identifier")) {
return new CustomJWTAuthentication(
ClientAuthenticationMethod.PRIVATE_KEY_JWT,
clientAssertion,
new ClientID(clientId())
);
}
throw new MsalClientException(e);
}
}
/**
* Creates instance of Builder of ConfidentialClientApplication
*
* @param clientId Client ID (Application ID) of the application as registered
* in the application registration portal (portal.azure.com)
* @param clientCredential The client credential to use for token acquisition.
* @return instance of Builder of ConfidentialClientApplication
*/
public static Builder builder(String clientId, IClientCredential clientCredential) {
return new Builder(clientId, clientCredential);
}
public static class Builder extends AbstractClientApplicationBase.Builder<Builder> {
private IClientCredential clientCredential;
private boolean sendX5c = true;
private Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider;
private Builder(String clientId, IClientCredential clientCredential) {
super(clientId);
this.clientCredential = clientCredential;
}
/**
* Specifies if the x5c claim (public key of the certificate) should be sent to the STS.
* Default value is true
*
* @param val true if the x5c should be sent. Otherwise false
* @return instance of the Builder on which method was called
*/
public ConfidentialClientApplication.Builder sendX5c(boolean val) {
this.sendX5c = val;
return self();
}
/// <summary>
/// Allows setting a callback which returns an access token, based on the passed-in parameters.
/// MSAL will pass in its authentication parameters to the callback and it is expected that the callback
/// will construct a <see cref="TokenProviderResult"/> and return it to MSAL.
/// MSAL will cache the token response the same way it does for other authentication results.
/// Note: This is only for client credential flows.
/// </summary>
/// <param name="appTokenProvider">Authentication callback which returns an access token.</param>
/// <returns>The builder to chain the .With methods</returns>
public ConfidentialClientApplication.Builder appTokenProvider(Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider){
if(appTokenProvider!=null){
this.appTokenProvider = appTokenProvider;
return self();
}
throw new NullPointerException("appTokenProvider is null") ;
}
@Override
public ConfidentialClientApplication build() {
return new ConfidentialClientApplication(this);
}
@Override
protected ConfidentialClientApplication.Builder self() {
return this;
}
}
}