diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b69987c6c1f252ebf824bd2a39bfe891fc4bedc6..3bd9ce007392a0dfe31ca6b87ebd268b3d516ede 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,28 +19,16 @@ variables:
   P_NAME: "app.access-token-provider"
   P_APP_TYPE: "java"
   P_CODE_SRC_PATH: "."
+  P_MAVEN_IMAGE_TAG: "3.6.3"
 
 
 # Define jobs
 code:
   stage: build
   extends:
-    - .codeForJava
-  variables:
-    P_MAVEN_IMAGE_TAG: "3.6.3"
+    - .buildCodeForJava
 
 quality:
   stage: tests
   extends:
-    - .sonarqubeForJava
-  variables:
-    P_MAVEN_IMAGE_TAG: "3.6.3"
-    P_CODE_BINARIES: "target/classes/"
-    P_CODE_JACOCO_REPORT_PATH: "target/jacoco.exec"
-    P_CODE_JUNIT_REPORTS_PATH: "target/surefire-reports"
-    P_CODE_DYNAMIC_ANALYSIS: "reuseReports"
-    P_CODE_COVERAGE_PLUGIN: "jacoco"
-    P_CODE_SOURCE_ENCODING: "UTF-8"
-    P_CODE_LANGUAGE: "java"
-    P_CODE_DEVELOPER_EDITION: "true"
-
+    - .testQualityForJavaWithSonarqube
\ No newline at end of file
diff --git a/access-token-provider-api/pom.xml b/access-token-provider-api/pom.xml
index 14535327c1b9cbc66ec5cb0519b429eff66fd508..040333cb44eaff7cd4009ac2965ceeb2d478d8f2 100644
--- a/access-token-provider-api/pom.xml
+++ b/access-token-provider-api/pom.xml
@@ -11,8 +11,58 @@
     </parent>
 
     <groupId>net.ihe.gazelle</groupId>
-    <artifactId>lib.access-token-provider-api</artifactId>
+    <artifactId>app.access-token-provider-api</artifactId>
     <name>Access Token Provider Api</name>
     <version>1.0.0-SNAPSHOT</version>
 
-</project>
\ No newline at end of file
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>fully.qualified.MainClass</mainClass>
+                        </manifest>
+                    </archive>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
+                        <phase>package</phase> <!-- bind to the packaging phase -->
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>lib.annotations</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>sb.iua-standard-block</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>sb.jwt-standard-block</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>app.audience-retriever</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceRegistry.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb34775e4990c31ed073f8bb311cbffccb6929fa
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceRegistry.java
@@ -0,0 +1,17 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.business.Credential;
+
+/**
+ * Interface to interact with the audience registry where is store all known audience with their credential
+ */
+public interface AudienceRegistry {
+
+    /**
+     * Get credential's audience
+     * @param audienceId
+     * @return credential
+     */
+    Credential getAudienceCredentials(String audienceId);
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServer.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..a63a7601576b8953a01a6999be167f8b70c6a9f8
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServer.java
@@ -0,0 +1,18 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+/**
+ * For SoapUI integration need, a simplified Authorization Server (or IDP) is required.
+ */
+public interface DummyAuthzServer {
+
+    /**
+     * get a dummy access token
+     * @param userId
+     * @param audienceId
+     * @param purposeOfUse
+     * @param resourceId
+     * @return an access token
+     */
+    byte[] getAccessToken(String userId, String audienceId, String purposeOfUse, String resourceId);
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapui.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapui.java
new file mode 100644
index 0000000000000000000000000000000000000000..907deacf605a3970c72c385332b4ac9fa09cb461
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapui.java
@@ -0,0 +1,97 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.business.AccessTokenRequest;
+import net.ihe.gazelle.app.accesstokenproviderapi.business.SymmetricSignature;
+import net.ihe.gazelle.app.audienceretriever.adapter.AudienceSecretRetrieverForSoapui;
+import net.ihe.gazelle.app.audienceretriever.application.AudienceSecretRetriever;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLogger;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLoggerFactory;
+import net.ihe.gazelle.modelapi.sb.business.EncodingException;
+import net.ihe.gazelle.sb.iua.business.TokenType;
+
+import java.time.Duration;
+
+/**
+ * Dummy soapui authorization server
+ */
+public class DummyAuthzServerSoapui implements DummyAuthzServer {
+
+    private static final GazelleLogger LOGGER = GazelleLoggerFactory.getInstance().getLogger(DummyAuthzServerSoapui.class);
+
+    private static final String ALGORITHM = "HS256";
+    private static final String ISSUER = "https://ehealthsuisse.ihe-europe.net/access-token-provider";
+    private static final TokenType TOKEN_TYPE = TokenType.JWT;
+    private static final Duration DURATION = Duration.ofHours(1);
+    private AudienceSecretRetriever audienceSecretRetriever;
+
+    /**
+     * Default constructor for the class.
+     */
+    public DummyAuthzServerSoapui() {
+        //Empty
+    }
+
+    /**
+     * Constructor with the path for the class.
+     */
+    public DummyAuthzServerSoapui(String path) {
+        audienceSecretRetriever = new AudienceSecretRetrieverForSoapui(path);
+    }
+
+    /**
+     * Setter for the audienceSecretRetriever property.
+     *
+     * @param audienceSecretRetriever value to set to the property.
+     */
+    public void setAudienceSecretRetriever(AudienceSecretRetriever audienceSecretRetriever) {
+        this.audienceSecretRetriever = audienceSecretRetriever;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte[] getAccessToken(String userId, String audienceId, String purposeOfUse, String resourceId) {
+        //todo purposeOfUse and resourceId are not yet implemented
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(this.audienceSecretRetriever);
+
+        return getTokenGenerator(userId, audienceId, tokenGenerator);
+    }
+
+
+    /**
+     * get the access token
+     *
+     * @param userId     String parameter
+     * @param audienceId String parameter
+     * @return AccessTokenRequest Element
+     */
+    public AccessTokenRequest getAccessTokenRequest(String userId, String audienceId){
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, userId, audienceId, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+        return accessTokenRequest;
+    }
+
+
+    /**
+     * get the generated token
+     *
+     * @param userId         String element
+     * @param audienceId     String element
+     * @param tokenGenerator TokenGenerator object
+     * @return The token as byte
+     */
+    public byte[] getTokenGenerator(String userId, String audienceId, TokenGenerator tokenGenerator){
+        byte[] token = null;
+        try {
+            token = tokenGenerator.generateAccessToken(getAccessTokenRequest(userId, audienceId)).getToken();
+        } catch (EncodingException | TokenRequestException e) {
+            LOGGER.error("Error generating Access Token", e);
+        }
+        return token;
+    }
+
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TestUserRegistry.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TestUserRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..290fdf5520bb7a78214f1b484b483bec119647c0
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TestUserRegistry.java
@@ -0,0 +1,16 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.business.testuser.TestUser;
+
+/**
+ * Interface to interact with the test-users’ database for authentication step and token content
+ */
+public interface TestUserRegistry {
+
+    /**
+     * @param userId
+     * @return TestUser
+     */
+    TestUser getTestUser(String userId);
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGenerator.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bd9b28b25f470cc6b81a3099380cbbe1185375c
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGenerator.java
@@ -0,0 +1,115 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.business.AccessTokenRequest;
+import net.ihe.gazelle.app.audienceretriever.application.AudienceSecretRetriever;
+import net.ihe.gazelle.modelapi.sb.business.EncodingException;
+import net.ihe.gazelle.sb.iua.business.EncodedIUAToken;
+import net.ihe.gazelle.sb.iua.business.TokenType;
+import net.ihe.gazelle.sb.jwtstandardblock.adapter.JJWTAdapter;
+import net.ihe.gazelle.sb.jwtstandardblock.application.JWSEncoderDecoder;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jose.JOSEHeader;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwk.JSONWebKey;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwk.KeyAlgorithm;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwk.SymmetricalKey;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwt.JSONWebSignature;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwt.JSONWebToken;
+import net.ihe.gazelle.sb.jwtstandardblock.business.jwt.JSONWebTokenClaimSet;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Class to generate the token
+ */
+public class TokenGenerator {
+
+    private static final String ALGORITHM = "HS256";
+    private static final String ISSUER = "https://ehealthsuisse.ihe-europe.net/access-token-provider";
+    private static final TokenType TOKEN_TYPE = TokenType.JWT;
+    private static final Duration DEFAULT_DURATION = Duration.ofMinutes(5);
+    private static final List<String> SUBJECTS = Arrays.asList("aamrein", "aerne");
+
+    private AudienceSecretRetriever audienceSecretRetriever;
+
+    /**
+     * Set an audience secret
+     *
+     * @param audienceSecretRetriever AudienceSecretRetriever element
+     */
+    public void setAudienceSecretRetriever(AudienceSecretRetriever audienceSecretRetriever) {
+        this.audienceSecretRetriever = audienceSecretRetriever;
+    }
+
+    /**
+     * Encode the IUA token
+     *
+     * @param accessTokenRequest AccessTokenRequest element
+     * @return The EncodedIUAToken
+     * @throws EncodingException
+     * @throws TokenRequestException
+     */
+    public EncodedIUAToken generateAccessToken(AccessTokenRequest accessTokenRequest) throws EncodingException, TokenRequestException {
+        if (accessTokenRequest.getSignature() == null) {
+            throw new TokenRequestException("Missing signature information");
+        }
+
+        if (accessTokenRequest.getSignature().getAlgorithm() == null || !accessTokenRequest.getSignature().getAlgorithm().equals(ALGORITHM)) {
+            throw new TokenRequestException("Unsupported Algorithm");
+        }
+
+        Duration duration = accessTokenRequest.getValidityTime() != null ? accessTokenRequest.getValidityTime() : DEFAULT_DURATION;
+
+        if (accessTokenRequest.getTokenType() == null || !accessTokenRequest.getTokenType().equals(TOKEN_TYPE)) {
+            throw new TokenRequestException("Unsupported token type");
+        }
+
+        if (accessTokenRequest.getIssuer() == null || !accessTokenRequest.getIssuer().equals(ISSUER)) {
+            throw new TokenRequestException("Unsupported issuer");
+        }
+
+        if (accessTokenRequest.getAudience() == null || accessTokenRequest.getAudience().isEmpty()) {
+            throw new TokenRequestException("Audience is null or empty");
+        }
+
+        if (accessTokenRequest.getSubject() == null || !SUBJECTS.contains(accessTokenRequest.getSubject())) {
+            throw new TokenRequestException("Unsupported subject");
+        }
+
+        String secret = audienceSecretRetriever.retrieveSecretForAudience(accessTokenRequest.getAudience());
+        if (secret == null || secret.isEmpty()) {
+            throw new TokenRequestException("Audience is not known");
+        }
+
+        JSONWebTokenClaimSet claimSet = new JSONWebTokenClaimSet();
+
+        claimSet.setSubject(accessTokenRequest.getSubject());
+        claimSet.setIssuer(ISSUER);
+        claimSet.setAudience(accessTokenRequest.getAudience());
+
+        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC"));
+        claimSet.setIssuedAt(String.valueOf(now.toEpochSecond()));
+        claimSet.setExpiration(String.valueOf(now.plus(duration).toEpochSecond()));
+
+        claimSet.setJwtId(UUID.randomUUID().toString());
+
+        JOSEHeader joseHeader = new JOSEHeader(false, null, KeyAlgorithm.HS256);
+        JSONWebKey jsonWebKey = new SymmetricalKey(secret, null, KeyAlgorithm.HS256);
+        JSONWebSignature jose = new JSONWebSignature(jsonWebKey, joseHeader);
+
+        JSONWebToken token = new JSONWebToken(UUID.randomUUID().toString(), "IUA", jose, claimSet); //Verify standard keyword
+
+        JWSEncoderDecoder jwsEncoderDecoder = new JWSEncoderDecoder(new JJWTAdapter());
+
+        EncodedIUAToken encodedIUAToken = new EncodedIUAToken(jwsEncoderDecoder.encode(token).getCompletePayload().getBytes(StandardCharsets.UTF_8));
+        encodedIUAToken.setTokenType(TokenType.JWT);
+
+        return encodedIUAToken;
+    }
+
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenRequestException.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenRequestException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a22ea5ce4a77509c16a7ea1bc778ec739761cbc9
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenRequestException.java
@@ -0,0 +1,63 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+/**
+ * Class to manage token request exception
+ */
+public class TokenRequestException extends Exception {
+
+    /**
+     * Constructs a new exception with null as its detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     */
+    public TokenRequestException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     */
+    public TokenRequestException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause. Note that the detail/TransactionRecordingDAO message associated with
+     * cause is not automatically incorporated in this exception's detail message.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause   the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates that the
+     *                cause is nonexistent or unknown.
+     */
+    public TokenRequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param cause              the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *                           that the cause is nonexistent or unknown.
+     */
+    public TokenRequestException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param message            the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause              the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *                           that the cause is nonexistent or unknown.
+     * @param enableSuppression  whether or not suppression is enabled or disabled
+     * @param writableStackTrace whether or not the stack trace should be writable
+     */
+    public TokenRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedAlgorithmException.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedAlgorithmException.java
new file mode 100644
index 0000000000000000000000000000000000000000..789d9bcce78ad572d22700014805356dfb4daf1f
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedAlgorithmException.java
@@ -0,0 +1,61 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application.exception;
+
+/**
+ * Class to manage unsupported algorithm exception
+ */
+public class UnsupportedAlgorithmException extends Exception {
+
+    /**
+     * Constructs a new exception with null as its detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     */
+    public UnsupportedAlgorithmException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     */
+    public UnsupportedAlgorithmException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause. Note that the detail/TransactionRecordingDAO message associated with
+     * cause is not automatically incorporated in this exception's detail message.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause   the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates that the
+     *                cause is nonexistent or unknown.
+     */
+    public UnsupportedAlgorithmException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param cause the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *              that the cause is nonexistent or unknown.
+     */
+    public UnsupportedAlgorithmException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param message            the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause              the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *                           that the cause is nonexistent or unknown.
+     * @param enableSuppression  whether or not suppression is enabled or disabled
+     * @param writableStackTrace whether or not the stack trace should be writable
+     */
+    public UnsupportedAlgorithmException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedTokenTypeException.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedTokenTypeException.java
new file mode 100644
index 0000000000000000000000000000000000000000..adea3058e9fcdbebc865d5b991eefad5cfcaaa7c
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/application/exception/UnsupportedTokenTypeException.java
@@ -0,0 +1,62 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application.exception;
+
+/**
+ * Class to manage unsupported token exception
+ */
+public class UnsupportedTokenTypeException extends Exception {
+
+    /**
+     * Constructs a new exception with null as its detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     */
+    public UnsupportedTokenTypeException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to
+     * {@link Throwable#initCause(Throwable)}.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     */
+    public UnsupportedTokenTypeException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause. Note that the detail/TransactionRecordingDAO message associated with
+     * cause is not automatically incorporated in this exception's detail message.
+     *
+     * @param message the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause   the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates that the
+     *                cause is nonexistent or unknown.
+     */
+    public UnsupportedTokenTypeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param cause the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *              that the cause is nonexistent or unknown.
+     */
+    public UnsupportedTokenTypeException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or
+     * disabled.
+     *
+     * @param message            the detail message. Can be retrieved by a later call of {@link Throwable#getMessage()} method.
+     * @param cause              the cause. Can be retrieved by a lter call to {@link Throwable#getCause()}. A null value is permitted, and indicates
+     *                           that the cause is nonexistent or unknown.
+     * @param enableSuppression  whether or not suppression is enabled or disabled
+     * @param writableStackTrace whether or not the stack trace should be writable
+     */
+    public UnsupportedTokenTypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtension.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtension.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd92b50c67f8c55d80175dad59ebba560cb60c1e
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtension.java
@@ -0,0 +1,256 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Extensions for the Access Token
+ */
+public class AccessTokenExtension {
+
+    private String subjectId;
+    private List<String> subjectOrganizations = new ArrayList<>();
+    private List<String> subjectOrganizationIds = new ArrayList<>();
+    private String homeCommunityId;
+    private String nationalProviderIdentifier;
+    private List<String> providerIds = new ArrayList<>();
+    private CodedValue subjectRole;
+    private CodedValue purposeOfUse;
+    private String resourceId;
+    private String onBehalfOf;
+
+    /**
+     * constructor
+     */
+    public AccessTokenExtension() {
+        // Constructor is empty because all variables are optionals.
+    }
+
+    /**
+     * get the subjectId
+     * @return subjectId
+     */
+    public String getSubjectId() {
+        return subjectId;
+    }
+
+    /**
+     * set the subjectId
+     * @param subjectId the subjectId
+     */
+    public void setSubjectId(String subjectId) {
+        this.subjectId = subjectId;
+    }
+
+    /**
+     * get subjectOrganizations list
+     * @return subjectOrganizations
+     */
+    public List<String> getSubjectOrganizations() {
+        return subjectOrganizations;
+    }
+
+    /**
+     * add a subjectOrganization in the subjectOrganizations list
+     * @param subjectOrganization a nationalProviderIdentifier
+     */
+    public void addSubjectOrganization(String subjectOrganization) {
+        this.subjectOrganizations.add(subjectOrganization);
+    }
+
+    /**
+     * remove a subjectOrganization from the subjectOrganizations list
+     * @param subjectOrganization a nationalProviderIdentifier
+     */
+    public void removeSubjectOrganization(String subjectOrganization) {
+        this.subjectOrganizations.remove(subjectOrganization);
+    }
+
+    /**
+     * get subjectOrganizationIds list
+     * @return subjectOrganizationIds
+     */
+    public List<String> getSubjectOrganizationIds() {
+        return subjectOrganizationIds;
+    }
+
+    /**
+     * add a subjectOrganizationId in the subjectOrganizationIds list
+     * @param subjectOrganizationId a subjectOrganizationId
+     */
+    public void addSubjectOrganizationId(String subjectOrganizationId) {
+        this.subjectOrganizationIds.add(subjectOrganizationId);
+    }
+
+    /**
+     * remove a subjectOrganizationId from the subjectOrganizationIds list
+     * @param subjectOrganizationId a subjectOrganizationId
+     */
+    public void removeSubjectOrganizationId(String subjectOrganizationId) {
+        this.subjectOrganizationIds.remove(subjectOrganizationId);
+    }
+
+    /**
+     * get the homeCommunityId
+     * @return homeCommunityId
+     */
+    public String getHomeCommunityId() {
+        return homeCommunityId;
+    }
+
+    /**
+     * set the homeCommunityId
+     * @param homeCommunityId the homeCommunityId
+     */
+    public void setHomeCommunityId(String homeCommunityId) {
+        this.homeCommunityId = homeCommunityId;
+    }
+
+    /**
+     * get the nationalProviderIdentifier
+     * @return nationalProviderIdentifier
+     */
+    public String getNationalProviderIdentifier() {
+        return nationalProviderIdentifier;
+    }
+
+    /**
+     * set the nationalProviderIdentifier
+     * @param nationalProviderIdentifier the nationalProviderIdentifier
+     */
+    public void setNationalProviderIdentifier(String nationalProviderIdentifier) {
+        this.nationalProviderIdentifier = nationalProviderIdentifier;
+    }
+
+    /**
+     * get providerIds list
+     * @return providerIds
+     */
+    public List<String> getProviderIds() {
+        return providerIds;
+    }
+
+    /**
+     * add a providerId in the providerIds list
+     * @param providerId a providerId
+     */
+    public void addProviderId(String providerId) {
+        this.providerIds.add(providerId);
+    }
+
+    /**
+     * remove a providerId in the providerIds list
+     * @param providerId a providerId
+     */
+    public void removeProviderId(String providerId) {
+        this.providerIds.remove(providerId);
+    }
+
+    /**
+     * get the subjectRole
+     * @return subjectRole
+     */
+    public CodedValue getSubjectRole() {
+        return subjectRole;
+    }
+
+    /**
+     * set the subjectRole
+     * @param subjectRole the subjectRole
+     */
+    public void setSubjectRole(CodedValue subjectRole) {
+        this.subjectRole = subjectRole;
+    }
+
+    /**
+     * get the purposeOfUse
+     * @return purposeOfUse
+     */
+    public CodedValue getPurposeOfUse() {
+        return purposeOfUse;
+    }
+
+    /**
+     * set the purposeOfUse
+     * @param purposeOfUse the purposeOfUse
+     */
+    public void setPurposeOfUse(CodedValue purposeOfUse) {
+        this.purposeOfUse = purposeOfUse;
+    }
+
+    /**
+     * get the resourceId
+     * @return resourceId
+     */
+    public String getResourceId() {
+        return resourceId;
+    }
+
+    /**
+     * set the resourceId
+     * @param resourceId the resourceId
+     */
+    public void setResourceId(String resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    /**
+     * get the onBehalfOf
+     * @return onBehalfOf
+     */
+    public String getOnBehalfOf() {
+        return onBehalfOf;
+    }
+
+    /**
+     * set the onBehalfOf
+     * @param onBehalfOf the onBehalfOf
+     */
+    public void setOnBehalfOf(String onBehalfOf) {
+        this.onBehalfOf = onBehalfOf;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AccessTokenExtension that = (AccessTokenExtension) o;
+
+        if (subjectId != null ? !subjectId.equals(that.subjectId) : that.subjectId != null) return false;
+        if (subjectOrganizations != null ? !subjectOrganizations.equals(that.subjectOrganizations) : that.subjectOrganizations != null)
+            return false;
+        if (subjectOrganizationIds != null ? !subjectOrganizationIds.equals(that.subjectOrganizationIds) : that.subjectOrganizationIds != null)
+            return false;
+        if (homeCommunityId != null ? !homeCommunityId.equals(that.homeCommunityId) : that.homeCommunityId != null)
+            return false;
+        if (nationalProviderIdentifier != null ? !nationalProviderIdentifier.equals(that.nationalProviderIdentifier) : that.nationalProviderIdentifier != null)
+            return false;
+        if (providerIds != null ? !providerIds.equals(that.providerIds) : that.providerIds != null) return false;
+        if (subjectRole != null ? !subjectRole.equals(that.subjectRole) : that.subjectRole != null) return false;
+        if (purposeOfUse != null ? !purposeOfUse.equals(that.purposeOfUse) : that.purposeOfUse != null) return false;
+        if (resourceId != null ? !resourceId.equals(that.resourceId) : that.resourceId != null) return false;
+        return onBehalfOf != null ? onBehalfOf.equals(that.onBehalfOf) : that.onBehalfOf == null;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int result = subjectId != null ? subjectId.hashCode() : 0;
+        result = 31 * result + (subjectOrganizations != null ? subjectOrganizations.hashCode() : 0);
+        result = 31 * result + (subjectOrganizationIds != null ? subjectOrganizationIds.hashCode() : 0);
+        result = 31 * result + (homeCommunityId != null ? homeCommunityId.hashCode() : 0);
+        result = 31 * result + (nationalProviderIdentifier != null ? nationalProviderIdentifier.hashCode() : 0);
+        result = 31 * result + (providerIds != null ? providerIds.hashCode() : 0);
+        result = 31 * result + (subjectRole != null ? subjectRole.hashCode() : 0);
+        result = 31 * result + (purposeOfUse != null ? purposeOfUse.hashCode() : 0);
+        result = 31 * result + (resourceId != null ? resourceId.hashCode() : 0);
+        result = 31 * result + (onBehalfOf != null ? onBehalfOf.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequest.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1dfad1ed718c4a314453a92814d3f0d84e246b3
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequest.java
@@ -0,0 +1,145 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.sb.iua.business.TokenType;
+
+import java.time.Duration;
+
+/**
+ * The Access Token request
+ */
+public class AccessTokenRequest {
+
+    private String issuer;
+    private String subject;
+    private String audience;
+    private Duration validityTime;
+    private TokenType tokenType;
+    private Signature signature;
+    private AccessTokenExtension extension;
+
+    /**
+     * constructor
+     */
+    public AccessTokenRequest(String issuer, String subject, String audience, Duration validityTime, TokenType tokenType) {
+        this.issuer = issuer;
+        this.subject = subject;
+        this.audience = audience;
+        this.validityTime = validityTime;
+        this.tokenType = tokenType;
+    }
+
+    /**
+     * get the issuer
+     *
+     * @return issuer
+     */
+    public String getIssuer() {
+        return issuer;
+    }
+
+    /**
+     * get the subject
+     *
+     * @return subject
+     */
+    public String getSubject() {
+        return subject;
+    }
+
+    /**
+     * get the audience
+     *
+     * @return audience
+     */
+    public String getAudience() {
+        return audience;
+    }
+
+    /**
+     * get the validityTime
+     *
+     * @return validityTime
+     */
+    public Duration getValidityTime() {
+        return validityTime;
+    }
+
+    /**
+     * get the tokenType
+     *
+     * @return tokenType
+     */
+    public TokenType getTokenType() {
+        return tokenType;
+    }
+
+    /**
+     * get the signature
+     *
+     * @return signature
+     */
+    public Signature getSignature() {
+        return signature;
+    }
+
+    /**
+     * set the signature
+     *
+     * @param signature the signature
+     */
+    public void setSignature(Signature signature) {
+        this.signature = signature;
+    }
+
+    /**
+     * get the extension
+     *
+     * @return extension
+     */
+    public AccessTokenExtension getExtension() {
+        return extension;
+    }
+
+    /**
+     * set the extension
+     *
+     * @param extension the extension
+     */
+    public void setExtension(AccessTokenExtension extension) {
+        this.extension = extension;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AccessTokenRequest that = (AccessTokenRequest) o;
+
+        if (!issuer.equals(that.issuer)) return false;
+        if (!subject.equals(that.subject)) return false;
+        if (!audience.equals(that.audience)) return false;
+        if (!validityTime.equals(that.validityTime)) return false;
+        if (tokenType != that.tokenType) return false;
+        if (signature != null ? !signature.equals(that.signature) : that.signature != null) return false;
+        return extension != null ? extension.equals(that.extension) : that.extension == null;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int result = issuer.hashCode();
+        result = 31 * result + subject.hashCode();
+        result = 31 * result + audience.hashCode();
+        result = 31 * result + validityTime.hashCode();
+        result = 31 * result + tokenType.hashCode();
+        result = 31 * result + (signature != null ? signature.hashCode() : 0);
+        result = 31 * result + (extension != null ? extension.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignature.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignature.java
new file mode 100644
index 0000000000000000000000000000000000000000..93cb4a39f52d6faeda98d989d0809bb047399177
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignature.java
@@ -0,0 +1,62 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import java.util.Arrays;
+
+/**
+ * Asymmetric signature information of the access token
+ */
+public class AsymmetricSignature extends Signature {
+
+    private byte[] privateKey;
+    private String privateKeyPassword;
+
+    /**
+     * constructor
+     */
+    public AsymmetricSignature(String algorithm, byte[] privateKey, String privateKeyPassword) {
+        super(algorithm);
+        this.privateKey = privateKey.clone();
+        this.privateKeyPassword = privateKeyPassword;
+    }
+
+    /**
+     * get the privateKey
+     * @return privateKey
+     */
+    public byte[] getPrivateKey() {
+        return privateKey.clone();
+    }
+
+    /**
+     * get the privateKeyPassword
+     * @return privateKeyPassword
+     */
+    public String getPrivateKeyPassword() {
+        return privateKeyPassword;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        AsymmetricSignature that = (AsymmetricSignature) o;
+
+        if (!Arrays.equals(privateKey, that.privateKey)) return false;
+        return privateKeyPassword.equals(that.privateKeyPassword);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int result = Arrays.hashCode(privateKey);
+        result = 31 * result + privateKeyPassword.hashCode();
+        return result;
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValue.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebd7e365c497b4eab5b7444c4af6ff0fd195e9bc
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValue.java
@@ -0,0 +1,74 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+/**
+ * A Coded value
+ */
+public class CodedValue {
+
+    private String code;
+    private String codeSystem;
+    private String codeSystemName;
+    private String displayName;
+
+    /**
+     * constructor
+     */
+    public CodedValue(String code, String codeSystem) {
+        this.code = code;
+        this.codeSystem = codeSystem;
+    }
+
+    /**
+     * get the code
+     *
+     * @return code
+     */
+    public String getCode() {
+        return code;
+    }
+
+    /**
+     * get the codeSystem
+     *
+     * @return codeSystem
+     */
+    public String getCodeSystem() {
+        return codeSystem;
+    }
+
+    /**
+     * get the codeSystemName
+     *
+     * @return codeSystemName
+     */
+    public String getCodeSystemName() {
+        return codeSystemName;
+    }
+
+    /**
+     * set the codeSystemName
+     *
+     * @param codeSystemName the codeSystemName
+     */
+    public void setCodeSystemName(String codeSystemName) {
+        this.codeSystemName = codeSystemName;
+    }
+
+    /**
+     * get the displayName
+     *
+     * @return displayName
+     */
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    /**
+     * set the displayName
+     *
+     * @param displayName the displayName
+     */
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Credential.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Credential.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e26d29844ec890430f55aaeecab12ae0ca58b25
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Credential.java
@@ -0,0 +1,7 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+/**
+ * Credential for an audience
+ */
+public interface Credential {
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Password.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Password.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0143af4f813a794e08b8e2dd73302b5d9f58532
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Password.java
@@ -0,0 +1,47 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import java.util.Arrays;
+
+/**
+ * A password
+ */
+public class Password implements Credential {
+
+    private byte[] value;
+
+    /**
+     * constructor
+     */
+    public Password(byte[] value) {
+        this.value = value.clone();
+    }
+
+    /**
+     * get the value
+     * @return value
+     */
+    public byte[] getValue() {
+        return value.clone();
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Password password = (Password) o;
+
+        return Arrays.equals(value, password.value);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return Arrays.hashCode(value);
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKey.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ba2fe7b76d739648fc5236505daf1b307bd44c5
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKey.java
@@ -0,0 +1,46 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import java.util.Arrays;
+
+/**
+ * A public key
+ */
+public class PublicKey implements Credential {
+    private byte[] key;
+
+    /**
+     * constructor
+     */
+    public PublicKey(byte[] key) {
+        this.key = key.clone();
+    }
+
+    /**
+     * get the key
+     * @return key
+     */
+    public byte[] getKey() {
+        return key.clone();
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        PublicKey publicKey = (PublicKey) o;
+
+        return Arrays.equals(key, publicKey.key);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return Arrays.hashCode(key);
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Signature.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Signature.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec065fc59c883571956776052d626bda93d4663f
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/Signature.java
@@ -0,0 +1,46 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+
+/**
+ * Signature information of the access token
+ */
+public abstract class Signature {
+
+    private String algorithm;
+
+    /**
+     * constructor
+     */
+    public Signature(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    /**
+     * get the algorithm
+     * @return algorithm
+     */
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Signature signature = (Signature) o;
+
+        return algorithm.equals(signature.algorithm);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return algorithm.hashCode();
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignature.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignature.java
new file mode 100644
index 0000000000000000000000000000000000000000..31628baae5de12ccbdcb0762a2f4e4f778af00db
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignature.java
@@ -0,0 +1,47 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+/**
+ * Symmetric signature information of the access token
+ */
+public class SymmetricSignature extends Signature {
+
+    private String secret;
+
+    /**
+     * constructor
+     */
+    public SymmetricSignature(String algorithm, String secret) {
+        super(algorithm);
+        this.secret = secret;
+    }
+
+    /**
+     * get the secret
+     * @return secret
+     */
+    public String getSecret() {
+        return secret;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        SymmetricSignature that = (SymmetricSignature) o;
+
+        return secret.equals(that.secret);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return secret.hashCode();
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUser.java b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUser.java
new file mode 100644
index 0000000000000000000000000000000000000000..2585df370d0834ed20191591beec7a3ad2b3e9f9
--- /dev/null
+++ b/access-token-provider-api/src/main/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUser.java
@@ -0,0 +1,140 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business.testuser;
+
+import java.util.*;
+
+/**
+ * Test user used for authentication and token content
+ */
+public class TestUser {
+
+    private String userId;
+    private List<String> givenNames = new ArrayList<>();
+    private String lastName;
+    private Date birthDate;
+    private String gender;
+    private Map<String, String> extensions = new HashMap<>();
+
+    /**
+     * Constructor
+     */
+    public TestUser(String userId, List<String> givenNames, String lastName) {
+        this.userId = userId;
+        this.givenNames = givenNames;
+        this.lastName = lastName;
+    }
+
+    /**
+     * get the userId
+     * @return userId
+     */
+    public String getUserId() {
+        return userId;
+    }
+
+    /**
+     * get the givenNames
+     * @return givenNames
+     */
+    public List<String> getGivenNames() {
+        return givenNames;
+    }
+
+    /**
+     * get the lastName
+     * @return lastName
+     */
+    public String getLastName() {
+        return lastName;
+    }
+
+    /**
+     * get the birthDate
+     * @return birthDate
+     */
+    public Date getBirthDate() {
+        return (Date) birthDate.clone();
+    }
+
+    /**
+     * set the birthDate
+     *
+     * @param birthDate the birthDate
+     */
+    public void setBirthDate(Date birthDate) {
+        this.birthDate = (Date) birthDate.clone();
+    }
+
+    /**
+     * get the gender
+     * @return gender
+     */
+    public String getGender() {
+        return gender;
+    }
+
+    /**
+     * set the gender
+     *
+     * @param gender the gender
+     */
+    public void setGender(String gender) {
+        this.gender = gender;
+    }
+
+    /**
+     * get the extensions
+     * @return extensions
+     */
+    public Map<String, String> getExtensions() {
+        return extensions;
+    }
+
+    /**
+     * add an extension in the extensions map
+     * @param key key of the extension
+     * @param value value of the extension
+     */
+    public void addExtension(String key, String value) {
+        extensions.put(key, value);
+    }
+
+    /**
+     * remove an extension in the extensions map
+     * @param key key of the extension
+     */
+    public void removeExtension(String key) {
+        extensions.remove(key);
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TestUser testUser = (TestUser) o;
+
+        if (!userId.equals(testUser.userId)) return false;
+        if (!givenNames.equals(testUser.givenNames)) return false;
+        if (!lastName.equals(testUser.lastName)) return false;
+        if (birthDate != null ? !birthDate.equals(testUser.birthDate) : testUser.birthDate != null) return false;
+        if (gender != null ? !gender.equals(testUser.gender) : testUser.gender != null) return false;
+        return extensions != null ? extensions.equals(testUser.extensions) : testUser.extensions == null;
+    }
+
+    @Override
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int result = userId.hashCode();
+        result = 31 * result + givenNames.hashCode();
+        result = 31 * result + lastName.hashCode();
+        result = 31 * result + (birthDate != null ? birthDate.hashCode() : 0);
+        result = 31 * result + (gender != null ? gender.hashCode() : 0);
+        result = 31 * result + (extensions != null ? extensions.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt b/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt
deleted file mode 100644
index f9f3a064a8060a6fac7ac2caba057ac521732a1d..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Put here classes from adapter layer :
-Data transformers, adapters, presenters or DAO. Abstraction of external libraries for
-application or business use.
-Web-services point, sockets, database connection and pool, GUI, file system, framework,
-external libraries.
\ No newline at end of file
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt b/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt
deleted file mode 100644
index 13b8d86e92813836491d776f448dfd3f0a81628f..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Put here classes from application layer :
-Use cases. Business elements applied in an application context or scenario.
\ No newline at end of file
diff --git a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt b/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt
deleted file mode 100644
index 277b09a0363f09d1d11ebcc56460392a69e4b60b..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/main/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Put here classes from business layer :
-Business model, rules and constraints. Always true. Independent from the application.
\ No newline at end of file
diff --git a/access-token-provider-api/src/main/resources/getToken-soapui-project.xml b/access-token-provider-api/src/main/resources/getToken-soapui-project.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b910e1f165102fbb24cbf8975bf3e8413b843d6
--- /dev/null
+++ b/access-token-provider-api/src/main/resources/getToken-soapui-project.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<con:soapui-project id="4527283c-83fc-419a-9fa3-d9c072053eac" activeEnvironment="Default" name="getToken" resourceRoot="" soapui-version="5.6.0" abortOnError="false" runType="SEQUENTIAL" xmlns:con="http://eviware.com/soapui/config"><con:settings/><con:testSuite id="a7dd34e8-2441-4bf8-b2bc-3ea43c3b0f42" name="getToken"><con:settings/><con:runType>SEQUENTIAL</con:runType><con:testCase id="f8ac5a96-e7e9-4a60-9e9c-84f80664db91" failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="getToken" searchProperties="true"><con:settings/><con:testStep type="groovy" name="exemple" id="c65c443d-1476-4c4a-b9e6-b63eac62939f"><con:settings/><con:config><script>import net.ihe.gazelle.app.accesstokenproviderapi.application.DummyAuthzServerSoapui
+
+    def server = new DummyAuthzServerSoapui("/opt/simulators/audience.properties");
+    def token = server.getAccessToken("aamrein", "audience", null, null );
+    log.info new String(token)
+</script>
+</con:config>
+</con:testStep>
+    <con:properties/>
+</con:testCase>
+    <con:properties/>
+</con:testSuite>
+    <con:properties/>
+    <con:wssContainer/>
+    <con:oAuth2ProfileContainer/>
+    <con:oAuth1ProfileContainer/>
+    <con:sensitiveInformation/>
+</con:soapui-project>
\ No newline at end of file
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceSecretRetrieverTestImpl.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceSecretRetrieverTestImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..3922a0928e8909cd898c9efad298d5fe9c327353
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/AudienceSecretRetrieverTestImpl.java
@@ -0,0 +1,38 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.audienceretriever.application.AudienceSecretRetriever;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class to test the retriever of the audience secret
+ */
+public class AudienceSecretRetrieverTestImpl implements AudienceSecretRetriever {
+
+    private Map<String, String> registry = new HashMap<>();
+
+    /**
+     * Constructor
+     */
+    public AudienceSecretRetrieverTestImpl() {
+    }
+
+    /**
+     * Add a new audience
+     *
+     * @param audience String element
+     * @param secret   String element
+     */
+    public void addAudience(String audience, String secret){
+        registry.put(audience, secret);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String retrieveSecretForAudience(String audience) {
+        return registry.get(audience);
+    }
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapuiTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapuiTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f0042970e738ad14662d2393003be6f17cc51c8
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/DummyAuthzServerSoapuiTest.java
@@ -0,0 +1,47 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Class to test the soapui authorization server
+ */
+class DummyAuthzServerSoapuiTest {
+
+    private static final String SUBJECT = "aamrein";
+    private static final String AUDIENCE = "audience";
+
+    /**
+     * get access token with an Audience path defined
+     */
+    @Test
+    public void getAccessTokenWithPathAudienceTest() {
+        DummyAuthzServerSoapui dummyAuthzServer = new DummyAuthzServerSoapui();
+        dummyAuthzServer.setAudienceSecretRetriever((String audience) -> "myBeautifulKeyWhichIsAJWTSecretSoSecret");
+
+
+        assertNotNull(dummyAuthzServer.getAccessToken(SUBJECT, AUDIENCE, null, null), "check that the access token is not null");
+    }
+
+    /**
+     * get access token without an Audience path defined (we keep the default Audience path in this case)
+     */
+    @Test
+    public void getAccessTokenWithDefaultPathAudienceTest() {
+        DummyAuthzServerSoapui dummyAuthzServer = new DummyAuthzServerSoapui();
+        dummyAuthzServer.setAudienceSecretRetriever((String audience) -> "myBeautifulKeyWhichIsAJWTSecretSoSecret");
+
+        assertNotNull(dummyAuthzServer.getAccessToken(SUBJECT, AUDIENCE, null, null), "check that the access token is not null");
+    }
+
+    /**
+     * get access token with an invalid audience path provide
+     */
+    @Test
+    public void getAccessTokenWithBadPathAudienceTest() {
+        DummyAuthzServerSoapui dummyAuthzServer = new DummyAuthzServerSoapui("test.properties");
+        assertNull(dummyAuthzServer.getAccessToken(SUBJECT, AUDIENCE, null, null), "check that the access token is null");
+    }
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGeneratorTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGeneratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..702ef7417b58654e9ee408642fcfcc9b81d9292b
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/application/TokenGeneratorTest.java
@@ -0,0 +1,280 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.application;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.business.AccessTokenRequest;
+import net.ihe.gazelle.app.accesstokenproviderapi.business.SymmetricSignature;
+import net.ihe.gazelle.modelapi.sb.business.EncodingException;
+import net.ihe.gazelle.sb.iua.business.EncodedIUAToken;
+import net.ihe.gazelle.sb.iua.business.TokenType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests for {@link TokenGenerator}
+ */
+class TokenGeneratorTest {
+
+    private static final String ALGORITHM = "HS256";
+    private static final String SUBJECT = "aamrein";
+    private static final String AUDIENCE = "audience";
+    private static final String ISSUER = "https://ehealthsuisse.ihe-europe.net/access-token-provider";
+    private static final TokenType TOKEN_TYPE = TokenType.JWT;
+    private static final Duration DURATION = Duration.ofMinutes(5);
+    private static AudienceSecretRetrieverTestImpl AUDIENCE_RETRIEVER = new AudienceSecretRetrieverTestImpl();
+
+    /**
+     * Init audience available to generate token.
+     */
+    @BeforeEach
+    public void initAudience() {
+        AUDIENCE_RETRIEVER.addAudience(AUDIENCE, "myBeautifulKeyWhichIsAJWTSecretSoSecret");
+    }
+
+    /**
+     * Default generation of a token
+     *
+     * @throws EncodingException     if something wrong happens during encoding
+     * @throws TokenRequestException if the token request is not valid
+     */
+    @Test
+    public void generateAccessTokenTest() throws EncodingException, TokenRequestException {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "myBeautifulKeyWhichIsAJWTSecretSoSecret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        EncodedIUAToken encodedIUAToken = tokenGenerator.generateAccessToken(accessTokenRequest);
+        assertNotNull(encodedIUAToken);
+    }
+
+    /**
+     * Test with a null signature.
+     */
+    @Test
+    public void generateAccessTokenNullSignatureTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(null, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(null);
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating token with unsupported issuer.
+     */
+    @Test
+    public void generateAccessTokenUnsupportedIssuerTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest("blabla", SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with a null subject.
+     */
+    @Test
+    public void generateAccessTokenNullIssuerTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(null, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with unsupported subject.
+     */
+    @Test
+    public void generateAccessTokenUnsupportedSubjectTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, "subject", AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null subject.
+     */
+    @Test
+    public void generateAccessTokenNullSubjectTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, null, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with unsupported token type.
+     */
+    @Test
+    public void generateAccessTokenUnsupportedTokenTypeTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TokenType.SAML);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null token type.
+     */
+    @Test
+    public void generateAccessTokenNullTokenTypeTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, null);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with unsupported algo.
+     */
+    @Test
+    public void generateAccessTokenUnsupportedAlgoTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature("algo", "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null algo.
+     */
+    @Test
+    public void generateAccessTokenNullAlgoTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(null, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null audience.
+     */
+    @Test
+    public void generateAccessTokenNullAudienceTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, null, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with unsupported audience.
+     */
+    @Test
+    public void generateAccessTokenUnknownAudienceTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, "pouet", DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with empty audience.
+     */
+    @Test
+    public void generateAccessTokenEmptyAudienceTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, "", DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(AUDIENCE_RETRIEVER);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with empty secret.
+     */
+    @Test
+    public void generateAccessTokenEmptySecretTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, "pouet", DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(audience -> "");
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null secret.
+     */
+    @Test
+    public void generateAccessTokenNullSecretTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, "pouet", DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(audience -> null);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+
+    /**
+     * Test generating a token with null Duration.
+     */
+    @Test
+    public void generateAccessTokenDurationNullTest() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, "pouet", null, TOKEN_TYPE);
+        accessTokenRequest.setSignature(new SymmetricSignature(ALGORITHM, "secret"));
+
+
+        TokenGenerator tokenGenerator = new TokenGenerator();
+        tokenGenerator.setAudienceSecretRetriever(audience -> null);
+
+        assertThrows(TokenRequestException.class, () -> tokenGenerator.generateAccessToken(accessTokenRequest), "Unsupported issuer");
+    }
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtensionTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtensionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0bfb297bb2a588776d32b2456581d710afa704f
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenExtensionTest.java
@@ -0,0 +1,305 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AccessTokenExtensionTest {
+
+    /**
+     * Test subjectId getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-012")
+    void getSetSubjectId(){
+        String subjectId = "id";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setSubjectId(subjectId);
+        assertEquals(subjectId, accessTokenExtension.getSubjectId(), "Getter shall return the value of subjectId !");
+    }
+
+    /**
+     * Test addSubjectOrganization and removeSubjectOrganization.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-013")
+    void subjectOrganization(){
+        String orga1 = "orga1";
+        String orga2 = "orga2";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganization(orga1);
+        assertEquals(1, accessTokenExtension.getSubjectOrganizations().size());
+        accessTokenExtension.addSubjectOrganization(orga2);
+        assertEquals(2, accessTokenExtension.getSubjectOrganizations().size());
+        accessTokenExtension.removeSubjectOrganization(orga1);
+        assertEquals(1, accessTokenExtension.getSubjectOrganizations().size());
+        assertEquals(orga2, accessTokenExtension.getSubjectOrganizations().get(0));
+    }
+
+    /**
+     * Test addSubjectOrganizationId and removeSubjectOrganizationId.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-014")
+    void subjectOrganizationId(){
+        String orgaId1 = "orgaId1";
+        String orgaId2 = "orgaId2";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganizationId(orgaId1);
+        assertEquals(1, accessTokenExtension.getSubjectOrganizationIds().size());
+        accessTokenExtension.addSubjectOrganizationId(orgaId2);
+        assertEquals(2, accessTokenExtension.getSubjectOrganizationIds().size());
+        accessTokenExtension.removeSubjectOrganizationId(orgaId1);
+        assertEquals(1, accessTokenExtension.getSubjectOrganizationIds().size());
+        assertEquals(orgaId2, accessTokenExtension.getSubjectOrganizationIds().get(0));
+    }
+
+    /**
+     * Test subjectId getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-015")
+    void homeCommunityId(){
+        String homeCommunityId = "id";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setHomeCommunityId(homeCommunityId);
+        assertEquals(homeCommunityId, accessTokenExtension.getHomeCommunityId());
+    }
+
+    /**
+     * Test subjectId getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-016")
+    void nationalProviderIdentifier(){
+        String nationalProviderIdentifier = "id";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setNationalProviderIdentifier(nationalProviderIdentifier);
+        assertEquals(nationalProviderIdentifier, accessTokenExtension.getNationalProviderIdentifier());
+    }
+
+    /**
+     * Test addProviderId and removeProviderId.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-017")
+    void providerId(){
+        String providerId1 = "provId1";
+        String providerId2 = "provId2";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addProviderId(providerId1);
+        assertEquals(1, accessTokenExtension.getProviderIds().size());
+        accessTokenExtension.addProviderId(providerId2);
+        assertEquals(2, accessTokenExtension.getProviderIds().size());
+        accessTokenExtension.removeProviderId(providerId1);
+        assertEquals(1, accessTokenExtension.getProviderIds().size());
+        assertEquals(providerId2, accessTokenExtension.getProviderIds().get(0));
+    }
+
+    /**
+     * Test subjectRole getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-018")
+    void subjectRole(){
+        CodedValue codedValue = new CodedValue("a", "b");
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setSubjectRole(codedValue);
+        assertEquals(codedValue, accessTokenExtension.getSubjectRole());
+    }
+
+    /**
+     * Test purposeOfUse getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-019")
+    void purposeOfUse(){
+        CodedValue codedValue = new CodedValue("a", "b");
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setPurposeOfUse(codedValue);
+        assertEquals(codedValue, accessTokenExtension.getPurposeOfUse());
+    }
+
+    /**
+     * Test resourceId getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-020")
+    void resourceId(){
+        String resourceId = "id";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setResourceId(resourceId);
+        assertEquals(resourceId, accessTokenExtension.getResourceId());
+    }
+
+    /**
+     * Test onBehalfOf getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-021")
+    void onBehalfOf(){
+        String onBehalfOf = "toto";
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setOnBehalfOf(onBehalfOf);
+        assertEquals(onBehalfOf, accessTokenExtension.getOnBehalfOf());
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        assertFalse(accessTokenExtension.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        assertTrue(accessTokenExtension.equals(accessTokenExtension), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        assertFalse(accessTokenExtension.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different subjectId
+     */
+    @Test
+    void equalsDifferentSubjectId() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setSubjectId("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setSubjectId("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different subjectId !");
+    }
+
+    /**
+     * Test equals with different subjectOrganization
+     */
+    @Test
+    void equalsDifferentSubjectOrganization() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganization("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganization("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different subjectOrganization !");
+    }
+
+    /**
+     * Test equals with different subjectOrganizationId
+     */
+    @Test
+    void equalsDifferentSubjectOrganizationId() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganizationId("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.addSubjectOrganizationId("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different subjectOrganizationId !");
+    }
+
+    /**
+     * Test equals with different homeCommunityId
+     */
+    @Test
+    void equalsDifferentHomeCommunityId() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setHomeCommunityId("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setHomeCommunityId("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different homeCommunityId !");
+    }
+
+    /**
+     * Test equals with different nationalProviderIdentifier
+     */
+    @Test
+    void equalsDifferentNationalProviderIdentifier() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setNationalProviderIdentifier("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setNationalProviderIdentifier("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different nationalProviderIdentifier !");
+    }
+
+    /**
+     * Test equals with different providerId
+     */
+    @Test
+    void equalsDifferentProviderId() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.addProviderId("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.addProviderId("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different providerId !");
+    }
+
+    /**
+     * Test equals with different purposeOfUse
+     */
+    @Test
+    void equalsDifferentPurposeOfUse() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setPurposeOfUse(new CodedValue("1", "1"));
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setPurposeOfUse(new CodedValue("2", "2"));
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different purposeOfUse !");
+    }
+
+    /**
+     * Test equals with different subjectRole
+     */
+    @Test
+    void equalsDifferentSubjectRole() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setSubjectRole(new CodedValue("1", "1"));
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setSubjectRole(new CodedValue("2", "2"));
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different subjectRole !");
+    }
+
+    /**
+     * Test equals with different resourceId
+     */
+    @Test
+    void equalsDifferentResourceId() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setResourceId("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setResourceId("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different resourceId !");
+    }
+
+    /**
+     * Test equals with different onBehalfOf
+     */
+    @Test
+    void equalsDifferentOnBehalfOf() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        accessTokenExtension.setOnBehalfOf("1");
+        AccessTokenExtension accessTokenExtension2 = new AccessTokenExtension();
+        accessTokenExtension.setOnBehalfOf("2");
+        assertFalse(accessTokenExtension.equals(accessTokenExtension2), "Objects shall not be equal if they have different onBehalfOf !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        AccessTokenExtension accessTokenExtension = new AccessTokenExtension();
+        assertNotNull(accessTokenExtension.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequestTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7737a96d937f1941f9755de6cdc346646bd80188
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AccessTokenRequestTest.java
@@ -0,0 +1,206 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import net.ihe.gazelle.sb.iua.business.TokenType;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AccessTokenRequestTest {
+
+    private static final String ISSUER = "issuer";
+    private static final String SUBJECT = "subject";
+    private static final String AUDIENCE = "audience";
+    private static final Duration DURATION = Duration.ofMinutes(1);
+    private static final TokenType TOKEN_TYPE = TokenType.JWT;
+    private static final SymmetricSignature SIGNATURE = new SymmetricSignature("RSALALA", "SECRET");
+    private static final AccessTokenExtension EXTENSION = new AccessTokenExtension();
+
+
+    /**
+     * Test issuer getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-001")
+    void getIssuer() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertEquals(ISSUER, accessTokenRequest.getIssuer(), "Getter shall return the value of issuer !");
+    }
+
+    /**
+     * Test subject getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-002")
+    void getSubject() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertEquals(SUBJECT, accessTokenRequest.getSubject(), "Getter shall return the value of subject !");
+    }
+
+    /**
+     * Test audience getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-003")
+    void getAudience() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertEquals(AUDIENCE, accessTokenRequest.getAudience(), "Getter shall return the value of audience !");
+    }
+
+    /**
+     * Test validityTime getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-004")
+    void getValidityTime() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertEquals(DURATION, accessTokenRequest.getValidityTime(), "Getter shall return the value of validityTime !");
+    }
+
+    /**
+     * Test tokenType getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-007")
+    void getTokenType() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertEquals(TOKEN_TYPE, accessTokenRequest.getTokenType(), "Getter shall return the value of tokenType !");
+    }
+
+    /**
+     * Test signature getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-005")
+    void signature() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setSignature(SIGNATURE);
+        assertEquals(SIGNATURE, accessTokenRequest.getSignature(), "Setter shall change the value of signature !");
+    }
+
+    /**
+     * Test extension getter and setter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-005")
+    void extension() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest.setExtension(EXTENSION);
+        assertEquals(EXTENSION, accessTokenRequest.getExtension(), "Setter shall change the value of extension !");
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertTrue(accessTokenRequest.equals(accessTokenRequest), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different issuer
+     */
+    @Test
+    void equalsDifferentIssuer() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest("ISSUER2", SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different issuer !");
+    }
+
+    /**
+     * Test equals with different subject
+     */
+    @Test
+    void equalsDifferentSubject() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, "SUBJECT2", AUDIENCE, DURATION, TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different subject !");
+    }
+
+    /**
+     * Test equals with different audience
+     */
+    @Test
+    void equalsDifferentAudience() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, SUBJECT, "AUDIENCE2", DURATION, TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different audience !");
+    }
+
+    /**
+     * Test equals with different validityTime
+     */
+    @Test
+    void equalsDifferentValidityTime() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, Duration.ofDays(3), TOKEN_TYPE);
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different validityTime !");
+    }
+
+    /**
+     * Test equals with different tokenType
+     */
+    @Test
+    void equalsDifferentTokenType() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TokenType.SAML);
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different tokenType !");
+    }
+
+    /**
+     * Test equals with different signature
+     */
+    @Test
+    void equalsDifferentSignature() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest2.setSignature(new SymmetricSignature("RSA", "secret"));
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different signature !");
+    }
+
+    /**
+     * Test equals with different extension
+     */
+    @Test
+    void equalsDifferentExtension() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        AccessTokenRequest accessTokenRequest2 = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        accessTokenRequest2.setExtension(new AccessTokenExtension());
+        assertFalse(accessTokenRequest.equals(accessTokenRequest2), "Objects shall not be equal if they have different extension !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        AccessTokenRequest accessTokenRequest = new AccessTokenRequest(ISSUER, SUBJECT, AUDIENCE, DURATION, TOKEN_TYPE);
+        assertNotNull(accessTokenRequest.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignatureTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignatureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..835451669733a76f7bbfa6bf325d8f7212ae3217
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/AsymmetricSignatureTest.java
@@ -0,0 +1,101 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AsymmetricSignatureTest {
+
+    private static final byte[] KEY = "key".getBytes();
+    private static final String PASSWORD = "password";
+    private static final String ALGORITHM = "RSAPOUETPOUET";
+
+    /**
+     * Test privateKey getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-010")
+    void getPrivateKey(){
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertArrayEquals(KEY, signature.getPrivateKey(), "Getter shall return the value of privateKey !");
+    }
+
+    /**
+     * Test privateKeyPassword getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-011")
+    void getPrivateKeyPassword(){
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertEquals(PASSWORD, signature.getPrivateKeyPassword(), "Getter shall return the value of privateKeyPassword !");
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertFalse(signature.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertTrue(signature.equals(signature), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertFalse(signature.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different algorithm
+     */
+    @Test
+    void equalsDifferentAlgorithm() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        AsymmetricSignature signature2 = new AsymmetricSignature("ALGORITHM", KEY, PASSWORD);
+        assertFalse(signature.equals(signature2), "Objects shall not be equal if they have different algorithm !");
+    }
+
+    /**
+     * Test equals with different privateKey
+     */
+    @Test
+    void equalsDifferentPrivateKey() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        AsymmetricSignature signature2 = new AsymmetricSignature(ALGORITHM, "KEY".getBytes(), PASSWORD);
+        assertFalse(signature.equals(signature2), "Objects shall not be equal if they have different privateKey !");
+    }
+
+    /**
+     * Test equals with different privateKeyPassword
+     */
+    @Test
+    void equalsDifferentPrivateKeyPassword() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        AsymmetricSignature signature2 = new AsymmetricSignature(ALGORITHM, KEY, "PASSWORD");
+        assertFalse(signature.equals(signature2), "Objects shall not be equal if they have different privateKeyPassword !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        AsymmetricSignature signature = new AsymmetricSignature(ALGORITHM, KEY, PASSWORD);
+        assertNotNull(signature.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValueTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1125c461192b6d9bf167b0b3df242a2ccfa6c1bf
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/CodedValueTest.java
@@ -0,0 +1,52 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CodedValueTest {
+
+    private static final String CODE = "100000007";
+    private static final String CODE_SYSTEM = "2.16.840.1.113883.3.3478.6.1";
+    private static final String CODE_SYSTEM_NAME = "ACC-Internal";
+    private static final String DISPLAY_NAME = "Hematoma Size";
+
+    /**
+     * Test code getter.
+     */
+    @Test
+    void getCode(){
+        CodedValue codedValue = new CodedValue(CODE, CODE_SYSTEM);
+        assertEquals(CODE, codedValue.getCode(), "Getter shall return the value of code !");
+    }
+
+    /**
+     * Test codeSystem getter.
+     */
+    @Test
+    void getCodeSystem(){
+        CodedValue codedValue = new CodedValue(CODE, CODE_SYSTEM);
+        assertEquals(CODE_SYSTEM, codedValue.getCodeSystem(), "Getter shall return the value of codeSystem !");
+    }
+
+    /**
+     * Test codeSystemName getter and setter.
+     */
+    @Test
+    void codeSystemName(){
+        CodedValue codedValue = new CodedValue(CODE, CODE_SYSTEM);
+        codedValue.setCodeSystemName(CODE_SYSTEM_NAME);
+        assertEquals(CODE_SYSTEM_NAME, codedValue.getCodeSystemName());
+    }
+
+    /**
+     * Test displayName getter and setter.
+     */
+    @Test
+    void codeDisplayName(){
+        CodedValue codedValue = new CodedValue(CODE, CODE_SYSTEM);
+        codedValue.setDisplayName(DISPLAY_NAME);
+        assertEquals(DISPLAY_NAME, codedValue.getDisplayName());
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PasswordTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PasswordTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..67e84278a58ee28999f69e64f3e2f806c87db3e2
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PasswordTest.java
@@ -0,0 +1,68 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class PasswordTest {
+
+    private static final byte[] VALUE = "value".getBytes();
+
+    /**
+     * Test value getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-031")
+    void getKey() {
+        Password publicKey = new Password(VALUE);
+        assertArrayEquals(VALUE, publicKey.getValue(), "Getter shall return the value of value !");
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        Password publicKey = new Password(VALUE);
+        assertFalse(publicKey.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        Password publicKey = new Password(VALUE);
+        assertTrue(publicKey.equals(publicKey), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        Password publicKey = new Password(VALUE);
+        assertFalse(publicKey.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different issuer
+     */
+    @Test
+    void equalsDifferentIssuer() {
+        Password publicKey = new Password(VALUE);
+        Password publicKey2 = new Password("VALUE".getBytes());
+        assertFalse(publicKey.equals(publicKey2), "Objects shall not be equal if they have different issuer !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        Password publicKey = new Password(VALUE);
+        assertNotNull(publicKey.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKeyTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKeyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb7796fabb03236d4b6681024876cd31e3ae7e79
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/PublicKeyTest.java
@@ -0,0 +1,68 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class PublicKeyTest {
+
+    private static final byte[] KEY = "key".getBytes();
+
+    /**
+     * Test key getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-031")
+    void getKey() {
+        PublicKey publicKey = new PublicKey(KEY);
+        assertArrayEquals(KEY, publicKey.getKey(), "Getter shall return the value of key !");
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        PublicKey publicKey = new PublicKey(KEY);
+        assertFalse(publicKey.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        PublicKey publicKey = new PublicKey(KEY);
+        assertTrue(publicKey.equals(publicKey), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        PublicKey publicKey = new PublicKey(KEY);
+        assertFalse(publicKey.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different issuer
+     */
+    @Test
+    void equalsDifferentIssuer() {
+        PublicKey publicKey = new PublicKey(KEY);
+        PublicKey publicKey2 = new PublicKey("KEY".getBytes());
+        assertFalse(publicKey.equals(publicKey2), "Objects shall not be equal if they have different issuer !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        PublicKey publicKey = new PublicKey(KEY);
+        assertNotNull(publicKey.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SignatureTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SignatureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c69fe304d0f4b4d3bc252c020086b5c0632f964
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SignatureTest.java
@@ -0,0 +1,31 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SignatureTest {
+
+    private static final String ALGORITHM = "RSAPOUETPOUET";
+
+    /**
+     * Test algorithm getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-008")
+    void getAlgorithm(){
+        Signature signature = new Signature(ALGORITHM) {};
+        assertEquals(ALGORITHM, signature.getAlgorithm(), "Getter shall return the value of algorithm !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        Signature signature = new Signature(ALGORITHM) {};
+        assertNotNull(signature.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignatureTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignatureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d42a0631d338bc7eb018cbe436fd4810dada4bb
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/SymmetricSignatureTest.java
@@ -0,0 +1,82 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SymmetricSignatureTest {
+
+    private static final String ALGO = "RSAPOUETPOUET";
+    private static final String SECRET = "PSST";
+
+    /**
+     * Test privateKey getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-009")
+    void getPrivateKey() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        assertEquals(SECRET, signature.getSecret(), "Getter shall return the value of privateKey !");
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        assertFalse(signature.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        assertTrue(signature.equals(signature), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        assertFalse(signature.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different algorithm
+     */
+    @Test
+    void equalsDifferentAlgorithm() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        SymmetricSignature signature2 = new SymmetricSignature("ALGO", SECRET);
+        assertFalse(signature.equals(signature2), "Objects shall not be equal if they have different algorithm !");
+    }
+
+    /**
+     * Test equals with different secret
+     */
+    @Test
+    void equalsDifferentSecret() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        SymmetricSignature signature2 = new SymmetricSignature(ALGO, "SECRET");
+        assertFalse(signature.equals(signature2), "Objects shall not be equal if they have different secret !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        SymmetricSignature signature = new SymmetricSignature(ALGO, SECRET);
+        assertNotNull(signature.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUserTest.java b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b4ee79d9a2a0383410c9b2c3977c172ca1550c3
--- /dev/null
+++ b/access-token-provider-api/src/test/java/net/ihe/gazelle/app/accesstokenproviderapi/business/testuser/TestUserTest.java
@@ -0,0 +1,229 @@
+package net.ihe.gazelle.app.accesstokenproviderapi.business.testuser;
+
+import net.ihe.gazelle.lib.annotations.Covers;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class TestUserTest {
+
+    private static final String USER_ID = "id";
+    private static final String GIVEN_NAME = "given";
+    private static final String LAST_NAME = "lastName";
+    private static final Date BIRTH_DATE = new Date();
+    private static final String GENDER = "gender";
+
+    /**
+     * Test userId getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-023")
+    void getUserId() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertEquals(USER_ID, testUser.getUserId(), "Getter shall return the value of userId !");
+    }
+
+    /**
+     * Test givenNames getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-024")
+    void getGivenNames() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertEquals(givenNames, testUser.getGivenNames(), "Getter shall return the value of givenNames !");
+    }
+
+    /**
+     * Test lastName getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-025")
+    void getlastName() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertEquals(LAST_NAME, testUser.getLastName(), "Getter shall return the value of lastName !");
+    }
+
+    /**
+     * Test birthDate getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-026")
+    void birthDate() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.setBirthDate(BIRTH_DATE);
+        assertEquals(BIRTH_DATE, testUser.getBirthDate(), "Getter shall return the value of birthDate !");
+    }
+
+    /**
+     * Test birthDate getter.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-027")
+    void gender() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.setGender(GENDER);
+        assertEquals(GENDER, testUser.getGender(), "Getter shall return the value of gender !");
+    }
+
+    /**
+     * Test addExtension and removeExtension.
+     */
+    @Test
+    @Covers(requirements = "TOKENPROV-028")
+    void addExtension() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.addExtension("key1", "value1");
+        assertEquals(1, testUser.getExtensions().size());
+        testUser.addExtension("key2", "value2");
+        assertEquals(2, testUser.getExtensions().size());
+        testUser.removeExtension("key1");
+        assertEquals(1, testUser.getExtensions().size());
+        assertEquals("value2", testUser.getExtensions().get("key2"));
+    }
+
+    /**
+     * Test equals with null.
+     */
+    @Test
+    void equalsNull() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertFalse(testUser.equals(null), "Object shall not be equal to null !");
+    }
+
+    /**
+     * Test equals with itself.
+     */
+    @Test
+    void equalsItself() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertTrue(testUser.equals(testUser), "Object shall be equal to itself !");
+    }
+
+    /**
+     * Test equals with another class instance.
+     */
+    @Test
+    void equalsOtherClass() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertFalse(testUser.equals(34), "Object shall not be equal to an instance of another class !");
+    }
+
+    /**
+     * Test equals with different issuerId
+     */
+    @Test
+    void equalsDifferentUserID() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        TestUser testUser2 = new TestUser("USER_ID", givenNames, LAST_NAME);
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different issuerId !");
+    }
+
+    /**
+     * Test equals with different givenNames
+     */
+    @Test
+    void equalsDifferentGivenNames() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        List<String> givenNames2 = new ArrayList<>();
+        givenNames2.add("given2");
+        TestUser testUser2 = new TestUser(USER_ID, givenNames2, LAST_NAME);
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different givenNames !");
+    }
+
+
+    /**
+     * Test equals with different lastName
+     */
+    @Test
+    void equalsDifferentLastName() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        TestUser testUser2 = new TestUser(USER_ID, givenNames, "LAST_NAME");
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different lastName !");
+    }
+
+    /**
+     * Test equals with different birthName
+     */
+    @Test
+    void equalsDifferentBirthName() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.setBirthDate(BIRTH_DATE);
+        TestUser testUser2 = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser2.setBirthDate(new Date());
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different birthName !");
+    }
+
+    /**
+     * Test equals with different gender
+     */
+    @Test
+    void equalsDifferentGender() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.setGender(GENDER);
+        TestUser testUser2 = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.setGender("GENDER");
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different gender !");
+    }
+
+    /**
+     * Test equals with different extensions
+     */
+    @Test
+    void equalsDifferentExtensions() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser.addExtension("1", "1");
+        TestUser testUser2 = new TestUser(USER_ID, givenNames, LAST_NAME);
+        testUser2.addExtension("1", "1");
+        testUser2.addExtension("2", "2");
+        assertFalse(testUser.equals(testUser2), "Objects shall not be equal if they have different extensions !");
+    }
+
+    /**
+     * Test for the hashcode method
+     */
+    @Test
+    void testHashCode() {
+        List<String> givenNames = new ArrayList<>();
+        givenNames.add(GIVEN_NAME);
+        TestUser testUser = new TestUser(USER_ID, givenNames, LAST_NAME);
+        assertNotNull(testUser.hashCode(), "Generated hashCode shall not be null !");
+    }
+
+}
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt b/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt
deleted file mode 100644
index ddaf15eb40c83e30470f2655d8071850c8b5c610..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/adapter/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Put here test classes for adapter layer.
\ No newline at end of file
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt b/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt
deleted file mode 100644
index fe1a7edcdb8e895cbfdcbfd15031c15da130d006..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/application/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Put here test classes for application layer.
\ No newline at end of file
diff --git a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt b/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt
deleted file mode 100644
index 9ab90c5deabdf0ede124604e6bb6d00ed83e7b8c..0000000000000000000000000000000000000000
--- a/access-token-provider-api/src/test/java/net/ihe/gazelle/lib/net.ihe.gazelle.app.accesstokenproviderapi/business/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Put here test classes for business layer.
\ No newline at end of file
diff --git a/access-token-provider-api/src/test/resources/audience.properties b/access-token-provider-api/src/test/resources/audience.properties
new file mode 100644
index 0000000000000000000000000000000000000000..d77f01fec364a61f4d77be2e612d764d0f3757f2
--- /dev/null
+++ b/access-token-provider-api/src/test/resources/audience.properties
@@ -0,0 +1 @@
+audience:monpetitsecret
\ No newline at end of file
diff --git a/audience-retriever/pom.xml b/audience-retriever/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..df3b5e911c0a3c63d91769345822d9f8bcfb9d28
--- /dev/null
+++ b/audience-retriever/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>app.access-token-provider</artifactId>
+        <groupId>net.ihe.gazelle</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>net.ihe.gazelle</groupId>
+    <artifactId>app.audience-retriever</artifactId>
+    <name>Audience Retriever</name>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax</groupId>
+            <artifactId>javaee-api</artifactId>
+            <version>8.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>framework.logger-service</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <!-- Preferences API -->
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>framework.preferences-model-api</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <!-- Implementation of Operational Preferences -->
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>framework.operational-preferences-service</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapui.java b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapui.java
new file mode 100644
index 0000000000000000000000000000000000000000..544e903ac4ae75f960e84b0409355bbd04d6da78
--- /dev/null
+++ b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapui.java
@@ -0,0 +1,55 @@
+package net.ihe.gazelle.app.audienceretriever.adapter;
+
+import net.ihe.gazelle.app.audienceretriever.application.AudienceSecretRetriever;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLogger;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * {@link AudienceSecretRetriever} used by SoapUI project to retrieve secrets.
+ */
+public class AudienceSecretRetrieverForSoapui implements AudienceSecretRetriever {
+
+    private static final GazelleLogger LOGGER = GazelleLoggerFactory.getInstance().getLogger(AudienceSecretRetrieverForSoapui.class);
+
+    private String propertiesFile;
+
+
+    /**
+     * Constructor allowing to configure the properties file path.
+     *
+     * @param propertiesFile path to the properties file.
+     */
+    public AudienceSecretRetrieverForSoapui(String propertiesFile) {
+        this.propertiesFile = propertiesFile;
+    }
+
+    /**
+     * Read property file as {@link Properties}.
+     *
+     * @param filePath path to hte properties file.
+     * @return the {@link Properties} defined by the file.
+     */
+    private static Properties readPropertiesFile(String filePath) {
+        Properties prop = null;
+        try (FileInputStream fis = new FileInputStream(filePath)) {
+            prop = new Properties();
+            prop.load(fis);
+        } catch (IOException e) {
+            LOGGER.error("Error reading properties file !", e);
+        }
+        return prop;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String retrieveSecretForAudience(String audience) {
+        Properties prop = readPropertiesFile(propertiesFile);
+        return prop != null ? prop.getProperty(audience) : null;
+    }
+}
diff --git a/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImpl.java b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a906392190451b160708140268b04e372501cc0
--- /dev/null
+++ b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImpl.java
@@ -0,0 +1,54 @@
+package net.ihe.gazelle.app.audienceretriever.adapter;
+
+import net.ihe.gazelle.app.audienceretriever.application.AudienceSecretRetriever;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLogger;
+import net.ihe.gazelle.framework.loggerservice.application.GazelleLoggerFactory;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.NamespaceException;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.OperationalPreferencesService;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.PreferenceException;
+
+import javax.inject.Inject;
+
+/**
+ * AudienceSecretRetriever implementation
+ */
+public class AudienceSecretRetrieverImpl implements AudienceSecretRetriever {
+
+    private static final GazelleLogger LOGGER = GazelleLoggerFactory.getInstance().getLogger(AudienceSecretRetrieverImpl.class);
+    private static final String AUDIENCE_JNDI_NAMESPACE = "java:app/gazelle/chiua-validator-service/operational-preferences";
+
+    @Inject
+    private OperationalPreferencesService preferencesService;
+
+    /**
+     * {@inheritDoc}
+     */
+    public AudienceSecretRetrieverImpl() {
+        //Empty constructor for injection
+    }
+
+    /**
+     * Setter for the preferencesService property.
+     *
+     * @param preferencesService value to set to the property.
+     */
+    public void setPreferencesService(OperationalPreferencesService preferencesService) {
+        this.preferencesService = preferencesService;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String retrieveSecretForAudience(String audience) {
+        try {
+            return preferencesService.getStringValue(AUDIENCE_JNDI_NAMESPACE, audience);
+        } catch (NamespaceException e) {
+            LOGGER.warn(e, "The JNDI namespace is not configured for Audiences !");
+        } catch (PreferenceException e) {
+            LOGGER.warn(e, String.format("The Audience [%s] is not correctly defined  in namespace !", audience));
+
+        }
+        return null;
+    }
+}
diff --git a/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/application/AudienceSecretRetriever.java b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/application/AudienceSecretRetriever.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d0679fba5efee04c076372e9b51425e12d3ce21
--- /dev/null
+++ b/audience-retriever/src/main/java/net/ihe/gazelle/app/audienceretriever/application/AudienceSecretRetriever.java
@@ -0,0 +1,14 @@
+package net.ihe.gazelle.app.audienceretriever.application;
+
+/**
+ * classe to retrieve the audience secret
+ */
+public interface AudienceSecretRetriever {
+
+    /**
+     * retrieve secret linked to an audience
+     * @param audience the audience
+     * @return the secret
+     */
+    String retrieveSecretForAudience(String audience);
+}
diff --git a/audience-retriever/src/main/resources/META-INF/beans.xml b/audience-retriever/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d29f9c2aedd99b79a8f82a7f18d27124c2e62358
--- /dev/null
+++ b/audience-retriever/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans bean-discovery-mode="all" version="2.0"
+       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+                           http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd">
+</beans>
\ No newline at end of file
diff --git a/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapuiTest.java b/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapuiTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..367b104ce2e757277a64688f6c88b2e5840a5f19
--- /dev/null
+++ b/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverForSoapuiTest.java
@@ -0,0 +1,24 @@
+package net.ihe.gazelle.app.audienceretriever.adapter;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Class to test the retrieve of the audience secret
+ */
+class AudienceSecretRetrieverForSoapuiTest {
+
+    /**
+     * Test the property set in the retriever secret audience method
+     */
+    @Test
+    void retrieveSecretForAudienceWithPropertiesPathTest() {
+        AudienceSecretRetrieverForSoapui audienceSecretRetrieverForSoapui = new AudienceSecretRetrieverForSoapui("/opt/simulators/audience" +
+                ".properties");
+        assertNull(audienceSecretRetrieverForSoapui.retrieveSecretForAudience("monpetitsecret"));
+    }
+
+
+}
diff --git a/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImplTest.java b/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..59d76347c044c4e5a96e1d31653a0935df9dbc41
--- /dev/null
+++ b/audience-retriever/src/test/java/net/ihe/gazelle/app/audienceretriever/adapter/AudienceSecretRetrieverImplTest.java
@@ -0,0 +1,92 @@
+package net.ihe.gazelle.app.audienceretriever.adapter;
+
+import net.ihe.gazelle.framework.preferencesmodelapi.application.NamespaceException;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.OperationalPreferencesService;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.PreferenceException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AudienceSecretRetrieverImplTest {
+
+    AudienceSecretRetrieverImpl impl = new AudienceSecretRetrieverImpl();
+
+    @Test
+    public void defaultConstructorTest() {
+        AudienceSecretRetrieverImpl impl = new AudienceSecretRetrieverImpl();
+        assertNotNull(impl);
+    }
+
+    @Test
+    public void setPreferencesAndRetrieveAudienceTest() {
+        String myValue = "myValue";
+        OperationalPreferencesService service = new OperationalPreferencesService() {
+            @Override
+            public boolean getBooleanValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return false;
+            }
+
+            @Override
+            public int getIntegerValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return 0;
+            }
+
+            @Override
+            public String getStringValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return myValue;
+            }
+        };
+        impl.setPreferencesService(service);
+        assertEquals(myValue, impl.retrieveSecretForAudience(null));
+
+    }
+
+    @Test
+    public void setPreferencesAndRetrieveAudiencePreferenceExceptionTest() {
+        OperationalPreferencesService service = new OperationalPreferencesService() {
+            @Override
+            public boolean getBooleanValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return false;
+            }
+
+            @Override
+            public int getIntegerValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return 0;
+            }
+
+            @Override
+            public String getStringValue(String s, String s1) throws PreferenceException, NamespaceException {
+                throw new PreferenceException();
+            }
+        };
+        impl.setPreferencesService(service);
+        assertNull(impl.retrieveSecretForAudience(null));
+
+    }
+
+    @Test
+    public void setPreferencesAndRetrieveAudienceNamespaceExceptionTest() {
+        OperationalPreferencesService service = new OperationalPreferencesService() {
+            @Override
+            public boolean getBooleanValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return false;
+            }
+
+            @Override
+            public int getIntegerValue(String s, String s1) throws PreferenceException, NamespaceException {
+                return 0;
+            }
+
+            @Override
+            public String getStringValue(String s, String s1) throws PreferenceException, NamespaceException {
+                throw new NamespaceException();
+            }
+        };
+        impl.setPreferencesService(service);
+        assertNull(impl.retrieveSecretForAudience(null));
+
+    }
+
+
+
+}
diff --git a/access-token-provider-api/src/test/resources/README.txt b/audience-retriever/src/test/resources/README.txt
similarity index 100%
rename from access-token-provider-api/src/test/resources/README.txt
rename to audience-retriever/src/test/resources/README.txt
diff --git a/dummy-authorization-server-service/README.md b/dummy-authorization-server-service/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6a1469b7c385b6b7a0805a4806582e711a52a3f4
--- /dev/null
+++ b/dummy-authorization-server-service/README.md
@@ -0,0 +1,40 @@
+# Dummy Authorization Server Service
+
+This service mock an Access Token Provider, giving a mocked token using one of available user id 
+and a secret that can be configured depending on the used audience.
+
+## Request examples 
+
+```http://localhost:8780/authorization-server/mock-token?userId=aamrein&audienceId=audience```
+
+| Parameter Name  | Usage                                                             |
+|-------------|-----------------------------------------------------------------------|
+| userId      | User for whom the token is generated                                  |
+| audienceId  | ID of the audience used to retrieve secret in Gazelle configurations. |
+
+The response body to this request will be the content of the generated token.
+
+## Available user ID
+
+| User ID  |
+|----------|
+| aamrein  |
+| aerne    |
+
+## Install the tool
+
+Sources are available [here](https://gitlab.inria.fr/gazelle/applications/test-execution/simulator/access-token-provider)
+
+Deploy the `app.dummy-authorization-server-service-X.X.X.war` artifact in a wildfly 18 server. 
+No specific wildfly configuration is needed for the tool to work.
+
+## Configure Audiences
+
+Edit the file `/opt/simulators/audience.properties`, add a property for each audience that needs to be configured :
+
+```
+audience1=secret1
+audience2=secret2
+audience3=secret3
+...
+```
\ No newline at end of file
diff --git a/dummy-authorization-server-service/pom.xml b/dummy-authorization-server-service/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b494a152864051db6b8c3bd11ccbbf2a36f97bc5
--- /dev/null
+++ b/dummy-authorization-server-service/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>app.access-token-provider</artifactId>
+        <groupId>net.ihe.gazelle</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>net.ihe.gazelle</groupId>
+    <artifactId>app.dummy-authorization-server-service</artifactId>
+    <name>Dummy Authorization Server Service</name>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>war</packaging>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>javax</groupId>
+            <artifactId>javaee-api</artifactId>
+            <version>8.0.1</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.ihe.gazelle</groupId>
+            <artifactId>app.access-token-provider-api</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerService.java b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..a445982f1222feb810d2f21a74def8affcfb7fa7
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerService.java
@@ -0,0 +1,52 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+
+import net.ihe.gazelle.app.accesstokenproviderapi.application.DummyAuthzServer;
+import net.ihe.gazelle.app.accesstokenproviderapi.application.DummyAuthzServerSoapui;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Service for Mock Access Token Provider.
+ */
+@Provider
+@Path(value = "/mock-token")
+public class AuthorizationServerService {
+
+    private DummyAuthzServer dummyAuthzServer;
+
+    /**
+     * Default constructor for the class.
+     */
+    public AuthorizationServerService() {
+        dummyAuthzServer = new DummyAuthzServerSoapui();
+    }
+
+    /**
+     * Setter for the dummyAuthzServer property.
+     *
+     * @param dummyAuthzServer value to set to the property.
+     */
+    public void setDummyAuthzServer(DummyAuthzServer dummyAuthzServer) {
+        this.dummyAuthzServer = dummyAuthzServer;
+    }
+
+    /**
+     * get a dummy access token
+     *
+     * @param userId
+     * @param audienceId
+     * @param purposeOfUse
+     * @param resourceId
+     * @return an access token
+     */
+    @GET
+    public byte[] getAccessToken(@QueryParam("userId") String userId, @QueryParam("audienceId") String audienceId,
+                                 @QueryParam("purposeOfUse") String purposeOfUse, @QueryParam("resourceId") String resourceId) {
+        return dummyAuthzServer.getAccessToken(userId, audienceId, purposeOfUse, resourceId);
+    }
+
+}
diff --git a/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplication.java b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..90f6d456b3bdaf8c7aba3e1ca966b1388d6c72a4
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplication.java
@@ -0,0 +1,23 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Our Application
+ */
+@ApplicationPath("/")
+public class DummyAuthorizationServerServiceApplication extends Application {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> s = new HashSet<>();
+        s.add(AuthorizationServerService.class);
+        return s;
+    }
+}
diff --git a/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummy.java b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummy.java
new file mode 100644
index 0000000000000000000000000000000000000000..34a7f6799fda60e08e126f2f99a1554a6f2a3d96
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummy.java
@@ -0,0 +1,21 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+import net.ihe.gazelle.framework.preferencesmodelapi.application.OperationalPreferencesClientApplication;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Define mandatory preferences.
+ */
+public class OperationalPreferencesDummy implements OperationalPreferencesClientApplication {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Map<String, List<String>> wantedMandatoryPreferences() {
+        return new HashMap<>();
+    }
+}
diff --git a/dummy-authorization-server-service/src/main/resources/META-INF/beans.xml b/dummy-authorization-server-service/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d29f9c2aedd99b79a8f82a7f18d27124c2e62358
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans bean-discovery-mode="all" version="2.0"
+       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+                           http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd">
+</beans>
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/main/resources/soapui/DummyAccessTokenProvider.xml b/dummy-authorization-server-service/src/main/resources/soapui/DummyAccessTokenProvider.xml
new file mode 100644
index 0000000000000000000000000000000000000000..93b4d01d9ac9641a639488d9e08c99a63e7a4945
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/resources/soapui/DummyAccessTokenProvider.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<con:soapui-project id="511bc175-43ea-4daa-b8b0-8135770e4931" activeEnvironment="Default" name="REST Project 1" resourceRoot="" soapui-version="5.5.0"
+                    xmlns:con="http://eviware.com/soapui/config">
+    <con:settings/>
+    <con:interface xsi:type="con:RestService" id="a83dfcbb-6422-4e5a-a181-094eaf6314b0" wadlVersion="http://wadl.dev.java.net/2009/02"
+                   name="http://localhost:8780" type="rest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <con:settings/>
+        <con:definitionCache type="TEXT" rootPart=""/>
+        <con:endpoints>
+            <con:endpoint>http://localhost:8780</con:endpoint>
+        </con:endpoints>
+        <con:resource name="Mock-token" path="/authorization-server/mock-token" id="7270205e-e39c-4360-84e7-e29e05aad49b">
+            <con:settings/>
+            <con:parameters>
+                <con:parameter>
+                    <con:name>userId</con:name>
+                    <con:value/>
+                    <con:style>QUERY</con:style>
+                    <con:default/>
+                    <con:description xsi:nil="true"/>
+                </con:parameter>
+                <con:parameter>
+                    <con:name>audienceId</con:name>
+                    <con:value/>
+                    <con:style>QUERY</con:style>
+                    <con:default/>
+                    <con:description xsi:nil="true"/>
+                </con:parameter>
+                <con:parameter>
+                    <con:name>purposeOfUse</con:name>
+                    <con:value/>
+                    <con:style>QUERY</con:style>
+                    <con:default/>
+                    <con:description xsi:nil="true"/>
+                </con:parameter>
+                <con:parameter>
+                    <con:name>resourceId</con:name>
+                    <con:value/>
+                    <con:style>QUERY</con:style>
+                    <con:default/>
+                    <con:description xsi:nil="true"/>
+                </con:parameter>
+            </con:parameters>
+            <con:method name="Mock-token 1" id="abfda5a1-8402-4e07-af8d-89b02e4d0c72" method="GET">
+                <con:settings/>
+                <con:parameters/>
+                <con:representation type="RESPONSE">
+                    <con:mediaType>application/octet-stream</con:mediaType>
+                    <con:status>200</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>0</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>0</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>0</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="FAULT">
+                    <con:mediaType>text/html</con:mediaType>
+                    <con:status>404</con:status>
+                    <con:params/>
+                    <con:element>html</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>0</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>0</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:representation type="RESPONSE">
+                    <con:mediaType xsi:nil="true"/>
+                    <con:status>204</con:status>
+                    <con:params/>
+                    <con:element>data</con:element>
+                </con:representation>
+                <con:request name="Request 1" id="1039bbd9-95cf-452e-ad96-4cfb23827782" mediaType="application/json">
+                    <con:settings/>
+                    <con:endpoint>http://localhost:8780</con:endpoint>
+                    <con:request/>
+                    <con:credentials>
+                        <con:authType>No Authorization</con:authType>
+                    </con:credentials>
+                    <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
+                    <con:jmsPropertyConfig/>
+                    <con:parameters>
+                        <con:entry key="resourceId" value="12"/>
+                        <con:entry key="purposeOfUse" value="test"/>
+                        <con:entry key="audienceId" value="audience"/>
+                        <con:entry key="userId" value="test"/>
+                    </con:parameters>
+                    <con:parameterOrder>
+                        <con:entry>userId</con:entry>
+                        <con:entry>audienceId</con:entry>
+                        <con:entry>purposeOfUse</con:entry>
+                        <con:entry>resourceId</con:entry>
+                    </con:parameterOrder>
+                </con:request>
+            </con:method>
+        </con:resource>
+    </con:interface>
+    <con:testSuite id="99a0a2d5-1bf9-4cd6-a66d-29830e75aef8" name="Test Mock Token Provider">
+        <con:settings/>
+        <con:runType>SEQUENTIAL</con:runType>
+        <con:testCase id="f4ab98d6-051e-4e34-9dca-7aac5409f375" failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0"
+                      name="Request" searchProperties="true">
+            <con:settings/>
+            <con:testStep type="restrequest" name="Valid request" id="fa834a0a-6dc7-4d84-a15a-f13224626c4e">
+                <con:settings/>
+                <con:config service="http://localhost:8780" resourcePath="/authorization-server/mock-token" methodName="Mock-token 1"
+                            xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+                    <con:restRequest name="Mock-token 1 - Request 1" id="1039bbd9-95cf-452e-ad96-4cfb23827782" mediaType="application/json">
+                        <con:settings>
+                            <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting>
+                        </con:settings>
+                        <con:endpoint>http://localhost:8780</con:endpoint>
+                        <con:request/>
+                        <con:originalUri>http://localhost/authorization-server/mock-token</con:originalUri>
+                        <con:assertion type="Simple Contains" id="eec24b99-0b19-4a1e-93f7-d791268e2f6d" name="Contains">
+                            <con:configuration>
+                                <token>.+\..+\..+</token>
+                                <ignoreCase>false</ignoreCase>
+                                <useRegEx>true</useRegEx>
+                            </con:configuration>
+                        </con:assertion>
+                        <con:credentials>
+                            <con:authType>No Authorization</con:authType>
+                        </con:credentials>
+                        <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
+                        <con:jmsPropertyConfig/>
+                        <con:parameters>
+                            <con:entry key="audienceId" value="audience"/>
+                            <con:entry key="userId" value="aamrein"/>
+                        </con:parameters>
+                        <con:parameterOrder>
+                            <con:entry>userId</con:entry>
+                            <con:entry>audienceId</con:entry>
+                            <con:entry>purposeOfUse</con:entry>
+                            <con:entry>resourceId</con:entry>
+                        </con:parameterOrder>
+                    </con:restRequest>
+                </con:config>
+            </con:testStep>
+            <con:properties/>
+        </con:testCase>
+        <con:testCase id="81ffbff9-1a6a-4389-951a-3f8ed143f4e8" failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0"
+                      name="Unknown UserID" searchProperties="true">
+            <con:settings/>
+            <con:testStep type="restrequest" name="Unknown userID" id="0821bbbe-9ed6-490b-88f5-c46be620eae6">
+                <con:settings/>
+                <con:config service="http://localhost:8780" resourcePath="/authorization-server/mock-token" methodName="Mock-token 1"
+                            xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+                    <con:restRequest name="Unknown userID" id="1039bbd9-95cf-452e-ad96-4cfb23827782" mediaType="application/json">
+                        <con:settings>
+                            <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting>
+                        </con:settings>
+                        <con:endpoint>http://localhost:8780</con:endpoint>
+                        <con:request/>
+                        <con:originalUri>http://localhost/authorization-server/mock-token</con:originalUri>
+                        <con:assertion type="Valid HTTP Status Codes" id="60be561e-c058-42cd-882f-0e60e9226c65" name="Valid HTTP Status Codes">
+                            <con:configuration>
+                                <codes>204</codes>
+                            </con:configuration>
+                        </con:assertion>
+                        <con:credentials>
+                            <con:authType>No Authorization</con:authType>
+                        </con:credentials>
+                        <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
+                        <con:jmsPropertyConfig/>
+                        <con:parameters>
+                            <con:entry key="audienceId" value="audience43"/>
+                            <con:entry key="userId" value="aamrein"/>
+                        </con:parameters>
+                        <con:parameterOrder>
+                            <con:entry>userId</con:entry>
+                            <con:entry>audienceId</con:entry>
+                            <con:entry>purposeOfUse</con:entry>
+                            <con:entry>resourceId</con:entry>
+                        </con:parameterOrder>
+                    </con:restRequest>
+                </con:config>
+            </con:testStep>
+            <con:properties/>
+        </con:testCase>
+        <con:testCase id="0b0c480f-8389-40e2-83bf-1adfad6f874f" failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0"
+                      name="Unknown Audience" searchProperties="true">
+            <con:settings/>
+            <con:testStep type="restrequest" name="Unknown audience" id="b3d2c1e7-36a2-43ad-8056-5ff54b75677b">
+                <con:settings/>
+                <con:config service="http://localhost:8780" resourcePath="/authorization-server/mock-token" methodName="Mock-token 1"
+                            xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+                    <con:restRequest name="Unknown audience" id="1039bbd9-95cf-452e-ad96-4cfb23827782" mediaType="application/json">
+                        <con:settings>
+                            <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting>
+                        </con:settings>
+                        <con:endpoint>http://localhost:8780</con:endpoint>
+                        <con:request/>
+                        <con:originalUri>http://localhost/authorization-server/mock-token</con:originalUri>
+                        <con:assertion type="Valid HTTP Status Codes" id="60be561e-c058-42cd-882f-0e60e9226c65" name="Valid HTTP Status Codes">
+                            <con:configuration>
+                                <codes>204</codes>
+                            </con:configuration>
+                        </con:assertion>
+                        <con:credentials>
+                            <con:authType>No Authorization</con:authType>
+                        </con:credentials>
+                        <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
+                        <con:jmsPropertyConfig/>
+                        <con:parameters>
+                            <con:entry key="audienceId" value="audience"/>
+                            <con:entry key="userId" value="wbars"/>
+                        </con:parameters>
+                        <con:parameterOrder>
+                            <con:entry>userId</con:entry>
+                            <con:entry>audienceId</con:entry>
+                            <con:entry>purposeOfUse</con:entry>
+                            <con:entry>resourceId</con:entry>
+                        </con:parameterOrder>
+                    </con:restRequest>
+                </con:config>
+            </con:testStep>
+            <con:properties/>
+        </con:testCase>
+        <con:properties/>
+    </con:testSuite>
+    <con:properties/>
+    <con:wssContainer/>
+    <con:oAuth2ProfileContainer/>
+    <con:oAuth1ProfileContainer/>
+</con:soapui-project>
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/main/webapp/WEB-INF/jboss-web.xml b/dummy-authorization-server-service/src/main/webapp/WEB-INF/jboss-web.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b832b6ea657c580d51bc40526ad9c4dbf3e568f5
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/webapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jboss-web>
+    <context-root>authorization-server</context-root>
+</jboss-web>
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/main/webapp/WEB-INF/web.xml b/dummy-authorization-server-service/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e84277a95f80e2ed59f5518458b90b473057e968
--- /dev/null
+++ b/dummy-authorization-server-service/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+
+</web-app>
diff --git a/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerServiceTest.java b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0d55465d28b9e78a1a6c869c774169e3824f22c
--- /dev/null
+++ b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/AuthorizationServerServiceTest.java
@@ -0,0 +1,29 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+import net.ihe.gazelle.app.accesstokenproviderapi.application.DummyAuthzServerSoapui;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Tests for {@link AuthorizationServerService}
+ */
+class AuthorizationServerServiceTest {
+
+    private static final String SUBJECT = "aamrein";
+    private static final String AUDIENCE = "audience";
+
+    /**
+     * Test the generation of a token
+     */
+    @Test
+    public void getAccessToken() {
+        AuthorizationServerService authorizationServerService = new AuthorizationServerService();
+        DummyAuthzServerSoapui dummyAuthzServer = new DummyAuthzServerSoapui();
+        dummyAuthzServer.setAudienceSecretRetriever((String audience) -> "myBeautifulKeyWhichIsAJWTSecretSoSecret");
+        authorizationServerService.setDummyAuthzServer(dummyAuthzServer);
+
+        assertNotNull(authorizationServerService.getAccessToken(SUBJECT, AUDIENCE, null, null),
+                "Provided access token shall not be null !");
+    }
+}
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplicationTest.java b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplicationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1abdbf9f2e380298181c80658ac2a8ab3cd33f52
--- /dev/null
+++ b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/DummyAuthorizationServerServiceApplicationTest.java
@@ -0,0 +1,24 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class DummyAuthorizationServerServiceApplicationTest {
+
+    /**
+     * Test class getter.
+     */
+    @Test
+    void getClasses() {
+        DummyAuthorizationServerServiceApplication application = new DummyAuthorizationServerServiceApplication();
+
+        Set classes = application.getClasses();
+
+        assertNotNull(classes, "Classes map shall not be null !");
+        assertEquals(1, classes.size(), "Classes map shall contain a single element !");
+    }
+}
\ No newline at end of file
diff --git a/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummyTest.java b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bd30b4de8262009eedfc15b106f13c970ccc9a7
--- /dev/null
+++ b/dummy-authorization-server-service/src/test/java/net/ihe/gazelle/app/dummyauthorizationserverservice/adapter/OperationalPreferencesDummyTest.java
@@ -0,0 +1,25 @@
+package net.ihe.gazelle.app.dummyauthorizationserverservice.adapter;
+
+import net.ihe.gazelle.framework.preferencesmodelapi.application.OperationalPreferencesClientApplication;
+import net.ihe.gazelle.framework.preferencesmodelapi.application.OperationalPreferencesService;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OperationalPreferencesDummyTest {
+
+    /**
+     * Test wanted mandatroy preferences list.
+     */
+    @Test
+    void wantedMandatoryPreferences() {
+        OperationalPreferencesClientApplication operationalPreferencesService = new OperationalPreferencesDummy();
+
+        Map wantedMandatoryPreferences = operationalPreferencesService.wantedMandatoryPreferences();
+
+        assertNotNull(wantedMandatoryPreferences, "Wanted Mandatory Preferences map shall not be null !");
+        assertEquals(0, wantedMandatoryPreferences.entrySet().size(), "Wanted Mandatory Preferences map shall be empty");
+    }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 3218089b7a5615ff9bf56668ec3cf6c951bab5ac..88a3a45e97ca83d2883fd93549cd6351c8e8c355 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>net.ihe.gazelle</groupId>
@@ -11,9 +12,9 @@
     <url>http://gazelle.ihe.net</url>
 
     <scm>
-        <connection>scm:git:git@gitlab.inria.fr:gazelle/applications/test-execution/simulator/access-token-provider.git</connection>
-        <url>scm:git:git@gitlab.inria.fr:gazelle/applications/test-execution/simulator/access-token-provider.git</url>
-        <developerConnection>scm:git:git@gitlab.inria.fr:gazelle/applications/test-execution/simulator/access-token-provider.git</developerConnection>
+        <connection>${git.project.url}</connection>
+        <url>${git.project.url}</url>
+        <developerConnection>${git.project.url}</developerConnection>
         <tag>HEAD</tag>
     </scm>
 
@@ -27,6 +28,11 @@
         <cvss.score.level.tolerate>8</cvss.score.level.tolerate>
         <nexus.url>https://gazelle.ihe.net/nexus</nexus.url>
         <nexus.path>/content/groups/public/</nexus.path>
+        <git.user.name>git</git.user.name>
+        <git.user.token>git</git.user.token>
+        <git.project.url>
+            scm:${git.user.name}:${git.user.token}@gitlab.inria.fr/gazelle/applications/test-execution/simulator/access-token-provider.git
+        </git.project.url>
     </properties>
 
     <repositories>
@@ -172,6 +178,17 @@
         </profile>
     </profiles>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>net.ihe.gazelle</groupId>
+                <artifactId>app.audience-retriever</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+
     <dependencies>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
@@ -181,7 +198,9 @@
         </dependency>
     </dependencies>
 
-  <modules>
-    <module>access-token-provider-api</module>
+    <modules>
+        <module>access-token-provider-api</module>
+        <module>audience-retriever</module>
+      <module>dummy-authorization-server-service</module>
   </modules>
 </project>
\ No newline at end of file