From 79ccd4c5a8dbad83d361ca33549feb2a4ef0c52b Mon Sep 17 00:00:00 2001 From: Gerafp Date: Thu, 19 Sep 2019 12:28:23 -0500 Subject: [PATCH 01/17] 6155 - OAuth 2.0 - Microsoft --- .../AuthenticationServiceBean.java | 5 +- .../AbstractOAuth2AuthenticationProvider.java | 4 ++ .../OAuth2AuthenticationProviderFactory.java | 2 + .../oauth2/impl/MicrosoftAzureApi.java | 65 +++++++++++++++++++ .../oauth2/impl/MicrosoftOAuth2AP.java | 59 +++++++++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java create mode 100644 src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java index 70e8c092df3..65bf466e3ef 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java @@ -20,6 +20,7 @@ import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.GitHubOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.GoogleOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.OrcidOAuth2AP; +import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.MicrosoftOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProviderFactory; import edu.harvard.iq.dataverse.authorization.users.ApiToken; @@ -880,13 +881,15 @@ public AuthenticatedUser canLogInAsBuiltinUser(String username, String password) public List getAuthenticationProviderIdsSorted() { GitHubOAuth2AP github = new GitHubOAuth2AP(null, null); GoogleOAuth2AP google = new GoogleOAuth2AP(null, null); + MicrosoftOAuth2AP microsoft = new MicrosoftOAuth2AP(null, null); return Arrays.asList( BuiltinAuthenticationProvider.PROVIDER_ID, ShibAuthenticationProvider.PROVIDER_ID, OrcidOAuth2AP.PROVIDER_ID_PRODUCTION, OrcidOAuth2AP.PROVIDER_ID_SANDBOX, github.getId(), - google.getId() + google.getId(), + microsoft.getId() ); } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java index 8cfb84e7ce3..29796c2d9ef 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java @@ -116,6 +116,10 @@ public OAuth2UserRecord getUserRecord(String code, String state, String redirect final OAuthRequest request = new OAuthRequest(Verb.GET, userEndpoint, service); request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); request.setCharset("UTF-8"); + + // Microsoft + request.addHeader("Accept", "application/json"); + final Response response = request.send(); int responseCode = response.getCode(); diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2AuthenticationProviderFactory.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2AuthenticationProviderFactory.java index eebe87dea97..594ee6f718d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2AuthenticationProviderFactory.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2AuthenticationProviderFactory.java @@ -7,6 +7,7 @@ import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.GitHubOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.GoogleOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.OrcidOAuth2AP; +import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.MicrosoftOAuth2AP; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -31,6 +32,7 @@ public OAuth2AuthenticationProviderFactory() { builders.put("github", (row, data) -> readRow(row, new GitHubOAuth2AP(data.get("clientId"), data.get("clientSecret")))); builders.put("google", (row, data) -> readRow(row, new GoogleOAuth2AP(data.get("clientId"), data.get("clientSecret")))); builders.put("orcid", (row, data) -> readRow(row, new OrcidOAuth2AP(data.get("clientId"), data.get("clientSecret"), data.get("userEndpoint")))); + builders.put("microsoft", (row, data) -> readRow(row, new MicrosoftOAuth2AP(data.get("clientId"), data.get("clientSecret")))); } @Override diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java new file mode 100644 index 00000000000..5e832d19e07 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java @@ -0,0 +1,65 @@ +package edu.harvard.iq.dataverse.authorization.providers.oauth2.impl; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.model.OAuthConfig; +import com.github.scribejava.core.model.ParameterList; +import com.github.scribejava.core.model.Verb; +import java.util.Map; + +/** + * + * @CIMMYT + * + */ + +public class MicrosoftAzureApi extends DefaultApi20{ + + private static class InstanceHolder { + private static final MicrosoftAzureApi INSTANCE = new MicrosoftAzureApi(); + } + + public static MicrosoftAzureApi instance() + { + return InstanceHolder.INSTANCE; + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.POST; + } + + @Override + public String getAccessTokenEndpoint() + { + return "https://login.microsoftonline.com/common/oauth2/v2.0/token"; + } + + @Override + protected String getAuthorizationBaseUrl() + { + return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config, Map additionalParams) + { + ParameterList parameters = new ParameterList(additionalParams); + + parameters.add("response_type", config.getResponseType()); + parameters.add("client_id", config.getApiKey()); + String callback = config.getCallback(); + if (callback != null) { + parameters.add("redirect_uri", callback); + } + parameters.add("scope", "user.read"); + //parameters.add("domain_hint", "cgiar.org"); + + String state = config.getState(); + if (state != null) { + parameters.add("state", state); + } + return parameters.appendTo(getAuthorizationBaseUrl()); + } + +} diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java new file mode 100644 index 00000000000..1a3ef59074f --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java @@ -0,0 +1,59 @@ +package edu.harvard.iq.dataverse.authorization.providers.oauth2.impl; + +import com.github.scribejava.core.builder.api.BaseApi; +import com.github.scribejava.core.oauth.OAuth20Service; +import edu.harvard.iq.dataverse.authorization.providers.oauth2.AbstractOAuth2AuthenticationProvider; + +import java.util.Collections; +import java.util.logging.Logger; +import java.io.StringReader; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; +import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo; + +/** + * + * @author + */ +public class MicrosoftOAuth2AP extends AbstractOAuth2AuthenticationProvider{ + + private static final Logger logger = Logger.getLogger(MicrosoftOAuth2AP.class.getCanonicalName()); + + public MicrosoftOAuth2AP(String aClientId, String aClientSecret){ + this.id = "microsoft"; + this.title = "Microsoft"; + this.clientId = aClientId; + this.clientSecret = aClientSecret; + this.scope = "user.read"; + this.baseUserEndpoint = "https://graph.microsoft.com/v1.0/me"; + } + + @Override + public BaseApi getApiInstance(){ + return MicrosoftAzureApi.instance(); + } + + @Override + protected ParsedUserResponse parseUserResponse(final String responseBody) { + try ( StringReader rdr = new StringReader(responseBody); + JsonReader jrdr = Json.createReader(rdr) ) { + JsonObject response = jrdr.readObject(); + AuthenticatedUserDisplayInfo displayInfo = new AuthenticatedUserDisplayInfo( + response.getString("givenName", ""), + response.getString("surname", ""), + response.getString("userPrincipalName", ""), + "", ""); + String persistentUserId = response.getString("id"); + String username = response.getString("userPrincipalName"); + return new ParsedUserResponse(displayInfo, persistentUserId, username, + (displayInfo.getEmailAddress().length() > 0 ? Collections.singletonList(displayInfo.getEmailAddress()) : Collections.emptyList() ) + ); + } + } + + public boolean isDisplayIdentifier() + { + return false; + } +} \ No newline at end of file From 481dc81c18f567d4448e58c7a9ce08be15e65f36 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Thu, 19 Sep 2019 14:31:43 -0400 Subject: [PATCH 02/17] adding documentation --- doc/sphinx-guides/source/installation/oauth2.rst | 10 ++++++---- doc/sphinx-guides/source/user/account.rst | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index d77a320446b..14fd614ac25 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -11,7 +11,7 @@ As explained under "Auth Modes" in the :doc:`config` section, OAuth2 is one of t `OAuth2 `_ is an authentication protocol that allows systems to share user data, while letting the users control what data is being shared. When you see buttons stating "login with Google" or "login through Facebook", OAuth2 is probably involved. For the purposes of this section, we will shorten "OAuth2" to just "OAuth." OAuth can be compared and contrasted with :doc:`shibboleth`. -Dataverse supports three OAuth providers: `ORCID `_, `GitHub `_, and `Google `_. +Dataverse supports four OAuth providers: `ORCID `_, `Microsoft `_, `GitHub `_, and `Google `_. Setup ----- @@ -24,18 +24,19 @@ Identity Provider Side Obtain Client ID and Client Secret ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Before OAuth providers will release information about their users (first name, last name, etc.) to your Dataverse installation, you must request a "Client ID" and "Client Secret" from them. In the case of GitHub and Google, this is as simple as clicking a few buttons and there is no cost associated with using their authentication service. ORCID, on the other hand, does not have an automated system for requesting these credentials, and it is not free to use the ORCID authentication service. +Before OAuth providers will release information about their users (first name, last name, etc.) to your Dataverse installation, you must request a "Client ID" and "Client Secret" from them. In the case of GitHub and Google, this is as simple as clicking a few buttons and there is no cost associated with using their authentication service. ORCID and Microsoft, on the other hand, do not have an automated system for requesting these credentials, and it is not free to use these authentication services. -URLs to help you request a Client ID and Client Secret from the providers supported by Dataverse are provided below. For all of these providers, it's a good idea to request the Client ID and Client secret using a generic account, perhaps the one that's associated with the ``:SystemEmail`` you've configured for Dataverse, rather than your own personal ORCID, GitHub, or Google account: +URLs to help you request a Client ID and Client Secret from the providers supported by Dataverse are provided below. For all of these providers, it's a good idea to request the Client ID and Client secret using a generic account, perhaps the one that's associated with the ``:SystemEmail`` you've configured for Dataverse, rather than your own personal Microsoft, ORCID, GitHub, or Google account: - ORCID: https://orcid.org/content/register-client-application-production-trusted-party +- Microsoft: https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code - GitHub: https://github.com/settings/applications/new via https://developer.github.com/v3/oauth/ - Google: https://console.developers.google.com/projectselector/apis/credentials via https://developers.google.com/identity/protocols/OAuth2WebServer (pick "OAuth client ID") Each of these providers will require the following information from you: - Basic information about your Dataverse installation such as a name, description, URL, logo, privacy policy, etc. -- OAuth2 Redirect URI (ORCID) or Authorization Callback URL (GitHub) or Authorized Redirect URIs (Google): This is the URL on the Dataverse side to which the user will be sent after successfully authenticating with the identity provider. This should be the advertised URL of your Dataverse installation (the protocol, fully qualified domain name, and optional port configured via the ``dataverse.siteUrl`` JVM option mentioned in the :doc:`config` section) appended with ``/oauth2/callback.xhtml`` such as ``https://dataverse.example.edu/oauth2/callback.xhtml``. +- OAuth2 Redirect URI (ORCID) or Redirect URI (Microsoft) or Authorization Callback URL (GitHub) or Authorized Redirect URIs (Google): This is the URL on the Dataverse side to which the user will be sent after successfully authenticating with the identity provider. This should be the advertised URL of your Dataverse installation (the protocol, fully qualified domain name, and optional port configured via the ``dataverse.siteUrl`` JVM option mentioned in the :doc:`config` section) appended with ``/oauth2/callback.xhtml`` such as ``https://dataverse.example.edu/oauth2/callback.xhtml``. When you are finished you should have a Client ID and Client Secret from the provider. Keep them safe and secret. @@ -51,6 +52,7 @@ We will ``POST`` a JSON file containing the Client ID and Client Secret to this - :download:`orcid.json <../_static/installation/files/root/auth-providers/orcid.json>` - :download:`github.json <../_static/installation/files/root/auth-providers/github.json>` - :download:`google.json <../_static/installation/files/root/auth-providers/google.json>` +- TBD Here's how the JSON template for GitHub looks, for example: diff --git a/doc/sphinx-guides/source/user/account.rst b/doc/sphinx-guides/source/user/account.rst index eaa86567265..4682f1a4561 100755 --- a/doc/sphinx-guides/source/user/account.rst +++ b/doc/sphinx-guides/source/user/account.rst @@ -22,12 +22,13 @@ Dataverse has been configured for one or more of the following log in options: - Username/Email and Password - Institutional Log In - ORCID +- Microsoft - GitHub - Google Please note that once you create your Dataverse account, it will be associated with only one of the log in options above. -The Institutional Log In, ORCID, GitHub, and Google options are described in more detail below under "Remote Authentication." +The Institutional Log In, ORCID, Microsoft, GitHub, and Google options are described in more detail below under "Remote Authentication." Create Account ~~~~~~~~~~~~~~ @@ -134,10 +135,10 @@ Convert your Dataverse account away from ORCID for log in If you don't want to log in to Dataverse using ORCID any more, you will want to convert your Dataverse account to the Dataverse Username/Email log in option. To do this, you will need to contact support for the Dataverse installation you are using. On your account page, there is a link that will open a popup form to contact support for assistance. -GitHub and Google Log In +Microsoft, GitHub, and Google Log In ~~~~~~~~~~~~~~~~~~~~~~~~~ -You can also convert your Dataverse account to use authentication provided by GitHub or Google. These options may be found in the "Other options" section of the log in page, and function similarly to how ORCID is outlined above. If you would like to convert your account away from using one of these services for log in, then you can follow the same steps as listed above for converting away from the ORCID log in. +You can also convert your Dataverse account to use authentication provided by GitHub, Microsoft, or Google. These options may be found in the "Other options" section of the log in page, and function similarly to how ORCID is outlined above. If you would like to convert your account away from using one of these services for log in, then you can follow the same steps as listed above for converting away from the ORCID log in. My Data ------- From 246d2b8e171bce0195498f239be8a78931a44b03 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Thu, 19 Sep 2019 14:37:04 -0400 Subject: [PATCH 03/17] updating documentation --- doc/sphinx-guides/source/installation/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index 3edb735637f..388e4e2a991 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -201,7 +201,7 @@ As for the "Remote only" authentication mode, it means that: - Shibboleth or OAuth has been enabled. - ``:AllowSignUp`` is set to "false" to prevent users from creating local accounts via the web interface. - ``:DefaultAuthProvider`` has been set to use the desired authentication provider -- The "builtin" authentication provider has been disabled (:ref:`api-toggle-auth-provider`). Note that disabling the "builtin" authentication provider means that the API endpoint for converting an account from a remote auth provider will not work. Converting directly from one remote authentication provider to another (i.e. from GitHub to Google) is not supported. Conversion from remote is always to "builtin". Then the user initiates a conversion from "builtin" to remote. Note that longer term, the plan is to permit multiple login options to the same Dataverse account per https://github.com/IQSS/dataverse/issues/3487 (so all this talk of conversion will be moot) but for now users can only use a single login option, as explained in the :doc:`/user/account` section of the User Guide. In short, "remote only" might work for you if you only plan to use a single remote authentication provider such that no conversion between remote authentication providers will be necessary. +- The "builtin" authentication provider has been disabled (:ref:`api-toggle-auth-provider`). Note that disabling the "builtin" authentication provider means that the API endpoint for converting an account from a remote auth provider will not work. Converting directly from one remote authentication provider to another (i.e. from GitHub to Microsoft) is not supported. Conversion from remote is always to "builtin". Then the user initiates a conversion from "builtin" to remote. Note that longer term, the plan is to permit multiple login options to the same Dataverse account per https://github.com/IQSS/dataverse/issues/3487 (so all this talk of conversion will be moot) but for now users can only use a single login option, as explained in the :doc:`/user/account` section of the User Guide. In short, "remote only" might work for you if you only plan to use a single remote authentication provider such that no conversion between remote authentication providers will be necessary. File Storage: Local Filesystem vs. Swift vs. S3 ----------------------------------------------- From 1bf38c2ee36dec99e667289198ea768228519924 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Thu, 19 Sep 2019 16:00:36 -0400 Subject: [PATCH 04/17] adding microsoft to page title --- doc/sphinx-guides/source/installation/oauth2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index 14fd614ac25..88f92f29870 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -1,4 +1,4 @@ -OAuth Login: ORCID, GitHub, Google +OAuth Login: ORCID, Microsoft, GitHub, Google ================================== .. contents:: |toctitle| From ff848552f917d8cbae68c56061728bbc0a232635 Mon Sep 17 00:00:00 2001 From: Alejandra Tenorio <31973211+alejandratenorio@users.noreply.github.com> Date: Fri, 20 Sep 2019 09:55:17 -0500 Subject: [PATCH 05/17] adding documentation --- doc/sphinx-guides/source/installation/oauth2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index 88f92f29870..5f59c709e63 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -52,7 +52,7 @@ We will ``POST`` a JSON file containing the Client ID and Client Secret to this - :download:`orcid.json <../_static/installation/files/root/auth-providers/orcid.json>` - :download:`github.json <../_static/installation/files/root/auth-providers/github.json>` - :download:`google.json <../_static/installation/files/root/auth-providers/google.json>` -- TBD +- :download:`microsoft.json <../_static/installation/files/root/auth-providers/microsoft.json>` Here's how the JSON template for GitHub looks, for example: From 1f9630ac9797c8bfbfa3326e92ae9e2d36d6d417 Mon Sep 17 00:00:00 2001 From: Alejandra Tenorio <31973211+alejandratenorio@users.noreply.github.com> Date: Fri, 20 Sep 2019 09:59:01 -0500 Subject: [PATCH 06/17] adding documentation --- .../installation/files/root/auth-providers/microsoft.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/sphinx-guides/source/_static/installation/files/root/auth-providers/microsoft.json diff --git a/doc/sphinx-guides/source/_static/installation/files/root/auth-providers/microsoft.json b/doc/sphinx-guides/source/_static/installation/files/root/auth-providers/microsoft.json new file mode 100644 index 00000000000..8c555735577 --- /dev/null +++ b/doc/sphinx-guides/source/_static/installation/files/root/auth-providers/microsoft.json @@ -0,0 +1,8 @@ +{ + "id":"microsoft", + "factoryAlias":"oauth2", + "title":"Microsoft", + "subtitle":"", + "factoryData":"type: microsoft | userEndpoint: NONE | clientId: FIXME | clientSecret: FIXME", + "enabled":true +} From 9ef52c5e9867bae5283fe929dbf7eee45c7ebdd5 Mon Sep 17 00:00:00 2001 From: Gerafp Date: Fri, 20 Sep 2019 15:27:01 -0500 Subject: [PATCH 07/17] Add fix for ORCID XML problem when an user is authenticated with Microsoft --- .../oauth2/AbstractOAuth2AuthenticationProvider.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java index 29796c2d9ef..0ecfd8fdde4 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java @@ -111,16 +111,18 @@ public OAuth2UserRecord getUserRecord(String code, String state, String redirect OAuth20Service service = getService(state, redirectUrl); OAuth2AccessToken accessToken = service.getAccessToken(code); + //logger.log(Level.FINE, "Autentication provider id: {0}", id); + final String userEndpoint = getUserEndpoint(accessToken); final OAuthRequest request = new OAuthRequest(Verb.GET, userEndpoint, service); request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); request.setCharset("UTF-8"); - // Microsoft - request.addHeader("Accept", "application/json"); + if (id.equals("microsoft")) { + request.addHeader("Accept", "application/json"); + } - final Response response = request.send(); int responseCode = response.getCode(); final String body = response.getBody(); From 4828bf7185c5d03cb9a23bb28041053d806a3b6a Mon Sep 17 00:00:00 2001 From: Gerafp Date: Tue, 22 Oct 2019 16:53:15 -0500 Subject: [PATCH 08/17] Update MicrosoftOauth2AP for ScribeJava 6.6.3 --- .gitignore | 3 + doc/Architecture/update-user-account-info.png | Bin 34757 -> 33229 bytes .../AbstractOAuth2AuthenticationProvider.java | 16 ++--- .../oauth2/impl/MicrosoftAzureApi.java | 65 ------------------ .../oauth2/impl/MicrosoftOAuth2AP.java | 11 +-- 5 files changed, 17 insertions(+), 78 deletions(-) delete mode 100644 src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java diff --git a/.gitignore b/.gitignore index 2904bc578f2..52fa71968b8 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ scripts/installer/default.config tests/node_modules tests/package-lock.json venv + +#Ignore +scripts/installer/default.config diff --git a/doc/Architecture/update-user-account-info.png b/doc/Architecture/update-user-account-info.png index a372104438cbaab499dac987bbad0b6122332c5d..aa7d5f881f1680d1c928eb7872eadc3126ffdc7b 100644 GIT binary patch literal 33229 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%0D{X#+*Y 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\t_i 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 G0sjTHkIRq% literal 34757 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%0DoPzA4^ifj0TY@8_Il{EwgHVvfzWSg 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 Sc?Hs1_oymqDdsC!Km30WAJYZ^ diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java index bcbd9f1cf61..f851ae47cb2 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java @@ -127,13 +127,13 @@ public OAuth2UserRecord getUserRecord(String code, @NotNull OAuth20Service servi throws IOException, OAuth2Exception, InterruptedException, ExecutionException { OAuth2AccessToken accessToken = service.getAccessToken(code); -<<<<<<< HEAD +//<<<<<<< HEAD //logger.log(Level.FINE, "Autentication provider id: {0}", id); final String userEndpoint = getUserEndpoint(accessToken); -======= - +//======= + // We need to check if scope is null first: GitHub is used without scope, so the responses scope is null. // Checking scopes via Stream to be independent from order. if ( ( accessToken.getScope() != null && ! getScope().stream().allMatch(accessToken.getScope()::contains) ) || @@ -141,22 +141,22 @@ public OAuth2UserRecord getUserRecord(String code, @NotNull OAuth20Service servi // We did not get the permissions on the scope(s) we need. Abort and inform the user. throw new OAuth2Exception(200, BundleUtil.getStringFromBundle("auth.providers.insufficientScope", Arrays.asList(this.getTitle())), ""); } ->>>>>>> develop +//>>>>>>> develop OAuthRequest request = new OAuthRequest(Verb.GET, getUserEndpoint(accessToken)); request.setCharset("UTF-8"); -<<<<<<< HEAD +//<<<<<<< HEAD if (id.equals("microsoft")) { request.addHeader("Accept", "application/json"); } - final Response response = request.send(); -======= + //final Response response = request.send(); +//======= service.signRequest(accessToken, request); Response response = service.execute(request); ->>>>>>> develop +//>>>>>>> develop int responseCode = response.getCode(); String body = response.getBody(); logger.log(Level.FINE, "In requestUserRecord. Body: {0}", body); diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java deleted file mode 100644 index 5e832d19e07..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftAzureApi.java +++ /dev/null @@ -1,65 +0,0 @@ -package edu.harvard.iq.dataverse.authorization.providers.oauth2.impl; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.ParameterList; -import com.github.scribejava.core.model.Verb; -import java.util.Map; - -/** - * - * @CIMMYT - * - */ - -public class MicrosoftAzureApi extends DefaultApi20{ - - private static class InstanceHolder { - private static final MicrosoftAzureApi INSTANCE = new MicrosoftAzureApi(); - } - - public static MicrosoftAzureApi instance() - { - return InstanceHolder.INSTANCE; - } - - @Override - public Verb getAccessTokenVerb() - { - return Verb.POST; - } - - @Override - public String getAccessTokenEndpoint() - { - return "https://login.microsoftonline.com/common/oauth2/v2.0/token"; - } - - @Override - protected String getAuthorizationBaseUrl() - { - return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; - } - - @Override - public String getAuthorizationUrl(OAuthConfig config, Map additionalParams) - { - ParameterList parameters = new ParameterList(additionalParams); - - parameters.add("response_type", config.getResponseType()); - parameters.add("client_id", config.getApiKey()); - String callback = config.getCallback(); - if (callback != null) { - parameters.add("redirect_uri", callback); - } - parameters.add("scope", "user.read"); - //parameters.add("domain_hint", "cgiar.org"); - - String state = config.getState(); - if (state != null) { - parameters.add("state", state); - } - return parameters.appendTo(getAuthorizationBaseUrl()); - } - -} diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java index 1a3ef59074f..e387b4ba1b1 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java @@ -1,9 +1,10 @@ package edu.harvard.iq.dataverse.authorization.providers.oauth2.impl; -import com.github.scribejava.core.builder.api.BaseApi; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.apis.MicrosoftAzureActiveDirectory20Api; +import com.github.scribejava.core.builder.api.DefaultApi20; import edu.harvard.iq.dataverse.authorization.providers.oauth2.AbstractOAuth2AuthenticationProvider; +import java.util.Arrays; import java.util.Collections; import java.util.logging.Logger; import java.io.StringReader; @@ -25,13 +26,13 @@ public MicrosoftOAuth2AP(String aClientId, String aClientSecret){ this.title = "Microsoft"; this.clientId = aClientId; this.clientSecret = aClientSecret; - this.scope = "user.read"; + this.scope = Arrays.asList("openid User.Read"); this.baseUserEndpoint = "https://graph.microsoft.com/v1.0/me"; } @Override - public BaseApi getApiInstance(){ - return MicrosoftAzureApi.instance(); + public DefaultApi20 getApiInstance(){ + return MicrosoftAzureActiveDirectory20Api.instance(); } @Override From 3f2420dc143328180b6a12ff6800f9769de0c6b1 Mon Sep 17 00:00:00 2001 From: Gerafp Date: Tue, 22 Oct 2019 16:54:04 -0500 Subject: [PATCH 09/17] Update MicrosoftOauth2AP for ScribeJava 6.6.3 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 52fa71968b8..360b1e20bc6 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ venv #Ignore scripts/installer/default.config +/doc/Architecture/plantuml.jar +/doc/Architecture/Authentication-oauth.png From ed8950f49967b3cb37577b09dc2f4092545ede6e Mon Sep 17 00:00:00 2001 From: Gerafp Date: Wed, 23 Oct 2019 09:49:04 -0500 Subject: [PATCH 10/17] Remove comments from AbstractOAuth2AuthenticationProvider.java --- .../AbstractOAuth2AuthenticationProvider.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java index f851ae47cb2..6bb5f6b30de 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java @@ -127,13 +127,7 @@ public OAuth2UserRecord getUserRecord(String code, @NotNull OAuth20Service servi throws IOException, OAuth2Exception, InterruptedException, ExecutionException { OAuth2AccessToken accessToken = service.getAccessToken(code); -//<<<<<<< HEAD - - //logger.log(Level.FINE, "Autentication provider id: {0}", id); - final String userEndpoint = getUserEndpoint(accessToken); -//======= - // We need to check if scope is null first: GitHub is used without scope, so the responses scope is null. // Checking scopes via Stream to be independent from order. if ( ( accessToken.getScope() != null && ! getScope().stream().allMatch(accessToken.getScope()::contains) ) || @@ -141,22 +135,13 @@ public OAuth2UserRecord getUserRecord(String code, @NotNull OAuth20Service servi // We did not get the permissions on the scope(s) we need. Abort and inform the user. throw new OAuth2Exception(200, BundleUtil.getStringFromBundle("auth.providers.insufficientScope", Arrays.asList(this.getTitle())), ""); } -//>>>>>>> develop - OAuthRequest request = new OAuthRequest(Verb.GET, getUserEndpoint(accessToken)); request.setCharset("UTF-8"); -//<<<<<<< HEAD - if (id.equals("microsoft")) { request.addHeader("Accept", "application/json"); } - - //final Response response = request.send(); -//======= service.signRequest(accessToken, request); - Response response = service.execute(request); -//>>>>>>> develop int responseCode = response.getCode(); String body = response.getBody(); logger.log(Level.FINE, "In requestUserRecord. Body: {0}", body); From 3ab5e259a6e097ca305e8b350a3be2dcc3015e38 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Wed, 23 Oct 2019 21:04:16 -0400 Subject: [PATCH 11/17] switching back to ol' Google --- doc/sphinx-guides/source/installation/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index a4b4c896ba2..b1e90e7a1c8 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -201,7 +201,7 @@ As for the "Remote only" authentication mode, it means that: - Shibboleth or OAuth has been enabled. - ``:AllowSignUp`` is set to "false" to prevent users from creating local accounts via the web interface. - ``:DefaultAuthProvider`` has been set to use the desired authentication provider -- The "builtin" authentication provider has been disabled (:ref:`api-toggle-auth-provider`). Note that disabling the "builtin" authentication provider means that the API endpoint for converting an account from a remote auth provider will not work. Converting directly from one remote authentication provider to another (i.e. from GitHub to Microsoft) is not supported. Conversion from remote is always to "builtin". Then the user initiates a conversion from "builtin" to remote. Note that longer term, the plan is to permit multiple login options to the same Dataverse account per https://github.com/IQSS/dataverse/issues/3487 (so all this talk of conversion will be moot) but for now users can only use a single login option, as explained in the :doc:`/user/account` section of the User Guide. In short, "remote only" might work for you if you only plan to use a single remote authentication provider such that no conversion between remote authentication providers will be necessary. +- The "builtin" authentication provider has been disabled (:ref:`api-toggle-auth-provider`). Note that disabling the "builtin" authentication provider means that the API endpoint for converting an account from a remote auth provider will not work. Converting directly from one remote authentication provider to another (i.e. from GitHub to Google) is not supported. Conversion from remote is always to "builtin". Then the user initiates a conversion from "builtin" to remote. Note that longer term, the plan is to permit multiple login options to the same Dataverse account per https://github.com/IQSS/dataverse/issues/3487 (so all this talk of conversion will be moot) but for now users can only use a single login option, as explained in the :doc:`/user/account` section of the User Guide. In short, "remote only" might work for you if you only plan to use a single remote authentication provider such that no conversion between remote authentication providers will be necessary. File Storage: Local Filesystem vs. Swift vs. S3 ----------------------------------------------- From 97a7549596ea170e59d242c9e49ed40701b4e042 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Wed, 23 Oct 2019 22:20:32 -0400 Subject: [PATCH 12/17] changing from ms to ms azure ad --- doc/sphinx-guides/source/installation/oauth2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index 5f59c709e63..2d75397a16d 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -1,4 +1,4 @@ -OAuth Login: ORCID, Microsoft, GitHub, Google +OAuth Login: ORCID, Microsoft Azure Active Directory, GitHub, Google ================================== .. contents:: |toctitle| @@ -11,7 +11,7 @@ As explained under "Auth Modes" in the :doc:`config` section, OAuth2 is one of t `OAuth2 `_ is an authentication protocol that allows systems to share user data, while letting the users control what data is being shared. When you see buttons stating "login with Google" or "login through Facebook", OAuth2 is probably involved. For the purposes of this section, we will shorten "OAuth2" to just "OAuth." OAuth can be compared and contrasted with :doc:`shibboleth`. -Dataverse supports four OAuth providers: `ORCID `_, `Microsoft `_, `GitHub `_, and `Google `_. +Dataverse supports four OAuth providers: `ORCID `_, `Microsoft Azure Active Directory (AD) `_, `GitHub `_, and `Google `_. Setup ----- From a44bb2b9a7d5998ae092e754a35aed7ed3724c87 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Wed, 23 Oct 2019 22:23:32 -0400 Subject: [PATCH 13/17] one more instance of ms to ms azure ad --- doc/sphinx-guides/source/installation/oauth2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index 2d75397a16d..e6dd6fcf1ac 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -26,7 +26,7 @@ Obtain Client ID and Client Secret Before OAuth providers will release information about their users (first name, last name, etc.) to your Dataverse installation, you must request a "Client ID" and "Client Secret" from them. In the case of GitHub and Google, this is as simple as clicking a few buttons and there is no cost associated with using their authentication service. ORCID and Microsoft, on the other hand, do not have an automated system for requesting these credentials, and it is not free to use these authentication services. -URLs to help you request a Client ID and Client Secret from the providers supported by Dataverse are provided below. For all of these providers, it's a good idea to request the Client ID and Client secret using a generic account, perhaps the one that's associated with the ``:SystemEmail`` you've configured for Dataverse, rather than your own personal Microsoft, ORCID, GitHub, or Google account: +URLs to help you request a Client ID and Client Secret from the providers supported by Dataverse are provided below. For all of these providers, it's a good idea to request the Client ID and Client secret using a generic account, perhaps the one that's associated with the ``:SystemEmail`` you've configured for Dataverse, rather than your own personal Microsoft Azure AD, ORCID, GitHub, or Google account: - ORCID: https://orcid.org/content/register-client-application-production-trusted-party - Microsoft: https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code From 31df5a2606029ed5be9144c9351a3b8849324628 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Wed, 23 Oct 2019 22:29:27 -0400 Subject: [PATCH 14/17] a few more cases where we want to be specific about azure ad --- doc/sphinx-guides/source/installation/oauth2.rst | 2 +- doc/sphinx-guides/source/user/account.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index e6dd6fcf1ac..8e1394d2677 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -36,7 +36,7 @@ URLs to help you request a Client ID and Client Secret from the providers suppor Each of these providers will require the following information from you: - Basic information about your Dataverse installation such as a name, description, URL, logo, privacy policy, etc. -- OAuth2 Redirect URI (ORCID) or Redirect URI (Microsoft) or Authorization Callback URL (GitHub) or Authorized Redirect URIs (Google): This is the URL on the Dataverse side to which the user will be sent after successfully authenticating with the identity provider. This should be the advertised URL of your Dataverse installation (the protocol, fully qualified domain name, and optional port configured via the ``dataverse.siteUrl`` JVM option mentioned in the :doc:`config` section) appended with ``/oauth2/callback.xhtml`` such as ``https://dataverse.example.edu/oauth2/callback.xhtml``. +- OAuth2 Redirect URI (ORCID) or Redirect URI (Microsoft Azure AD) or Authorization Callback URL (GitHub) or Authorized Redirect URIs (Google): This is the URL on the Dataverse side to which the user will be sent after successfully authenticating with the identity provider. This should be the advertised URL of your Dataverse installation (the protocol, fully qualified domain name, and optional port configured via the ``dataverse.siteUrl`` JVM option mentioned in the :doc:`config` section) appended with ``/oauth2/callback.xhtml`` such as ``https://dataverse.example.edu/oauth2/callback.xhtml``. When you are finished you should have a Client ID and Client Secret from the provider. Keep them safe and secret. diff --git a/doc/sphinx-guides/source/user/account.rst b/doc/sphinx-guides/source/user/account.rst index cb10c5f2f0e..cd08fe3dbbc 100755 --- a/doc/sphinx-guides/source/user/account.rst +++ b/doc/sphinx-guides/source/user/account.rst @@ -22,7 +22,7 @@ Dataverse has been configured for one or more of the following log in options: - Username/Email and Password - Institutional Log In - ORCID -- Microsoft +- Microsoft Azure AD - GitHub - Google @@ -135,8 +135,8 @@ Convert your Dataverse account away from ORCID for log in If you don't want to log in to Dataverse using ORCID any more, you will want to convert your Dataverse account to the Dataverse Username/Email log in option. To do this, you will need to contact support for the Dataverse installation you are using. On your account page, there is a link that will open a popup form to contact support for assistance. -Microsoft, GitHub, and Google Log In -~~~~~~~~~~~~~~~~~~~~~~~~~ +Microsoft Azure AD, GitHub, and Google Log In +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also convert your Dataverse account to use authentication provided by GitHub, Microsoft, or Google. These options may be found in the "Other options" section of the log in page, and function similarly to how ORCID is outlined above. If you would like to convert your account away from using one of these services for log in, then you can follow the same steps as listed above for converting away from the ORCID log in. From 4931d6afaf2d58d4bca0b4255655c9d43076a882 Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Thu, 24 Oct 2019 12:32:39 -0400 Subject: [PATCH 15/17] correct number of =, scalable title --- doc/sphinx-guides/source/installation/oauth2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/installation/oauth2.rst b/doc/sphinx-guides/source/installation/oauth2.rst index 8e1394d2677..14483d655c3 100644 --- a/doc/sphinx-guides/source/installation/oauth2.rst +++ b/doc/sphinx-guides/source/installation/oauth2.rst @@ -1,5 +1,5 @@ -OAuth Login: ORCID, Microsoft Azure Active Directory, GitHub, Google -================================== +OAuth Login Options +=================== .. contents:: |toctitle| :local: From 44c37b45171e9223e1a4071bded5520ebc94ba70 Mon Sep 17 00:00:00 2001 From: Gerafp Date: Fri, 25 Oct 2019 09:46:40 -0500 Subject: [PATCH 16/17] Update clases and .gitignore --- .gitignore | 5 ----- .../oauth2/AbstractOAuth2AuthenticationProvider.java | 2 +- .../providers/oauth2/impl/MicrosoftOAuth2AP.java | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 360b1e20bc6..2904bc578f2 100644 --- a/.gitignore +++ b/.gitignore @@ -60,8 +60,3 @@ scripts/installer/default.config tests/node_modules tests/package-lock.json venv - -#Ignore -scripts/installer/default.config -/doc/Architecture/plantuml.jar -/doc/Architecture/Authentication-oauth.png diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java index 6bb5f6b30de..700bd17a176 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java @@ -127,7 +127,7 @@ public OAuth2UserRecord getUserRecord(String code, @NotNull OAuth20Service servi throws IOException, OAuth2Exception, InterruptedException, ExecutionException { OAuth2AccessToken accessToken = service.getAccessToken(code); - final String userEndpoint = getUserEndpoint(accessToken); + //final String userEndpoint = getUserEndpoint(accessToken); // We need to check if scope is null first: GitHub is used without scope, so the responses scope is null. // Checking scopes via Stream to be independent from order. if ( ( accessToken.getScope() != null && ! getScope().stream().allMatch(accessToken.getScope()::contains) ) || diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java index e387b4ba1b1..da260a9fb0e 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/MicrosoftOAuth2AP.java @@ -26,7 +26,7 @@ public MicrosoftOAuth2AP(String aClientId, String aClientSecret){ this.title = "Microsoft"; this.clientId = aClientId; this.clientSecret = aClientSecret; - this.scope = Arrays.asList("openid User.Read"); + this.scope = Arrays.asList("User.Read"); this.baseUserEndpoint = "https://graph.microsoft.com/v1.0/me"; } From ab8714e2f9fbdfb0896ff61907ba83b4702e419e Mon Sep 17 00:00:00 2001 From: Danny Brooke Date: Fri, 25 Oct 2019 12:32:55 -0400 Subject: [PATCH 17/17] make error message non-ORCID specific --- src/main/java/propertyFiles/Bundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 5014d2b3b26..ddbea69e1d2 100755 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -393,7 +393,7 @@ oauth2.convertAccount.success=Your Dataverse account is now associated with your # oauth2/callback.xhtml oauth2.callback.page.title=OAuth Callback -oauth2.callback.message=Authentication Error - Dataverse could not authenticate your ORCID login. Please make sure you authorize your ORCID account to connect with Dataverse. For more details about the information being requested, see the User Guide. +oauth2.callback.message=Authentication Error - Dataverse could not authenticate your login with the provider that you selected. Please make sure you authorize your account to connect with Dataverse. For more details about the information being requested, see the User Guide. # tab on dataverseuser.xhtml apitoken.title=API Token