├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── CustomOIDCProtocolMapper.java └── resources └── META-INF ├── jboss-deployment-structure.xml └── services └── org.keycloak.protocol.ProtocolMapper /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeycloakCustomProtocolMapper 2 | 3 | implement a CustomProtocolMapper class based on AbstractOIDCProtocolMapper 4 | 5 | META-INF/services File with the name org.keycloak.protocol.ProtocolMapper must be available and contains the name of mapper 6 | 7 | jboss-deployment-structure.xml need to be available to use keycloak built in classes 8 | 9 | Jar File is deployed in /opt/jboss/keycloak/standalone/deployments/ 10 | 11 | custom Mapper 12 | use POM.xml and resolve dependencies 13 | Extend AbstractOIDCProtocolMapper and need to implement all abstract methods. If want to have a SAML Protocol Mapper then it's another base class (AbstractSAMLProtocolMapper) 14 | one relevant method is transformAccessToken -can implement logic here. 15 | 16 | Services File 17 | The services File is important for keycloak to find the custom-Implementation. 18 | Place a file with the fileName org.keycloak.protocol.ProtocolMapper inside \src\main\resources\META-INF\services\ 19 | 20 | Inside this file write to Name of your custom Provider - then keycloak knows that this class is available as Protocol Mapper 21 | can add multiple file names 22 | 23 | CustomOIDCProtocolMapper 24 | 25 | Deployment Structure XML 26 | In custom mapper use files from keycloak. In order to use them it's needed to inform jboss about this dependency. Therefore create a file jboss-deployment-structure.xml inside \src\main\resources\META-INF\ Content: 27 | 28 | Build and deploy your Extension 29 | Build a jar File of the Extension (mvn clean package) - and place the jar in /opt/jboss/keycloak/standalone/deployments/ and restart keycloak 30 | 31 | create a Mapper in keycloak admin ui and select getDisplayType given in the code 32 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.cake.keycloak.custom 8 | keycloak-customProtocolMapper 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | 13 | 8.0.0 14 | 15 | 16 | 17 | 18 | 19 | org.keycloak 20 | keycloak-core 21 | ${keycloak.version} 22 | provided 23 | 24 | 25 | org.keycloak 26 | keycloak-server-spi 27 | ${keycloak.version} 28 | provided 29 | 30 | 31 | org.keycloak 32 | keycloak-server-spi-private 33 | ${keycloak.version} 34 | provided 35 | 36 | 37 | org.keycloak 38 | keycloak-services 39 | ${keycloak.version} 40 | provided 41 | 42 | 43 | 44 | org.keycloak 45 | keycloak-saml-core 46 | ${keycloak.version} 47 | provided 48 | 49 | 50 | org.keycloak 51 | keycloak-saml-adapter-core 52 | ${keycloak.version} 53 | provided 54 | 55 | 56 | 57 | org.keycloak 58 | keycloak-saml-adapter-api-public 59 | ${keycloak.version} 60 | provided 61 | 62 | 63 | org.keycloak 64 | keycloak-saml-core-public 65 | ${keycloak.version} 66 | provided 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-compiler-plugin 76 | 3.7.0 77 | 78 | true 79 | 1.8 80 | 1.8 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-shade-plugin 86 | 3.1.0 87 | 88 | 89 | 90 | package 91 | 92 | shade 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/CustomOIDCProtocolMapper.java: -------------------------------------------------------------------------------- 1 | import org.keycloak.models.ClientSessionContext; 2 | import org.keycloak.models.KeycloakSession; 3 | import org.keycloak.models.ProtocolMapperModel; 4 | import org.keycloak.models.UserSessionModel; 5 | import org.keycloak.protocol.oidc.OIDCLoginProtocol; 6 | import org.keycloak.protocol.oidc.mappers.*; 7 | import org.keycloak.provider.ProviderConfigProperty; 8 | import org.keycloak.representations.AccessToken; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class CustomOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { 16 | 17 | public static final String PROVIDER_ID = "oidc-customprotocolmapper"; 18 | 19 | private static final List configProperties = new ArrayList(); 20 | 21 | /** 22 | * Maybe you want to have config fields for your Mapper 23 | */ 24 | /* 25 | static { 26 | ProviderConfigProperty property; 27 | property = new ProviderConfigProperty(); 28 | property.setName(ProtocolMapperUtils.USER_ATTRIBUTE); 29 | property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL); 30 | property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT); 31 | property.setType(ProviderConfigProperty.STRING_TYPE); 32 | configProperties.add(property); 33 | 34 | property = new ProviderConfigProperty(); 35 | property.setName(ProtocolMapperUtils.MULTIVALUED); 36 | property.setLabel(ProtocolMapperUtils.MULTIVALUED_LABEL); 37 | property.setHelpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT); 38 | property.setType(ProviderConfigProperty.BOOLEAN_TYPE); 39 | configProperties.add(property); 40 | 41 | } 42 | */ 43 | @Override 44 | public List getConfigProperties() { 45 | return configProperties; 46 | } 47 | 48 | @Override 49 | public String getDisplayCategory() { 50 | return TOKEN_MAPPER_CATEGORY; 51 | } 52 | 53 | @Override 54 | public String getDisplayType() { 55 | return "ohhhhhh noooooooooooo"; 56 | } 57 | 58 | @Override 59 | public String getId() { 60 | return PROVIDER_ID; 61 | } 62 | 63 | @Override 64 | public String getHelpText() { 65 | return "some help text"; 66 | } 67 | 68 | // public IDToken transformAccessToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession keycloakSession, 69 | // UserSessionModel userSession, ClientSessionContext clientSessionCtx) { 70 | ////IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx 71 | // token.getOtherClaims().put("stackoverflowCustomToken", "stackoverflow"); 72 | //// token.getOtherClaims(). 73 | // // call profile API and enrich with permission claims 74 | //// setClaim(token, mappingModel, userSession); 75 | // setClaim(token, mappingModel, userSession, keycloakSession,clientSessionCtx); 76 | // return token; 77 | // } 78 | 79 | public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession keycloakSession, 80 | UserSessionModel userSession, ClientSessionContext clientSessionCtx) { 81 | 82 | token.getOtherClaims().put("supiri", "token_supiri"); 83 | 84 | setClaim(token, mappingModel, userSession, keycloakSession, clientSessionCtx); 85 | return token; 86 | } 87 | 88 | // public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) { 89 | // ProtocolMapperModel mapper = new ProtocolMapperModel(); 90 | // mapper.setName(name); 91 | // mapper.setProtocolMapper(PROVIDER_ID); 92 | // mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); 93 | // Map config = new HashMap(); 94 | // config.put("id.token.claim", "true"); 95 | // config.put("access.token.claim", "true"); 96 | // mapper.setConfig(config); 97 | // return mapper; 98 | // } 99 | 100 | public static ProtocolMapperModel create(String name, 101 | boolean accessToken, boolean idToken, boolean userInfo) { 102 | ProtocolMapperModel mapper = new ProtocolMapperModel(); 103 | mapper.setName(name); 104 | mapper.setProtocolMapper(PROVIDER_ID); 105 | mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); 106 | Map config = new HashMap(); 107 | // config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, hardcodedName); 108 | // config.put(CLAIM_VALUE, hardcodedValue); 109 | // config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType); 110 | config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); 111 | config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); 112 | mapper.setConfig(config); 113 | return mapper; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/jboss-deployment-structure.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper: -------------------------------------------------------------------------------- 1 | CustomOIDCProtocolMapper --------------------------------------------------------------------------------