2

I am working on a spring-boot project where we are using keycloak for authorization. I have to update the access-token coming from keycloak with some additional details.

I have tried many jwt libraries and oauth springboot but not able to achieve it

AFzal Khan
  • 81
  • 6
  • 2
    *I have to update the access-token* - A JWT is signed, you can't just add anything to an existing token. This would invalidate the signature. – jps Nov 08 '22 at 09:17
  • thanks for the reply- in some solution i saw adding custom field [link](https://stackoverflow.com/questions/49426086/how-to-add-more-data-in-access-token-jwt) can i try similar to that – AFzal Khan Nov 08 '22 at 09:42
  • 1
    You can't change JWT token after it is was issued, JWT is immutable, signed data structure. So, as I see you have two options... First is to find out how to setup JWT generating/issuing with custom attributes/claims in Keycloak, there are many built-in so called "mappers" or you can create you custom plugin for Keycloak. I recommend this article: https://www.baeldung.com/keycloak-custom-user-attributes Second option is to find out how to enrich SecurityContext or RequestContext or HttpServletRequest with some details in your Spring Boot app, based on incoming JWT. – artild Nov 08 '22 at 10:03

1 Answers1

3

To add claims, you have to do it from authorization-server (Keycloak in your case), not resource-server (REST API) nor client. This is obvious for JWTs (which are sealed with content signed by authorization-server), but should be respected for introspection too.

Keycloak "mapper"

For Keycloak, private claims are added using "mappers". I have a complete sample in this project.

All you have to do is:

  • pull Keycloak dependencies and add maven shade plugin to your build
    <dependencies>
        <!-- provided keycloak dependencies -->
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-services</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <!-- Run shade goal on package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • provide src/main/resources/META-INF/jboss-deployment-structure.xml with this content:
<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.keycloak.keycloak-services" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>
  • provide src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper with a content like (replace with your own mapper implementation):
com.c4_soft.user_proxies.api.keycloak.ProxiesMapper
  • provide just above referenced mapper implementation using Keycloak base class and interfaces you need (OIDCAccessTokenMapper to add claims to access-tokens is probably a minimum in your case):
public class ProxiesMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { }
  • package and deploy to Keycloak providers directory (might need to run keycloak with build arg once as instructed in providers folder README)
  • configure your new mapper from Keycloak administration UI (for the realm or individual clients)

Parsing private claims in Spring

You can access all claims, including privates ones, from spring default Authentication implementations: JwtAuthenticationToken::getTokenAttributes (for JWT decoder) and BearerTokenAuthentication::getTokenAttributes (for introspection). Both return a Map<String, Object> you can use in security expressions.

You can also provide your own Authentication in place of Spring default ones and parse private claims there (return something more usefull than Object for the claims you use). Switching Authentication implementation is done the same way as switching authorities mapping:

http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(...)

or

http.oauth2ResourceServer().opaqueToken().authenticationConverter(...)

I have a set of 3 tutorials which incrementally explain how to easily parse (and use in security expression) such private claims: https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials

Those tutorials are for servlets. If you are writing reactive applications, follow the tutorials and then refer to one of the samples matching your exact configuration

ch4mp
  • 6,622
  • 6
  • 29
  • 49