You are viewing the documentation for Blueriq 16. Documentation for other versions is available in our documentation directory.

Blueriq provides AuthenticationMapper interface that should be implementation when a CustomBean authentication provider is used and the authentication provider returns a custom Authentication implementation. Default implementations are already provided by Blueriq for the following Authentication implementations:

  • UsernamePasswordAuthenticationToken
  • AnonymousAuthenticationToken


To implementation a custom AuthenticationMapper  the following has to be done:

  • Add the blueriq-component-api to the project dependencies.
pom.xml
<project ...>
  ...  
  <dependencies>
    ...
    <dependency>
	  <groupId>com.blueriq</groupId>
	  <artifactId>blueriq-component-api</artifactId>
	  <version>${blueriq.version}</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>
  • Define the custom implementation like below:
CustomExternalFlowStore.java
package ...;

import com.blueriq.component.api.externalflow.data.mapping.AuthenticationMapper;
// other imports ...

@Component
public class CustomAuthenticationMapper implements AuthenticationMapper {

  @Override
  public String authenticationType() {
    // add implementation here
    return null;
  }

  @Override
  public boolean supportsContract(ContractVersion contractVersion) {
    // add implementation here
    return false;
  }

  @Override
  public boolean canMap(Authentication authentication) {
    // add implementation here
    return false;
  }

  @Override
  public void fillModel(ObjectModelCreation objectModel, Authentication authentication) {
    // add implementation here
  }

  @Override
  public Authentication toAuthentication(ObjectModelRetrieval objectModel) {
    // add implementation here
    return null;
  }
}

Example CustomBlueriqAuthentication AuthenticationMapper

Adding the dependencies

The following dependencies are required in the pom.xml

pom.xml
<project ...>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>com.blueriq</groupId>
      <artifactId>blueriq-component-api</artifactId>
      <version>${blueriq.version}</version>
    </dependency>
    ...
  <dependencies>
  ...
</project>

The CustomBlueriqAuthentication

Here is an example implementation of the authentication mapper for a custom BlueriqAuthentication implementation. This implementation has an additional token field.

CustomBlueriqAuthentcation
public class CustomBlueriqAuthentication implements BlueriqAuthentication {

  private final String username;
  private final String token;
  private final List<String> teams;
  private final List<String> roles;
  private final List<? extends GrantedAuthority> authorities;
  private final Map<String, List<String>> claims;
  private final boolean authenticated;

  public CustomBlueriqAuthentication(String username, String token, List<String> teams, List<String> roles,
      Map<String, List<String>> claims, boolean authenticated) {
    this.username = username;
    this.token = token;
    this.teams = teams;
    this.roles = roles;
    this.authorities = roles.stream().map(SimpleGrantedAuthority::new).toList();
    this.claims = claims;
    this.authenticated = authenticated;
  }

  @Override
  public List<String> getTeams() {
    return teams;
  }

  @Override
  public List<String> getRoles() {
    return roles;
  }

  @Override
  public boolean isAnonymous() {
    return false;
  }

  @Override
  public boolean isAutomatic() {
    return false;
  }

  @Override
  public Set<String> getClaimNames() {
    return claims.keySet();
  }

  @Override
  public List<String> getClaim(String name) {
    return claims.get(name);
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return authorities;
  }

  @Override
  public Object getCredentials() {
    return null;
  }

  @Override
  public Object getDetails() {
    return null;
  }

  @Override
  public Object getPrincipal() {
    return username;
  }

  @Override
  public boolean isAuthenticated() {
    return authenticated;
  }

  @Override
  public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
    throw new IllegalArgumentException("Cannot set authenticated after construction");
  }

  @Override
  public String getName() {
    return username;
  }

  public String getToken() {
    return token;
  }
}

Implementing the AuthenticationMapper Interface

Please make sure that CustomBlueriqAuthenticationMapper component is scanned by Spring.

CustomAuthenticationMapper.java
package ...;

import com.blueriq.component.api.externalflow.data.mapping.AuthenticationMapper;
// other imports ...

@Component
public class CustomAuthenticationMapper implements AuthenticationMapper {

  @Override
  public String authenticationType() {
    // provide a unique authentication type identifier that will be used to identity which mapper should be used.
    return "custom-blueriq-token-authentication";
  }

  @Override
  public boolean supportsContract(ContractVersion contractVersion) {
    // check if the AuthenticationMapper supports writing and reading for the incoming contract version.
    return SupportedContractVersions.V_1_0.equals(contractVersion);
  }

  @Override
  public boolean canMap(Authentication authentication) {
    // check if the provided authentication is supported by the AuthenticationMapper
    return authentication instanceof CustomBlueriqAuthentication;
  }

  @Override
  public void fillModel(ObjectModelCreation objectModel, Authentication authentication) {
    // verify that we still have the supported Authentication object.
    if (!(authentication instanceof CustomBlueriqAuthentication)) {
      throw new IllegalArgumentException(
          "Unable to fill authentication model, unexpected authentication implementation: "
              + authentication.getClass().getSimpleName());
    }

    CustomBlueriqAuthentication customAuthentication = (CustomBlueriqAuthentication) authentication;

    // We need to supply the objectModel with enough information that it is able to the reconstruct the
    // CustomBlueriqAuthentication object in the toAuthentication method.
    objectModel.put("token", customAuthentication.getToken());
    objectModel.put("user", customAuthentication.getName());
    objectModel.put("authenticated", customAuthentication.isAuthenticated());

    ObjectModelCreation claims = objectModel.createObject("claims");
    customAuthentication.getClaimNames().forEach(key -> {
      ArrayModelCreation values = claims.createArray(key);
      customAuthentication.getClaim(key).forEach(values::add);
    });

    ArrayModelCreation roles = objectModel.createArray("roles");
    customAuthentication.getRoles().forEach(roles::add);

    ArrayModelCreation teams = objectModel.createArray("teams");
    customAuthentication.getTeams().forEach(teams::add);
  }

  @Override
  public Authentication toAuthentication(ObjectModelRetrieval objectModel) {
    // We retrieve all of the filled information from the objectModel, in order to recreate the
    // CustomBlueriqAuthentication

    String token = objectModel.getString("token");
    String user = objectModel.getString("user");
    boolean isAuthenticated = objectModel.getBoolean("authenticated");

    List<String> roles = objectModel.getStringList("roles");
    List<String> teams = objectModel.getStringList("teams");

    ObjectModelRetrieval claimsModel = objectModel.getObject("claims");
    Map<String, List<String>> claims =
        claimsModel.getProperties().collect(Collectors.toMap(Function.identity(), claimsModel::getStringList));

    return new CustomBlueriqAuthentication(user, token, teams, roles, claims, isAuthenticated);
  }
}


Testing the CustomerAuthenticationMapper

Here is an example on how to create a testcase for the AuthenticationMapper implementation. The AuthenticationMapper interface only works with interface object to mitigate implementation errors, for the testcases the ObjectModel implementation class should be used to fill the model and to create the authentication

CustomAuthenticationMapperTest.java
package ...;

// imports ...

public class CustomAuthenticationMapperTest {

  @Test
  void roundTripAuthentication() {
	// First we provide a CustomBlueriqAuthentication object filled with data.
    String user = "user";
    String token = "token";
    List<String> roles = List.of("role");
    List<String> teams = List.of("team");
    boolean authenticated = true;
    Map<String, List<String>> claims = Map.of("claim1", List.of("x", "y"), "claim2", List.of());
    CustomBlueriqAuthentication originalAuthentication =
        new CustomBlueriqAuthentication(user, token, roles, teams, claims, authenticated);

	// Second we fill an ObjectModel with the  CustomBlueriqAuthentication.
	AuthenticationMapper authenticationMapper = new CustomAuthenticationMapper();
	ObjectModel objectModel = new ObjectModel();
    authenticationMapper.fillModel(objectModel, originalAuthentication);


	// Third we transform the objectModel back into a Authentication implementation.
    Authentication authentication = authenticationMapper.toAuthentication(objectModel);

	// Fourth we verify that all of the field are set correctly. 
    assertThat(authentication, instanceOf(CustomBlueriqAuthentication.class));
    CustomBlueriqAuthenticationresult = (CustomBlueriqAuthentication) authentication;

    assertThat(result.getName(), equalTo(user));
    assertThat(result.getToken(), equalTo(tplem));
    assertThat(result.isAuthenticated(), equalTo(authenticated));

    assertThat(result.getAuthorities(), equalTo(roles.stream().map(SimpleGrantedAuthority::new).toList()));
    assertThat(result.getRoles(), equalTo(roles));
    assertThat(result.getTeams(), equalTo(teams));

    Set<String> claimNames = result.getClaimNames();
    assertThat(claimNames, hasSize(2));
    assertThat(claimNames, containsInAnyOrder("claim1", "claim2"));
    assertThat(result.getClaim("claim1"), hasSize(2));
    assertThat(result.getClaim("claim1"), containsInAnyOrder("x", "y"));
    assertThat(result.getClaim("claim2"), hasSize(0));
  }
}