1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package davmail.exchange.auth;
21
22 import davmail.BundleMessage;
23 import davmail.Settings;
24 import davmail.exception.DavMailAuthenticationException;
25 import davmail.http.HttpClientAdapter;
26 import davmail.http.request.GetRequest;
27 import davmail.http.request.PostRequest;
28 import davmail.http.request.ResponseWrapper;
29 import davmail.http.request.RestRequest;
30 import davmail.ui.NumberMatchingFrame;
31 import davmail.ui.PasswordPromptDialog;
32 import org.apache.http.HttpStatus;
33 import org.apache.http.client.utils.URIBuilder;
34 import org.apache.log4j.Logger;
35 import org.codehaus.jettison.json.JSONException;
36 import org.codehaus.jettison.json.JSONObject;
37
38 import javax.swing.*;
39 import java.awt.*;
40 import java.io.BufferedReader;
41 import java.io.IOException;
42 import java.io.InputStreamReader;
43 import java.net.URI;
44 import java.net.URISyntaxException;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47
48 public class O365Authenticator implements ExchangeAuthenticator {
49 protected static final Logger LOGGER = Logger.getLogger(O365Authenticator.class);
50
51 private String tenantId;
52
53 private String username;
54
55 private String userid;
56 private String password;
57 private O365Token token;
58
59 public static String buildAuthorizeUrl(String tenantId, String clientId, String redirectUri, String username) throws IOException {
60 URI uri;
61 try {
62 URIBuilder uriBuilder = new URIBuilder(Settings.getO365LoginUrl())
63 .addParameter("client_id", clientId)
64 .addParameter("response_type", "code")
65 .addParameter("redirect_uri", redirectUri)
66 .addParameter("response_mode", "query")
67 .addParameter("login_hint", username);
68
69
70
71
72 if (Settings.getBooleanProperty("davmail.enableOidc", false)) {
73 uriBuilder.setPath("/" + tenantId + "/oauth2/v2.0/authorize")
74 .addParameter("scope", "openid " + Settings.getOutlookUrl() + "/EWS.AccessAsUser.All");
75 } else if (Settings.getBooleanProperty("davmail.enableGraph", false)) {
76 uriBuilder.setPath("/" + tenantId + "/oauth2/authorize")
77 .addParameter("resource", "https://graph.microsoft.com");
78
79
80
81
82 } else {
83 uriBuilder.setPath("/" + tenantId + "/oauth2/authorize")
84 .addParameter("resource", Settings.getOutlookUrl());
85 }
86
87 uri = uriBuilder.build();
88 } catch (URISyntaxException e) {
89 throw new IOException(e);
90 }
91 return uri.toString();
92 }
93
94 public void setUsername(String username) {
95 if (username.contains("|")) {
96 this.userid = username.substring(0, username.indexOf("|"));
97 this.username = username.substring(username.indexOf("|") + 1);
98 } else {
99 this.username = username;
100 this.userid = username;
101 }
102 }
103
104 public void setPassword(String password) {
105 this.password = password;
106 }
107
108 public O365Token getToken() {
109 return token;
110 }
111
112 public URI getExchangeUri() {
113 return URI.create(Settings.getO365Url());
114 }
115
116
117
118
119
120
121 @Override
122 public HttpClientAdapter getHttpClientAdapter() {
123 return new HttpClientAdapter(getExchangeUri(), username, password, true);
124 }
125
126 public void authenticate() throws IOException {
127
128 String clientId = Settings.getProperty("davmail.oauth.clientId", "facd6cff-a294-4415-b59f-c5b01937d7bd");
129
130 String redirectUri = Settings.getProperty("davmail.oauth.redirectUri", Settings.getO365LoginUrl()+"/common/oauth2/nativeclient");
131
132 tenantId = Settings.getProperty("davmail.oauth.tenantId", "common");
133
134
135 token = O365Token.load(tenantId, clientId, redirectUri, username, password);
136 if (token != null) {
137 return;
138 }
139
140 String url = O365Authenticator.buildAuthorizeUrl(tenantId, clientId, redirectUri, username);
141
142 try (
143 HttpClientAdapter httpClientAdapter = new HttpClientAdapter(url, userid, password)
144 ) {
145
146 GetRequest getRequest = new GetRequest(url);
147 String responseBodyAsString = executeFollowRedirect(httpClientAdapter, getRequest);
148 String code;
149 if (!responseBodyAsString.contains("Config=")) {
150
151 code = authenticateADFS(httpClientAdapter, responseBodyAsString, url);
152 } else {
153 JSONObject config = extractConfig(responseBodyAsString);
154
155 String context = config.getString("sCtx");
156 String apiCanary = config.getString("apiCanary");
157 String clientRequestId = config.getString("correlationId");
158 String hpgact = config.getString("hpgact");
159 String hpgid = config.getString("hpgid");
160 String flowToken = config.getString("sFT");
161 String canary = config.getString("canary");
162 String sessionId = config.getString("sessionId");
163
164 String referer = getRequest.getURI().toString();
165
166 RestRequest getCredentialMethod = new RestRequest(Settings.getO365LoginUrl() + "/" + tenantId + "/GetCredentialType");
167 getCredentialMethod.setRequestHeader("Accept", "application/json");
168 getCredentialMethod.setRequestHeader("canary", apiCanary);
169 getCredentialMethod.setRequestHeader("client-request-id", clientRequestId);
170 getCredentialMethod.setRequestHeader("hpgact", hpgact);
171 getCredentialMethod.setRequestHeader("hpgid", hpgid);
172 getCredentialMethod.setRequestHeader("hpgrequestid", sessionId);
173 getCredentialMethod.setRequestHeader("Referer", referer);
174
175 final JSONObject jsonObject = new JSONObject();
176 jsonObject.put("username", username);
177 jsonObject.put("isOtherIdpSupported", true);
178 jsonObject.put("checkPhones", false);
179 jsonObject.put("isRemoteNGCSupported", false);
180 jsonObject.put("isCookieBannerShown", false);
181 jsonObject.put("isFidoSupported", false);
182 jsonObject.put("flowToken", flowToken);
183 jsonObject.put("originalRequest", context);
184
185 getCredentialMethod.setJsonBody(jsonObject);
186
187 JSONObject credentialType = httpClientAdapter.executeRestRequest(getCredentialMethod);
188
189 LOGGER.debug("CredentialType=" + credentialType);
190
191 JSONObject credentials = credentialType.getJSONObject("Credentials");
192 String federationRedirectUrl = credentials.optString("FederationRedirectUrl");
193
194 if (federationRedirectUrl != null && !federationRedirectUrl.isEmpty()) {
195 LOGGER.debug("Detected ADFS, redirecting to " + federationRedirectUrl);
196 code = authenticateRedirectADFS(httpClientAdapter, federationRedirectUrl, url);
197 } else {
198 PostRequest logonMethod = new PostRequest(Settings.getO365LoginUrl() + "/" + tenantId + "/login");
199 logonMethod.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
200 logonMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
201
202 logonMethod.setRequestHeader("Referer", referer);
203
204 logonMethod.setParameter("canary", canary);
205 logonMethod.setParameter("ctx", context);
206 logonMethod.setParameter("flowToken", flowToken);
207 logonMethod.setParameter("hpgrequestid", sessionId);
208 logonMethod.setParameter("login", username);
209 logonMethod.setParameter("loginfmt", username);
210 logonMethod.setParameter("passwd", password);
211
212 responseBodyAsString = httpClientAdapter.executePostRequest(logonMethod);
213 URI location = logonMethod.getRedirectLocation();
214
215 if (responseBodyAsString != null && responseBodyAsString.contains("arrUserProofs")) {
216 location = handleMfa(httpClientAdapter, logonMethod, username, clientRequestId);
217 }
218
219 if (location == null || !location.toString().startsWith(redirectUri)) {
220
221 config = extractConfig(logonMethod.getResponseBodyAsString());
222 if (config.optJSONArray("arrScopes") != null || config.optJSONArray("urlPostRedirect") != null) {
223 LOGGER.warn("Authentication successful but user consent or validation needed, please open the following url in a browser");
224 LOGGER.warn(url);
225 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
226 } else if ("50126".equals(config.optString("sErrorCode"))) {
227 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
228 } else if ("50125".equals(config.optString("sErrorCode"))) {
229 throw new DavMailAuthenticationException("LOG_MESSAGE", "Your organization needs more information to keep your account secure, authenticate once in a web browser and try again");
230 } else if (config.optString("strServiceExceptionMessage") != null) {
231 LOGGER.debug("O365 returned error: " + config.optString("strServiceExceptionMessage"));
232 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
233 } else {
234 throw new DavMailAuthenticationException("LOG_MESSAGE", "Authentication failed, unknown error: " + config);
235 }
236 }
237 String query = location.toString();
238 if (query.contains("code=")) {
239 code = query.substring(query.indexOf("code=") + 5, query.indexOf("&session_state="));
240 } else {
241 throw new DavMailAuthenticationException("LOG_MESSAGE", "Authentication failed, unknown error: " + query);
242 }
243 }
244 }
245 LOGGER.debug("Authentication Code: " + code);
246
247 token = O365Token.build(tenantId, clientId, redirectUri, code, password);
248
249 LOGGER.debug("Authenticated username: " + token.getUsername());
250 if (!username.equalsIgnoreCase(token.getUsername())) {
251 throw new IOException("Authenticated username " + token.getUsername() + " does not match " + username);
252 }
253
254 } catch (JSONException e) {
255 throw new IOException(e + " " + e.getMessage());
256 }
257
258 }
259
260 private String authenticateRedirectADFS(HttpClientAdapter httpClientAdapter, String federationRedirectUrl, String authorizeUrl) throws IOException, JSONException {
261
262 GetRequest logonFormMethod = new GetRequest(federationRedirectUrl);
263 logonFormMethod = httpClientAdapter.executeFollowRedirect(logonFormMethod);
264 String responseBodyAsString = logonFormMethod.getResponseBodyAsString();
265 return authenticateADFS(httpClientAdapter, responseBodyAsString, authorizeUrl);
266 }
267
268 private String authenticateADFS(HttpClientAdapter httpClientAdapter, String responseBodyAsString, String authorizeUrl) throws IOException, JSONException {
269 URI location;
270
271 if (responseBodyAsString.contains(Settings.getO365LoginUrl())) {
272 LOGGER.info("Already authenticated through Basic or NTLM");
273 } else {
274
275 PostRequest logonMethod = new PostRequest(extract("method=\"post\" action=\"([^\"]+)\"", responseBodyAsString));
276 logonMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
277
278 logonMethod.setParameter("UserName", userid);
279 logonMethod.setParameter("Password", password);
280 logonMethod.setParameter("AuthMethod", "FormsAuthentication");
281
282 httpClientAdapter.executePostRequest(logonMethod);
283 location = logonMethod.getRedirectLocation();
284 if (location == null) {
285 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
286 }
287
288 GetRequest redirectMethod = new GetRequest(location);
289 responseBodyAsString = httpClientAdapter.executeGetRequest(redirectMethod);
290 }
291
292 if (!responseBodyAsString.contains(Settings.getO365LoginUrl())) {
293 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
294 }
295 String targetUrl = extract("action=\"([^\"]+)\"", responseBodyAsString);
296 String wa = extract("name=\"wa\" value=\"([^\"]+)\"", responseBodyAsString);
297 String wresult = extract("name=\"wresult\" value=\"([^\"]+)\"", responseBodyAsString);
298
299 wresult = wresult.replaceAll(""", "\"");
300 wresult = wresult.replaceAll("<", "<");
301 wresult = wresult.replaceAll(">", ">");
302 String wctx = extract("name=\"wctx\" value=\"([^\"]+)\"", responseBodyAsString);
303 wctx = wctx.replaceAll("&", "&");
304
305 PostRequest targetMethod = new PostRequest(targetUrl);
306 targetMethod.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
307 targetMethod.setParameter("wa", wa);
308 targetMethod.setParameter("wresult", wresult);
309 targetMethod.setParameter("wctx", wctx);
310
311 responseBodyAsString = httpClientAdapter.executePostRequest(targetMethod);
312 location = targetMethod.getRedirectLocation();
313
314 LOGGER.debug(targetMethod.getURI().toString());
315 LOGGER.debug(targetMethod.getReasonPhrase());
316 LOGGER.debug(responseBodyAsString);
317
318 if (targetMethod.getStatusCode() == HttpStatus.SC_OK) {
319 JSONObject config = extractConfig(responseBodyAsString);
320 if (config.optJSONArray("arrScopes") != null || config.optJSONArray("urlPostRedirect") != null) {
321 LOGGER.warn("Authentication successful but user consent or validation needed, please open the following url in a browser");
322 LOGGER.warn(authorizeUrl);
323 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
324 }
325 } else if (targetMethod.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY || location == null) {
326 throw new IOException("Unknown ADFS authentication failure");
327 }
328
329 if (location.getHost().startsWith("device")) {
330 location = processDeviceLogin(httpClientAdapter, location);
331 }
332 String query = location.getQuery();
333 if (query == null) {
334
335 query = location.getSchemeSpecificPart();
336 }
337
338 if (query.contains("code=") && query.contains("&session_state=")) {
339 String code = query.substring(query.indexOf("code=") + 5, query.indexOf("&session_state="));
340 LOGGER.debug("Authentication Code: " + code);
341 return code;
342 }
343 throw new IOException("Unknown ADFS authentication failure");
344 }
345
346 private URI processDeviceLogin(HttpClientAdapter httpClient, URI location) throws IOException, JSONException {
347 URI result = location;
348 LOGGER.debug("Proceed to device authentication, must have access to a client certificate signed by MS-Organization-Access");
349 if (Settings.isWindows() &&
350 (System.getProperty("java.version").compareTo("13") < 0
351 || !"MSCAPI".equals(Settings.getProperty("davmail.ssl.clientKeystoreType")))
352 ) {
353 LOGGER.warn("MSCAPI and Java version 13 or higher required to access TPM protected client certificate on Windows");
354 }
355 GetRequest deviceLoginMethod = new GetRequest(location);
356
357 String responseBodyAsString = httpClient.executeGetRequest(deviceLoginMethod);
358
359 if (responseBodyAsString.contains(Settings.getO365LoginUrl())) {
360 String ctx = extract("name=\"ctx\" value=\"([^\"]+)\"", responseBodyAsString);
361 String flowtoken = extract("name=\"flowtoken\" value=\"([^\"]+)\"", responseBodyAsString);
362
363 PostRequest processMethod = new PostRequest(extract("action=\"([^\"]+)\"", responseBodyAsString));
364 processMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
365
366 processMethod.setParameter("ctx", ctx);
367 processMethod.setParameter("flowtoken", flowtoken);
368
369 responseBodyAsString = httpClient.executePostRequest(processMethod);
370 result = processMethod.getRedirectLocation();
371
372
373 if (result == null && responseBodyAsString != null && responseBodyAsString.contains("arrUserProofs")) {
374 result = handleMfa(httpClient, processMethod, username, null);
375 }
376
377 if (result == null) {
378 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
379 }
380
381 }
382 return result;
383 }
384
385 private URI handleMfa(HttpClientAdapter httpClientAdapter, PostRequest logonMethod, String username, String clientRequestId) throws IOException, JSONException {
386 JSONObject config = extractConfig(logonMethod.getResponseBodyAsString());
387 LOGGER.debug("Config=" + config);
388
389 String urlBeginAuth = config.getString("urlBeginAuth");
390 String urlEndAuth = config.getString("urlEndAuth");
391
392 String urlProcessAuth = config.optString("urlPost", Settings.getO365LoginUrl() + "/" + tenantId + "/SAS/ProcessAuth");
393
394 boolean isMFAMethodSupported = false;
395 String chosenAuthMethodId = null;
396 String chosenAuthMethodPrompt = null;
397
398 for (int i = 0; i < config.getJSONArray("arrUserProofs").length(); i++) {
399 JSONObject authMethod = (JSONObject) config.getJSONArray("arrUserProofs").get(i);
400 String authMethodId = authMethod.getString("authMethodId");
401 LOGGER.debug("Authentication method: " + authMethodId);
402 if ("PhoneAppNotification".equals(authMethodId)) {
403 LOGGER.debug("Found phone app auth method " + authMethod.getString("display"));
404 isMFAMethodSupported = true;
405 chosenAuthMethodId = authMethodId;
406 chosenAuthMethodPrompt = authMethod.getString("display");
407
408 break;
409 }
410 if ("OneWaySMS".equals(authMethodId)) {
411 LOGGER.debug("Found OneWaySMS auth method " + authMethod.getString("display"));
412 chosenAuthMethodId = authMethodId;
413 chosenAuthMethodPrompt = authMethod.getString("display");
414 isMFAMethodSupported = true;
415 }
416 }
417
418 if (!isMFAMethodSupported) {
419 throw new IOException("MFA authentication methods not supported");
420 }
421
422 String context = config.getString("sCtx");
423 String flowToken = config.getString("sFT");
424
425 String canary = config.getString("canary");
426 String apiCanary = config.getString("apiCanary");
427
428 String hpgrequestid = logonMethod.getResponseHeader("x-ms-request-id").getValue();
429 String hpgact = config.getString("hpgact");
430 String hpgid = config.getString("hpgid");
431
432
433 String correlationId = clientRequestId;
434 if (correlationId == null) {
435 correlationId = config.getString("correlationId");
436 }
437
438 RestRequest beginAuthMethod = new RestRequest(urlBeginAuth);
439 beginAuthMethod.setRequestHeader("Accept", "application/json");
440 beginAuthMethod.setRequestHeader("canary", apiCanary);
441 beginAuthMethod.setRequestHeader("client-request-id", correlationId);
442 beginAuthMethod.setRequestHeader("hpgact", hpgact);
443 beginAuthMethod.setRequestHeader("hpgid", hpgid);
444 beginAuthMethod.setRequestHeader("hpgrequestid", hpgrequestid);
445
446
447 JSONObject beginAuthJson = new JSONObject();
448 beginAuthJson.put("AuthMethodId", chosenAuthMethodId);
449 beginAuthJson.put("Ctx", context);
450 beginAuthJson.put("FlowToken", flowToken);
451 beginAuthJson.put("Method", "BeginAuth");
452 beginAuthMethod.setJsonBody(beginAuthJson);
453
454 config = httpClientAdapter.executeRestRequest(beginAuthMethod);
455 LOGGER.debug(config);
456
457 if (!config.getBoolean("Success")) {
458 throw new IOException("Authentication failed: " + config);
459 }
460
461
462 String entropy = config.optString("Entropy", null);
463
464
465 NumberMatchingFrame numberMatchingFrame = null;
466 if (entropy != null && !"0".equals(entropy)) {
467 LOGGER.info("Number matching value for " + username + ": " + entropy);
468 if (!Settings.getBooleanProperty("davmail.server") && !GraphicsEnvironment.isHeadless()) {
469 numberMatchingFrame = new NumberMatchingFrame(entropy);
470 }
471 }
472
473 String smsCode = retrieveSmsCode(chosenAuthMethodId, chosenAuthMethodPrompt);
474
475 context = config.getString("Ctx");
476 flowToken = config.getString("FlowToken");
477 String sessionId = config.getString("SessionId");
478
479 int i = 0;
480 boolean success = false;
481 try {
482 while (!success && i++ < 12) {
483
484 try {
485 Thread.sleep(5000);
486 } catch (InterruptedException e) {
487 LOGGER.debug("Interrupted");
488 Thread.currentThread().interrupt();
489 }
490
491 RestRequest endAuthMethod = new RestRequest(urlEndAuth);
492 endAuthMethod.setRequestHeader("Accept", "application/json");
493 endAuthMethod.setRequestHeader("canary", apiCanary);
494 endAuthMethod.setRequestHeader("client-request-id", clientRequestId);
495 endAuthMethod.setRequestHeader("hpgact", hpgact);
496 endAuthMethod.setRequestHeader("hpgid", hpgid);
497 endAuthMethod.setRequestHeader("hpgrequestid", hpgrequestid);
498
499 JSONObject endAuthJson = new JSONObject();
500 endAuthJson.put("AuthMethodId", chosenAuthMethodId);
501 endAuthJson.put("Ctx", context);
502 endAuthJson.put("FlowToken", flowToken);
503 endAuthJson.put("Method", "EndAuth");
504 endAuthJson.put("PollCount", "1");
505 endAuthJson.put("SessionId", sessionId);
506
507
508
509 endAuthJson.put("AdditionalAuthData", smsCode);
510
511 endAuthMethod.setJsonBody(endAuthJson);
512
513 config = httpClientAdapter.executeRestRequest(endAuthMethod);
514 LOGGER.debug(config);
515 String resultValue = config.getString("ResultValue");
516 if ("PhoneAppDenied".equals(resultValue) || "PhoneAppNoResponse".equals(resultValue)) {
517 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED_REASON", resultValue);
518 }
519 if ("SMSAuthFailedWrongCodeEntered".equals(resultValue)) {
520 smsCode = retrieveSmsCode(chosenAuthMethodId, chosenAuthMethodPrompt);
521 }
522 if (config.getBoolean("Success")) {
523 success = true;
524 }
525 }
526 } finally {
527
528 if (numberMatchingFrame != null && numberMatchingFrame.isVisible()) {
529 final JFrame finalNumberMatchingFrame = numberMatchingFrame;
530 SwingUtilities.invokeLater(() -> {
531 finalNumberMatchingFrame.setVisible(false);
532 finalNumberMatchingFrame.dispose();
533 });
534 }
535
536 }
537 if (!success) {
538 throw new IOException("Authentication failed: " + config);
539 }
540
541 String authMethod = "PhoneAppOTP";
542 String type = "22";
543
544 context = config.getString("Ctx");
545 flowToken = config.getString("FlowToken");
546
547
548 PostRequest processAuthMethod = new PostRequest(urlProcessAuth);
549 processAuthMethod.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
550 processAuthMethod.setParameter("type", type);
551 processAuthMethod.setParameter("request", context);
552 processAuthMethod.setParameter("mfaAuthMethod", authMethod);
553 processAuthMethod.setParameter("canary", canary);
554 processAuthMethod.setParameter("login", username);
555 processAuthMethod.setParameter("flowToken", flowToken);
556 processAuthMethod.setParameter("hpgrequestid", hpgrequestid);
557
558 httpClientAdapter.executePostRequest(processAuthMethod);
559 return processAuthMethod.getRedirectLocation();
560
561 }
562
563 private String retrieveSmsCode(String chosenAuthMethodId, String chosenAuthMethodPrompt) throws IOException {
564 String smsCode = null;
565 if ("OneWaySMS".equals(chosenAuthMethodId)) {
566 LOGGER.info("Need to retrieve SMS verification code for " + username);
567 if (Settings.getBooleanProperty("davmail.server") || GraphicsEnvironment.isHeadless()) {
568
569 System.out.print(BundleMessage.format("UI_SMS_PHONE_CODE", chosenAuthMethodPrompt));
570 BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
571 smsCode = inReader.readLine();
572 } else {
573 PasswordPromptDialog passwordPromptDialog = new PasswordPromptDialog(BundleMessage.format("UI_SMS_PHONE_CODE", chosenAuthMethodPrompt));
574 smsCode = String.valueOf(passwordPromptDialog.getPassword());
575 }
576 }
577 return smsCode;
578 }
579
580 private String executeFollowRedirect(HttpClientAdapter httpClientAdapter, GetRequest getRequest) throws IOException {
581 LOGGER.debug(getRequest.getURI());
582 ResponseWrapper responseWrapper = httpClientAdapter.executeFollowRedirect(getRequest);
583 if (responseWrapper.getURI().getHost().endsWith("okta.com")) {
584 throw new DavMailAuthenticationException("LOG_MESSAGE", "Okta authentication not supported, please try O365Interactive");
585 }
586 return responseWrapper.getResponseBodyAsString();
587 }
588
589 public JSONObject extractConfig(String content) throws IOException {
590 try {
591 return new JSONObject(extract("Config=([^\n]+);", content));
592 } catch (JSONException e1) {
593 LOGGER.debug(content);
594 throw new IOException("Unable to extract config from response body");
595 }
596 }
597
598 public String extract(String pattern, String content) throws IOException {
599 String value;
600 Matcher matcher = Pattern.compile(pattern).matcher(content);
601 if (matcher.find()) {
602 value = matcher.group(1);
603 } else {
604 throw new IOException("pattern not found");
605 }
606 return value;
607 }
608
609 }