This is an automated email from the ASF dual-hosted git repository.

aleks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new c74bfed  FINERACT-1012: Upgrading OAuth support to Spring Security 5.6
c74bfed is described below

commit c74bfed1fe4d3f7b024005e3fe30e30b455d1475
Author: Petri Tuomola <[email protected]>
AuthorDate: Sun Dec 26 15:42:35 2021 +0800

    FINERACT-1012: Upgrading OAuth support to Spring Security 5.6
---
 .../{build-twofactor.yml => build-oauth2.yml}      |  16 +-
 .github/workflows/build-twofactor.yml              |   6 +-
 .github/workflows/build.yml                        |   6 +-
 .gitignore                                         |   1 +
 build.gradle                                       |   2 +-
 fineract-doc/src/docs/en/03_oauth.adoc             |  49 +++--
 fineract-provider/dependencies.gradle              |   2 +-
 .../properties/oauth/application.properties        |   3 +
 .../oauth/twofactor/application.properties         |   3 +
 .../core/boot/WebXmlOauthConfiguration.java        |  44 ----
 .../core/config/OAuth2SecurityConfig.java          | 145 +++++++++++++
 .../exceptionmapper/OAuth2ExceptionEntryPoint.java |  46 +++++
 .../security/api/UserDetailsApiResource.java       | 105 +++++-----
 .../data/FineractJwtAuthenticationToken.java       |  42 ++++
 .../InsecureTwoFactorAuthenticationFilter.java     |  27 ++-
 .../filter/TenantAwareTenantIdentifierFilter.java  |   2 +-
 .../filter/TwoFactorAuthenticationFilter.java      |  31 +--
 .../security/api/SelfUserDetailsApiResource.java   |   7 +-
 .../resources/META-INF/spring/securityContext.xml  | 167 ---------------
 .../src/main/resources/static/api-docs/apiLive.htm | 225 +--------------------
 oauth2-tests/build.gradle                          |  67 ++++++
 .../dependencies.gradle                            |  24 ++-
 .../oauth2tests/OAuth2AuthenticationTest.java      | 176 ++++++++++++++++
 settings.gradle                                    |   1 +
 24 files changed, 655 insertions(+), 542 deletions(-)

diff --git a/.github/workflows/build-twofactor.yml 
b/.github/workflows/build-oauth2.yml
similarity index 66%
copy from .github/workflows/build-twofactor.yml
copy to .github/workflows/build-oauth2.yml
index 9e55d9f..7b715fe 100644
--- a/.github/workflows/build-twofactor.yml
+++ b/.github/workflows/build-oauth2.yml
@@ -1,4 +1,4 @@
-name: Fineract Gradle build - twofactor
+name: Fineract Gradle build - oauth2
 on: [push, pull_request]
 
 jobs:
@@ -14,6 +14,14 @@ jobs:
                 MARIADB_ROOT_PASSWORD: mysql
             options: --health-cmd="mysqladmin ping" --health-interval=5s 
--health-timeout=2s --health-retries=3
 
+        mock-oauth2-server:
+            image: ghcr.io/navikt/mock-oauth2-server:0.4.0
+            ports:
+                - 9000:9000
+            env:
+                SERVER_PORT: 9000
+                JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": 
"NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", 
"tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": 
"fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
+
     env:
         TZ: Asia/Kolkata
     steps:
@@ -23,9 +31,9 @@ jobs:
           path: |
             ~/.gradle/caches
             ~/.gradle/wrapper
-          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
+          key: ${{ runner.os }}-gradle-oauth2-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
           restore-keys: |
-            ${{ runner.os }}-gradle-
+            ${{ runner.os }}-gradle-oauth2-
       - name: Checkout
         uses: actions/checkout@v2
       - name: Set up JDK 11
@@ -49,4 +57,4 @@ jobs:
             sudo apt-get update
             sudo apt-get install ghostscript -y
       - name: Build & Test
-        run: ./gradlew --no-daemon -q --console=plain build test --fail-fast 
-x :integration-tests:test -Ptwofactor=enabled
+        run: ./gradlew --no-daemon -q --console=plain build test --fail-fast 
-x :integration-tests:test -x :twofactor-tests:test -Psecurity=oauth
diff --git a/.github/workflows/build-twofactor.yml 
b/.github/workflows/build-twofactor.yml
index 9e55d9f..aac7141 100644
--- a/.github/workflows/build-twofactor.yml
+++ b/.github/workflows/build-twofactor.yml
@@ -23,9 +23,9 @@ jobs:
           path: |
             ~/.gradle/caches
             ~/.gradle/wrapper
-          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
+          key: ${{ runner.os }}-gradle-twofactor-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
           restore-keys: |
-            ${{ runner.os }}-gradle-
+            ${{ runner.os }}-gradle-twofactor-
       - name: Checkout
         uses: actions/checkout@v2
       - name: Set up JDK 11
@@ -49,4 +49,4 @@ jobs:
             sudo apt-get update
             sudo apt-get install ghostscript -y
       - name: Build & Test
-        run: ./gradlew --no-daemon -q --console=plain build test --fail-fast 
-x :integration-tests:test -Ptwofactor=enabled
+        run: ./gradlew --no-daemon -q --console=plain build test --fail-fast 
-x :integration-tests:test -x :oauth2-tests:test -Ptwofactor=enabled
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 908d030..9e89ac6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,9 +22,9 @@ jobs:
           path: |
             ~/.gradle/caches
             ~/.gradle/wrapper
-          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
+          key: ${{ runner.os }}-gradle-basicauth-${{ hashFiles('**/*.gradle*', 
'**/gradle-wrapper.properties') }}
           restore-keys: |
-            ${{ runner.os }}-gradle-
+            ${{ runner.os }}-gradle-basicauth-
       - name: Checkout
         uses: actions/checkout@v2
       - name: Set up JDK 11
@@ -48,4 +48,4 @@ jobs:
             sudo apt-get update
             sudo apt-get install ghostscript -y
       - name: Build & Test
-        run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest 
check build test --fail-fast doc -x :twofactor-tests:test
+        run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest 
check build test --fail-fast doc -x :twofactor-tests:test -x :oauth2-test:test
diff --git a/.gitignore b/.gitignore
index b62b52d..e56ba7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ build
 *.iml
 *.ipr
 *.iws
+*.swp
 *.DS_Store
 .idea
 .vscode
diff --git a/build.gradle b/build.gradle
index 120046e..25530a8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,6 +29,7 @@ buildscript {
                 'fineract-provider',
                 'integration-tests',
                 'twofactor-tests',
+                'oauth2-tests',
                 'fineract-client'
             ].contains(it.name)
         }
@@ -111,7 +112,6 @@ allprojects  {
             // We do not use :+ to get the latest available version available 
on Maven Central, as that could suddenly break things.
             // We use the Renovate Bot to automatically propose Pull Requests 
(PRs) when upgrades for all of these versions are available.
 
-            dependency 
'org.springframework.security.oauth:spring-security-oauth2:2.5.1.RELEASE'
             dependency 'org.apache.openjpa:openjpa:3.2.0' // when upgrading, 
also change OpenJPA version repeated above in buildscript!
             dependency 'com.google.guava:guava:31.0.1-jre'
             dependency 'com.google.code.gson:gson:2.8.9'
diff --git a/fineract-doc/src/docs/en/03_oauth.adoc 
b/fineract-doc/src/docs/en/03_oauth.adoc
index fec07da..933b719 100644
--- a/fineract-doc/src/docs/en/03_oauth.adoc
+++ b/fineract-doc/src/docs/en/03_oauth.adoc
@@ -1,41 +1,66 @@
 == OAuth
 
-Fineract has (basic) OAuth (2.0?) support. Here's how to use it:
+Fineract has a (basic) OAuth2 support based on Spring Boot Security. Here's 
how to use it:
 
 === Build
 
 You must re-build the distribution JAR (or WAR) using the special 
`-Psecurity=oauth` flag:
 
 ----
-./gradlew bootJAR -Psecurity=oauth
-java -jar build/libs/fineract-provider.jar
+./gradlew bootRun -Psecurity=oauth
 ----
 
 Downloads from https://fineract.apache.org, or using e.g. the 
https://hub.docker.com/r/apache/fineract container image, or on 
https://www.fineract.dev, this will not work, because they have not been built 
using this flag.
 
-=== Invoke `/fineract-provider/api/oauth/token`
+Previous versions of Fineract included a built-in authorisation server for 
issuing OAuth tokens. However, as the spring-security-oauth2 package was 
deprecated and replaced by built-in OAuth support in Spring Security, this is 
no longer supported as part of the package. Instead, you need to run a separate 
OAuth authorization server (e.g. 
https://github.com/spring-projects/spring-authorization-server) or use a 
3rd-party OAuth authorization provider 
(https://en.wikipedia.org/wiki/List_of_OA [...]
+
+This instruction describes how to get Fineract OAuth working with a Keycloak 
(http://keycloak.org) based authentication provider running in a Docker 
container. The steps required for other OAuth providers will be similar. 
+
+=== Set up Keycloak
+
+1. From terminal, run: 'docker run -p 9000:8080 -e KEYCLOAK_USER=admin -e 
KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2'
+1. Go to URL 'http://localhost:9000/auth/admin' and login with admin/admin
+1. Hover your mouse over text "Master" and click on "Add realm"
+1. Enter name "fineract" for your realm
+1. Click on tab "Users" on the left, then "Add user" and create user with 
username "mifos" 
+1. Click on tab "Credentials" at the top, and set password to "password", 
turning "temporary" setting to off
+1. Click on tab "Clients" on the left, and create client with ID 
'community-app'
+1. In settings tab, set 'access-type' to 'confidential' and enter 'localhost' 
in the valid redirect URIs. 
+1. In credentials tab, copy string in field 'secret' as this will be needed in 
the step to request the access token
+
+Finally we need to change Keycloak configuration so that it uses the username 
as a subject of the token:
+
+1. Choose client 'community-app' in the tab 'Clients'
+1. Go to tab 'Mappers' and click on 'Create'
+1. Enter 'usernameInSub' as 'Name'
+1. Choose mapper type 'User Property'
+1. Enter 'username' into the field 'Property' and 'sub' into the field 'Token 
Claim Name'. Choose 'String' as 'Claim JSON Type'
+
+You are now ready to test out OAuth:
+
+=== Retrieve an access token from Keycloak
 
 ----
-curl [--insecure] --location --request POST \
-'https://localhost:8443/fineract-provider/api/oauth/token' \
---header 'Fineract-Platform-TenantId: default' \
+curl --location --request POST \
+'http://localhost:9000/auth/realms/fineract/protocol/openid-connect/token' \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'username=mifos' \
 --data-urlencode 'password=password' \
 --data-urlencode 'client_id=community-app' \
 --data-urlencode 'grant_type=password' \
---data-urlencode 'client_secret=123'
+--data-urlencode 'client_secret=<enter the client secret from credentials tab>'
 ----
 
-Note that the `client_id` and `client_secret` are stored in the 
`oauth_client_details` table in the database.
+The reply should contain a field 'access_token'. Copy the field's value and 
use it in the API call below:
 
 === Invoke APIs and pass `Authorization: bearer ...` header
 
 ----
 curl --location --request GET \
-'https://localhost:8443/fineract-provider/api/v1/clients' \
+'https://localhost:8443/fineract-provider/api/v1/offices' \
 --header 'Fineract-Platform-TenantId: default' \
---header 'Authorization: bearer RzfUyQ0wEnxxq4PyFCF1J-XGFCI'
+--header 'Authorization: bearer <enter the value of the access_token field>'
+
 ----
 
-NOTE: See also 
https://demo.fineract.dev/fineract-provider/api-docs/apiLive.htm#authentication_oauth
\ No newline at end of file
+NOTE: See also 
https://demo.fineract.dev/fineract-provider/api-docs/apiLive.htm#authentication_oauth
diff --git a/fineract-provider/dependencies.gradle 
b/fineract-provider/dependencies.gradle
index 3378a29..393562f 100644
--- a/fineract-provider/dependencies.gradle
+++ b/fineract-provider/dependencies.gradle
@@ -30,12 +30,12 @@ dependencies {
             'org.springframework.boot:spring-boot-starter-web',
             'org.springframework.boot:spring-boot-starter-security',
             'org.springframework.boot:spring-boot-starter-cache',
+            
'org.springframework.boot:spring-boot-starter-oauth2-resource-server',
 
             'org.glassfish.jersey.media:jersey-media-multipart:2.35',
 
             'org.springframework:spring-jms',
             'org.springframework:spring-context-support',
-            'org.springframework.security.oauth:spring-security-oauth2',
 
             'com.google.guava:guava',
             'com.google.code.gson:gson',
diff --git a/fineract-provider/properties/oauth/application.properties 
b/fineract-provider/properties/oauth/application.properties
index 057b361..6936db5 100644
--- a/fineract-provider/properties/oauth/application.properties
+++ b/fineract-provider/properties/oauth/application.properties
@@ -32,3 +32,6 @@ management.endpoints.web.exposure.include=health,info
 
 # FINERACT-914
 server.forward-headers-strategy=framework
+
+# OAuth authorisation server endpoint
+spring.security.oauth2.resourceserver.jwt.issuer-uri: 
http://localhost:9000/auth/realms/fineract
diff --git 
a/fineract-provider/properties/oauth/twofactor/application.properties 
b/fineract-provider/properties/oauth/twofactor/application.properties
index dc84c31..3a2e01d 100644
--- a/fineract-provider/properties/oauth/twofactor/application.properties
+++ b/fineract-provider/properties/oauth/twofactor/application.properties
@@ -32,3 +32,6 @@ management.endpoints.web.exposure.include=health,info
 
 # FINERACT-914
 server.forward-headers-strategy=framework
+
+# OAuth authorisation server endpoint
+spring.security.oauth2.resourceserver.jwt.issuer-uri: 
http://localhost:9000/auth/realms/fineract
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebXmlOauthConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebXmlOauthConfiguration.java
deleted file mode 100644
index 7b67579..0000000
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebXmlOauthConfiguration.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.fineract.infrastructure.core.boot;
-
-import org.springframework.boot.web.servlet.ServletRegistrationBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Profile;
-import org.springframework.web.servlet.DispatcherServlet;
-
-/**
- * This Configuration replaces what formerly was in web.xml. Beans are loaded 
only when "oauth" Profile is enabled.
- *
- * @see <a href=
- *      
"http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot";>#howto-convert-an-existing-application-to-spring-boot</a>
- */
-@Configuration
-@Profile("oauth")
-public class WebXmlOauthConfiguration {
-
-    @Bean
-    public ServletRegistrationBean dispatcherRegistration(DispatcherServlet 
dispatcherServlet) {
-        ServletRegistrationBean<DispatcherServlet> registrationBean = new 
ServletRegistrationBean<DispatcherServlet>(dispatcherServlet);
-        registrationBean.addUrlMappings("/api/oauth/token");
-        return registrationBean;
-    }
-
-}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
new file mode 100644
index 0000000..a2226c9
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
@@ -0,0 +1,145 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.infrastructure.core.config;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import 
org.apache.fineract.infrastructure.core.exceptionmapper.OAuth2ExceptionEntryPoint;
+import 
org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
+import 
org.apache.fineract.infrastructure.security.filter.TenantAwareTenantIdentifierFilter;
+import 
org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
+import 
org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService;
+import 
org.apache.fineract.infrastructure.security.vote.SelfServiceUserAccessVote;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.vote.AuthenticatedVoter;
+import org.springframework.security.access.vote.RoleVoter;
+import org.springframework.security.access.vote.UnanimousBased;
+import 
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import 
org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import 
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
+import org.springframework.security.oauth2.jwt.Jwt;
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+import org.springframework.security.web.access.expression.WebExpressionVoter;
+import 
org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import 
org.springframework.security.web.context.SecurityContextPersistenceFilter;
+
+@Configuration
+@Profile("oauth")
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Autowired
+    private TwoFactorAuthenticationFilter twoFactorAuthenticationFilter;
+
+    @Autowired
+    private TenantAwareTenantIdentifierFilter 
tenantAwareTenantIdentifierFilter;
+
+    @Autowired
+    private TenantAwareJpaPlatformUserDetailsService userDetailsService;
+
+    private static final JwtGrantedAuthoritiesConverter 
jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+
+        http //
+                .csrf().disable() // NOSONAR only creating a service that is 
used by non-browser clients
+                .antMatcher("/api/**").authorizeRequests() //
+                .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll() //
+                .antMatchers(HttpMethod.POST, "/api/*/echo").permitAll() //
+                .antMatchers(HttpMethod.POST, 
"/api/*/authentication").permitAll() //
+                .antMatchers(HttpMethod.POST, 
"/api/*/self/authentication").permitAll() //
+                .antMatchers(HttpMethod.POST, 
"/api/*/self/registration").permitAll() //
+                .antMatchers(HttpMethod.POST, 
"/api/*/self/registration/user").permitAll() //
+                .antMatchers(HttpMethod.POST, 
"/api/*/twofactor/validate").fullyAuthenticated() //
+                .antMatchers("/api/*/twofactor").fullyAuthenticated() //
+                .antMatchers("/api/**").access("isFullyAuthenticated() and 
hasAuthority('TWOFACTOR_AUTHENTICATED')") //
+                .accessDecisionManager(accessDecisionManager()).and() //
+                .exceptionHandling().authenticationEntryPoint(new 
OAuth2ExceptionEntryPoint()).and()
+                .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> 
jwt.jwtAuthenticationConverter(authenticationConverter()))
+                        .authenticationEntryPoint(new 
OAuth2ExceptionEntryPoint())) //
+                .sessionManagement() //
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //
+                .and() //
+                .addFilterAfter(tenantAwareTenantIdentifierFilter, 
SecurityContextPersistenceFilter.class) //
+                .addFilterAfter(twoFactorAuthenticationFilter, 
BasicAuthenticationFilter.class) //
+                .requiresChannel(channel -> 
channel.antMatchers("/api/**").requiresSecure());
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
+    }
+
+    private Converter<Jwt, FineractJwtAuthenticationToken> 
authenticationConverter() {
+        return jwt -> {
+            try {
+                UserDetails user = 
userDetailsService.loadUserByUsername(jwt.getSubject());
+                jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
+                Collection<GrantedAuthority> authorities = 
jwtGrantedAuthoritiesConverter.convert(jwt);
+                return new FineractJwtAuthenticationToken(jwt, authorities, 
user);
+            } catch (UsernameNotFoundException ex) {
+                throw new OAuth2AuthenticationException(new 
OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN), ex);
+            }
+        };
+    }
+
+    @Bean
+    public AccessDecisionManager accessDecisionManager() {
+        List<AccessDecisionVoter<? extends Object>> decisionVoters = 
Arrays.asList(new RoleVoter(), new AuthenticatedVoter(),
+                new WebExpressionVoter(), new SelfServiceUserAccessVote());
+
+        return new UnanimousBased(decisionVoters);
+    }
+
+    @Bean
+    public FilterRegistrationBean<TenantAwareTenantIdentifierFilter> 
tenantAwareTenantIdentifierFilterRegistration() throws Exception {
+        FilterRegistrationBean<TenantAwareTenantIdentifierFilter> registration 
= new FilterRegistrationBean<TenantAwareTenantIdentifierFilter>(
+                tenantAwareTenantIdentifierFilter);
+        registration.setEnabled(false);
+        return registration;
+    }
+
+    @Bean
+    public FilterRegistrationBean<TwoFactorAuthenticationFilter> 
twoFactorAuthenticationFilterRegistration() {
+        FilterRegistrationBean<TwoFactorAuthenticationFilter> registration = 
new FilterRegistrationBean<TwoFactorAuthenticationFilter>(
+                twoFactorAuthenticationFilter);
+        registration.setEnabled(false);
+        return registration;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/OAuth2ExceptionEntryPoint.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/OAuth2ExceptionEntryPoint.java
new file mode 100644
index 0000000..7b41234
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/OAuth2ExceptionEntryPoint.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.exceptionmapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+public class OAuth2ExceptionEntryPoint implements AuthenticationEntryPoint {
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse 
response, AuthenticationException authException)
+            throws IOException, ServletException {
+
+        ApiGlobalErrorResponse errorResponse = 
ApiGlobalErrorResponse.unAuthenticated();
+        response.setContentType("application/json");
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.writeValue(response.getOutputStream(), errorResponse);
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
index 05ce28b..64e96bf 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
@@ -19,7 +19,6 @@
 package org.apache.fineract.infrastructure.security.api;
 
 import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -31,24 +30,23 @@ import java.util.Set;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import 
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
 import 
org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
 import 
org.apache.fineract.infrastructure.security.data.AuthenticatedOauthUserData;
+import 
org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
 import 
org.apache.fineract.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
 import org.apache.fineract.infrastructure.security.service.TwoFactorUtils;
 import org.apache.fineract.useradministration.data.RoleData;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.apache.fineract.useradministration.domain.Role;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Profile;
 import org.springframework.context.annotation.Scope;
-import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
-import 
org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
 
 /*
@@ -60,19 +58,15 @@ import org.springframework.stereotype.Component;
 @Scope("singleton")
 
 @Tag(name = "Fetch authenticated user details", description = "")
-@SuppressWarnings("deprecation") // TODO FINERACT-1012
 public class UserDetailsApiResource {
 
-    private final ResourceServerTokenServices tokenServices;
     private final ToApiJsonSerializer<AuthenticatedOauthUserData> 
apiJsonSerializerService;
     private final SpringSecurityPlatformSecurityContext 
springSecurityPlatformSecurityContext;
     private final TwoFactorUtils twoFactorUtils;
 
     @Autowired
-    public UserDetailsApiResource(@Qualifier("tokenServices") final 
ResourceServerTokenServices tokenServices,
-            final ToApiJsonSerializer<AuthenticatedOauthUserData> 
apiJsonSerializerService,
+    public UserDetailsApiResource(final 
ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService,
             final SpringSecurityPlatformSecurityContext 
springSecurityPlatformSecurityContext, final TwoFactorUtils twoFactorUtils) {
-        this.tokenServices = tokenServices;
         this.apiJsonSerializerService = apiJsonSerializerService;
         this.springSecurityPlatformSecurityContext = 
springSecurityPlatformSecurityContext;
         this.twoFactorUtils = twoFactorUtils;
@@ -83,48 +77,57 @@ public class UserDetailsApiResource {
     @Operation(summary = "Fetch authenticated user details\n", description = 
"checks the Authentication and returns the set roles and permissions allowed.")
     @ApiResponses({
             @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
UserDetailsApiResourceSwagger.GetUserDetailsResponse.class))) })
-    public String fetchAuthenticatedUserData(
-            @QueryParam("access_token") @Parameter(description = 
"access_token") final String accessToken) {
-
-        final Authentication authentication = 
this.tokenServices.loadAuthentication(accessToken);
-        if (authentication.isAuthenticated()) {
-            final AppUser principal = (AppUser) authentication.getPrincipal();
-
-            final Collection<String> permissions = new ArrayList<>();
-            AuthenticatedOauthUserData authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), permissions);
-
-            final Collection<GrantedAuthority> authorities = new 
ArrayList<>(authentication.getAuthorities());
-            for (final GrantedAuthority grantedAuthority : authorities) {
-                permissions.add(grantedAuthority.getAuthority());
-            }
-
-            final Collection<RoleData> roles = new ArrayList<>();
-            final Set<Role> userRoles = principal.getRoles();
-            for (final Role role : userRoles) {
-                roles.add(role.toData());
-            }
-
-            final Long officeId = principal.getOffice().getId();
-            final String officeName = principal.getOffice().getName();
-
-            final Long staffId = principal.getStaffId();
-            final String staffDisplayName = principal.getStaffDisplayName();
-
-            final EnumOptionData organisationalRole = 
principal.organisationalRoleData();
-
-            final boolean requireTwoFactorAuth = 
twoFactorUtils.isTwoFactorAuthEnabled()
-                    && 
!principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
-            if 
(this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal))
 {
-                authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), principal.getId(), 
accessToken,
-                        requireTwoFactorAuth);
-            } else {
-
-                authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, 
staffId,
-                        staffDisplayName, organisationalRole, roles, 
permissions, principal.getId(), accessToken, requireTwoFactorAuth);
-            }
-            return 
this.apiJsonSerializerService.serialize(authenticatedUserData);
+    public String fetchAuthenticatedUserData() {
+
+        final SecurityContext context = SecurityContextHolder.getContext();
+        if (context == null) {
+            return null;
+        }
+
+        final FineractJwtAuthenticationToken authentication = 
(FineractJwtAuthenticationToken) context.getAuthentication();
+        if (authentication == null) {
+            return null;
+        }
+
+        final AppUser principal = (AppUser) authentication.getPrincipal();
+        if (principal == null) {
+            return null;
+        }
+
+        final Collection<String> permissions = new ArrayList<>();
+        AuthenticatedOauthUserData authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), permissions);
+
+        final Collection<GrantedAuthority> authorities = new 
ArrayList<>(authentication.getAuthorities());
+        for (final GrantedAuthority grantedAuthority : authorities) {
+            permissions.add(grantedAuthority.getAuthority());
+        }
+
+        final Collection<RoleData> roles = new ArrayList<>();
+        final Set<Role> userRoles = principal.getRoles();
+        for (final Role role : userRoles) {
+            roles.add(role.toData());
+        }
+
+        final Long officeId = principal.getOffice().getId();
+        final String officeName = principal.getOffice().getName();
+
+        final Long staffId = principal.getStaffId();
+        final String staffDisplayName = principal.getStaffDisplayName();
+
+        final EnumOptionData organisationalRole = 
principal.organisationalRoleData();
+
+        final boolean requireTwoFactorAuth = 
twoFactorUtils.isTwoFactorAuthEnabled()
+                && 
!principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
+
+        if 
(this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal))
 {
+            authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), principal.getId(),
+                    authentication.getToken().getTokenValue(), 
requireTwoFactorAuth);
+        } else {
+            authenticatedUserData = new 
AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, 
staffId, staffDisplayName,
+                    organisationalRole, roles, permissions, principal.getId(), 
authentication.getToken().getTokenValue(),
+                    requireTwoFactorAuth);
         }
-        return null;
 
+        return this.apiJsonSerializerService.serialize(authenticatedUserData);
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/FineractJwtAuthenticationToken.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/FineractJwtAuthenticationToken.java
new file mode 100644
index 0000000..22e1d54
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/FineractJwtAuthenticationToken.java
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.infrastructure.security.data;
+
+import java.util.Collection;
+import java.util.Objects;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.jwt.Jwt;
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+
+public class FineractJwtAuthenticationToken extends JwtAuthenticationToken {
+
+    private final UserDetails user;
+
+    public FineractJwtAuthenticationToken(Jwt jwt, 
Collection<GrantedAuthority> authorities, UserDetails user) {
+        super(jwt, authorities, user.getUsername());
+        this.user = Objects.requireNonNull(user, "user");
+    }
+
+    @Override
+    public UserDetails getPrincipal() {
+        return user;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/InsecureTwoFactorAuthenticationFilter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/InsecureTwoFactorAuthenticationFilter.java
index 7216f11..64e40b8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/InsecureTwoFactorAuthenticationFilter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/InsecureTwoFactorAuthenticationFilter.java
@@ -20,12 +20,13 @@ package org.apache.fineract.infrastructure.security.filter;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
-import org.apache.fineract.useradministration.domain.AppUser;
+import 
org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
 import org.springframework.context.annotation.Profile;
 import 
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
@@ -33,6 +34,7 @@ import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Service;
 
 /**
@@ -58,18 +60,21 @@ public class InsecureTwoFactorAuthenticationFilter extends 
TwoFactorAuthenticati
         }
 
         // Add two-factor authenticated authority if user is authenticated
-        if (authentication != null && authentication.isAuthenticated() && 
authentication.getPrincipal() instanceof AppUser) {
-            AppUser user = (AppUser) authentication.getPrincipal();
-
-            if (user == null) {
-                return;
-            }
-
+        if (authentication != null && authentication.isAuthenticated()) {
             List<GrantedAuthority> updatedAuthorities = new 
ArrayList<>(authentication.getAuthorities());
             updatedAuthorities.add(new 
SimpleGrantedAuthority("TWOFACTOR_AUTHENTICATED"));
-            UsernamePasswordAuthenticationToken updatedAuthentication = new 
UsernamePasswordAuthenticationToken(
-                    authentication.getPrincipal(), 
authentication.getCredentials(), updatedAuthorities);
-            context.setAuthentication(updatedAuthentication);
+
+            if (authentication instanceof UsernamePasswordAuthenticationToken) 
{
+                UsernamePasswordAuthenticationToken updatedAuthentication = 
new UsernamePasswordAuthenticationToken(
+                        authentication.getPrincipal(), 
authentication.getCredentials(), updatedAuthorities);
+                context.setAuthentication(updatedAuthentication);
+            } else if (authentication instanceof 
FineractJwtAuthenticationToken) {
+                FineractJwtAuthenticationToken fineractJwtAuthenticationToken 
= (FineractJwtAuthenticationToken) authentication;
+                FineractJwtAuthenticationToken updatedAuthentication = new 
FineractJwtAuthenticationToken(
+                        fineractJwtAuthenticationToken.getToken(), 
(Collection<GrantedAuthority>) updatedAuthorities,
+                        (UserDetails) authentication.getPrincipal());
+                context.setAuthentication(updatedAuthentication);
+            }
         }
 
         chain.doFilter(req, res);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
index 42a26fc..bbc23de 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
@@ -54,7 +54,7 @@ import org.springframework.web.filter.GenericFilterBean;
  *
  * Used to support Oauth2 authentication and the service is loaded only when 
"oauth" profile is active.
  */
-@Service(value = "tenantIdentifierProcessingFilter")
+@Service
 @Profile("oauth")
 public class TenantAwareTenantIdentifierFilter extends GenericFilterBean {
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java
index cc877f0..14dff4b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java
@@ -20,6 +20,7 @@ package org.apache.fineract.infrastructure.security.filter;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -28,6 +29,7 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import 
org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
+import 
org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
 import org.apache.fineract.infrastructure.security.domain.TFAccessToken;
 import org.apache.fineract.infrastructure.security.service.TwoFactorService;
 import org.apache.fineract.useradministration.domain.AppUser;
@@ -39,7 +41,7 @@ import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Service;
 import org.springframework.web.filter.GenericFilterBean;
 
@@ -106,25 +108,28 @@ public class TwoFactorAuthenticationFilter extends 
GenericFilterBean {
 
             List<GrantedAuthority> updatedAuthorities = new 
ArrayList<>(authentication.getAuthorities());
             updatedAuthorities.add(new 
SimpleGrantedAuthority("TWOFACTOR_AUTHENTICATED"));
-            final Authentication updatedAuthentication = 
createUpdatedAuthentication(authentication, updatedAuthorities);
-            context.setAuthentication(updatedAuthentication);
+            
context.setAuthentication(createUpdatedAuthentication(authentication, 
updatedAuthorities));
         }
 
         chain.doFilter(req, res);
     }
 
-    @SuppressWarnings("deprecation") // TODO FINERACT-1012
     private Authentication createUpdatedAuthentication(final Authentication 
currentAuthentication,
-            final List<GrantedAuthority> updatedAuthorities) {
-
-        final UsernamePasswordAuthenticationToken authentication = new 
UsernamePasswordAuthenticationToken(
-                currentAuthentication.getPrincipal(), 
currentAuthentication.getCredentials(), updatedAuthorities);
-
-        if (currentAuthentication instanceof OAuth2Authentication) {
-            final OAuth2Authentication oAuth2Authentication = 
(OAuth2Authentication) currentAuthentication;
-            return new 
OAuth2Authentication(oAuth2Authentication.getOAuth2Request(), authentication);
+            final List<GrantedAuthority> updatedAuthorities) throws 
ServletException {
+
+        if (currentAuthentication instanceof 
UsernamePasswordAuthenticationToken) {
+            UsernamePasswordAuthenticationToken updatedAuthentication = new 
UsernamePasswordAuthenticationToken(
+                    currentAuthentication.getPrincipal(), 
currentAuthentication.getCredentials(), updatedAuthorities);
+            return updatedAuthentication;
+        } else if (currentAuthentication instanceof 
FineractJwtAuthenticationToken) {
+            FineractJwtAuthenticationToken fineractJwtAuthenticationToken = 
(FineractJwtAuthenticationToken) currentAuthentication;
+            FineractJwtAuthenticationToken updatedAuthentication = new 
FineractJwtAuthenticationToken(
+                    fineractJwtAuthenticationToken.getToken(), 
(Collection<GrantedAuthority>) updatedAuthorities,
+                    (UserDetails) currentAuthentication.getPrincipal());
+            return updatedAuthentication;
+        } else {
+            throw new ServletException("Unknown authentication type: " + 
currentAuthentication.getClass().getName());
         }
 
-        return authentication;
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java
index dda8715..2104cdb 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java
@@ -19,7 +19,6 @@
 package org.apache.fineract.portfolio.self.security.api;
 
 import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -28,7 +27,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import org.apache.fineract.infrastructure.security.api.UserDetailsApiResource;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -57,8 +55,7 @@ public class SelfUserDetailsApiResource {
             + "For more info visit this link - 
https://demo.fineract.dev/fineract-provider/api-docs/apiLive.htm#selfoauth";)
     @ApiResponses({
             @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
SelfUserDetailsApiResourceSwagger.GetSelfUserDetailsResponse.class))) })
-    public String fetchAuthenticatedUserData(
-            @QueryParam("access_token") @Parameter(description = 
"äccess_token") final String accessToken) {
-        return 
this.userDetailsApiResource.fetchAuthenticatedUserData(accessToken);
+    public String fetchAuthenticatedUserData() {
+        return this.userDetailsApiResource.fetchAuthenticatedUserData();
     }
 }
diff --git 
a/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml 
b/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml
deleted file mode 100644
index 255db23..0000000
--- a/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml
+++ /dev/null
@@ -1,167 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements. See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership. The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License. You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied. See the License for the
-    specific language governing permissions and limitations
-    under the License.
-
--->
-
-<beans:beans xmlns="http://www.springframework.org/schema/security";
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:beans="http://www.springframework.org/schema/beans";
-    xmlns:oauth="http://www.springframework.org/schema/security/oauth2";
-    xsi:schemaLocation="
-   http://www.springframework.org/schema/beans
-   https://www.springframework.org/schema/beans/spring-beans.xsd
-   http://www.springframework.org/schema/security/oauth2
-   https://www.springframework.org/schema/security/spring-security-oauth2.xsd
-   http://www.springframework.org/schema/security
-   https://www.springframework.org/schema/security/spring-security.xsd";>
-
-    <beans:beans profile="oauth">
-        <http create-session="stateless" use-expressions="true" 
pattern="/api/v1/**"
-            entry-point-ref="oauthAuthenticationEntryPoint"
-            access-decision-manager-ref="accessDecisionManager">
-            <csrf disabled="true"/>
-            <anonymous enabled="false" />
-            <intercept-url pattern="/api//v1/**" method="OPTIONS"
-                    access="permitAll" requires-channel="https" />
-            <intercept-url pattern="/api/*/twofactor" 
access="isFullyAuthenticated()"
-                           method="GET" requires-channel="https" />
-            <intercept-url pattern="/api/*/twofactor" 
access="isFullyAuthenticated()"
-                           method="POST" requires-channel="https" />
-            <intercept-url pattern="/api/*/twofactor/validate" 
access="isFullyAuthenticated()"
-                           method="POST" requires-channel="https" />
-            <intercept-url pattern="/api/v1/**" access="isFullyAuthenticated() 
and hasAuthority('TWOFACTOR_AUTHENTICATED')"
-                method="GET" requires-channel="https" />
-            <intercept-url pattern="/api/v1/**" access="isFullyAuthenticated() 
and hasAuthority('TWOFACTOR_AUTHENTICATED')"
-                method="POST" requires-channel="https" />
-            <intercept-url pattern="/api/v1/**" access="isFullyAuthenticated() 
and hasAuthority('TWOFACTOR_AUTHENTICATED')"
-                method="PUT" requires-channel="https" />
-            <intercept-url pattern="/api/v1/**" access="isFullyAuthenticated() 
and hasAuthority('TWOFACTOR_AUTHENTICATED')"
-                method="DELETE" requires-channel="https" />
-            <intercept-url pattern="/api/v1/**" access="isFullyAuthenticated() 
and hasAuthority('TWOFACTOR_AUTHENTICATED')"
-                method="HEAD" requires-channel="https" />
-            <custom-filter ref="tenantIdentifierProcessingFilter"
-                position="FIRST" />
-            <custom-filter before="PRE_AUTH_FILTER" ref="resourceServerFilter" 
/>
-            <custom-filter ref="twoFactorAuthFilter" after="BASIC_AUTH_FILTER" 
/>
-            <access-denied-handler ref="oauthAccessDeniedHandler" />
-        </http>
-
-        <http pattern="/api/oauth/token" create-session="stateless"
-            entry-point-ref="oauthAuthenticationEntryPoint" 
use-expressions="true"
-            authentication-manager-ref="clientAuthenticationManager">
-            <csrf disabled="true"/>
-            <intercept-url pattern="/api/oauth/token" method="OPTIONS"
-                access="permitAll" requires-channel="https" />
-            <intercept-url pattern="/api/oauth/token" method="POST"
-                access="isFullyAuthenticated()" requires-channel="https" />
-            <anonymous enabled="false" />
-
-            <custom-filter ref="tenantIdentifierProcessingFilter"
-                position="FIRST" />
-            <custom-filter ref="clientCredentialsTokenEndpointFilter"
-                before="BASIC_AUTH_FILTER" />
-            <access-denied-handler ref="oauthAccessDeniedHandler" />
-        </http>
-
-        <beans:bean id="oauthAuthenticationEntryPoint"
-            
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
-            <beans:property name="realmName" value="Fineract Platform API" />
-        </beans:bean>
-
-        <beans:bean id="oauthAccessDeniedHandler"
-            
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"
 />
-
-        <beans:bean id="clientCredentialsTokenEndpointFilter"
-            
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
-            <beans:constructor-arg value="/api/oauth/token" />
-            <beans:property name="authenticationManager" 
ref="clientAuthenticationManager" />
-        </beans:bean>
-
-        <beans:bean id="accessDecisionManager"
-            class="org.springframework.security.access.vote.UnanimousBased">
-            <beans:constructor-arg>
-                <beans:list>
-                    <beans:bean
-                        
class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
-                    <beans:bean 
class="org.springframework.security.access.vote.RoleVoter" />
-                    <beans:bean
-                        
class="org.springframework.security.access.vote.AuthenticatedVoter" />
-                    <beans:bean
-                        
class="org.springframework.security.web.access.expression.WebExpressionVoter" />
-                    <beans:bean
-                        
class="org.apache.fineract.infrastructure.security.vote.SelfServiceUserAccessVote"
 />
-                </beans:list>
-            </beans:constructor-arg>
-        </beans:bean>
-
-        <authentication-manager id="clientAuthenticationManager" 
erase-credentials="false">
-            <authentication-provider 
user-service-ref="clientDetailsUserService" />
-            <authentication-provider ref="customAuthenticationProvider" />
-        </authentication-manager>
-
-        <beans:bean id="clientDetailsUserService"
-            
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
-            <beans:constructor-arg ref="clientDetailsService" />
-        </beans:bean>
-
-        <beans:bean id="clientDetailsService"
-            
class="org.springframework.security.oauth2.provider.client.JdbcClientDetailsService">
-            <beans:constructor-arg ref="routingDataSource" />
-        </beans:bean>
-
-        <beans:bean id="tokenStore"
-            
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
-            <beans:constructor-arg ref="routingDataSource" />
-        </beans:bean>
-
-        <beans:bean id="tokenServices"
-            
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
-            <beans:property name="tokenStore" ref="tokenStore" />
-            <beans:property name="clientDetailsService" 
ref="clientDetailsService" />
-            <beans:property name="supportRefreshToken" value="true" />
-            <beans:property name="refreshTokenValiditySeconds"
-                value="86400" />
-            <beans:property name="accessTokenValiditySeconds"
-                value="3600" />
-        </beans:bean>
-
-        <beans:bean id="userApprovalHandler"
-            
class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
-            <beans:property name="tokenStore" ref="tokenStore" />
-            <beans:property name="clientDetailsService" 
ref="clientDetailsService" />
-            <beans:property name="requestFactory" ref="oAuth2RequestFactory" />
-        </beans:bean>
-
-        <beans:bean id="oAuth2RequestFactory"
-            
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
-            <beans:constructor-arg ref="clientDetailsService" />
-        </beans:bean>
-
-        <oauth:authorization-server
-            client-details-service-ref="clientDetailsService" 
token-services-ref="tokenServices"
-            user-approval-handler-ref="userApprovalHandler" 
token-endpoint-url="/api/oauth/token"
-            authorization-endpoint-url="/api/oauth/authorize">
-            <oauth:refresh-token />
-            <oauth:password />
-        </oauth:authorization-server>
-
-        <oauth:resource-server id="resourceServerFilter" stateless="false"
-            token-services-ref="tokenServices" />
-    </beans:beans>
-</beans:beans>
diff --git a/fineract-provider/src/main/resources/static/api-docs/apiLive.htm 
b/fineract-provider/src/main/resources/static/api-docs/apiLive.htm
index 61c0ea0..2c8c583 100644
--- a/fineract-provider/src/main/resources/static/api-docs/apiLive.htm
+++ b/fineract-provider/src/main/resources/static/api-docs/apiLive.htm
@@ -2769,22 +2769,6 @@
                                                        </tr>
                                                        <tr>
                                                                <td><a 
href="#authenticationoauth">Authentication Oauth2</a></td>
-                                                               
<td>oauth/token</td>
-                                                               <td><a 
href="#oauth">OAuth2 Access and refresh Token Request</a></td>
-                                                               <td></td>
-                                                               <td></td>
-                                                               <td></td>
-                                                       </tr>
-                                                       <tr>
-                                                               <td></td>
-                                                               <td></td>
-                                                               <td><a 
href="#oauth_access_token_req">OAuth2 Access Token Request from refresh 
token</a></td>
-                                                               <td></td>
-                                                               <td></td>
-                                                               <td></td>
-                                                       </tr>
-                                                       <tr>
-                                                               <td></td>
                                                                
<td>authenticated user</td>
                                                                <td></td>
                                                                <td><a 
href="#userdetails_request">Fetch Authenticated user details</a></td>
@@ -4499,7 +4483,7 @@ function executeAjaxRequest(url, verbType, jsonData, 
basicAuthKey, successFuncti
 function getOauthToken(username, password) {
 
        var jqxhr = $.ajax({
-               url : "/fineract-provider/api/oauth/token",
+               url : "URL of the authentication server",
                type : 'POST',
                dataType : 'json',
                data : {
@@ -39103,174 +39087,6 @@ No Request Body
                                <div class="method-description">
                                        <h3>Authentication Oauth2</h3>
                                        <p>An API capability that allows client 
applications to fetch current user details details using Oauth2.</p>
-                                       <table class=matrixHeading>
-                                               <tr class="matrixHeadingBG">
-                                                       <td><div 
class="fineractHeading2">Field Descriptions</div></td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>accessToken</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>HTTP Auth bearer key. See <a
-                                                               
href="#authentication_overview">Authentication Overview</a> for
-                                                               an example of 
its use.
-                                                       </td>
-                                               </tr>
-                                               </tr>
-                                       </table>
-                               </div>
-                       </div>
-
-                       <a id="oauth" name="oauth" 
class="old-syle-anchor">&nbsp;</a>
-                       <div class="method-section">
-                               <div class="method-description">
-                                       <h3>Authentication via OAuth2</h3>
-                                       <p>API for requesting OAuth2 access 
token and refresh token.</p>
-                                       <table class=matrixHeading>
-                                               <tr class="matrixHeadingBG">
-                                                       <td><div 
class="fineractHeading2">Field Descriptions</div></td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>grant_type</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Indicates the requested 
grant type. Supported values:
-                                                       <span>password</span>.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>client_id</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Indicates the client 
application identity.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>client_secret</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Optional field.
-                                                       Indicates the client 
application password.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>username</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Application 
User(resource) login name.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>password</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Application 
User(resource) password.
-                                                       </td>
-                                               </tr>
-                                       </table>
-                               </div>
-                       </div>
-
-                       <a id="oauth_request" name="oauth_request" 
class="old-syle-anchor">&nbsp;</a>
-                       <div class="method-section">
-                               <div class="method-description">
-                                       <h4>OAuth2 Refresh and Access Token 
Request</h4>
-                               </div>
-                               <div class="method-example">
-                                       <code class="method-declaration">
-POST 
https://DomainName/api/oauth/token?username={username}&password={password}&client_id={clientId}&grant_type={grant_type}&client_secret={client_secret}
-                                       </code>
-                                       <code class="method-request">
-POST 
api/oauth/token?username=mifos&password=password&client_id=community-app&grant_type=password&client_secret=123
-Content-Type: application/json
-No Request Body
-                                       </code>
-                                       <code class="method-response">
-{
-       ""access_token": "b771987f-82fc-45ba-b521-bfe280c4e603",
-       "token_type": "bearer",
-       "refresh_token":"a2a89b23-8d22-4d90-8585-8f464db433b0",
-       "expires_in": 3599,
-       "scope": "all"
-}
-                                       </code>
-                               </div>
-                       </div>
-
-
-                       <a id="oauth_access_token_req" 
name="oauth_access_token_req" class="old-syle-anchor">&nbsp;</a>
-                       <div class="method-section">
-                               <div class="method-description">
-                                       <h3>Authentication via OAuth2</h3>
-                                       <p>API for requesting OAuth2 access 
tokens through oAuth2 refresh tokens.</p>
-                                       <table class=matrixHeading>
-                                               <tr class="matrixHeadingBG">
-                                                       <td><div 
class="fineractHeading2">Field Descriptions</div></td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>grant_type</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Indicates the requested 
grant type. Supported values:
-                                                       
<span>refresh_token</span>.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>client_id</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Indicates the client 
application identity.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>client_secret</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Optional field.
-                                                       Indicates the client 
application password.
-                                                       </td>
-                                               </tr>
-                                               <tr class=alt>
-                                                       <td>refresh_token</td>
-                                               </tr>
-                                               <tr>
-                                                       <td 
class=fielddesc>Mandatory field.
-                                                       Application refresh 
token to generate access token.
-                                                       </td>
-                                               </tr>
-                                       </table>
-                               </div>
-                       </div>
-
-                       <a id="oauth_request_with_refresh" 
name="oauth_request_with_refresh" class="old-syle-anchor">&nbsp;</a>
-                       <div class="method-section">
-                               <div class="method-description">
-                                       <h4>OAuth2 Token Request through 
Refresh Token</h4>
-                               </div>
-                               <div class="method-example">
-                                       <code class="method-declaration">
-POST 
https://DomainName/api/oauth/token?refresh_token={refresh_token}&client_id={clientId}&grant_type={grant_type}&client_secret={client_secret}
-                                       </code>
-                                       <code class="method-request">
-POST 
api/oauth/token?client_id=community-app&grant_type=refresh_token&client_secret=123&refresh_token=a2a89b23-8d22-4d90-8585-8f464db433b0
-Content-Type: application/json
-No Request Body
-                                       </code>
-                                       <code class="method-response">
-{
-       ""access_token": "b771987f-82fc-45ba-b521-bfe280c4e643",
-       "token_type": "bearer",
-       "refresh_token":"a2a89b23-8d22-4d90-8585-8f464db433b0",
-       "expires_in": 3599,
-       "scope": "all"
-}
-                                       </code>
                                </div>
                        </div>
 
@@ -39282,10 +39098,10 @@ No Request Body
                                </div>
                                <div class="method-example">
                                        <code class="method-declaration">
-POST https://DomainName/api/v1/userdetails?access_token={access_token}
+GET https://DomainName/api/v1/userdetails
                                        </code>
                                        <code class="method-request">
-POST userdetails?access_token=bWlmb3M6cGFzc3dvcmQ=
+GET userdetails
 Content-Type: application/json
 No Request Body
                                        </code>
@@ -39342,22 +39158,6 @@ No Request Body
 
 
 
-
-                                       <code class="method-request">
-POST 
api/oauth/token?username=mifos&password=password&client_id=community-app&grant_type=password&client_secret=123
-Content-Type: application/json
-No Request Body
-                                       </code>
-                                       <code class="method-response">
-{
-    "developerMessage": "Invalid authentication details were passed in api 
request.",
-    "developerDocLink": 
"https://github.com/openMF/mifosx/wiki/HTTP-API-Error-codes";,
-    "httpStatusCode": "401",
-    "defaultUserMessage": "Unauthenticated. Please login.",
-    "userMessageGlobalisationCode": "error.msg.not.authenticated",
-    "errors": []
-}
-                                       </code>
                                </div>
                        </div>
 
@@ -47220,10 +47020,10 @@ No Request Body
                                </div>
                                <div class="method-example">
                                        <code class="method-declaration">
-POST https://DomainName/api/v1/self/userdetails?access_token={access_token}
+POST https://DomainName/api/v1/self/userdetails
                                        </code>
                                        <code class="method-request">
-POST self/userdetails?access_token=bWlmb3M6cGFzc3dvcmQ=
+POST self/userdetails
 Content-Type: application/json
 No Request Body
                                        </code>
@@ -47258,21 +47058,6 @@ No Request Body
 }
 </code>
 
-                                       <code class="method-request">
-POST 
api/oauth/token?username=mifos&password=password&client_id=community-app&grant_type=password&client_secret=123
-Content-Type: application/json
-No Request Body
-                                       </code>
-                                       <code class="method-response">
-{
-    "developerMessage": "Invalid authentication details were passed in api 
request.",
-    "developerDocLink": 
"https://github.com/openMF/mifosx/wiki/HTTP-API-Error-codes";,
-    "httpStatusCode": "401",
-    "defaultUserMessage": "Unauthenticated. Please login.",
-    "userMessageGlobalisationCode": "error.msg.not.authenticated",
-    "errors": []
-}
-                                       </code>
                                </div>
                        </div>
 
diff --git a/oauth2-tests/build.gradle b/oauth2-tests/build.gradle
new file mode 100644
index 0000000..8c805db
--- /dev/null
+++ b/oauth2-tests/build.gradle
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+description = 'Fineract Integration Tests for Oauth2'
+
+apply plugin: 'com.bmuschko.cargo'
+
+// Configuration for the Gradle Cargo plugin
+// https://github.com/bmuschko/gradle-cargo-plugin
+configurations {
+    tomcat
+}
+
+apply from: 'dependencies.gradle'
+
+cargo {
+    containerId "tomcat9x"
+
+    // looks like Cargo doesn't detect the WAR file automatically in the 
multi-module setup
+    deployable {
+        file = 
file("$rootDir/fineract-provider/build/libs/fineract-provider.war")
+        context = 'fineract-provider'
+    }
+
+    local {
+        installer {
+            installConfiguration = configurations.tomcat
+            downloadDir = file("$buildDir/download")
+            extractDir = file("$buildDir/tomcat")
+        }
+        startStopTimeout = 240000
+        containerProperties {
+            property 'cargo.start.jvmargs', '-Dspring.profiles.active=oauth'
+            property 'cargo.tomcat.connector.keystoreFile', 
file("$rootDir/fineract-provider/src/main/resources/keystore.jks")
+            property 'cargo.tomcat.connector.keystorePass', 'openmf'
+            property 'cargo.tomcat.httpSecure', true
+            property 'cargo.tomcat.connector.sslProtocol', 'TLS'
+            property 'cargo.tomcat.connector.clientAuth', false
+            property 'cargo.protocol', 'https'
+            property 'cargo.servlet.port', 8443
+        }
+    }
+}
+
+cargoRunLocal.dependsOn ':fineract-provider:war'
+cargoStartLocal.dependsOn ':fineract-provider:war'
+cargoStartLocal.mustRunAfter 'testClasses'
+
+test {
+    dependsOn cargoStartLocal
+    finalizedBy cargoStopLocal
+}
diff --git a/settings.gradle b/oauth2-tests/dependencies.gradle
similarity index 51%
copy from settings.gradle
copy to oauth2-tests/dependencies.gradle
index 596dc4e..38065ae 100644
--- a/settings.gradle
+++ b/oauth2-tests/dependencies.gradle
@@ -16,9 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-rootProject.name='fineract'
-include ':fineract-provider'
-include ':integration-tests'
-include ':twofactor-tests'
-include ':fineract-client'
-include ':fineract-doc'
+dependencies {
+    // testCompile dependencies are ONLY used in src/test, not src/main.
+    // Do NOT repeat dependencies which are ALREADY in implementation or 
runtimeOnly!
+    //
+    tomcat 'org.apache.tomcat:tomcat:9.0.54@zip'
+    testImplementation( 
files("$rootDir/fineract-provider/build/classes/java/main/"),
+            project(path: ':fineract-provider', configuration: 
'runtimeElements'),
+            'org.junit.jupiter:junit-jupiter-api'
+            )
+    testImplementation ('io.rest-assured:rest-assured') {
+        exclude group: 'commons-logging'
+        exclude group: 'org.apache.sling'
+        exclude group: 'com.sun.xml.bind'
+    }
+    testRuntimeOnly(
+            'org.junit.jupiter:junit-jupiter-engine'
+            )
+}
diff --git 
a/oauth2-tests/src/test/java/org/apache/fineract/oauth2tests/OAuth2AuthenticationTest.java
 
b/oauth2-tests/src/test/java/org/apache/fineract/oauth2tests/OAuth2AuthenticationTest.java
new file mode 100644
index 0000000..d688de1
--- /dev/null
+++ 
b/oauth2-tests/src/test/java/org/apache/fineract/oauth2tests/OAuth2AuthenticationTest.java
@@ -0,0 +1,176 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.oauth2tests;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import io.restassured.RestAssured;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.path.json.JsonPath;
+import io.restassured.response.Response;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.io.IOException;
+import javax.mail.MessagingException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class OAuth2AuthenticationTest {
+
+    private ResponseSpecification responseSpec;
+    private ResponseSpecification responseSpec403;
+    private ResponseSpecification responseSpec401;
+    private RequestSpecification requestSpec;
+    private RequestSpecification requestFormSpec;
+
+    public static final String TENANT_PARAM_NAME = "tenantIdentifier";
+    public static final String DEFAULT_TENANT = "default";
+    public static final String TENANT_IDENTIFIER = TENANT_PARAM_NAME + '=' + 
DEFAULT_TENANT;
+    private static final String HEALTH_URL = 
"/fineract-provider/actuator/health";
+
+    @BeforeEach
+    public void setup() throws InterruptedException {
+        initializeRestAssured();
+
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+
+        // Login with basic authentication
+        awaitSpringBootActuatorHealthyUp();
+
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestFormSpec = new 
RequestSpecBuilder().setContentType(ContentType.URLENC).build();
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.responseSpec403 = new 
ResponseSpecBuilder().expectStatusCode(403).build();
+        this.responseSpec401 = new 
ResponseSpecBuilder().expectStatusCode(401).build();
+    }
+
+    @Test
+    public void testActuatorAccess() {
+        performServerGet(requestSpec, responseSpec, 
"/fineract-provider/actuator/info", null);
+    }
+
+    @Test
+    public void testApiDocsAccess() {
+        performServerGet(requestSpec, responseSpec, 
"/fineract-provider/api-docs/apiLive.htm", null);
+    }
+
+    @Test
+    public void testAccessWithoutAuthentication() {
+        performServerGet(requestSpec, responseSpec401, 
"/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+    }
+
+    @Test
+    public void testOAuth2Login() throws IOException, MessagingException {
+
+        performServerGet(requestSpec, responseSpec401, 
"/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+
+        String accessToken = performServerPost(requestFormSpec, responseSpec, 
"http://localhost:9000/auth/realms/fineract/token";,
+                
"grant_type=client_credentials&client_id=community-app&client_secret=123123", 
"access_token");
+        assertNotNull(accessToken);
+
+        String bearerToken = performServerPost(requestFormSpec, responseSpec, 
"http://localhost:9000/auth/realms/fineract/token";,
+                
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + 
accessToken
+                        + "&client_id=community-app&scope=fineract",
+                "access_token");
+        assertNotNull(bearerToken);
+
+        RequestSpecification requestSpecWithToken = new RequestSpecBuilder() //
+                .setContentType(ContentType.JSON) //
+                .addHeader("Authorization", "Bearer " + bearerToken) //
+                .build();
+
+        performServerGet(requestSpecWithToken, responseSpec, 
"/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+    }
+
+    @Test
+    public void testGetOAuth2UserDetails() {
+        performServerGet(requestSpec, responseSpec401, 
"/fineract-provider/api/v1/offices/1?" + TENANT_IDENTIFIER, "");
+
+        String accessToken = performServerPost(requestFormSpec, responseSpec, 
"http://localhost:9000/auth/realms/fineract/token";,
+                
"grant_type=client_credentials&client_id=community-app&client_secret=123123", 
"access_token");
+        assertNotNull(accessToken);
+
+        String bearerToken = performServerPost(requestFormSpec, responseSpec, 
"http://localhost:9000/auth/realms/fineract/token";,
+                
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + 
accessToken
+                        + "&client_id=community-app&scope=fineract",
+                "access_token");
+        assertNotNull(bearerToken);
+
+        RequestSpecification requestSpecWithToken = new RequestSpecBuilder() //
+                .setContentType(ContentType.JSON) //
+                .addHeader("Authorization", "Bearer " + bearerToken) //
+                .build();
+
+        Boolean authenticationCheck = performServerGet(requestSpecWithToken, 
responseSpec,
+                "/fineract-provider/api/v1/userdetails?" + TENANT_IDENTIFIER, 
"authenticated");
+        assertEquals(authenticationCheck, true);
+    }
+
+    private static void initializeRestAssured() {
+        RestAssured.baseURI = "https://localhost";;
+        RestAssured.port = 8443;
+        RestAssured.keyStore("src/main/resources/keystore.jks", "openmf");
+        RestAssured.useRelaxedHTTPSValidation();
+    }
+
+    private static void awaitSpringBootActuatorHealthyUp() throws 
InterruptedException {
+        int attempt = 0;
+        final int max_attempts = 10;
+        Response response = null;
+
+        do {
+            try {
+                response = RestAssured.get(HEALTH_URL);
+
+                if (response.statusCode() == 200) {
+                    return;
+                }
+
+                Thread.sleep(3000);
+            } catch (Exception e) {
+                Thread.sleep(3000);
+            }
+        } while (attempt < max_attempts);
+
+        fail(HEALTH_URL + " returned " + response.prettyPrint());
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T performServerGet(final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec,
+            final String getURL, final String jsonAttributeToGetBack) {
+        final String json = 
given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString();
+        if (jsonAttributeToGetBack == null) {
+            return (T) json;
+        }
+        return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T performServerPost(final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec,
+            final String putURL, final String formBody, final String 
jsonAttributeToGetBack) {
+        final String json = 
given().spec(requestSpec).body(formBody).expect().spec(responseSpec).log().ifError().when().post(putURL)
+                .andReturn().asString();
+        return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
+    }
+}
diff --git a/settings.gradle b/settings.gradle
index 596dc4e..69f7147 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -20,5 +20,6 @@ rootProject.name='fineract'
 include ':fineract-provider'
 include ':integration-tests'
 include ':twofactor-tests'
+include ':oauth2-tests'
 include ':fineract-client'
 include ':fineract-doc'

Reply via email to