Remove deprecated LinkedIn social provider
Closes #23127 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
d807093f63
commit
ddacfbdefd
8 changed files with 7 additions and 249 deletions
|
@ -95,9 +95,6 @@ public class Profile {
|
|||
|
||||
DPOP("OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer", Type.PREVIEW),
|
||||
|
||||
@Deprecated
|
||||
LINKEDIN_OAUTH("LinkedIn Social Identity Provider based on OAuth", Type.DEPRECATED),
|
||||
|
||||
DEVICE_FLOW("OAuth 2.0 Device Authorization Grant", Type.DEFAULT),
|
||||
|
||||
TRANSIENT_USERS("Transient users for brokering", Type.EXPERIMENTAL),
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ProfileTest {
|
|||
private static final Profile.Feature DISABLED_BY_DEFAULT_FEATURE = Profile.Feature.DOCKER;
|
||||
private static final Profile.Feature PREVIEW_FEATURE = Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ;
|
||||
private static final Profile.Feature EXPERIMENTAL_FEATURE = Profile.Feature.DYNAMIC_SCOPES;
|
||||
private static Profile.Feature DEPRECATED_FEATURE = Profile.Feature.LINKEDIN_OAUTH;
|
||||
private static Profile.Feature DEPRECATED_FEATURE = Profile.Feature.HOSTNAME_V1;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
@ -132,7 +132,7 @@ public class ProfileTest {
|
|||
public void configWithCommaSeparatedList() {
|
||||
String enabledFeatures = DISABLED_BY_DEFAULT_FEATURE.getKey() + "," + PREVIEW_FEATURE.getKey() + "," + EXPERIMENTAL_FEATURE.getKey();
|
||||
if (DEPRECATED_FEATURE != null) {
|
||||
enabledFeatures += "," + DEPRECATED_FEATURE.getKey();
|
||||
enabledFeatures += "," + DEPRECATED_FEATURE.getVersionedKey();
|
||||
}
|
||||
|
||||
String disabledFeatures = DEFAULT_FEATURE.getKey();
|
||||
|
|
|
@ -247,3 +247,7 @@ bin/kc.[sh|bat] start --spi-group-jpa-escape-slashes-in-group-path=true
|
|||
The escape char is the tilde character `~`. The previous example results in the path `/top/group~/slash`. The escape marks the last slash is part of the name and not a hierarchy separator.
|
||||
|
||||
The escaping is currently disabled by default because it represents a change in behavior. Nevertheless enabling escaping is recommended and it can be the default in future versions.
|
||||
|
||||
= Removal of the deprecated LinkedIn provider
|
||||
|
||||
In version 22.0.2 the OAuh 2.0 social provider for LinkedIn was replaced by a new OpenId Connect implementation. The legacy provider was deprecated but not removed, just in case it was still functional in some existing realms. {project_name} 25.0.0 is definitely removing the old provider and its associated `linkedin-oauth` feature. From now on, the default `LinkedIn` social provider is the only option available.
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.keycloak.social.github.GitHubIdentityProviderFactory;
|
|||
import org.keycloak.social.gitlab.GitLabIdentityProviderFactory;
|
||||
import org.keycloak.social.google.GoogleIdentityProviderFactory;
|
||||
import org.keycloak.social.instagram.InstagramIdentityProviderFactory;
|
||||
import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory;
|
||||
import org.keycloak.social.linkedin.LinkedInOIDCIdentityProviderFactory;
|
||||
import org.keycloak.social.microsoft.MicrosoftIdentityProviderFactory;
|
||||
import org.keycloak.social.openshift.OpenshiftV3IdentityProviderFactory;
|
||||
|
@ -73,7 +72,6 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
GitLabIdentityProviderFactory.PROVIDER_ID,
|
||||
GoogleIdentityProviderFactory.PROVIDER_ID,
|
||||
InstagramIdentityProviderFactory.PROVIDER_ID,
|
||||
LinkedInIdentityProviderFactory.PROVIDER_ID,
|
||||
LinkedInOIDCIdentityProviderFactory.PROVIDER_ID,
|
||||
MicrosoftIdentityProviderFactory.PROVIDER_ID,
|
||||
OpenshiftV3IdentityProviderFactory.PROVIDER_ID,
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.social.linkedin;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
|
||||
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
||||
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* LinkedIn social provider. See https://developer.linkedin.com/docs/oauth2
|
||||
*
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
@Deprecated
|
||||
public class LinkedInIdentityProvider extends AbstractOAuth2IdentityProvider<OAuth2IdentityProviderConfig> implements SocialIdentityProvider<OAuth2IdentityProviderConfig> {
|
||||
|
||||
private static final Logger log = Logger.getLogger(LinkedInIdentityProvider.class);
|
||||
|
||||
public static final String AUTH_URL = "https://www.linkedin.com/oauth/v2/authorization";
|
||||
public static final String TOKEN_URL = "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
public static final String PROFILE_URL = "https://api.linkedin.com/v2/me";
|
||||
public static final String EMAIL_URL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))";
|
||||
public static final String EMAIL_SCOPE = "r_emailaddress";
|
||||
public static final String DEFAULT_SCOPE = "r_liteprofile " + EMAIL_SCOPE;
|
||||
|
||||
private static final String PROFILE_PROJECTION = "profileProjection";
|
||||
|
||||
public LinkedInIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
|
||||
super(session, config);
|
||||
config.setAuthorizationUrl(AUTH_URL);
|
||||
config.setTokenUrl(TOKEN_URL);
|
||||
config.setUserInfoUrl(getUserInfoUrl(config.getConfig().get(PROFILE_PROJECTION)));
|
||||
// email scope is mandatory in order to resolve the username using the email address
|
||||
if (!config.getDefaultScope().contains(EMAIL_SCOPE)) {
|
||||
config.setDefaultScope(config.getDefaultScope() + " " + EMAIL_SCOPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsExternalExchange() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProfileEndpointForValidation(EventBuilder event) {
|
||||
return getConfig().getUserInfoUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BrokeredIdentityContext extractIdentityFromProfile(EventBuilder event, JsonNode profile) {
|
||||
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
|
||||
|
||||
user.setFirstName(getFirstMultiLocaleString(profile, "firstName"));
|
||||
user.setLastName(getFirstMultiLocaleString(profile, "lastName"));
|
||||
user.setIdpConfig(getConfig());
|
||||
user.setIdp(this);
|
||||
|
||||
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
|
||||
log.debug("doGetFederatedIdentity()");
|
||||
try {
|
||||
BrokeredIdentityContext identity = extractIdentityFromProfile(null, doHttpGet(getConfig().getUserInfoUrl(), accessToken));
|
||||
|
||||
identity.setEmail(fetchEmailAddress(accessToken, identity));
|
||||
|
||||
if (identity.getUsername() == null) {
|
||||
identity.setUsername(identity.getEmail());
|
||||
}
|
||||
|
||||
return identity;
|
||||
} catch (Exception e) {
|
||||
throw new IdentityBrokerException("Could not obtain user profile from linkedIn.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultScopes() {
|
||||
return DEFAULT_SCOPE;
|
||||
}
|
||||
|
||||
private String fetchEmailAddress(String accessToken, BrokeredIdentityContext identity) {
|
||||
if (identity.getEmail() == null && getConfig().getDefaultScope() != null && getConfig().getDefaultScope().contains(EMAIL_SCOPE)) {
|
||||
try {
|
||||
JsonNode emailAddressNode = doHttpGet(EMAIL_URL, accessToken).findPath("emailAddress");
|
||||
|
||||
if (emailAddressNode != null) {
|
||||
return emailAddressNode.asText();
|
||||
}
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to retrieve user email", cause);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private JsonNode doHttpGet(String url, String bearerToken) throws IOException {
|
||||
JsonNode response = SimpleHttp.doGet(url, session).header("Authorization", "Bearer " + bearerToken).asJson();
|
||||
|
||||
if (response.hasNonNull("serviceErrorCode")) {
|
||||
throw new IdentityBrokerException("Could not obtain response from [" + url + "]. Response from server: " + response);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private String getFirstMultiLocaleString(JsonNode node, String name) {
|
||||
JsonNode claim = node.get(name);
|
||||
|
||||
if (claim != null) {
|
||||
JsonNode localized = claim.get("localized");
|
||||
|
||||
if (localized != null) {
|
||||
Iterator<JsonNode> iterator = localized.iterator();
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
return iterator.next().asText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* append profileProjection to profile URL if exists
|
||||
*
|
||||
* @param projection parameter
|
||||
* @return Profile URL
|
||||
*/
|
||||
private String getUserInfoUrl(String projection) {
|
||||
return projection == null || projection.isEmpty()
|
||||
? PROFILE_URL
|
||||
: PROFILE_URL + "?projection=" + projection;
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.social.linkedin;
|
||||
|
||||
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
@Deprecated
|
||||
public class LinkedInIdentityProviderFactory extends AbstractIdentityProviderFactory<LinkedInIdentityProvider>
|
||||
implements SocialIdentityProviderFactory<LinkedInIdentityProvider>, EnvironmentDependentProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "linkedin";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LinkedIn (deprecated)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedInIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
|
||||
return new LinkedInIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2IdentityProviderConfig createConfig() {
|
||||
return new OAuth2IdentityProviderConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property().name("profileProjection")
|
||||
.label("Profile projection")
|
||||
.helpText("Projection parameter for profile request. Leave empty for default projection.")
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.LINKEDIN_OAUTH);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
|
|||
*/
|
||||
public class LinkedInUserAttributeMapper extends AbstractJsonUserAttributeMapper {
|
||||
|
||||
private static final String[] cp = new String[] { LinkedInIdentityProviderFactory.PROVIDER_ID, LinkedInOIDCIdentityProviderFactory.PROVIDER_ID };
|
||||
private static final String[] cp = new String[] { LinkedInOIDCIdentityProviderFactory.PROVIDER_ID };
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
|
|
|
@ -19,7 +19,6 @@ org.keycloak.social.facebook.FacebookIdentityProviderFactory
|
|||
org.keycloak.social.paypal.PayPalIdentityProviderFactory
|
||||
org.keycloak.social.github.GitHubIdentityProviderFactory
|
||||
org.keycloak.social.google.GoogleIdentityProviderFactory
|
||||
org.keycloak.social.linkedin.LinkedInIdentityProviderFactory
|
||||
org.keycloak.social.linkedin.LinkedInOIDCIdentityProviderFactory
|
||||
org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory
|
||||
org.keycloak.social.twitter.TwitterIdentityProviderFactory
|
||||
|
|
Loading…
Reference in a new issue