diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe4d2b8d81..6acb683b5d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@
### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx)
+### Next version
+
+* Added Outlook Tenant ID field
+
### 1.1794 - 2021-12-22
* Added logarithmic PIN failure delay of 3 seconds
diff --git a/app/src/main/assets/CHANGELOG.md b/app/src/main/assets/CHANGELOG.md
index fe4d2b8d81..6acb683b5d 100644
--- a/app/src/main/assets/CHANGELOG.md
+++ b/app/src/main/assets/CHANGELOG.md
@@ -4,6 +4,10 @@
### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx)
+### Next version
+
+* Added Outlook Tenant ID field
+
### 1.1794 - 2021-12-22
* Added logarithmic PIN failure delay of 3 seconds
diff --git a/app/src/main/java/eu/faircode/email/FragmentOAuth.java b/app/src/main/java/eu/faircode/email/FragmentOAuth.java
index d0f8b6a2bb..4e10437765 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOAuth.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOAuth.java
@@ -108,6 +108,7 @@ public class FragmentOAuth extends FragmentBase {
private TextView tvPrivacy;
private EditText etName;
private EditText etEmail;
+ private EditText etTenant;
private CheckBox cbUpdate;
private Button btnOAuth;
private ContentLoadingProgressBar pbOAuth;
@@ -120,6 +121,7 @@ public class FragmentOAuth extends FragmentBase {
private Button btnSupport;
private Button btnHelp;
+ private Group grpTenant;
private Group grpError;
private static final int MAILRU_TIMEOUT = 20 * 1000; // milliseconds
@@ -153,6 +155,7 @@ public class FragmentOAuth extends FragmentBase {
tvPrivacy = view.findViewById(R.id.tvPrivacy);
etName = view.findViewById(R.id.etName);
etEmail = view.findViewById(R.id.etEmail);
+ etTenant = view.findViewById(R.id.etTenant);
cbUpdate = view.findViewById(R.id.cbUpdate);
btnOAuth = view.findViewById(R.id.btnOAuth);
pbOAuth = view.findViewById(R.id.pbOAuth);
@@ -165,6 +168,7 @@ public class FragmentOAuth extends FragmentBase {
btnSupport = view.findViewById(R.id.btnSupport);
btnHelp = view.findViewById(R.id.btnHelp);
+ grpTenant = view.findViewById(R.id.grpTenant);
grpError = view.findViewById(R.id.grpError);
// Wire controls
@@ -208,6 +212,7 @@ public class FragmentOAuth extends FragmentBase {
tvTitle.setText(getString(R.string.title_setup_oauth_rationale, name));
etName.setVisibility(askAccount ? View.VISIBLE : View.GONE);
etEmail.setVisibility(askAccount ? View.VISIBLE : View.GONE);
+ grpTenant.setVisibility(isOutlook(id) ? View.VISIBLE : View.GONE);
pbOAuth.setVisibility(View.GONE);
tvConfiguring.setVisibility(View.GONE);
tvGmailHint.setVisibility("gmail".equals(id) ? View.VISIBLE : View.GONE);
@@ -215,6 +220,7 @@ public class FragmentOAuth extends FragmentBase {
etName.setText(personal);
etEmail.setText(address);
+ etTenant.setText(null);
cbUpdate.setChecked(update);
return view;
@@ -263,10 +269,12 @@ public class FragmentOAuth extends FragmentBase {
etName.clearFocus();
etEmail.clearFocus();
+ etTenant.clearFocus();
Helper.hideKeyboard(view);
etName.setEnabled(false);
etEmail.setEnabled(false);
+ etTenant.setEnabled(false);
cbUpdate.setEnabled(false);
btnOAuth.setEnabled(false);
pbOAuth.setVisibility(View.VISIBLE);
@@ -336,9 +344,19 @@ public class FragmentOAuth extends FragmentBase {
AuthorizationService authService = new AuthorizationService(context, appAuthConfig);
+ String authorizationEndpoint = provider.oauth.authorizationEndpoint;
+ String tokenEndpoint = provider.oauth.tokenEndpoint;
+ String tenant = etTenant.getText().toString().trim();
+
+ if (TextUtils.isEmpty(tenant))
+ tenant = "common";
+
+ authorizationEndpoint = authorizationEndpoint.replace("{tenant}", tenant);
+ tokenEndpoint = tokenEndpoint.replace("{tenant}", tenant);
+
AuthorizationServiceConfiguration serviceConfig = new AuthorizationServiceConfiguration(
- Uri.parse(provider.oauth.authorizationEndpoint),
- Uri.parse(provider.oauth.tokenEndpoint));
+ Uri.parse(authorizationEndpoint),
+ Uri.parse(tokenEndpoint));
AuthState authState = new AuthState(serviceConfig);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
@@ -384,10 +402,8 @@ public class FragmentOAuth extends FragmentBase {
authRequestBuilder.setPrompt("consent");
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
- if ("office365".equals(provider.id))
+ if (isOutlook(provider.id))
authRequestBuilder.setPrompt("select_account");
- if ("outlook".equals(provider.id))
- authRequestBuilder.setPrompt("consent");
AuthorizationRequest authRequest = authRequestBuilder.build();
@@ -413,6 +429,7 @@ public class FragmentOAuth extends FragmentBase {
try {
etName.setEnabled(true);
etEmail.setEnabled(true);
+ etTenant.setEnabled(true);
cbUpdate.setEnabled(true);
AuthorizationResponse auth = AuthorizationResponse.fromIntent(data);
@@ -450,7 +467,7 @@ public class FragmentOAuth extends FragmentBase {
.setAdditionalParameters(Collections.emptyMap())
.setNonce(auth.request.nonce);
- if ("office365".equals(provider.id) || "outlook".equals(provider.id))
+ if (isOutlook(provider.id))
builder.setScope(TextUtils.join(" ", provider.oauth.scopes));
TokenRequest request = builder.build();
@@ -921,6 +938,7 @@ public class FragmentOAuth extends FragmentBase {
private void onHandleCancel() {
etName.setEnabled(true);
etEmail.setEnabled(true);
+ etTenant.setEnabled(true);
cbUpdate.setEnabled(true);
btnOAuth.setEnabled(true);
pbOAuth.setVisibility(View.GONE);
@@ -942,7 +960,7 @@ public class FragmentOAuth extends FragmentBase {
if ("gmail".equals(id))
tvGmailDraftsHint.setVisibility(View.VISIBLE);
- if ("office365".equals(id) || "outlook".equals(id)) {
+ if (isOutlook(id)) {
if (ex instanceof AuthenticationFailedException)
tvOfficeAuthHint.setVisibility(View.VISIBLE);
}
@@ -959,6 +977,7 @@ public class FragmentOAuth extends FragmentBase {
etName.setEnabled(true);
etEmail.setEnabled(true);
+ etTenant.setEnabled(true);
cbUpdate.setEnabled(true);
btnOAuth.setEnabled(true);
pbOAuth.setVisibility(View.GONE);
@@ -979,4 +998,8 @@ public class FragmentOAuth extends FragmentBase {
tvGmailDraftsHint.setVisibility(View.GONE);
tvOfficeAuthHint.setVisibility(View.GONE);
}
+
+ private static boolean isOutlook(String id) {
+ return ("office365".equals(id) || "outlook".equals(id));
+ }
}
diff --git a/app/src/main/res/layout/fragment_oauth.xml b/app/src/main/res/layout/fragment_oauth.xml
index a73142d34c..3eda08c1da 100644
--- a/app/src/main/res/layout/fragment_oauth.xml
+++ b/app/src/main/res/layout/fragment_oauth.xml
@@ -67,6 +67,29 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etName" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/tvTenantHint" />
+
+
Select …
Your name
Your email address
+ Tenant ID
+ This field should be empty in most cases
Identity colors take precedence over folder and account colors
Allow editing sender address
Use name when sender address has been edited
diff --git a/app/src/main/res/xml/providers.xml b/app/src/main/res/xml/providers.xml
index 4056633d3d..559445f2ec 100644
--- a/app/src/main/res/xml/providers.xml
+++ b/app/src/main/res/xml/providers.xml
@@ -97,14 +97,14 @@
starttls="true" />
+ tokenEndpoint="https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" />
+ tokenEndpoint="https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" />
diff --git a/metadata/en-US/changelogs/1794.txt b/metadata/en-US/changelogs/1794.txt
index fe4d2b8d81..6acb683b5d 100644
--- a/metadata/en-US/changelogs/1794.txt
+++ b/metadata/en-US/changelogs/1794.txt
@@ -4,6 +4,10 @@
### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx)
+### Next version
+
+* Added Outlook Tenant ID field
+
### 1.1794 - 2021-12-22
* Added logarithmic PIN failure delay of 3 seconds