diff --git a/.travis.yml b/.travis.yml
index 8bc14a93..f1078b3f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,9 @@
language: java
-
cache:
- directories:
- - $HOME/.m2
-
+ directories:
+ - $HOME/.m2
jdk:
- - oraclejdk8
+ - oraclejdk8
+branches:
+ only:
+ - master
diff --git a/README.md b/README.md
index e7cf98eb..3fc69bd4 100644
--- a/README.md
+++ b/README.md
@@ -7,12 +7,19 @@ built on top of light-4j and light-rest-4j frameworks.
[](https://travis-ci.org/networknt/light-oauth2)
+Light platform follows security first design and we have provided an OAuth 2.0 provider
+light-oauth2 which is based on light-4j and light-rest-4j frameworks with 7 microservices.
+Some of the services implement the OAuth 2.0 specifications and others implement some
+extensions to make OAuth more suitable to protect service to service communication, other
+styles of services like GraphQL, RPC and Event Driven, Key management and distribution,
+service registration, token scope calculation and token exchange.
+
## Why this OAuth 2.0 Authorization Server
### Fast and small memory footprint to lower production cost.
-The authorization code service can support 60000 user login and get authorization code redirected in a second on a Mac Book Pro.
-The token service can generate 700 JWT access tokens signed by PKI per second on a Mac Book Pro.
+It can support 60000 user login and get authorization code redirect and can generate
+700 access tokens per second on my laptop.
It has 7 microservices connected with in-memory data grid and each service can be
scaled individually.
@@ -24,29 +31,36 @@ OAuth 2.0 is just a specification and a lot of details are in the individual
implementation. Our implementation has a lot of extensions and enhancements
for additional security and prevent users making mistakes. For example, we
have added an additional client type called "trusted" and only this type of
-client can issue resource owner password credentials grant type and any custom
-grant types. Also, when expose your OAuth 2.0 services the Internet, you only
-need to expose authorization code service for user login and token service for
-client to get access token. Other services can only be accessed internal.
+client can issue resource owner password credentials grant type.
+
+### More deployment options
+
+You can deploy all services or just deploy the services for your use cases. You can
+deploy token and code service to DMZ and all others internal for maximum security.
+You can have several token services or deploy token service as sidecar pattern in
+each node. You can start more instance of key service on the day that your public
+key certificate for signature verification is changed and shutdown all of the but
+one the next day. You can take the full advantages of microservices deployment.
### Seamlessly integration with Light-Java framework
* Built on top of light-4j and light-rest-4j
-* light-4j client and security modules manages all the communication with OAuth2
-* Support service on-boarding from Light-Portal
-* Support client on-boarding from Light-Portal
-* Support user management from Light-Portal
+* Light-4j Client and Security modules manages most of the communications with OAuth2
+* Support service on-boarding from light-portal
+* Support client on-boarding from light-portal
+* Support user management from light-portal
* Open sourced OpenAPI specifications for all microserivces
### Easy to integrate with your APIs or services
-The OAuth2 services can be started in a docker compose for your local development
-and can be managed by Kubernetes on official environment.
+The OAuth2 services can be started in a docker-compose for your local development and
+can be managed by Kubernetes on official test and production environment. It exposes
+RESTful APIs and can be access from all languages and applications.
### Support multiple databases and can be extended and customized easily
Out of the box, it supports Mysql, Postgres and Oracle XE and H2 for unit tests. Other
-databases can be easily added with configuration change in service.yml config file.
+databases can be easily added with configuration change in service.yml.
### Public key certificate distribution
@@ -55,16 +69,42 @@ With distributed security verification, JWT signature public key certificates mu
but distributed to all resource servers. The traditional push approach is not
working with microservices architecture and pull approach is adopted. There is a
key service with endpoint to retrieve public key certificate from microservices
-during runtime based on the key_id from JWT header. For more details on key service,
-please refer to [key distribution][]
+during runtime based on the key_id from JWT header.
+
+### Two tokens to support microservices architecture
+
+Each service in a microservices application needs a subject token which identifies the
+original caller (the person who logged in the original client) and an access token
+which identifies the immediate caller (might be another microservices). Both tokens
+will be verified with scopes to the API endpoint level. Additional claims in these
+tokens will be used for fine-grained authorization which happens within the business
+context.
-### Additional documents
+### Token exchange for high security
-Along with the documentation on each [light-oauth2 service][], there are
-[light-oauth2 tutorial][] to help user to get started.
+Even with two tokens, we can only verify who is the original calller and which client is
+the immediate caller. For some highly protected service like payment or fund transfer,
+we need to ensure that the call is routed through some known services. light-oauth2
+token service support token exchange and chaining so that a service can verify the
+entire call tree to authorize if the call is authorized or not.
+### Service registration for scope calculation
-### OAuth2 server, portal and light Java to form ecosystem
+light-oauth2 has a service registration to allow all service to be registered with service
+id and all endpoints as well as scopes for the endpoint. During client registration, you
+can link a client to services/endpoints and the scope of the client can be calculated
+and updated in client table. This avoids developers to pass in scopes when getting
+access token as there might be hundreds of them for a client that accesses dozens of
+microservices.
+
+### All activities are audited
+
+A database audit handler has been wired into all light-oauth2 services to log each
+activity across services with sensitive info masked. In the future we will put these
+logs into AI stream processing to identify abnormal behaviors just like normal service
+log processing.
+
+### OAuth2 server, portal and light-4j to form ecosystem
[light-java](https://github.com/networknt/light-java) to build API
@@ -72,6 +112,30 @@ Along with the documentation on each [light-oauth2 service][], there are
[light-portal](https://github.com/networknt/light-portal) to manage clients and APIs
-[key distribution]: https://doc.networknt.com/service/oauth/service/key/
-[light-oauth2 service]: https://doc.networknt.com/service/oauth/service/
-[light-oauth2 tutorial]: https://doc.networknt.com/tutorial/oauth/
+## Introduction
+
+This [introduction](https://doc.networknt.com/service/oauth/introduction/) document contains all the basic concept of OAuth 2.0 specification and how it work in general.
+
+## Getting started
+
+The easiest way to start using light-oauth2 in your development environment is through
+docker-compose in light-docker repository. Please refer to [getting started](https://doc.networknt.com/getting-started/light-oauth2/) for more information.
+
+## Architecture
+
+There are some key decision points that are documented in [architecture](https://doc.networknt.com/service/oauth/architecture/) section.
+
+## Documentation
+
+The detailed [service document](https://doc.networknt.com/service/oauth/service/) help users to understand how each individual service
+works and the specification for each services. It also contains information on which scenarios will trigger what kind of errors.
+
+## Tutorial
+
+There are [tutorials](https://doc.networknt.com/tutorial/oauth/) for each service that shows how to use the most common use cases with examples.
+
+## Reference
+
+There are vast amount of information about OAuth 2.0 specifications and implementations.
+Here are some important [references](https://doc.networknt.com/service/oauth/reference/) that can help you to understand OAuth 2.0 Authorization.
+
diff --git a/authorize/docker/Dockerfile b/authorize/Dockerfile
similarity index 100%
rename from authorize/docker/Dockerfile
rename to authorize/Dockerfile
diff --git a/authorize/build.sh b/authorize/build.sh
index 8080f575..1a2e4be9 100755
--- a/authorize/build.sh
+++ b/authorize/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/authorize/pom.xml b/authorize/pom.xml
index 5b9168f8..3b412d6c 100644
--- a/authorize/pom.xml
+++ b/authorize/pom.xml
@@ -3,7 +3,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/authorize/src/main/resources/logback.xml b/authorize/src/main/resources/logback.xml
index 6e58aff8..455f137d 100644
--- a/authorize/src/main/resources/logback.xml
+++ b/authorize/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
diff --git a/authorize/src/test/resources/create_h2.sql b/authorize/src/test/resources/create_h2.sql
index ad8e3dea..52c37cdf 100644
--- a/authorize/src/test/resources/create_h2.sql
+++ b/authorize/src/test/resources/create_h2.sql
@@ -1,8 +1,8 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
@@ -13,13 +13,13 @@ create table users (
update_dt DATE
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
- client_type VARCHAR,
client_secret VARCHAR,
- client_profile VARCHAR,
+ client_type VARCHAR, -- public, confidential, trusted
+ client_profile VARCHAR, -- server, mobile, service, batch, browser
client_name VARCHAR,
client_desc VARCHAR,
scope VARCHAR,
@@ -30,7 +30,7 @@ create table clients (
update_dt DATE
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
service_type VARCHAR, -- api, ms
service_name VARCHAR,
@@ -41,10 +41,10 @@ create table services (
update_dt DATE
);
-INSERT INTO users(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/cache/pom.xml b/cache/pom.xml
index e9cf51f4..ba41e2b6 100644
--- a/cache/pom.xml
+++ b/cache/pom.xml
@@ -4,7 +4,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/cache/src/main/java/com/networknt/oauth/cache/CacheStartupHookProvider.java b/cache/src/main/java/com/networknt/oauth/cache/CacheStartupHookProvider.java
index aa770942..51731ad2 100644
--- a/cache/src/main/java/com/networknt/oauth/cache/CacheStartupHookProvider.java
+++ b/cache/src/main/java/com/networknt/oauth/cache/CacheStartupHookProvider.java
@@ -3,10 +3,7 @@
import com.hazelcast.config.*;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
-import com.networknt.oauth.cache.model.ClientDataSerializableFactory;
-import com.networknt.oauth.cache.model.RefreshTokenDataSerializableFactory;
-import com.networknt.oauth.cache.model.ServiceDataSerializableFactory;
-import com.networknt.oauth.cache.model.UserDataSerializableFactory;
+import com.networknt.oauth.cache.model.*;
import com.networknt.server.StartupHookProvider;
/**
@@ -25,6 +22,7 @@ public void onStartup() {
config.getSerializationConfig().addDataSerializableFactory(2, new ClientDataSerializableFactory());
config.getSerializationConfig().addDataSerializableFactory(3, new ServiceDataSerializableFactory());
config.getSerializationConfig().addDataSerializableFactory(4, new RefreshTokenDataSerializableFactory());
+ config.getSerializationConfig().addDataSerializableFactory(5, new ServiceEndpointDataSerializableFactory());
// service map with near cache.
MapConfig serviceConfig = new MapConfig();
@@ -41,6 +39,21 @@ public void onStartup() {
config.addMapConfig(serviceConfig);
+ // service endpoint map with near cache.
+ MapConfig serviceEndpointConfig = new MapConfig();
+ serviceEndpointConfig.setName("serviceEndpoints");
+ NearCacheConfig serviceEndpointCacheConfig = new NearCacheConfig();
+ serviceEndpointCacheConfig.setEvictionPolicy("NONE");
+ serviceEndpointCacheConfig.setInMemoryFormat(InMemoryFormat.OBJECT);
+ serviceEndpointCacheConfig.setCacheLocalEntries(true); // this enables the local caching
+ serviceEndpointConfig.setNearCacheConfig(serviceEndpointCacheConfig);
+
+ serviceEndpointConfig.getMapStoreConfig()
+ .setEnabled(true)
+ .setClassName("com.networknt.oauth.cache.ServiceEndpointMapStore");
+
+ config.addMapConfig(serviceEndpointConfig);
+
// client map with near cache.
MapConfig clientConfig = new MapConfig();
clientConfig.setName("clients");
diff --git a/cache/src/main/java/com/networknt/oauth/cache/ClientMapStore.java b/cache/src/main/java/com/networknt/oauth/cache/ClientMapStore.java
index 5106c97d..384ad5e3 100644
--- a/cache/src/main/java/com/networknt/oauth/cache/ClientMapStore.java
+++ b/cache/src/main/java/com/networknt/oauth/cache/ClientMapStore.java
@@ -19,11 +19,11 @@
public class ClientMapStore implements MapStore {
private static final Logger logger = LoggerFactory.getLogger(ClientMapStore.class);
private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
- private static final String insert = "INSERT INTO clients (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id, create_dt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
- private static final String delete = "DELETE FROM clients WHERE client_id = ?";
- private static final String select = "SELECT * FROM clients WHERE client_id = ?";
- private static final String update = "UPDATE clients SET client_type=?, client_profile=?, client_name=?, client_desc=?, scope=?, redirect_uri=?, owner_id=?, update_dt=? WHERE client_id=?";
- private static final String loadall = "SELECT client_id FROM clients";
+ private static final String insert = "INSERT INTO client (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ private static final String delete = "DELETE FROM client WHERE client_id = ?";
+ private static final String select = "SELECT * FROM client WHERE client_id = ?";
+ private static final String update = "UPDATE client SET client_type=?, client_profile=?, client_name=?, client_desc=?, scope=?, redirect_uri=?, owner_id=? WHERE client_id=?";
+ private static final String loadall = "SELECT client_id FROM client";
@Override
public synchronized void delete(String key) {
@@ -50,7 +50,6 @@ public synchronized void store(String key, Client client) {
stmt.setString(7, client.getScope());
stmt.setString(8, client.getRedirectUri());
stmt.setString(9, client.getOwnerId());
- stmt.setDate(10, client.getCreateDt());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -65,8 +64,7 @@ public synchronized void store(String key, Client client) {
stmt.setString(5, client.getScope());
stmt.setString(6, client.getRedirectUri());
stmt.setString(7, client.getOwnerId());
- stmt.setDate(8, client.getUpdateDt());
- stmt.setString(9, client.getClientId());
+ stmt.setString(8, client.getClientId());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -101,8 +99,6 @@ public synchronized Client load(String key) {
client.setScope(rs.getString("scope"));
client.setRedirectUri(rs.getString("redirect_uri"));
client.setOwnerId(rs.getString("owner_id"));
- client.setCreateDt(rs.getDate("create_dt"));
- client.setUpdateDt(rs.getDate("update_dt"));
}
}
} catch (SQLException e) {
diff --git a/cache/src/main/java/com/networknt/oauth/cache/ServiceEndpointMapStore.java b/cache/src/main/java/com/networknt/oauth/cache/ServiceEndpointMapStore.java
new file mode 100644
index 00000000..cacccf8b
--- /dev/null
+++ b/cache/src/main/java/com/networknt/oauth/cache/ServiceEndpointMapStore.java
@@ -0,0 +1,130 @@
+package com.networknt.oauth.cache;
+
+import com.hazelcast.core.MapStore;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
+import com.networknt.service.SingletonServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+public class ServiceEndpointMapStore implements MapStore> {
+ private static final Logger logger = LoggerFactory.getLogger(ServiceEndpointMapStore.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String insert = "INSERT INTO service_endpoint (service_id, endpoint, operation, scope) VALUES (?, ?, ?, ?)";
+ private static final String delete = "DELETE FROM service_endpoint WHERE service_id = ?";
+ private static final String select = "SELECT * FROM service_endpoint WHERE service_id = ?";
+ private static final String loadall = "SELECT service_id FROM service_endpoint";
+
+ @Override
+ public synchronized void store(String key, List serviceEndpoints) {
+ if(logger.isDebugEnabled()) logger.debug("Store:" + key);
+ try (Connection connection = ds.getConnection()) {
+ connection.setAutoCommit(false);
+ if(load(key) != null) {
+ // delete all endpoints for this serviceId first
+ try (PreparedStatement stmt = connection.prepareStatement(delete)) {
+ stmt.setString(1, key);
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+ }
+ try (PreparedStatement stmt = connection.prepareStatement(insert)) {
+ for (ServiceEndpoint serviceEndpoint : serviceEndpoints) {
+ stmt.setString(1, key);
+ stmt.setString(2, serviceEndpoint.getEndpoint());
+ stmt.setString(3, serviceEndpoint.getOperation());
+ stmt.setString(4, serviceEndpoint.getScope());
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+ connection.commit();
+ } catch (SQLException e) {
+ logger.error("SQLException:", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public synchronized void storeAll(Map> map) {
+ for (Map.Entry> entry : map.entrySet())
+ store(entry.getKey(), entry.getValue());
+ }
+
+ @Override
+ public synchronized void delete(String key) {
+ if(logger.isDebugEnabled()) logger.debug("Delete:" + key);
+ try (Connection connection = ds.getConnection(); PreparedStatement stmt = connection.prepareStatement(delete)) {
+ stmt.setString(1, key);
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public synchronized void deleteAll(Collection keys) {
+ keys.forEach(this::delete);
+ }
+
+ @Override
+ public synchronized List load(String key) {
+ if(logger.isDebugEnabled()) logger.debug("Load:" + key);
+ List serviceEndpoints = new ArrayList<>();
+ try (Connection connection = ds.getConnection(); PreparedStatement stmt = connection.prepareStatement(select)) {
+ stmt.setString(1, key);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ ServiceEndpoint serviceEndpoint = new ServiceEndpoint();
+ serviceEndpoint.setEndpoint(rs.getString("endpoint"));
+ serviceEndpoint.setOperation(rs.getString("operation"));
+ serviceEndpoint.setScope(rs.getString("scope"));
+ serviceEndpoints.add(serviceEndpoint);
+ }
+ }
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+ return serviceEndpoints;
+ }
+
+ @Override
+ public synchronized Map> loadAll(Collection keys) {
+ Map> result = new HashMap<>();
+ for (String key : keys) result.put(key, load(key));
+ return result;
+ }
+
+ @Override
+ public Iterable loadAllKeys() {
+ if(logger.isDebugEnabled()) logger.debug("loadAllKeys is called");
+ List keys = new ArrayList<>();
+ try (Connection connection = ds.getConnection(); PreparedStatement stmt = connection.prepareStatement(loadall)) {
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ keys.add(rs.getString("service_id"));
+ }
+ }
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+ return keys;
+ }
+}
diff --git a/cache/src/main/java/com/networknt/oauth/cache/ServiceMapStore.java b/cache/src/main/java/com/networknt/oauth/cache/ServiceMapStore.java
index 735d63f5..7eff5857 100644
--- a/cache/src/main/java/com/networknt/oauth/cache/ServiceMapStore.java
+++ b/cache/src/main/java/com/networknt/oauth/cache/ServiceMapStore.java
@@ -19,11 +19,11 @@
public class ServiceMapStore implements MapStore {
private static final Logger logger = LoggerFactory.getLogger(ServiceMapStore.class);
private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
- private static final String insert = "INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id, create_dt) VALUES (?, ?, ?, ?, ?, ?, ?)";
- private static final String delete = "DELETE FROM services WHERE service_id = ?";
- private static final String select = "SELECT * FROM services WHERE service_id = ?";
- private static final String update = "UPDATE services SET service_type = ?, service_name=?, service_desc=?, scope=?, owner_id=?, update_dt=? WHERE service_id=?";
- private static final String loadall = "SELECT service_id FROM services";
+ private static final String insert = "INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES (?, ?, ?, ?, ?, ?)";
+ private static final String delete = "DELETE FROM service WHERE service_id = ?";
+ private static final String select = "SELECT * FROM service WHERE service_id = ?";
+ private static final String update = "UPDATE service SET service_type = ?, service_name=?, service_desc=?, scope=?, owner_id=? WHERE service_id=?";
+ private static final String loadall = "SELECT service_id FROM service";
@Override
public synchronized void delete(String key) {
@@ -47,7 +47,6 @@ public synchronized void store(String key, Service service) {
stmt.setString(4, service.getServiceDesc());
stmt.setString(5, service.getScope());
stmt.setString(6, service.getOwnerId());
- stmt.setDate(7, service.getCreateDt());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -60,8 +59,7 @@ public synchronized void store(String key, Service service) {
stmt.setString(3, service.getServiceDesc());
stmt.setString(4, service.getScope());
stmt.setString(5, service.getOwnerId());
- stmt.setDate(6, service.getUpdateDt());
- stmt.setString(7, service.getServiceId());
+ stmt.setString(6, service.getServiceId());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -93,8 +91,6 @@ public synchronized Service load(String key) {
service.setServiceDesc(rs.getString("service_desc"));
service.setScope(rs.getString("scope"));
service.setOwnerId(rs.getString("owner_id"));
- service.setCreateDt(rs.getDate("create_dt"));
- service.setUpdateDt(rs.getDate("update_dt"));
}
}
} catch (SQLException e) {
diff --git a/cache/src/main/java/com/networknt/oauth/cache/UserMapStore.java b/cache/src/main/java/com/networknt/oauth/cache/UserMapStore.java
index d624f063..2524a427 100644
--- a/cache/src/main/java/com/networknt/oauth/cache/UserMapStore.java
+++ b/cache/src/main/java/com/networknt/oauth/cache/UserMapStore.java
@@ -17,13 +17,13 @@
* Created by stevehu on 2016-12-27.
*/
public class UserMapStore implements MapStore {
- static final Logger logger = LoggerFactory.getLogger(ServiceMapStore.class);
+ static final Logger logger = LoggerFactory.getLogger(UserMapStore.class);
static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
- private static final String insert = "INSERT INTO users (user_id, user_type, first_name, last_name, email, password, create_dt) VALUES (?, ?, ?, ?, ?, ?, ?)";
- private static final String delete = "DELETE FROM users WHERE user_id = ?";
- private static final String select = "SELECT * FROM users WHERE user_id = ?";
- private static final String update = "UPDATE users SET user_type=?, first_name=?, last_name=?, email=?, password=?, update_dt=? WHERE user_id = ?";
- private static final String loadall = "SELECT user_id FROM users";
+ private static final String insert = "INSERT INTO user_profile (user_id, user_type, first_name, last_name, email, password) VALUES (?, ?, ?, ?, ?, ?)";
+ private static final String delete = "DELETE FROM user_profile WHERE user_id = ?";
+ private static final String select = "SELECT * FROM user_profile WHERE user_id = ?";
+ private static final String update = "UPDATE user_profile SET user_type=?, first_name=?, last_name=?, email=?, password=? WHERE user_id = ?";
+ private static final String loadall = "SELECT user_id FROM user_profile";
@Override
public synchronized void delete(String key) {
@@ -47,7 +47,6 @@ public synchronized void store(String key, User user) {
stmt.setString(4, user.getLastName());
stmt.setString(5, user.getEmail());
stmt.setString(6, user.getPassword());
- stmt.setDate(7, user.getCreateDt());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -60,8 +59,7 @@ public synchronized void store(String key, User user) {
stmt.setString(3, user.getLastName());
stmt.setString(4, user.getEmail());
stmt.setString(5, user.getPassword());
- stmt.setDate(6, user.getUpdateDt());
- stmt.setString(7, user.getUserId());
+ stmt.setString(6, user.getUserId());
stmt.executeUpdate();
} catch (SQLException e) {
logger.error("Exception:", e);
@@ -93,8 +91,6 @@ public synchronized User load(String key) {
user.setLastName(rs.getString("last_name"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
- user.setCreateDt(rs.getDate("create_dt"));
- user.setUpdateDt(rs.getDate("update_dt"));
}
}
} catch (SQLException e) {
diff --git a/cache/src/main/java/com/networknt/oauth/cache/model/Service.java b/cache/src/main/java/com/networknt/oauth/cache/model/Service.java
index a96953ab..57399687 100644
--- a/cache/src/main/java/com/networknt/oauth/cache/model/Service.java
+++ b/cache/src/main/java/com/networknt/oauth/cache/model/Service.java
@@ -10,7 +10,6 @@
import io.swagger.annotations.ApiModelProperty;
import java.io.IOException;
-import java.sql.Date;
import java.util.Objects;
public class Service implements IdentifiedDataSerializable {
@@ -20,9 +19,10 @@ public class Service implements IdentifiedDataSerializable {
* service type
*/
public enum ServiceTypeEnum {
- MS("ms"),
-
- API("api");
+ SWAGGER("swagger"),
+ GRAPHQL("graphql"),
+ HYBRID("hybrid"),
+ OPENAPI("openapi");
private final String value;
@@ -57,10 +57,6 @@ public static ServiceTypeEnum fromValue(String text) {
private String scope = null;
- private Date createDt = null;
-
- private Date updateDt = null;
-
public Service serviceId(String serviceId) {
this.serviceId = serviceId;
return this;
@@ -151,37 +147,6 @@ public void setScope(String scope) {
this.scope = scope;
}
- public Service createDt(Date createDt) {
- this.createDt = createDt;
- return this;
- }
-
-
- @ApiModelProperty(example = "null", value = "create date time")
- @JsonProperty("createDt")
- public Date getCreateDt() {
- return createDt;
- }
- public void setCreateDt(Date createDt) {
- this.createDt = createDt;
- }
-
- public Service updateDt(Date updateDt) {
- this.updateDt = updateDt;
- return this;
- }
-
-
- @ApiModelProperty(example = "null", value = "update date time")
- @JsonProperty("updateDt")
- public Date getUpdateDt() {
- return updateDt;
- }
- public void setUpdateDt(Date updateDt) {
- this.updateDt = updateDt;
- }
-
-
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -196,14 +161,12 @@ public boolean equals(Object o) {
Objects.equals(serviceName, service.serviceName) &&
Objects.equals(serviceDesc, service.serviceDesc) &&
Objects.equals(ownerId, service.ownerId) &&
- Objects.equals(scope, service.scope) &&
- Objects.equals(createDt, service.createDt) &&
- Objects.equals(updateDt, service.updateDt);
+ Objects.equals(scope, service.scope);
}
@Override
public int hashCode() {
- return Objects.hash(serviceId, serviceType, serviceName, serviceDesc, ownerId, scope, createDt, updateDt);
+ return Objects.hash(serviceId, serviceType, serviceName, serviceDesc, ownerId, scope);
}
@Override
@@ -217,8 +180,6 @@ public String toString() {
sb.append(" serviceDesc: ").append(toIndentedString(serviceDesc)).append("\n");
sb.append(" ownerId: ").append(toIndentedString(ownerId)).append("\n");
sb.append(" scope: ").append(toIndentedString(scope)).append("\n");
- sb.append(" createDt: ").append(toIndentedString(createDt)).append("\n");
- sb.append(" updateDt: ").append(toIndentedString(updateDt)).append("\n");
sb.append("}");
return sb.toString();
}
@@ -246,8 +207,6 @@ public void readData(ObjectDataInput in) throws IOException {
this.serviceDesc = in.readUTF();
this.ownerId = in.readUTF();
this.scope = in.readUTF();
- this.createDt = in.readObject();
- this.updateDt = in.readObject();
}
@Override
@@ -258,8 +217,6 @@ public void writeData(ObjectDataOutput out) throws IOException {
out.writeUTF(this.serviceDesc);
out.writeUTF(this.ownerId);
out.writeUTF(this.scope);
- out.writeObject(this.createDt);
- out.writeObject(this.updateDt);
}
@JsonIgnore
diff --git a/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpoint.java b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpoint.java
new file mode 100644
index 00000000..7f795828
--- /dev/null
+++ b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpoint.java
@@ -0,0 +1,124 @@
+package com.networknt.oauth.cache.model;
+import java.io.IOException;
+import java.util.Objects;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.hazelcast.nio.ObjectDataInput;
+import com.hazelcast.nio.ObjectDataOutput;
+import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
+
+public class ServiceEndpoint implements IdentifiedDataSerializable {
+
+
+ private String operation;
+
+ private String endpoint;
+
+ private String scope;
+
+
+ public ServiceEndpoint () {
+ }
+
+
+
+ @JsonProperty("operation")
+ public String getOperation() {
+ return operation;
+ }
+
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+
+
+ @JsonProperty("endpoint")
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+
+
+ @JsonProperty("scope")
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ServiceEndpoint ServiceEndpoint = (ServiceEndpoint) o;
+
+ return Objects.equals(operation, ServiceEndpoint.operation) &&
+ Objects.equals(endpoint, ServiceEndpoint.endpoint) &&
+
+ Objects.equals(scope, ServiceEndpoint.scope);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(operation, endpoint, scope);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ServiceEndpoint {\n");
+
+ sb.append(" operation: ").append(toIndentedString(operation)).append("\n");
+ sb.append(" endpoint: ").append(toIndentedString(endpoint)).append("\n");
+ sb.append(" scope: ").append(toIndentedString(scope)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+ @Override
+ public int getFactoryId() {
+ return ServiceEndpointDataSerializableFactory.ID;
+ }
+
+ @Override
+ public int getId() {
+ return ServiceEndpointDataSerializableFactory.SERVICE_ENDPOINT_TYPE;
+ }
+
+ @Override
+ public void writeData(ObjectDataOutput objectDataOutput) throws IOException {
+ objectDataOutput.writeUTF(this.endpoint);
+ objectDataOutput.writeUTF(this.operation);
+ objectDataOutput.writeUTF(this.scope);
+ }
+
+ @Override
+ public void readData(ObjectDataInput objectDataInput) throws IOException {
+ this.endpoint = objectDataInput.readUTF();
+ this.operation = objectDataInput.readUTF();
+ this.scope = objectDataInput.readUTF();
+ }
+}
diff --git a/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointComparator.java b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointComparator.java
new file mode 100644
index 00000000..f1e292a8
--- /dev/null
+++ b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointComparator.java
@@ -0,0 +1,14 @@
+package com.networknt.oauth.cache.model;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Map;
+
+public class ServiceEndpointComparator implements Comparator, Serializable {
+ @Override
+ public int compare(Map.Entry o1, Map.Entry o2) {
+ ServiceEndpoint c1 = (ServiceEndpoint) o1.getValue();
+ ServiceEndpoint c2 = (ServiceEndpoint) o2.getValue();
+ return c1.getEndpoint().compareTo(c2.getEndpoint());
+ }
+}
diff --git a/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointDataSerializableFactory.java b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointDataSerializableFactory.java
new file mode 100644
index 00000000..32c4a1b3
--- /dev/null
+++ b/cache/src/main/java/com/networknt/oauth/cache/model/ServiceEndpointDataSerializableFactory.java
@@ -0,0 +1,19 @@
+package com.networknt.oauth.cache.model;
+
+import com.hazelcast.nio.serialization.DataSerializableFactory;
+import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
+
+public class ServiceEndpointDataSerializableFactory implements DataSerializableFactory {
+
+ static final int ID = 5;
+ static final int SERVICE_ENDPOINT_TYPE = 5;
+
+ @Override
+ public IdentifiedDataSerializable create(int typeId) {
+ if (typeId == SERVICE_ENDPOINT_TYPE) {
+ return new ServiceEndpoint();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/cache/src/main/resources/logback.xml b/cache/src/main/resources/logback.xml
index 6e58aff8..455f137d 100644
--- a/cache/src/main/resources/logback.xml
+++ b/cache/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
diff --git a/cache/src/test/java/com/networknt/oauth/cache/CacheStartupHookProviderTest.java b/cache/src/test/java/com/networknt/oauth/cache/CacheStartupHookProviderTest.java
index 9d187cd7..7d7ac879 100644
--- a/cache/src/test/java/com/networknt/oauth/cache/CacheStartupHookProviderTest.java
+++ b/cache/src/test/java/com/networknt/oauth/cache/CacheStartupHookProviderTest.java
@@ -5,6 +5,7 @@
import com.hazelcast.query.SqlPredicate;
import com.networknt.oauth.cache.model.Client;
import com.networknt.oauth.cache.model.Service;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
import com.networknt.oauth.cache.model.User;
import com.networknt.service.SingletonServiceFactory;
import org.h2.tools.RunScript;
@@ -18,6 +19,7 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -89,7 +91,7 @@ public void testServiceCache() {
Service service = services.get("AACT0001");
System.out.println("service = " + service);
- service.setServiceType(Service.ServiceTypeEnum.fromValue("api"));
+ service.setServiceType(Service.ServiceTypeEnum.fromValue("swagger"));
services.replace("AACT0001", service);
@@ -183,4 +185,25 @@ public void testRefreshTokenCache() {
}
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testServiceEndpointCache() {
+ CacheStartupHookProvider start = new CacheStartupHookProvider();
+ start.onStartup();
+
+ final IMap> serviceEndpoints = CacheStartupHookProvider.hz.getMap("serviceEndpoints");
+
+ List list = serviceEndpoints.get("AACT0001");
+ System.out.println("list size = " + list.size());
+
+ serviceEndpoints.delete("AACT0001");
+
+ System.out.println("list size = " + serviceEndpoints.size());
+
+ CacheShutdownHookProvider shutdown = new CacheShutdownHookProvider();
+ shutdown.onShutdown();
+
+ }
+
}
diff --git a/cache/src/test/resources/create_h2.sql b/cache/src/test/resources/create_h2.sql
index db248a09..c763a579 100644
--- a/cache/src/test/resources/create_h2.sql
+++ b/cache/src/test/resources/create_h2.sql
@@ -1,21 +1,22 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
+DROP table IF EXISTS service_endpoint;
+DROP table IF EXISTS client_service;
+DROP table IF EXISTS audit_log;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
last_name varchar,
email varchar,
- password varchar,
- create_dt DATE,
- update_dt DATE
+ password varchar
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
client_secret VARCHAR,
client_type VARCHAR, -- public, confidential, trusted
@@ -25,26 +26,56 @@ create table clients (
scope VARCHAR,
redirect_uri VARCHAR,
authenticate_class VARCHAR,
- owner_id VARCHAR,
- create_dt DATE,
- update_dt DATE
+ owner_id VARCHAR
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
- service_type VARCHAR, -- api, ms
+ service_type VARCHAR, -- swagger, openapi, graphql, hybrid
service_name VARCHAR,
service_desc VARCHAR,
scope VARCHAR,
- owner_id VARCHAR,
- create_dt DATE,
- update_dt DATE
+ owner_id VARCHAR
);
-INSERT INTO users(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+create table service_endpoint (
+ service_id VARCHAR,
+ endpoint VARCHAR, -- different framework will have different endpoint format.
+ operation VARCHAR,
+ scope VARCHAR,
+ PRIMARY KEY (service_id, endpoint),
+ FOREIGN KEY (service_id) REFERENCES service(service_id)
+);
+
+create table client_service (
+ client_id VARCHAR NOT NULL,
+ service_id VARCHAR NOT NULL,
+ endpoint VARCHAR NOT NULL, -- different framework will have different endpoint format.
+ PRIMARY KEY (client_id, service_id, endpoint),
+ FOREIGN KEY (service_id, endpoint) REFERENCES service_endpoint(service_id, endpoint),
+ FOREIGN KEY (client_id) REFERENCES client(client_id)
+);
+
+create table audit_log (
+ log_id VARCHAR NOT NULL,
+ action_dt DATE,
+ service_id VARCHAR NOT NULL,
+ path VARCHAR NOT NULL,
+ method VARCHAR NOT NULL,
+ request_header VARCHAR NOT NULL,
+ request_body VARCHAR,
+ response_code INT,
+ response_header VARCHAR NOT NULL,
+ response_body VARCHAR,
+ PRIMARY KEY (log_id)
+);
+
+
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'openapi', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO service_endpoint(service_id, endpoint, operation, scope) VALUES ('AACT0001', '/v1/data@get', 'retrieveData', 'data.r');
\ No newline at end of file
diff --git a/client/.gitignore b/client/.gitignore
index 3901d721..7883fdc0 100644
--- a/client/.gitignore
+++ b/client/.gitignore
@@ -13,6 +13,7 @@ dist/
*.tmp
*.zip
*.bak
+dependency-reduced-pom.xml
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
diff --git a/client/docker/Dockerfile b/client/Dockerfile
similarity index 100%
rename from client/docker/Dockerfile
rename to client/Dockerfile
diff --git a/client/build.sh b/client/build.sh
index b5d44c2b..1b59e8b3 100755
--- a/client/build.sh
+++ b/client/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/client/pom.xml b/client/pom.xml
index ca81d91a..2cf16d3d 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -4,7 +4,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/client/src/main/java/com/networknt/oauth/client/PathHandlerProvider.java b/client/src/main/java/com/networknt/oauth/client/PathHandlerProvider.java
index 471a0b2a..c6a4badf 100644
--- a/client/src/main/java/com/networknt/oauth/client/PathHandlerProvider.java
+++ b/client/src/main/java/com/networknt/oauth/client/PathHandlerProvider.java
@@ -1,26 +1,45 @@
+
package com.networknt.oauth.client;
-import com.networknt.health.HealthGetHandler;
-import com.networknt.info.ServerInfoGetHandler;
-import com.networknt.oauth.client.handler.*;
+import com.networknt.config.Config;
import com.networknt.server.HandlerProvider;
import io.undertow.Handlers;
import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
import io.undertow.util.Methods;
+import com.networknt.info.ServerInfoGetHandler;
+import com.networknt.health.HealthGetHandler;
+import com.networknt.oauth.client.handler.*;
public class PathHandlerProvider implements HandlerProvider {
@Override
public HttpHandler getHandler() {
- HttpHandler handler = Handlers.routing()
+ return Handlers.routing()
+
+ .add(Methods.DELETE, "/oauth2/client/{clientId}/service", new Oauth2ClientClientIdServiceDeleteHandler())
+
+ .add(Methods.GET, "/oauth2/client/{clientId}/service", new Oauth2ClientClientIdServiceGetHandler())
+
.add(Methods.GET, "/health", new HealthGetHandler())
- .add(Methods.GET, "/server/info", new ServerInfoGetHandler())
- .add(Methods.DELETE, "/oauth2/client/{clientId}", new Oauth2ClientClientIdDeleteHandler())
- .add(Methods.GET, "/oauth2/client/{clientId}", new Oauth2ClientClientIdGetHandler())
- .add(Methods.GET, "/oauth2/client", new Oauth2ClientGetHandler())
+
.add(Methods.POST, "/oauth2/client", new Oauth2ClientPostHandler())
+
.add(Methods.PUT, "/oauth2/client", new Oauth2ClientPutHandler())
+
+ .add(Methods.GET, "/oauth2/client", new Oauth2ClientGetHandler())
+
+ .add(Methods.DELETE, "/oauth2/client/{clientId}", new Oauth2ClientClientIdDeleteHandler())
+
+ .add(Methods.GET, "/oauth2/client/{clientId}", new Oauth2ClientClientIdGetHandler())
+
+ .add(Methods.POST, "/oauth2/client/{clientId}/service/{serviceId}", new Oauth2ClientClientIdServiceServiceIdPostHandler())
+
+ .add(Methods.DELETE, "/oauth2/client/{clientId}/service/{serviceId}", new Oauth2ClientClientIdServiceServiceIdDeleteHandler())
+
+ .add(Methods.GET, "/oauth2/client/{clientId}/service/{serviceId}", new Oauth2ClientClientIdServiceServiceIdGetHandler())
+
+ .add(Methods.GET, "/server/info", new ServerInfoGetHandler())
+
;
- return handler;
}
}
-
diff --git a/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandler.java b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandler.java
new file mode 100644
index 00000000..81de86de
--- /dev/null
+++ b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandler.java
@@ -0,0 +1,94 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Client;
+import com.networknt.service.SingletonServiceFactory;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * delete all services and endpoints for a client. It is a very dangerous API
+ * and be careful. It is supposed to be used only when a client is retired.
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ClientClientIdServiceDeleteHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdGetHandler.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String delete = "DELETE FROM client_service WHERE client_id = ?";
+ private static final String scope = "SELECT DISTINCT scope FROM client_service s, service_endpoint e WHERE s.service_id = e.service_id AND s.endpoint = e.endpoint AND client_id = ?";
+ private static final String CLIENT_NOT_FOUND = "ERR12014";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ // ensure that both clientId and serviceId exist.
+ String clientId = exchange.getQueryParameters().get("clientId").getFirst();
+ IMap clients = CacheStartupHookProvider.hz.getMap("clients");
+ Client client = clients.get(clientId);
+ if(client == null) {
+ Status status = new Status(CLIENT_NOT_FOUND, clientId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ Map result = new HashMap<>();
+ try (Connection connection = ds.getConnection()) {
+ connection.setAutoCommit(false);
+ try (PreparedStatement stmt = connection.prepareStatement(delete)) {
+ stmt.setString(1, clientId);
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+
+ StringJoiner joiner = new StringJoiner(" ");
+ try (PreparedStatement stmt = connection.prepareStatement(scope)) {
+ stmt.setString(1, clientId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ joiner.add(rs.getString("scope"));
+ }
+ }
+ }
+
+ String s = Arrays.stream(joiner.toString().split(" "))
+ .distinct()
+ .filter(st -> !st.isEmpty())
+ .collect(Collectors.joining(" "));
+
+ result.put("old_scope", client.getScope());
+ // update client scope in cache and db
+ client.setScope(s);
+ result.put("new_scope", s);
+
+ connection.commit();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(result));
+
+ }
+}
diff --git a/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandler.java b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandler.java
new file mode 100644
index 00000000..bd11b86e
--- /dev/null
+++ b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandler.java
@@ -0,0 +1,74 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Client;
+import com.networknt.service.SingletonServiceFactory;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * get all the linked services and endpoints for a client
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ClientClientIdServiceGetHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceGetHandler.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String select = "SELECT * FROM client_service WHERE client_id = ? ORDER BY service_id";
+ private static final String CLIENT_NOT_FOUND = "ERR12014";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ // ensure that clientId exists.
+ String clientId = exchange.getQueryParameters().get("clientId").getFirst();
+ IMap clients = CacheStartupHookProvider.hz.getMap("clients");
+ Client client = clients.get(clientId);
+ if(client == null) {
+ Status status = new Status(CLIENT_NOT_FOUND, clientId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ Map> serviceEndpoints = new HashMap<>();
+
+ try (Connection connection = ds.getConnection(); PreparedStatement stmt = connection.prepareStatement(select)) {
+ stmt.setString(1, clientId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ String serviceId = rs.getString("service_id");
+ String endpoint = rs.getString("endpoint");
+ List endpoints = serviceEndpoints.get(serviceId);
+ if(endpoints == null) {
+ endpoints = new ArrayList<>();
+ serviceEndpoints.put(serviceId, endpoints);
+ }
+ endpoints.add(endpoint);
+ }
+ }
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(serviceEndpoints));
+ }
+}
diff --git a/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandler.java b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandler.java
new file mode 100644
index 00000000..4ca26a5c
--- /dev/null
+++ b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandler.java
@@ -0,0 +1,103 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Client;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.service.SingletonServiceFactory;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * delete endpoints for a service that is linked to a client.
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ClientClientIdServiceServiceIdDeleteHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdGetHandler.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String delete = "DELETE FROM client_service WHERE client_id = ? AND service_id = ?";
+ private static final String scope = "SELECT DISTINCT scope FROM client_service s, service_endpoint e WHERE s.service_id = e.service_id AND s.endpoint = e.endpoint AND client_id = ?";
+ private static final String CLIENT_NOT_FOUND = "ERR12014";
+ private static final String SERVICE_NOT_FOUND = "ERR12015";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ // ensure that both clientId and serviceId exist.
+ String clientId = exchange.getQueryParameters().get("clientId").getFirst();
+ IMap clients = CacheStartupHookProvider.hz.getMap("clients");
+ Client client = clients.get(clientId);
+ if(client == null) {
+ Status status = new Status(CLIENT_NOT_FOUND, clientId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ String serviceId = exchange.getQueryParameters().get("serviceId").getFirst();
+ IMap services = CacheStartupHookProvider.hz.getMap("services");
+ if(services.get(serviceId) == null) {
+ Status status = new Status(SERVICE_NOT_FOUND, serviceId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ Map result = new HashMap<>();
+ try (Connection connection = ds.getConnection()) {
+ connection.setAutoCommit(false);
+ try (PreparedStatement stmt = connection.prepareStatement(delete)) {
+ stmt.setString(1, clientId);
+ stmt.setString(2, serviceId);
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+
+ StringJoiner joiner = new StringJoiner(" ");
+ try (PreparedStatement stmt = connection.prepareStatement(scope)) {
+ stmt.setString(1, clientId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ joiner.add(rs.getString("scope"));
+ }
+ }
+ }
+
+ String s = Arrays.stream(joiner.toString().split(" "))
+ .distinct()
+ .filter(st -> !st.isEmpty())
+ .collect(Collectors.joining(" "));
+
+ result.put("old_scope", client.getScope());
+ client.setScope(s);
+ result.put("new_scope", s);
+
+ connection.commit();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(result));
+ }
+}
diff --git a/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandler.java b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandler.java
new file mode 100644
index 00000000..bc8385ed
--- /dev/null
+++ b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandler.java
@@ -0,0 +1,78 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Client;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.service.SingletonServiceFactory;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * get a list of endpoints of a service linked to a client
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ClientClientIdServiceServiceIdGetHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdGetHandler.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String select = "SELECT * FROM client_service WHERE client_id = ? AND service_id = ?";
+ private static final String CLIENT_NOT_FOUND = "ERR12014";
+ private static final String SERVICE_NOT_FOUND = "ERR12015";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ // ensure that both clientId and serviceId exist.
+ String clientId = exchange.getQueryParameters().get("clientId").getFirst();
+ IMap clients = CacheStartupHookProvider.hz.getMap("clients");
+ Client client = clients.get(clientId);
+ if(client == null) {
+ Status status = new Status(CLIENT_NOT_FOUND, clientId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ String serviceId = exchange.getQueryParameters().get("serviceId").getFirst();
+ IMap services = CacheStartupHookProvider.hz.getMap("services");
+ if(services.get(serviceId) == null) {
+ Status status = new Status(SERVICE_NOT_FOUND, serviceId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+
+ List endpoints = new ArrayList<>();
+ try (Connection connection = ds.getConnection(); PreparedStatement stmt = connection.prepareStatement(select)) {
+ stmt.setString(1, clientId);
+ stmt.setString(2, serviceId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ endpoints.add(rs.getString("endpoint"));
+ }
+ }
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ throw new RuntimeException(e);
+ }
+
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(endpoints));
+ }
+}
diff --git a/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandler.java b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandler.java
new file mode 100644
index 00000000..10d463e8
--- /dev/null
+++ b/client/src/main/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandler.java
@@ -0,0 +1,121 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.body.BodyHandler;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Client;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
+import com.networknt.service.SingletonServiceFactory;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Add one or more endpoints of a service to a client
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ClientClientIdServiceServiceIdPostHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdPostHandler.class);
+ private static final DataSource ds = (DataSource) SingletonServiceFactory.getBean(DataSource.class);
+ private static final String insert = "INSERT INTO client_service (client_id, service_id, endpoint) VALUES (?, ?, ?)";
+ private static final String delete = "DELETE FROM client_service WHERE client_id = ? AND service_id = ?";
+ private static final String scope = "SELECT DISTINCT scope FROM client_service s, service_endpoint e WHERE s.service_id = e.service_id AND s.endpoint = e.endpoint AND client_id = ?";
+ private static final String CLIENT_NOT_FOUND = "ERR12014";
+ private static final String SERVICE_NOT_FOUND = "ERR12015";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ // ensure that both clientId and serviceId exist.
+ String clientId = exchange.getQueryParameters().get("clientId").getFirst();
+ IMap clients = CacheStartupHookProvider.hz.getMap("clients");
+ Client client = clients.get(clientId);
+ if(client == null) {
+ Status status = new Status(CLIENT_NOT_FOUND, clientId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ if(logger.isDebugEnabled()) logger.debug("Could not find clientId " + clientId);
+ return;
+ }
+
+ String serviceId = exchange.getQueryParameters().get("serviceId").getFirst();
+ IMap services = CacheStartupHookProvider.hz.getMap("services");
+ if(services.get(serviceId) == null) {
+ Status status = new Status(SERVICE_NOT_FOUND, serviceId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ if(logger.isDebugEnabled()) logger.debug("Could not find serviceId " + serviceId);
+ return;
+ }
+ List endpoints = (List)exchange.getAttachment(BodyHandler.REQUEST_BODY);
+ if(logger.isDebugEnabled()) logger.debug("Create endpoints {} for clientId {}, serviceId {}", endpoints, clientId, serviceId);
+ Map result = new HashMap<>();
+ if(endpoints != null && endpoints.size() > 0) {
+ try (Connection connection = ds.getConnection()) {
+ connection.setAutoCommit(false);
+ // remove existing endpoints and add new ones.
+ try (PreparedStatement stmt = connection.prepareStatement(delete)) {
+ stmt.setString(1, clientId);
+ stmt.setString(2, serviceId);
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+ try (PreparedStatement stmt = connection.prepareStatement(insert)) {
+ for (String endpoint : endpoints) {
+ stmt.setString(1, clientId);
+ stmt.setString(2, serviceId);
+ stmt.setString(3, endpoint);
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ } catch (SQLException e) {
+ logger.error("Exception:", e);
+ connection.rollback();
+ throw new RuntimeException(e);
+ }
+
+ StringJoiner joiner = new StringJoiner(" ");
+ try (PreparedStatement stmt = connection.prepareStatement(scope)) {
+ stmt.setString(1, clientId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ joiner.add(rs.getString("scope"));
+ }
+ }
+ }
+
+ String s = Arrays.stream(joiner.toString().split(" "))
+ .distinct()
+ .filter(st -> !st.isEmpty())
+ .collect(Collectors.joining(" "));
+ // update client scope in cache and db
+ result.put("old_scope", client.getScope());
+ client.setScope(s);
+ result.put("new_scope", s);
+ connection.commit();
+ } catch (SQLException e) {
+ logger.error("SQLException:", e);
+ throw new RuntimeException(e);
+ }
+ }
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(result));
+ }
+}
diff --git a/client/src/main/resources/logback.xml b/client/src/main/resources/logback.xml
index 6e58aff8..49df2a1c 100644
--- a/client/src/main/resources/logback.xml
+++ b/client/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -44,7 +44,7 @@
target/audit.log
- %-5level [%thread] %date{ISO8601} %F:%L - %msg%n
+ %-5level [%thread] %date{ISO8601} %X{sId} %X{cId} %F:%L - %msg%n
true
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/HealthGetHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/HealthGetHandlerTest.java
new file mode 100644
index 00000000..2e22d273
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/HealthGetHandlerTest.java
@@ -0,0 +1,68 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class HealthGetHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(HealthGetHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testHealthGetHandlerTest() throws ClientException, ApiException {
+ /*
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/health").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ */
+ }
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdDeleteHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdDeleteHandlerTest.java
index 84eada76..cf0c05f4 100644
--- a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdDeleteHandlerTest.java
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdDeleteHandlerTest.java
@@ -43,7 +43,7 @@ public void testOauth2ClientClientIdDeleteHandler() throws ClientException, ApiE
}
final AtomicReference reference = new AtomicReference<>();
try {
- ClientRequest request = new ClientRequest().setMethod(Methods.DELETE).setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01");
+ ClientRequest request = new ClientRequest().setMethod(Methods.DELETE).setPath("/oauth2/client/f7d42348-c647-4efb-a52d-4c5787421e72");
connection.sendRequest(request, client.createClientCallback(reference, latch));
latch.await();
} catch (Exception e) {
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandlerTest.java
new file mode 100644
index 00000000..f3768ed9
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceDeleteHandlerTest.java
@@ -0,0 +1,106 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.config.Config;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import com.networknt.status.Status;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class Oauth2ClientClientIdServiceDeleteHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceDeleteHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testOauth2ClientClientIdServiceDeleteHandlerTest() throws ClientException, ApiException {
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01/service").setMethod(Methods.DELETE);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ Assert.assertTrue(body.indexOf("new_scope") > 0);
+ Assert.assertTrue(body.indexOf("old_scope") > 0);
+
+ }
+
+ @Test
+ public void testClientNotFound() throws ClientException, IOException {
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/fake/service").setMethod(Methods.DELETE);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12014", status.getCode());
+ Assert.assertEquals("CLIENT_NOT_FOUND", status.getMessage());
+ }
+ }
+
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandlerTest.java
new file mode 100644
index 00000000..9698ff34
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceGetHandlerTest.java
@@ -0,0 +1,107 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.config.Config;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import com.networknt.status.Status;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class Oauth2ClientClientIdServiceGetHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceGetHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testOauth2ClientClientIdServiceGetHandlerTest() throws ClientException, ApiException {
+
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01/service").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+
+ }
+
+ @Test
+ public void testClientNotFound() throws ClientException, IOException {
+
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/fake/service").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12014", status.getCode());
+ Assert.assertEquals("CLIENT_NOT_FOUND", status.getMessage());
+ }
+
+ }
+
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandlerTest.java
new file mode 100644
index 00000000..3424abf5
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdDeleteHandlerTest.java
@@ -0,0 +1,142 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.config.Config;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import com.networknt.status.Status;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class Oauth2ClientClientIdServiceServiceIdDeleteHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdDeleteHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testOauth2ClientClientIdServiceServiceIdDeleteHandlerTest() throws ClientException, ApiException {
+
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/f7d42348-c647-4efb-a52d-4c5787421e72/service/AACT0001").setMethod(Methods.DELETE);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ Assert.assertTrue(body.indexOf("new_scope") > 0);
+ Assert.assertTrue(body.indexOf("old_scope") > 0);
+ }
+
+ @Test
+ public void testClientNotFound() throws ClientException, IOException {
+
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/fake/service/AACT0001").setMethod(Methods.DELETE);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12014", status.getCode());
+ Assert.assertEquals("CLIENT_NOT_FOUND", status.getMessage());
+ }
+ }
+
+ @Test
+ public void testServiceNotFound() throws ClientException, IOException {
+
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/f7d42348-c647-4efb-a52d-4c5787421e72/service/fake").setMethod(Methods.DELETE);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12015", status.getCode());
+ Assert.assertEquals("SERVICE_NOT_FOUND", status.getMessage());
+ }
+ }
+
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandlerTest.java
new file mode 100644
index 00000000..10001d82
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdGetHandlerTest.java
@@ -0,0 +1,137 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.config.Config;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import com.networknt.status.Status;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class Oauth2ClientClientIdServiceServiceIdGetHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdGetHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testOauth2ClientClientIdServiceServiceIdGetHandlerTest() throws ClientException, ApiException {
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01/service/AACT0001").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ }
+
+ @Test
+ public void testClientNotFound() throws ClientException, IOException {
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/fake/service/AACT0001").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12014", status.getCode());
+ Assert.assertEquals("CLIENT_NOT_FOUND", status.getMessage());
+ }
+ }
+
+ @Test
+ public void testServiceNotFound() throws ClientException, IOException {
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/f7d42348-c647-4efb-a52d-4c5787421e72/service/fake").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12015", status.getCode());
+ Assert.assertEquals("SERVICE_NOT_FOUND", status.getMessage());
+ }
+ }
+
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandlerTest.java
new file mode 100644
index 00000000..94ce4397
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientClientIdServiceServiceIdPostHandlerTest.java
@@ -0,0 +1,158 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.config.Config;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import com.networknt.status.Status;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class Oauth2ClientClientIdServiceServiceIdPostHandlerTest {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ClientClientIdServiceServiceIdPostHandlerTest.class);
+
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testOauth2ClientClientIdServiceServiceIdPostHandlerTest() throws ClientException, ApiException {
+ logger.debug("Successful test");
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ String requestBody = "[\"/v1/data@post\",\"/v1/data@put\",\"/v1/data@get\",\"/v1/data@delete\"]";
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01/service/AACT0001").setMethod(Methods.POST);
+
+ request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json");
+ request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked");
+ connection.sendRequest(request, client.createClientCallback(reference, latch, requestBody));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ logger.debug("statusCode = " + statusCode + " body = " + body);
+ System.out.println("********************statusCode = " + statusCode + " body = " + body);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ Assert.assertTrue(body.indexOf("new_scope") > 0);
+ Assert.assertTrue(body.indexOf("old_scope") > 0);
+ }
+
+
+ @Test
+ public void testClientNotFound() throws ClientException, IOException {
+ logger.debug("Client not found test");
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ String requestBody = "[\"/v1/data@post\",\"/v1/data@put\",\"/v1/data@get\",\"/v1/data@delete\"]";
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/fake/service/AACT0001").setMethod(Methods.POST);
+
+ request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json");
+ request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked");
+ connection.sendRequest(request, client.createClientCallback(reference, latch, requestBody));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ logger.debug("statusCode = " + statusCode + " body = " + body);
+ System.out.println("********************statusCode = " + statusCode + " body = " + body);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12014", status.getCode());
+ Assert.assertEquals("CLIENT_NOT_FOUND", status.getMessage());
+ }
+ }
+
+ @Test
+ public void testSerivceNotFound() throws ClientException, IOException {
+ logger.debug("Service not found test");
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ String requestBody = "[\"/v1/data@post\",\"/v1/data@put\",\"/v1/data@get\",\"/v1/data@delete\"]";
+ try {
+ ClientRequest request = new ClientRequest().setPath("/oauth2/client/59f347a0-c92d-11e6-9d9d-cec0c932ce01/service/fake").setMethod(Methods.POST);
+
+ request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json");
+ request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked");
+ connection.sendRequest(request, client.createClientCallback(reference, latch, requestBody));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ logger.debug("statusCode = " + statusCode + " body = " + body);
+ System.out.println("********************statusCode = " + statusCode + " body = " + body);
+ Assert.assertEquals(404, statusCode);
+ if(statusCode == 404) {
+ Status status = Config.getInstance().getMapper().readValue(body, Status.class);
+ Assert.assertNotNull(status);
+ Assert.assertEquals("ERR12015", status.getCode());
+ Assert.assertEquals("SERVICE_NOT_FOUND", status.getMessage());
+ }
+ }
+
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientPutHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientPutHandlerTest.java
index 607bb7a1..07b294de 100644
--- a/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientPutHandlerTest.java
+++ b/client/src/test/java/com/networknt/oauth/client/handler/Oauth2ClientPutHandlerTest.java
@@ -36,7 +36,7 @@ public class Oauth2ClientPutHandlerTest {
@Test
public void testOauth2ClientPutHandler() throws ClientException, ApiException, UnsupportedEncodingException {
- String s = "{\"clientId\":\"f7d42348-c647-4efb-a52d-4c5787421e72\",\"clientType\":\"trusted\",\"clientProfile\":\"webserver\",\"clientName\":\"Retail Account\",\"clientDesc\":\"Microservices for Retail Account\",\"scope\":\"act.r act.w\", \"redirectUri\": \"http://localhost:8080/authorization\", \"ownerId\":\"admin\"}";
+ String s = "{\"clientId\":\"59f347a0-c92d-11e6-9d9d-cec0c932ce01\",\"clientType\":\"trusted\",\"clientProfile\":\"webserver\",\"clientName\":\"Retail Account\",\"clientDesc\":\"Microservices for Retail Account\",\"scope\":\"act.r act.w\", \"redirectUri\": \"http://localhost:8080/authorization\", \"ownerId\":\"admin\"}";
final AtomicReference reference = new AtomicReference<>();
final Http2Client client = Http2Client.getInstance();
final CountDownLatch latch = new CountDownLatch(1);
@@ -75,7 +75,7 @@ public void run() {
@Test
public void testOwnerNotFound() throws ClientException, ApiException, UnsupportedEncodingException {
- String s = "{\"clientId\":\"f7d42348-c647-4efb-a52d-4c5787421e72\",\"clientType\":\"trusted\",\"clientProfile\":\"webserver\",\"clientName\":\"Retail Account\",\"clientDesc\":\"Microservices for Retail Account\",\"scope\":\"act.r act.w\", \"redirectUri\": \"http://localhost:8080/authorization\", \"ownerId\":\"fake\"}";
+ String s = "{\"clientId\":\"59f347a0-c92d-11e6-9d9d-cec0c932ce01\",\"clientType\":\"trusted\",\"clientProfile\":\"webserver\",\"clientName\":\"Retail Account\",\"clientDesc\":\"Microservices for Retail Account\",\"scope\":\"act.r act.w\", \"redirectUri\": \"http://localhost:8080/authorization\", \"ownerId\":\"fake\"}";
final AtomicReference reference = new AtomicReference<>();
final Http2Client client = Http2Client.getInstance();
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/ServerInfoGetHandlerTest.java b/client/src/test/java/com/networknt/oauth/client/handler/ServerInfoGetHandlerTest.java
new file mode 100644
index 00000000..975947c5
--- /dev/null
+++ b/client/src/test/java/com/networknt/oauth/client/handler/ServerInfoGetHandlerTest.java
@@ -0,0 +1,68 @@
+
+package com.networknt.oauth.client.handler;
+
+import com.networknt.client.Http2Client;
+import com.networknt.exception.ApiException;
+import com.networknt.exception.ClientException;
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Headers;
+import io.undertow.util.Methods;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import java.io.IOException;
+
+
+public class ServerInfoGetHandlerTest {
+ @ClassRule
+ public static TestServer server = TestServer.getInstance();
+
+ static final Logger logger = LoggerFactory.getLogger(ServerInfoGetHandlerTest.class);
+ static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2();
+ static final boolean enableHttps = server.getServerConfig().isEnableHttps();
+ static final int httpPort = server.getServerConfig().getHttpPort();
+ static final int httpsPort = server.getServerConfig().getHttpsPort();
+ static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort;
+
+ @Test
+ public void testServerInfoGetHandlerTest() throws ClientException, ApiException {
+ /*
+ final Http2Client client = Http2Client.getInstance();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ClientConnection connection;
+ try {
+ connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get();
+ } catch (Exception e) {
+ throw new ClientException(e);
+ }
+ final AtomicReference reference = new AtomicReference<>();
+ try {
+ ClientRequest request = new ClientRequest().setPath("/server/info").setMethod(Methods.GET);
+
+ connection.sendRequest(request, client.createClientCallback(reference, latch));
+
+ latch.await();
+ } catch (Exception e) {
+ logger.error("Exception: ", e);
+ throw new ClientException(e);
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ int statusCode = reference.get().getResponseCode();
+ String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY);
+ Assert.assertEquals(200, statusCode);
+ Assert.assertNotNull(body);
+ */
+ }
+}
diff --git a/client/src/test/java/com/networknt/oauth/client/handler/TestServer.java b/client/src/test/java/com/networknt/oauth/client/handler/TestServer.java
index 1011c0a5..9e841ad0 100644
--- a/client/src/test/java/com/networknt/oauth/client/handler/TestServer.java
+++ b/client/src/test/java/com/networknt/oauth/client/handler/TestServer.java
@@ -1,6 +1,7 @@
package com.networknt.oauth.client.handler;
import com.networknt.server.Server;
+import com.networknt.server.ServerConfig;
import com.networknt.service.SingletonServiceFactory;
import org.h2.tools.RunScript;
import org.junit.rules.ExternalResource;
@@ -46,6 +47,10 @@ private TestServer() {
}
+ public ServerConfig getServerConfig() {
+ return Server.config;
+ }
+
@Override
protected void before() {
try {
diff --git a/client/src/test/resources/config/swagger.json b/client/src/test/resources/config/swagger.json
index 5b1dc0de..1c42a623 100644
--- a/client/src/test/resources/config/swagger.json
+++ b/client/src/test/resources/config/swagger.json
@@ -198,31 +198,183 @@
]
}
},
- "/server/info": {
- "get": {
+ "/oauth2/client/{clientId}/service": {
+ "delete": {
+ "description": "Delete all associated services for a client by clientId",
+ "operationId": "deleteAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
"security": [
{
"client_auth": [
- "server.info.r"
+ "oauth.client.w"
]
}
+ ]
+ },
+ "get": {
+ "description": "Get all associated services and endpoints by clientId",
+ "operationId": "getAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
],
"responses": {
"200": {
- "description": "successful operation"
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
}
},
- "parameters": []
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
}
},
- "/health": {
+ "/oauth2/client/{clientId}/service/{serviceId}": {
+ "post": {
+ "description": "Link a service and its endpoints to a client",
+ "operationId": "linkClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "description": "A list of endpoints that needs to be linked to the client",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints of a service for a client",
+ "operationId": "deleteClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
"get": {
+ "description": "Get linked endpoints of a service from a client",
+ "operationId": "getClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
"responses": {
"200": {
- "description": "successful operation"
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
}
},
- "parameters": []
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
}
}
},
@@ -233,8 +385,7 @@
"flow": "implicit",
"scopes": {
"oauth.client.w": "write oauth client",
- "oauth.client.r": "read oauth client",
- "server.info.r": "read server info"
+ "oauth.client.r": "read oauth client"
}
}
},
@@ -297,16 +448,6 @@
"redirectUri": {
"type": "string",
"description": "redirect uri"
- },
- "createDt": {
- "type": "string",
- "format": "date-time",
- "description": "create date time"
- },
- "updateDt": {
- "type": "string",
- "format": "date-time",
- "description": "update date time"
}
}
}
diff --git a/client/src/test/resources/create_h2.sql b/client/src/test/resources/create_h2.sql
index 35b11b97..6723afc7 100644
--- a/client/src/test/resources/create_h2.sql
+++ b/client/src/test/resources/create_h2.sql
@@ -1,50 +1,86 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
+DROP table IF EXISTS service_endpoint;
+DROP table IF EXISTS client_service;
+DROP table IF EXISTS audit_log;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
last_name varchar,
email varchar,
- password varchar,
- create_dt DATE,
- update_dt DATE
+ password varchar
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
client_secret VARCHAR,
client_type VARCHAR, -- public, confidential, trusted
- client_profile VARCHAR, -- webserver, browser, mobile, service, batch
+ client_profile VARCHAR, -- server, mobile, service, batch, browser
client_name VARCHAR,
client_desc VARCHAR,
scope VARCHAR,
redirect_uri VARCHAR,
authenticate_class VARCHAR,
- owner_id VARCHAR,
- create_dt DATE,
- update_dt DATE
+ owner_id VARCHAR
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
- service_type VARCHAR, -- api, ms
+ service_type VARCHAR, -- swagger, openapi, graphql, hybrid
service_name VARCHAR,
service_desc VARCHAR,
scope VARCHAR,
- owner_id VARCHAR,
- create_dt DATE,
- update_dt DATE
+ owner_id VARCHAR
);
-INSERT INTO users(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+create table service_endpoint (
+ service_id VARCHAR,
+ endpoint VARCHAR, -- different framework will have different endpoint format.
+ operation VARCHAR,
+ scope VARCHAR,
+ PRIMARY KEY (service_id, endpoint),
+ FOREIGN KEY (service_id) REFERENCES service(service_id)
+);
+
+create table client_service (
+ client_id VARCHAR NOT NULL,
+ service_id VARCHAR NOT NULL,
+ endpoint VARCHAR NOT NULL, -- different framework will have different endpoint format.
+ PRIMARY KEY (client_id, service_id, endpoint),
+ FOREIGN KEY (service_id, endpoint) REFERENCES service_endpoint(service_id, endpoint),
+ FOREIGN KEY (client_id) REFERENCES client(client_id)
+);
+
+create table audit_log (
+ log_id VARCHAR NOT NULL,
+ action_dt DATE,
+ service_id VARCHAR NOT NULL,
+ path VARCHAR NOT NULL,
+ method VARCHAR NOT NULL,
+ request_header VARCHAR NOT NULL,
+ request_body VARCHAR,
+ response_code INT,
+ response_header VARCHAR NOT NULL,
+ response_body VARCHAR,
+ PRIMARY KEY (log_id)
+);
+
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'swagger', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO service_endpoint(service_id, endpoint, operation, scope) VALUES ('AACT0001', '/v1/data@post', 'post', 'data.w');
+INSERT INTO service_endpoint(service_id, endpoint, operation, scope) VALUES ('AACT0001', '/v1/data@put', 'put', 'data.w');
+INSERT INTO service_endpoint(service_id, endpoint, operation, scope) VALUES ('AACT0001', '/v1/data@delete', 'delete', 'data.w');
+INSERT INTO service_endpoint(service_id, endpoint, operation, scope) VALUES ('AACT0001', '/v1/data@get', 'get', 'data.w data.r');
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO client_service(client_id, service_id, endpoint) VALUES ('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'AACT0001', '/v1/data@get');
\ No newline at end of file
diff --git a/code/docker/Dockerfile b/code/Dockerfile
similarity index 100%
rename from code/docker/Dockerfile
rename to code/Dockerfile
diff --git a/code/build.sh b/code/build.sh
index a4e45344..faf205cc 100755
--- a/code/build.sh
+++ b/code/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/code/pom.xml b/code/pom.xml
index 89e9787b..447df99b 100644
--- a/code/pom.xml
+++ b/code/pom.xml
@@ -3,7 +3,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/code/src/main/resources/logback.xml b/code/src/main/resources/logback.xml
index 6e58aff8..455f137d 100644
--- a/code/src/main/resources/logback.xml
+++ b/code/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
diff --git a/code/src/test/resources/create_h2.sql b/code/src/test/resources/create_h2.sql
index ad8e3dea..52c37cdf 100644
--- a/code/src/test/resources/create_h2.sql
+++ b/code/src/test/resources/create_h2.sql
@@ -1,8 +1,8 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
@@ -13,13 +13,13 @@ create table users (
update_dt DATE
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
- client_type VARCHAR,
client_secret VARCHAR,
- client_profile VARCHAR,
+ client_type VARCHAR, -- public, confidential, trusted
+ client_profile VARCHAR, -- server, mobile, service, batch, browser
client_name VARCHAR,
client_desc VARCHAR,
scope VARCHAR,
@@ -30,7 +30,7 @@ create table clients (
update_dt DATE
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
service_type VARCHAR, -- api, ms
service_name VARCHAR,
@@ -41,10 +41,10 @@ create table services (
update_dt DATE
);
-INSERT INTO users(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/db/mysql/config/oauth2-client/cors.yml b/db/mysql/config/oauth2-client/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/mysql/config/oauth2-client/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-client/oauth/primary.crt b/db/mysql/config/oauth2-client/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-client/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-client/oauth/secondary.crt b/db/mysql/config/oauth2-client/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-client/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-client/secret.yml b/db/mysql/config/oauth2-client/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-client/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-client/security.yml b/db/mysql/config/oauth2-client/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-client/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-client/server.yml b/db/mysql/config/oauth2-client/server.yml
new file mode 100644
index 00000000..151859b6
--- /dev/null
+++ b/db/mysql/config/oauth2-client/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6884
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6884
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-client-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-client/swagger.json b/db/mysql/config/oauth2-client/swagger.json
new file mode 100644
index 00000000..1c42a623
--- /dev/null
+++ b/db/mysql/config/oauth2-client/swagger.json
@@ -0,0 +1,455 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Client Registration",
+ "description": "OAuth2 Client Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/client": {
+ "post": {
+ "description": "Return a client object",
+ "operationId": "createClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated client",
+ "operationId": "updateClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all clients",
+ "operationId": "getAllClient",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "clientName",
+ "in": "query",
+ "description": "Partial clientName for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}": {
+ "delete": {
+ "description": "Delete a client by Id",
+ "operationId": "deleteClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a client by Id",
+ "operationId": "getClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ },
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service": {
+ "delete": {
+ "description": "Delete all associated services for a client by clientId",
+ "operationId": "deleteAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all associated services and endpoints by clientId",
+ "operationId": "getAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service/{serviceId}": {
+ "post": {
+ "description": "Link a service and its endpoints to a client",
+ "operationId": "linkClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "description": "A list of endpoints that needs to be linked to the client",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints of a service for a client",
+ "operationId": "deleteClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get linked endpoints of a service from a client",
+ "operationId": "getClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "client_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.client.w": "write oauth client",
+ "oauth.client.r": "read oauth client"
+ }
+ }
+ },
+ "definitions": {
+ "Client": {
+ "type": "object",
+ "required": [
+ "clientType",
+ "clientProfile",
+ "clientName",
+ "clientDesc",
+ "ownerId",
+ "scope"
+ ],
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "description": "a unique client id"
+ },
+ "clientSecret": {
+ "type": "string",
+ "description": "client secret"
+ },
+ "clientType": {
+ "type": "string",
+ "description": "client type",
+ "enum": [
+ "confidential",
+ "public",
+ "trusted"
+ ]
+ },
+ "clientProfile": {
+ "type": "string",
+ "description": "client profile",
+ "enum": [
+ "webserver",
+ "browser",
+ "mobile",
+ "service",
+ "batch"
+ ]
+ },
+ "clientName": {
+ "type": "string",
+ "description": "client name"
+ },
+ "clientDesc": {
+ "type": "string",
+ "description": "client description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "client owner id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "client scope separated by space"
+ },
+ "redirectUri": {
+ "type": "string",
+ "description": "redirect uri"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-client/tls/server.keystore b/db/mysql/config/oauth2-client/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-client/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-client/tls/server.truststore b/db/mysql/config/oauth2-client/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-client/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-code/cors.yml b/db/mysql/config/oauth2-code/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/mysql/config/oauth2-code/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-code/jwt.yml b/db/mysql/config/oauth2-code/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/mysql/config/oauth2-code/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/mysql/config/oauth2-code/oauth/primary.crt b/db/mysql/config/oauth2-code/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-code/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-code/oauth/secondary.crt b/db/mysql/config/oauth2-code/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-code/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-code/secret.yml b/db/mysql/config/oauth2-code/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-code/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-code/security.yml b/db/mysql/config/oauth2-code/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-code/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-code/server.yml b/db/mysql/config/oauth2-code/server.yml
new file mode 100644
index 00000000..95816ef8
--- /dev/null
+++ b/db/mysql/config/oauth2-code/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6881
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6881
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-code-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-code/swagger.json b/db/mysql/config/oauth2-code/swagger.json
new file mode 100644
index 00000000..8e24d450
--- /dev/null
+++ b/db/mysql/config/oauth2-code/swagger.json
@@ -0,0 +1,197 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Authorization Code",
+ "description": "OAuth2 Service that logs in user and provide authorization code.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/code": {
+ "get": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "getAuthCode",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "description": "encoded username:password mandatory if Basic Authentication is used",
+ "in": "header",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "query",
+ "description": "The response type for authorization code",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "query",
+ "description": "The client id for authorization code",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "query",
+ "description": "The redirect uri for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for authorization code in clear text",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "query",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "query",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "query",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "query",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ },
+ "post": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "postAuthCode",
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "j_username",
+ "in": "formData",
+ "description": "User name",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "j_password",
+ "in": "formData",
+ "description": "Password",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "formData",
+ "description": "Response type",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "formData",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "formData",
+ "description": "Redirect Uri",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "formData",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "formData",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "formData",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "formData",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-code/tls/server.keystore b/db/mysql/config/oauth2-code/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-code/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-code/tls/server.truststore b/db/mysql/config/oauth2-code/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-code/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-key/cors.yml b/db/mysql/config/oauth2-key/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/mysql/config/oauth2-key/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-key/oauth/primary.crt b/db/mysql/config/oauth2-key/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-key/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-key/oauth/secondary.crt b/db/mysql/config/oauth2-key/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-key/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-key/secret.yml b/db/mysql/config/oauth2-key/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-key/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-key/security.yml b/db/mysql/config/oauth2-key/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-key/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-key/server.yml b/db/mysql/config/oauth2-key/server.yml
new file mode 100644
index 00000000..454ba61a
--- /dev/null
+++ b/db/mysql/config/oauth2-key/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6886
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6886
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-key-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-key/swagger.json b/db/mysql/config/oauth2-key/swagger.json
new file mode 100644
index 00000000..1f8a6c2a
--- /dev/null
+++ b/db/mysql/config/oauth2-key/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 Key Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Key Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/key/{keyId}": {
+ "get": {
+ "description": "Get a key by Id",
+ "operationId": "getKeyById",
+ "parameters": [
+ {
+ "name": "keyId",
+ "in": "path",
+ "description": "Key Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Key"
+ }
+ },
+ "400": {
+ "description": "Invalid keyId supplied"
+ },
+ "404": {
+ "description": "Key not found"
+ }
+ },
+ "security": [
+ {
+ "key_auth": [
+ "oauth.key.r",
+ "oauth.key.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "key_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "key_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.key.w": "write key",
+ "oauth.key.r": "read key",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "Key": {
+ "type": "object",
+ "required": [
+ "certificate",
+ "keyId"
+ ],
+ "properties": {
+ "keyId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "certificate": {
+ "type": "string",
+ "description": "certificate"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-key/tls/server.keystore b/db/mysql/config/oauth2-key/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-key/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-key/tls/server.truststore b/db/mysql/config/oauth2-key/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-key/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-refresh-token/cors.yml b/db/mysql/config/oauth2-refresh-token/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-refresh-token/oauth/primary.crt b/db/mysql/config/oauth2-refresh-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-refresh-token/oauth/secondary.crt b/db/mysql/config/oauth2-refresh-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-refresh-token/secret.yml b/db/mysql/config/oauth2-refresh-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-refresh-token/security.yml b/db/mysql/config/oauth2-refresh-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-refresh-token/server.yml b/db/mysql/config/oauth2-refresh-token/server.yml
new file mode 100644
index 00000000..58ec6aec
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6887
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6887
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-refresh-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-refresh-token/swagger.json b/db/mysql/config/oauth2-refresh-token/swagger.json
new file mode 100644
index 00000000..fc2c1fbb
--- /dev/null
+++ b/db/mysql/config/oauth2-refresh-token/swagger.json
@@ -0,0 +1,215 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 refresh token management microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Refresh Token Management",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/refresh_token": {
+ "get": {
+ "description": "Return all refresh tokens",
+ "operationId": "getAllRefreshToken",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/refresh_token/{refreshToken}": {
+ "get": {
+ "description": "Get a refresh token",
+ "operationId": "getRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ },
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r",
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a refresh token",
+ "operationId": "deleteRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh Token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "refresh_token_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "refresh_token_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.refresh_token.w": "write oauth refresh token",
+ "oauth.refresh_token.r": "read oauth refresh token",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "RefreshToken": {
+ "type": "object",
+ "required": [
+ "clientId",
+ "refreshToken",
+ "userId"
+ ],
+ "properties": {
+ "refreshToken": {
+ "type": "string",
+ "description": "refresh token"
+ },
+ "userId": {
+ "type": "string",
+ "description": "user id"
+ },
+ "clientId": {
+ "type": "string",
+ "description": "client id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-refresh-token/tls/server.keystore b/db/mysql/config/oauth2-refresh-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-refresh-token/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-refresh-token/tls/server.truststore b/db/mysql/config/oauth2-refresh-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-refresh-token/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-service/cors.yml b/db/mysql/config/oauth2-service/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/mysql/config/oauth2-service/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-service/oauth/primary.crt b/db/mysql/config/oauth2-service/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-service/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-service/oauth/secondary.crt b/db/mysql/config/oauth2-service/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-service/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-service/secret.yml b/db/mysql/config/oauth2-service/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-service/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-service/security.yml b/db/mysql/config/oauth2-service/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-service/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-service/server.yml b/db/mysql/config/oauth2-service/server.yml
new file mode 100644
index 00000000..ff558b00
--- /dev/null
+++ b/db/mysql/config/oauth2-service/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6883
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6883
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-service-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-service/swagger.json b/db/mysql/config/oauth2-service/swagger.json
new file mode 100644
index 00000000..c51d14a3
--- /dev/null
+++ b/db/mysql/config/oauth2-service/swagger.json
@@ -0,0 +1,393 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Registration",
+ "description": "OAuth2 Service Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/service": {
+ "post": {
+ "description": "Return a service object",
+ "operationId": "createService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated service",
+ "operationId": "updateService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all services",
+ "operationId": "getAllService",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "serviceId",
+ "in": "query",
+ "description": "Partial serviceId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}": {
+ "delete": {
+ "description": "Delete a service by Id",
+ "operationId": "deleteService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a service by Id",
+ "operationId": "getService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}/endpoint": {
+ "post": {
+ "description": "create endpoints for service",
+ "operationId": "createServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "body",
+ "name": "body",
+ "description": "A list of endpoint object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints for a service",
+ "operationId": "deleteServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all endpoints for a service",
+ "operationId": "getServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "ServiceEndpoint not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "service_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.service.w": "write oauth service",
+ "oauth.service.r": "read oauth service"
+ }
+ }
+ },
+ "definitions": {
+ "Service": {
+ "type": "object",
+ "required": [
+ "serviceId",
+ "serviceName",
+ "serviceType",
+ "scope"
+ ],
+ "properties": {
+ "serviceId": {
+ "type": "string",
+ "description": "a unique service id"
+ },
+ "serviceType": {
+ "type": "string",
+ "description": "service type",
+ "enum": [
+ "swagger",
+ "openapi",
+ "graphql",
+ "hybrid"
+ ]
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "service name"
+ },
+ "serviceDesc": {
+ "type": "string",
+ "description": "service description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "service owner userId"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "ServiceEndpoint": {
+ "type": "object",
+ "required": [
+ "endpoint",
+ "operation",
+ "scope"
+ ],
+ "properties": {
+ "endpoint": {
+ "type": "string",
+ "description": "a combination of path and method to uniquely identify an operation"
+ },
+ "operation": {
+ "type": "string",
+ "description": "operationId of the endpoint"
+ },
+ "scope": {
+ "type": "string",
+ "description": "scope associated with the endpoint"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-service/tls/server.keystore b/db/mysql/config/oauth2-service/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-service/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-service/tls/server.truststore b/db/mysql/config/oauth2-service/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-service/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-token/cors.yml b/db/mysql/config/oauth2-token/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/mysql/config/oauth2-token/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-token/jwt.yml b/db/mysql/config/oauth2-token/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/mysql/config/oauth2-token/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/mysql/config/oauth2-token/oauth/primary.crt b/db/mysql/config/oauth2-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-token/oauth/primary.jks b/db/mysql/config/oauth2-token/oauth/primary.jks
new file mode 100644
index 00000000..0d6ae7cf
Binary files /dev/null and b/db/mysql/config/oauth2-token/oauth/primary.jks differ
diff --git a/db/mysql/config/oauth2-token/oauth/secondary.crt b/db/mysql/config/oauth2-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-token/oauth/secondary.jks b/db/mysql/config/oauth2-token/oauth/secondary.jks
new file mode 100644
index 00000000..33824c89
Binary files /dev/null and b/db/mysql/config/oauth2-token/oauth/secondary.jks differ
diff --git a/db/mysql/config/oauth2-token/secret.yml b/db/mysql/config/oauth2-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-token/security.yml b/db/mysql/config/oauth2-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-token/server.yml b/db/mysql/config/oauth2-token/server.yml
new file mode 100644
index 00000000..2a414220
--- /dev/null
+++ b/db/mysql/config/oauth2-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6882
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6882
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-token/swagger.json b/db/mysql/config/oauth2-token/swagger.json
new file mode 100644
index 00000000..b56f7433
--- /dev/null
+++ b/db/mysql/config/oauth2-token/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Token Service",
+ "description": "OAuth2 Service that issues access tokens.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/token": {
+ "post": {
+ "description": "JSON object that contains access token",
+ "operationId": "postToken",
+ "parameters": [
+ {
+ "name": "authorization",
+ "description": "encoded client_id and client_secret pair",
+ "in": "header",
+ "type": "string",
+ "required": false
+ },
+ {
+ "name": "grant_type",
+ "type": "string",
+ "enum": [
+ "authorization_code",
+ "client_credentials",
+ "password",
+ "refresh_token",
+ "client_authenticated_user"
+ ],
+ "required": true,
+ "in": "formData"
+ },
+ {
+ "name": "client_id",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "client_secret",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code",
+ "description": "used in authorization_code to specify the code",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "username",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "password",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "scope",
+ "description": "used by all flows to specify scope in the access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "redirect_uri",
+ "description": "used in authorization code if code endpoint with rediret_uri",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "refresh_token",
+ "description": "refresh token used to get another access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code_verifier",
+ "description": "PKCE code verifier",
+ "type": "string",
+ "in": "formData"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ }
+}
diff --git a/db/mysql/config/oauth2-token/tls/server.keystore b/db/mysql/config/oauth2-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-token/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-token/tls/server.truststore b/db/mysql/config/oauth2-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-token/tls/server.truststore differ
diff --git a/db/mysql/config/oauth2-user/cors.yml b/db/mysql/config/oauth2-user/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/mysql/config/oauth2-user/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/mysql/config/oauth2-user/oauth/primary.crt b/db/mysql/config/oauth2-user/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/mysql/config/oauth2-user/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-user/oauth/secondary.crt b/db/mysql/config/oauth2-user/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/mysql/config/oauth2-user/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/mysql/config/oauth2-user/secret.yml b/db/mysql/config/oauth2-user/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/mysql/config/oauth2-user/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/mysql/config/oauth2-user/security.yml b/db/mysql/config/oauth2-user/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/mysql/config/oauth2-user/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/mysql/config/oauth2-user/server.yml b/db/mysql/config/oauth2-user/server.yml
new file mode 100644
index 00000000..8718d5a6
--- /dev/null
+++ b/db/mysql/config/oauth2-user/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6885
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6885
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-user-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/mysql/config/oauth2-user/swagger.json b/db/mysql/config/oauth2-user/swagger.json
new file mode 100644
index 00000000..9717cc2a
--- /dev/null
+++ b/db/mysql/config/oauth2-user/swagger.json
@@ -0,0 +1,368 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 User Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 User Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/password/{userId}": {
+ "post": {
+ "description": "Reset Password",
+ "operationId": "resetPassword",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Password object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Password"
+ }
+ },
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user": {
+ "get": {
+ "description": "Return all users",
+ "operationId": "getAllUsers",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "post": {
+ "description": "Return a user object",
+ "operationId": "createUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "put": {
+ "description": "Return the updated user",
+ "operationId": "updateUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user/{userId}": {
+ "get": {
+ "description": "Get a user by Id",
+ "operationId": "getUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ },
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r",
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a user by Id",
+ "operationId": "deleteUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "service_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "user_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.user.w": "write user",
+ "oauth.user.r": "read user",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "User": {
+ "type": "object",
+ "required": [
+ "email",
+ "firstName",
+ "lastName",
+ "userId",
+ "userType"
+ ],
+ "properties": {
+ "userId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "userType": {
+ "type": "string",
+ "description": "user type",
+ "enum": [
+ "admin",
+ "employee",
+ "customer",
+ "partner"
+ ]
+ },
+ "firstName": {
+ "type": "string",
+ "description": "first name"
+ },
+ "lastName": {
+ "type": "string",
+ "description": "last name"
+ },
+ "email": {
+ "type": "string",
+ "description": "email address"
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "password"
+ },
+ "passwordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "password confirm"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "Password": {
+ "type": "object",
+ "required": [
+ "newPassword",
+ "newPasswordConfirm",
+ "password"
+ ],
+ "properties": {
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "existing password"
+ },
+ "newPassword": {
+ "type": "string",
+ "format": "password",
+ "description": "new password"
+ },
+ "newPasswordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "new password confirm"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/mysql/config/oauth2-user/tls/server.keystore b/db/mysql/config/oauth2-user/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/mysql/config/oauth2-user/tls/server.keystore differ
diff --git a/db/mysql/config/oauth2-user/tls/server.truststore b/db/mysql/config/oauth2-user/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/mysql/config/oauth2-user/tls/server.truststore differ
diff --git a/db/mysql/create_mysql.sql b/db/mysql/create_mysql.sql
index 35bd6a11..27c2dfba 100644
--- a/db/mysql/create_mysql.sql
+++ b/db/mysql/create_mysql.sql
@@ -2,56 +2,86 @@ DROP DATABASE IF EXISTS oauth2;
CREATE DATABASE oauth2;
USE oauth2;
-DROP TABLE IF EXISTS users;
-CREATE TABLE users (
+DROP TABLE IF EXISTS user_profile;
+CREATE TABLE user_profile (
user_id VARCHAR(32) NOT NULL,
user_type VARCHAR(16) NOT NULL, -- admin, customer, employee, partner
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
email VARCHAR(64) NOT NULL,
password VARCHAR(1024) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (user_id)
)
ENGINE=INNODB;
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-DROP TABLE IF EXISTS clients;
-CREATE TABLE clients (
+DROP TABLE IF EXISTS client;
+CREATE TABLE client (
client_id VARCHAR(36) NOT NULL,
client_type VARCHAR(12) NOT NULL, -- public, confidential, trusted
client_profile VARCHAR(10) NOT NULL, -- webserver, mobile, browser, batch, service
client_secret VARCHAR(1024) NOT NULL,
client_name VARCHAR(32) NOT NULL,
client_desc VARCHAR(2048),
- scope VARCHAR(1024),
+ scope VARCHAR(4096),
redirect_uri VARCHAR(1024),
authenticate_class VARCHAR(256),
owner_id VARCHAR(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (client_id),
- FOREIGN KEY (owner_id) REFERENCES users(user_id)
+ FOREIGN KEY (owner_id) REFERENCES user_profile(user_id)
)
ENGINE=INNODB;
-DROP TABLE IF EXISTS services;
-CREATE TABLE services (
+DROP TABLE IF EXISTS service;
+CREATE TABLE service (
service_id VARCHAR(32) NOT NULL,
- service_type VARCHAR(8) NOT NULL, -- api, ms
+ service_type VARCHAR(16) NOT NULL, -- swagger, openapi, graphql, hybrid
service_name VARCHAR(32) NOT NULL,
service_desc VARCHAR(1024),
scope VARCHAR(1024),
owner_id VARCHAR(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (service_id),
- FOREIGN KEY (owner_id) REFERENCES users(user_id)
+ FOREIGN KEY (owner_id) REFERENCES user_profile(user_id)
+)
+ENGINE=INNODB;
+
+DROP TABLE IF EXISTS service_endpoint;
+CREATE TABLE service_endpoint (
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL, -- different framework will have different endpoint format.
+ operation VARCHAR(256) NOT NULL,
+ scope VARCHAR(64) NOT NULL,
+ PRIMARY KEY (service_id, endpoint),
+ FOREIGN KEY (service_id) REFERENCES service(service_id)
)
ENGINE=INNODB;
+DROP TABLE IF EXISTS client_service;
+CREATE TABLE client_service (
+ client_id VARCHAR(36) NOT NULL,
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL, -- different framework will have different endpoint format.
+ PRIMARY KEY (client_id, service_id, endpoint),
+ FOREIGN KEY (service_id, endpoint) REFERENCES service_endpoint(service_id, endpoint),
+ FOREIGN KEY (client_id) REFERENCES client(client_id)
+)
+ENGINE=INNODB;
+
+DROP TABLE IF EXISTS audit_log;
+create table audit_log (
+ log_id INT, -- system milliseonds from 1970.
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL,
+ request_header VARCHAR(4096),
+ request_body VARCHAR(4096),
+ response_code INT,
+ response_header VARCHAR(4096),
+ response_body VARCHAR(4096)
+)
+ENGINE=INNODB;
+
+/*
CREATE TABLE IF NOT EXISTS client (
client_id VARCHAR(32) NOT NULL,
client_secret VARCHAR(512) NOT NULL,
@@ -105,14 +135,14 @@ CREATE TABLE IF NOT EXISTS client (
PRIMARY KEY (client_id)
)
ENGINE=INNODB;
+*/
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password)
+VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO users (user_id, user_type, first_name, last_name, email, password, create_dt)
-VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992', NOW());
-
-INSERT INTO clients (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id, create_dt)
-VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin', NOW());
+INSERT INTO client (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id)
+VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin');
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id, create_dt)
-VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin', NOW());
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id)
+VALUES ('AACT0001', 'openapi', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/db/oracle/config/oauth2-client/cors.yml b/db/oracle/config/oauth2-client/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/oracle/config/oauth2-client/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-client/oauth/primary.crt b/db/oracle/config/oauth2-client/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-client/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-client/oauth/secondary.crt b/db/oracle/config/oauth2-client/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-client/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-client/secret.yml b/db/oracle/config/oauth2-client/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-client/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-client/security.yml b/db/oracle/config/oauth2-client/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-client/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-client/server.yml b/db/oracle/config/oauth2-client/server.yml
new file mode 100644
index 00000000..151859b6
--- /dev/null
+++ b/db/oracle/config/oauth2-client/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6884
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6884
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-client-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-client/swagger.json b/db/oracle/config/oauth2-client/swagger.json
new file mode 100644
index 00000000..1c42a623
--- /dev/null
+++ b/db/oracle/config/oauth2-client/swagger.json
@@ -0,0 +1,455 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Client Registration",
+ "description": "OAuth2 Client Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/client": {
+ "post": {
+ "description": "Return a client object",
+ "operationId": "createClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated client",
+ "operationId": "updateClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all clients",
+ "operationId": "getAllClient",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "clientName",
+ "in": "query",
+ "description": "Partial clientName for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}": {
+ "delete": {
+ "description": "Delete a client by Id",
+ "operationId": "deleteClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a client by Id",
+ "operationId": "getClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ },
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service": {
+ "delete": {
+ "description": "Delete all associated services for a client by clientId",
+ "operationId": "deleteAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all associated services and endpoints by clientId",
+ "operationId": "getAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service/{serviceId}": {
+ "post": {
+ "description": "Link a service and its endpoints to a client",
+ "operationId": "linkClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "description": "A list of endpoints that needs to be linked to the client",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints of a service for a client",
+ "operationId": "deleteClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get linked endpoints of a service from a client",
+ "operationId": "getClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "client_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.client.w": "write oauth client",
+ "oauth.client.r": "read oauth client"
+ }
+ }
+ },
+ "definitions": {
+ "Client": {
+ "type": "object",
+ "required": [
+ "clientType",
+ "clientProfile",
+ "clientName",
+ "clientDesc",
+ "ownerId",
+ "scope"
+ ],
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "description": "a unique client id"
+ },
+ "clientSecret": {
+ "type": "string",
+ "description": "client secret"
+ },
+ "clientType": {
+ "type": "string",
+ "description": "client type",
+ "enum": [
+ "confidential",
+ "public",
+ "trusted"
+ ]
+ },
+ "clientProfile": {
+ "type": "string",
+ "description": "client profile",
+ "enum": [
+ "webserver",
+ "browser",
+ "mobile",
+ "service",
+ "batch"
+ ]
+ },
+ "clientName": {
+ "type": "string",
+ "description": "client name"
+ },
+ "clientDesc": {
+ "type": "string",
+ "description": "client description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "client owner id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "client scope separated by space"
+ },
+ "redirectUri": {
+ "type": "string",
+ "description": "redirect uri"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-client/tls/server.keystore b/db/oracle/config/oauth2-client/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-client/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-client/tls/server.truststore b/db/oracle/config/oauth2-client/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-client/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-code/cors.yml b/db/oracle/config/oauth2-code/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/oracle/config/oauth2-code/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-code/jwt.yml b/db/oracle/config/oauth2-code/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/oracle/config/oauth2-code/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/oracle/config/oauth2-code/oauth/primary.crt b/db/oracle/config/oauth2-code/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-code/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-code/oauth/secondary.crt b/db/oracle/config/oauth2-code/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-code/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-code/secret.yml b/db/oracle/config/oauth2-code/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-code/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-code/security.yml b/db/oracle/config/oauth2-code/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-code/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-code/server.yml b/db/oracle/config/oauth2-code/server.yml
new file mode 100644
index 00000000..95816ef8
--- /dev/null
+++ b/db/oracle/config/oauth2-code/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6881
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6881
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-code-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-code/swagger.json b/db/oracle/config/oauth2-code/swagger.json
new file mode 100644
index 00000000..8e24d450
--- /dev/null
+++ b/db/oracle/config/oauth2-code/swagger.json
@@ -0,0 +1,197 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Authorization Code",
+ "description": "OAuth2 Service that logs in user and provide authorization code.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/code": {
+ "get": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "getAuthCode",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "description": "encoded username:password mandatory if Basic Authentication is used",
+ "in": "header",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "query",
+ "description": "The response type for authorization code",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "query",
+ "description": "The client id for authorization code",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "query",
+ "description": "The redirect uri for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for authorization code in clear text",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "query",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "query",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "query",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "query",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ },
+ "post": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "postAuthCode",
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "j_username",
+ "in": "formData",
+ "description": "User name",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "j_password",
+ "in": "formData",
+ "description": "Password",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "formData",
+ "description": "Response type",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "formData",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "formData",
+ "description": "Redirect Uri",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "formData",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "formData",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "formData",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "formData",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-code/tls/server.keystore b/db/oracle/config/oauth2-code/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-code/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-code/tls/server.truststore b/db/oracle/config/oauth2-code/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-code/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-key/cors.yml b/db/oracle/config/oauth2-key/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/oracle/config/oauth2-key/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-key/oauth/primary.crt b/db/oracle/config/oauth2-key/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-key/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-key/oauth/secondary.crt b/db/oracle/config/oauth2-key/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-key/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-key/secret.yml b/db/oracle/config/oauth2-key/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-key/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-key/security.yml b/db/oracle/config/oauth2-key/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-key/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-key/server.yml b/db/oracle/config/oauth2-key/server.yml
new file mode 100644
index 00000000..454ba61a
--- /dev/null
+++ b/db/oracle/config/oauth2-key/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6886
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6886
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-key-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-key/swagger.json b/db/oracle/config/oauth2-key/swagger.json
new file mode 100644
index 00000000..1f8a6c2a
--- /dev/null
+++ b/db/oracle/config/oauth2-key/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 Key Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Key Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/key/{keyId}": {
+ "get": {
+ "description": "Get a key by Id",
+ "operationId": "getKeyById",
+ "parameters": [
+ {
+ "name": "keyId",
+ "in": "path",
+ "description": "Key Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Key"
+ }
+ },
+ "400": {
+ "description": "Invalid keyId supplied"
+ },
+ "404": {
+ "description": "Key not found"
+ }
+ },
+ "security": [
+ {
+ "key_auth": [
+ "oauth.key.r",
+ "oauth.key.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "key_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "key_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.key.w": "write key",
+ "oauth.key.r": "read key",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "Key": {
+ "type": "object",
+ "required": [
+ "certificate",
+ "keyId"
+ ],
+ "properties": {
+ "keyId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "certificate": {
+ "type": "string",
+ "description": "certificate"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-key/tls/server.keystore b/db/oracle/config/oauth2-key/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-key/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-key/tls/server.truststore b/db/oracle/config/oauth2-key/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-key/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-refresh-token/cors.yml b/db/oracle/config/oauth2-refresh-token/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-refresh-token/oauth/primary.crt b/db/oracle/config/oauth2-refresh-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-refresh-token/oauth/secondary.crt b/db/oracle/config/oauth2-refresh-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-refresh-token/primary.crt b/db/oracle/config/oauth2-refresh-token/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-refresh-token/secret.yml b/db/oracle/config/oauth2-refresh-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-refresh-token/security.yml b/db/oracle/config/oauth2-refresh-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-refresh-token/server.yml b/db/oracle/config/oauth2-refresh-token/server.yml
new file mode 100644
index 00000000..58ec6aec
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6887
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6887
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-refresh-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-refresh-token/swagger.json b/db/oracle/config/oauth2-refresh-token/swagger.json
new file mode 100644
index 00000000..fc2c1fbb
--- /dev/null
+++ b/db/oracle/config/oauth2-refresh-token/swagger.json
@@ -0,0 +1,215 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 refresh token management microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Refresh Token Management",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/refresh_token": {
+ "get": {
+ "description": "Return all refresh tokens",
+ "operationId": "getAllRefreshToken",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/refresh_token/{refreshToken}": {
+ "get": {
+ "description": "Get a refresh token",
+ "operationId": "getRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ },
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r",
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a refresh token",
+ "operationId": "deleteRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh Token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "refresh_token_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "refresh_token_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.refresh_token.w": "write oauth refresh token",
+ "oauth.refresh_token.r": "read oauth refresh token",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "RefreshToken": {
+ "type": "object",
+ "required": [
+ "clientId",
+ "refreshToken",
+ "userId"
+ ],
+ "properties": {
+ "refreshToken": {
+ "type": "string",
+ "description": "refresh token"
+ },
+ "userId": {
+ "type": "string",
+ "description": "user id"
+ },
+ "clientId": {
+ "type": "string",
+ "description": "client id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-refresh-token/tls/server.keystore b/db/oracle/config/oauth2-refresh-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-refresh-token/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-refresh-token/tls/server.truststore b/db/oracle/config/oauth2-refresh-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-refresh-token/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-service/cors.yml b/db/oracle/config/oauth2-service/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/oracle/config/oauth2-service/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-service/oauth/primary.crt b/db/oracle/config/oauth2-service/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-service/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-service/oauth/secondary.crt b/db/oracle/config/oauth2-service/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-service/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-service/primary.crt b/db/oracle/config/oauth2-service/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-service/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-service/secret.yml b/db/oracle/config/oauth2-service/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-service/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-service/security.yml b/db/oracle/config/oauth2-service/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-service/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-service/server.yml b/db/oracle/config/oauth2-service/server.yml
new file mode 100644
index 00000000..ff558b00
--- /dev/null
+++ b/db/oracle/config/oauth2-service/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6883
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6883
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-service-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-service/swagger.json b/db/oracle/config/oauth2-service/swagger.json
new file mode 100644
index 00000000..c51d14a3
--- /dev/null
+++ b/db/oracle/config/oauth2-service/swagger.json
@@ -0,0 +1,393 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Registration",
+ "description": "OAuth2 Service Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/service": {
+ "post": {
+ "description": "Return a service object",
+ "operationId": "createService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated service",
+ "operationId": "updateService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all services",
+ "operationId": "getAllService",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "serviceId",
+ "in": "query",
+ "description": "Partial serviceId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}": {
+ "delete": {
+ "description": "Delete a service by Id",
+ "operationId": "deleteService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a service by Id",
+ "operationId": "getService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}/endpoint": {
+ "post": {
+ "description": "create endpoints for service",
+ "operationId": "createServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "body",
+ "name": "body",
+ "description": "A list of endpoint object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints for a service",
+ "operationId": "deleteServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all endpoints for a service",
+ "operationId": "getServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "ServiceEndpoint not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "service_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.service.w": "write oauth service",
+ "oauth.service.r": "read oauth service"
+ }
+ }
+ },
+ "definitions": {
+ "Service": {
+ "type": "object",
+ "required": [
+ "serviceId",
+ "serviceName",
+ "serviceType",
+ "scope"
+ ],
+ "properties": {
+ "serviceId": {
+ "type": "string",
+ "description": "a unique service id"
+ },
+ "serviceType": {
+ "type": "string",
+ "description": "service type",
+ "enum": [
+ "swagger",
+ "openapi",
+ "graphql",
+ "hybrid"
+ ]
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "service name"
+ },
+ "serviceDesc": {
+ "type": "string",
+ "description": "service description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "service owner userId"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "ServiceEndpoint": {
+ "type": "object",
+ "required": [
+ "endpoint",
+ "operation",
+ "scope"
+ ],
+ "properties": {
+ "endpoint": {
+ "type": "string",
+ "description": "a combination of path and method to uniquely identify an operation"
+ },
+ "operation": {
+ "type": "string",
+ "description": "operationId of the endpoint"
+ },
+ "scope": {
+ "type": "string",
+ "description": "scope associated with the endpoint"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-service/tls/server.keystore b/db/oracle/config/oauth2-service/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-service/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-service/tls/server.truststore b/db/oracle/config/oauth2-service/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-service/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-token/cors.yml b/db/oracle/config/oauth2-token/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/oracle/config/oauth2-token/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-token/jwt.yml b/db/oracle/config/oauth2-token/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/oracle/config/oauth2-token/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/oracle/config/oauth2-token/oauth/primary.crt b/db/oracle/config/oauth2-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-token/oauth/primary.jks b/db/oracle/config/oauth2-token/oauth/primary.jks
new file mode 100644
index 00000000..0d6ae7cf
Binary files /dev/null and b/db/oracle/config/oauth2-token/oauth/primary.jks differ
diff --git a/db/oracle/config/oauth2-token/oauth/secondary.crt b/db/oracle/config/oauth2-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-token/oauth/secondary.jks b/db/oracle/config/oauth2-token/oauth/secondary.jks
new file mode 100644
index 00000000..33824c89
Binary files /dev/null and b/db/oracle/config/oauth2-token/oauth/secondary.jks differ
diff --git a/db/oracle/config/oauth2-token/secret.yml b/db/oracle/config/oauth2-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-token/security.yml b/db/oracle/config/oauth2-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-token/server.yml b/db/oracle/config/oauth2-token/server.yml
new file mode 100644
index 00000000..2a414220
--- /dev/null
+++ b/db/oracle/config/oauth2-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6882
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6882
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-token/swagger.json b/db/oracle/config/oauth2-token/swagger.json
new file mode 100644
index 00000000..b56f7433
--- /dev/null
+++ b/db/oracle/config/oauth2-token/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Token Service",
+ "description": "OAuth2 Service that issues access tokens.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/token": {
+ "post": {
+ "description": "JSON object that contains access token",
+ "operationId": "postToken",
+ "parameters": [
+ {
+ "name": "authorization",
+ "description": "encoded client_id and client_secret pair",
+ "in": "header",
+ "type": "string",
+ "required": false
+ },
+ {
+ "name": "grant_type",
+ "type": "string",
+ "enum": [
+ "authorization_code",
+ "client_credentials",
+ "password",
+ "refresh_token",
+ "client_authenticated_user"
+ ],
+ "required": true,
+ "in": "formData"
+ },
+ {
+ "name": "client_id",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "client_secret",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code",
+ "description": "used in authorization_code to specify the code",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "username",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "password",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "scope",
+ "description": "used by all flows to specify scope in the access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "redirect_uri",
+ "description": "used in authorization code if code endpoint with rediret_uri",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "refresh_token",
+ "description": "refresh token used to get another access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code_verifier",
+ "description": "PKCE code verifier",
+ "type": "string",
+ "in": "formData"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ }
+}
diff --git a/db/oracle/config/oauth2-token/tls/server.keystore b/db/oracle/config/oauth2-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-token/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-token/tls/server.truststore b/db/oracle/config/oauth2-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-token/tls/server.truststore differ
diff --git a/db/oracle/config/oauth2-user/cors.yml b/db/oracle/config/oauth2-user/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/oracle/config/oauth2-user/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/oracle/config/oauth2-user/oauth/primary.crt b/db/oracle/config/oauth2-user/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/oracle/config/oauth2-user/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-user/oauth/secondary.crt b/db/oracle/config/oauth2-user/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/oracle/config/oauth2-user/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/oracle/config/oauth2-user/secret.yml b/db/oracle/config/oauth2-user/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/oracle/config/oauth2-user/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/oracle/config/oauth2-user/security.yml b/db/oracle/config/oauth2-user/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/oracle/config/oauth2-user/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/oracle/config/oauth2-user/server.yml b/db/oracle/config/oauth2-user/server.yml
new file mode 100644
index 00000000..8718d5a6
--- /dev/null
+++ b/db/oracle/config/oauth2-user/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6885
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6885
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-user-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/oracle/config/oauth2-user/swagger.json b/db/oracle/config/oauth2-user/swagger.json
new file mode 100644
index 00000000..9717cc2a
--- /dev/null
+++ b/db/oracle/config/oauth2-user/swagger.json
@@ -0,0 +1,368 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 User Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 User Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/password/{userId}": {
+ "post": {
+ "description": "Reset Password",
+ "operationId": "resetPassword",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Password object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Password"
+ }
+ },
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user": {
+ "get": {
+ "description": "Return all users",
+ "operationId": "getAllUsers",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "post": {
+ "description": "Return a user object",
+ "operationId": "createUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "put": {
+ "description": "Return the updated user",
+ "operationId": "updateUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user/{userId}": {
+ "get": {
+ "description": "Get a user by Id",
+ "operationId": "getUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ },
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r",
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a user by Id",
+ "operationId": "deleteUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "service_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "user_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.user.w": "write user",
+ "oauth.user.r": "read user",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "User": {
+ "type": "object",
+ "required": [
+ "email",
+ "firstName",
+ "lastName",
+ "userId",
+ "userType"
+ ],
+ "properties": {
+ "userId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "userType": {
+ "type": "string",
+ "description": "user type",
+ "enum": [
+ "admin",
+ "employee",
+ "customer",
+ "partner"
+ ]
+ },
+ "firstName": {
+ "type": "string",
+ "description": "first name"
+ },
+ "lastName": {
+ "type": "string",
+ "description": "last name"
+ },
+ "email": {
+ "type": "string",
+ "description": "email address"
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "password"
+ },
+ "passwordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "password confirm"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "Password": {
+ "type": "object",
+ "required": [
+ "newPassword",
+ "newPasswordConfirm",
+ "password"
+ ],
+ "properties": {
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "existing password"
+ },
+ "newPassword": {
+ "type": "string",
+ "format": "password",
+ "description": "new password"
+ },
+ "newPasswordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "new password confirm"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/oracle/config/oauth2-user/tls/server.keystore b/db/oracle/config/oauth2-user/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/oracle/config/oauth2-user/tls/server.keystore differ
diff --git a/db/oracle/config/oauth2-user/tls/server.truststore b/db/oracle/config/oauth2-user/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/oracle/config/oauth2-user/tls/server.truststore differ
diff --git a/db/oracle/create_oracle.sql b/db/oracle/create_oracle.sql
index 535e9b37..2bae362e 100644
--- a/db/oracle/create_oracle.sql
+++ b/db/oracle/create_oracle.sql
@@ -1,60 +1,88 @@
-DROP TABLE users CASCADE CONSTRAINTS;
-CREATE TABLE users (
+DROP TABLE user_profile CASCADE CONSTRAINTS;
+CREATE TABLE user_profile (
user_id VARCHAR2(32) NOT NULL,
user_type VARCHAR2(16) NOT NULL, -- admin, customer, employee, partner
first_name VARCHAR2(32) NOT NULL,
last_name VARCHAR2(32) NOT NULL,
email VARCHAR2(64) NOT NULL,
password VARCHAR2(1024) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
- CONSTRAINT users_pk PRIMARY KEY (user_id)
+ CONSTRAINT user_profile_pk PRIMARY KEY (user_id)
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-DROP TABLE clients CASCADE CONSTRAINTS;
-CREATE TABLE clients (
+DROP TABLE client CASCADE CONSTRAINTS;
+CREATE TABLE client (
client_id VARCHAR2(36) NOT NULL,
client_type VARCHAR2(12) NOT NULL, -- public, confidential, trusted
client_profile VARCHAR2(10) NOT NULL, -- webserver, mobile, browser, service, batch
client_secret VARCHAR2(1024) NOT NULL,
client_name VARCHAR2(32) NOT NULL,
client_desc VARCHAR2(2048),
- scope VARCHAR2(1024),
+ scope VARCHAR2(4000),
redirect_uri VARCHAR2(1024),
authenticate_class VARCHAR2(256),
owner_id VARCHAR2(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
- CONSTRAINT clients_pk PRIMARY KEY (client_id),
- CONSTRAINT clients_users_fk
+ CONSTRAINT client_pk PRIMARY KEY (client_id),
+ CONSTRAINT client_user_fk
FOREIGN KEY (owner_id)
- REFERENCES users(user_id)
+ REFERENCES user_profile(user_id)
);
-DROP TABLE services CASCADE CONSTRAINTS;
-CREATE TABLE services (
+DROP TABLE service CASCADE CONSTRAINTS;
+CREATE TABLE service (
service_id VARCHAR2(32) NOT NULL,
- service_type VARCHAR2(8) NOT NULL, -- api, ms
+ service_type VARCHAR2(16) NOT NULL, -- swagger, openapi, graphql, hybrid
service_name VARCHAR2(32) NOT NULL,
service_desc VARCHAR2(1024),
scope VARCHAR2(1024),
owner_id VARCHAR2(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
- CONSTRAINT services_pk PRIMARY KEY (service_id),
- CONSTRAINT services_users_fk
+ CONSTRAINT service_pk PRIMARY KEY (service_id),
+ CONSTRAINT service_user_fk
FOREIGN KEY (owner_id)
- REFERENCES users(user_id)
+ REFERENCES user_profile(user_id)
+);
+
+DROP TABLE service_endpoint CASCADE CONSTRAINTS;
+CREATE TABLE service_endpoint (
+ service_id VARCHAR2(32) NOT NULL,
+ endpoint VARCHAR2(256) NOT NULL, -- different framework will have different endpoint format.
+ operation VARCHAR2(256) NOT NULL,
+ scope VARCHAR2(64) NOT NULL,
+ CONSTRAINT service_endpoint_pk PRIMARY KEY (service_id, endpoint),
+ CONSTRAINT service_endpoint_service_fk FOREIGN KEY (service_id) REFERENCES service(service_id)
);
-INSERT INTO users (user_id, user_type, first_name, last_name, email, password, create_dt)
-VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992', SYSDATE);
+DROP TABLE client_service CASCADE CONSTRAINTS;
+CREATE TABLE client_service (
+ client_id VARCHAR2(36) NOT NULL,
+ service_id VARCHAR2(32) NOT NULL,
+ endpoint VARCHAR2(256) NOT NULL, -- different framework will have different endpoint format.
+ CONSTRAINT client_service_pk PRIMARY KEY (client_id, service_id, endpoint),
+ CONSTRAINT client_service_endpoint_fk FOREIGN KEY (service_id, endpoint) REFERENCES service_endpoint(service_id, endpoint),
+ CONSTRAINT client_service_client_fk FOREIGN KEY (client_id) REFERENCES client(client_id)
+);
+
+DROP TABLE audit_log CASCADE CONSTRAINTS;
+create table audit_log (
+ log_id INT, -- system milliseonds from 1970.
+ service_id VARCHAR2(32) NOT NULL,
+ endpoint VARCHAR2(256) NOT NULL,
+ request_header VARCHAR2(4000),
+ request_body VARCHAR2(4000),
+ response_code INT,
+ response_header VARCHAR2(4000),
+ response_body VARCHAR2(4000)
+);
+
+
+
+INSERT INTO user_profile (user_id, user_type, first_name, last_name, email, password)
+VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id, create_dt)
-VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin', SYSDATE);
+INSERT INTO client (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id)
+VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin');
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id, create_dt)
-VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin', SYSDATE);
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id)
+VALUES ('AACT0001', 'openapi', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/db/postgres/config/oauth2-client/cors.yml b/db/postgres/config/oauth2-client/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/postgres/config/oauth2-client/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-client/oauth/primary.crt b/db/postgres/config/oauth2-client/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-client/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-client/oauth/secondary.crt b/db/postgres/config/oauth2-client/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-client/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-client/secret.yml b/db/postgres/config/oauth2-client/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-client/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-client/security.yml b/db/postgres/config/oauth2-client/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-client/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-client/server.yml b/db/postgres/config/oauth2-client/server.yml
new file mode 100644
index 00000000..151859b6
--- /dev/null
+++ b/db/postgres/config/oauth2-client/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6884
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6884
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-client-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-client/swagger.json b/db/postgres/config/oauth2-client/swagger.json
new file mode 100644
index 00000000..1c42a623
--- /dev/null
+++ b/db/postgres/config/oauth2-client/swagger.json
@@ -0,0 +1,455 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Client Registration",
+ "description": "OAuth2 Client Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/client": {
+ "post": {
+ "description": "Return a client object",
+ "operationId": "createClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated client",
+ "operationId": "updateClient",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Client object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all clients",
+ "operationId": "getAllClient",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "clientName",
+ "in": "query",
+ "description": "Partial clientName for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Client"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}": {
+ "delete": {
+ "description": "Delete a client by Id",
+ "operationId": "deleteClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a client by Id",
+ "operationId": "getClient",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Client"
+ }
+ },
+ "400": {
+ "description": "Invalid clientId supplied"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service": {
+ "delete": {
+ "description": "Delete all associated services for a client by clientId",
+ "operationId": "deleteAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all associated services and endpoints by clientId",
+ "operationId": "getAllClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/client/{clientId}/service/{serviceId}": {
+ "post": {
+ "description": "Link a service and its endpoints to a client",
+ "operationId": "linkClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "description": "A list of endpoints that needs to be linked to the client",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints of a service for a client",
+ "operationId": "deleteClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get linked endpoints of a service from a client",
+ "operationId": "getClientService",
+ "parameters": [
+ {
+ "name": "clientId",
+ "in": "path",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ },
+ "404": {
+ "description": "Client or service not found"
+ }
+ },
+ "security": [
+ {
+ "client_auth": [
+ "oauth.client.r",
+ "oauth.client.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "client_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.client.w": "write oauth client",
+ "oauth.client.r": "read oauth client"
+ }
+ }
+ },
+ "definitions": {
+ "Client": {
+ "type": "object",
+ "required": [
+ "clientType",
+ "clientProfile",
+ "clientName",
+ "clientDesc",
+ "ownerId",
+ "scope"
+ ],
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "description": "a unique client id"
+ },
+ "clientSecret": {
+ "type": "string",
+ "description": "client secret"
+ },
+ "clientType": {
+ "type": "string",
+ "description": "client type",
+ "enum": [
+ "confidential",
+ "public",
+ "trusted"
+ ]
+ },
+ "clientProfile": {
+ "type": "string",
+ "description": "client profile",
+ "enum": [
+ "webserver",
+ "browser",
+ "mobile",
+ "service",
+ "batch"
+ ]
+ },
+ "clientName": {
+ "type": "string",
+ "description": "client name"
+ },
+ "clientDesc": {
+ "type": "string",
+ "description": "client description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "client owner id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "client scope separated by space"
+ },
+ "redirectUri": {
+ "type": "string",
+ "description": "redirect uri"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-client/tls/server.keystore b/db/postgres/config/oauth2-client/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-client/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-client/tls/server.truststore b/db/postgres/config/oauth2-client/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-client/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-code/cors.yml b/db/postgres/config/oauth2-code/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/postgres/config/oauth2-code/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-code/jwt.yml b/db/postgres/config/oauth2-code/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/postgres/config/oauth2-code/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/postgres/config/oauth2-code/oauth/primary.crt b/db/postgres/config/oauth2-code/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-code/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-code/oauth/secondary.crt b/db/postgres/config/oauth2-code/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-code/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-code/secret.yml b/db/postgres/config/oauth2-code/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-code/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-code/security.yml b/db/postgres/config/oauth2-code/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-code/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-code/server.yml b/db/postgres/config/oauth2-code/server.yml
new file mode 100644
index 00000000..95816ef8
--- /dev/null
+++ b/db/postgres/config/oauth2-code/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6881
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6881
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-code-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-code/swagger.json b/db/postgres/config/oauth2-code/swagger.json
new file mode 100644
index 00000000..8e24d450
--- /dev/null
+++ b/db/postgres/config/oauth2-code/swagger.json
@@ -0,0 +1,197 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Authorization Code",
+ "description": "OAuth2 Service that logs in user and provide authorization code.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/code": {
+ "get": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "getAuthCode",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "description": "encoded username:password mandatory if Basic Authentication is used",
+ "in": "header",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "query",
+ "description": "The response type for authorization code",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "query",
+ "description": "The client id for authorization code",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "query",
+ "description": "The redirect uri for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for authorization code",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for authorization code in clear text",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "query",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "query",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "query",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "query",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ },
+ "post": {
+ "description": "Return 302 redirect with authorization code",
+ "operationId": "postAuthCode",
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "j_username",
+ "in": "formData",
+ "description": "User name",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "j_password",
+ "in": "formData",
+ "description": "Password",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "response_type",
+ "in": "formData",
+ "description": "Response type",
+ "required": true,
+ "type": "string",
+ "enum": [
+ "code"
+ ]
+ },
+ {
+ "name": "client_id",
+ "in": "formData",
+ "description": "Client Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "redirect_uri",
+ "in": "formData",
+ "description": "Redirect Uri",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "state",
+ "in": "formData",
+ "description": "to prevent cross-site request forgery",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "scope",
+ "in": "formData",
+ "description": "scope of the request",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge",
+ "in": "formData",
+ "description": "PKCE code challenge",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "code_challenge_method",
+ "in": "formData",
+ "description": "PKCE code challenge method",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-code/tls/server.keystore b/db/postgres/config/oauth2-code/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-code/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-code/tls/server.truststore b/db/postgres/config/oauth2-code/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-code/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-key/cors.yml b/db/postgres/config/oauth2-key/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/postgres/config/oauth2-key/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-key/oauth/primary.crt b/db/postgres/config/oauth2-key/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-key/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-key/oauth/secondary.crt b/db/postgres/config/oauth2-key/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-key/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-key/secret.yml b/db/postgres/config/oauth2-key/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-key/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-key/security.yml b/db/postgres/config/oauth2-key/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-key/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-key/server.yml b/db/postgres/config/oauth2-key/server.yml
new file mode 100644
index 00000000..454ba61a
--- /dev/null
+++ b/db/postgres/config/oauth2-key/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6886
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6886
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-key-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-key/swagger.json b/db/postgres/config/oauth2-key/swagger.json
new file mode 100644
index 00000000..1f8a6c2a
--- /dev/null
+++ b/db/postgres/config/oauth2-key/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 Key Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Key Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/key/{keyId}": {
+ "get": {
+ "description": "Get a key by Id",
+ "operationId": "getKeyById",
+ "parameters": [
+ {
+ "name": "keyId",
+ "in": "path",
+ "description": "Key Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Key"
+ }
+ },
+ "400": {
+ "description": "Invalid keyId supplied"
+ },
+ "404": {
+ "description": "Key not found"
+ }
+ },
+ "security": [
+ {
+ "key_auth": [
+ "oauth.key.r",
+ "oauth.key.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "key_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "key_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.key.w": "write key",
+ "oauth.key.r": "read key",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "Key": {
+ "type": "object",
+ "required": [
+ "certificate",
+ "keyId"
+ ],
+ "properties": {
+ "keyId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "certificate": {
+ "type": "string",
+ "description": "certificate"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-key/tls/server.keystore b/db/postgres/config/oauth2-key/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-key/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-key/tls/server.truststore b/db/postgres/config/oauth2-key/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-key/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-refresh-token/cors.yml b/db/postgres/config/oauth2-refresh-token/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-refresh-token/oauth/primary.crt b/db/postgres/config/oauth2-refresh-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-refresh-token/oauth/secondary.crt b/db/postgres/config/oauth2-refresh-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-refresh-token/primary.crt b/db/postgres/config/oauth2-refresh-token/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-refresh-token/secret.yml b/db/postgres/config/oauth2-refresh-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-refresh-token/security.yml b/db/postgres/config/oauth2-refresh-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-refresh-token/server.yml b/db/postgres/config/oauth2-refresh-token/server.yml
new file mode 100644
index 00000000..58ec6aec
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6887
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6887
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-refresh-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-refresh-token/swagger.json b/db/postgres/config/oauth2-refresh-token/swagger.json
new file mode 100644
index 00000000..fc2c1fbb
--- /dev/null
+++ b/db/postgres/config/oauth2-refresh-token/swagger.json
@@ -0,0 +1,215 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 refresh token management microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 Refresh Token Management",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/refresh_token": {
+ "get": {
+ "description": "Return all refresh tokens",
+ "operationId": "getAllRefreshToken",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/refresh_token/{refreshToken}": {
+ "get": {
+ "description": "Get a refresh token",
+ "operationId": "getRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/RefreshToken"
+ }
+ },
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.r",
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a refresh token",
+ "operationId": "deleteRefreshToken",
+ "parameters": [
+ {
+ "name": "refreshToken",
+ "in": "path",
+ "description": "Refresh Token",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid refresh token supplied"
+ },
+ "404": {
+ "description": "Refresh token not found"
+ }
+ },
+ "security": [
+ {
+ "refresh_token_auth": [
+ "oauth.refresh_token.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "refresh_token_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "refresh_token_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.refresh_token.w": "write oauth refresh token",
+ "oauth.refresh_token.r": "read oauth refresh token",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "RefreshToken": {
+ "type": "object",
+ "required": [
+ "clientId",
+ "refreshToken",
+ "userId"
+ ],
+ "properties": {
+ "refreshToken": {
+ "type": "string",
+ "description": "refresh token"
+ },
+ "userId": {
+ "type": "string",
+ "description": "user id"
+ },
+ "clientId": {
+ "type": "string",
+ "description": "client id"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-refresh-token/tls/server.keystore b/db/postgres/config/oauth2-refresh-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-refresh-token/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-refresh-token/tls/server.truststore b/db/postgres/config/oauth2-refresh-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-refresh-token/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-service/cors.yml b/db/postgres/config/oauth2-service/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/postgres/config/oauth2-service/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-service/oauth/primary.crt b/db/postgres/config/oauth2-service/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-service/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-service/oauth/secondary.crt b/db/postgres/config/oauth2-service/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-service/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-service/primary.crt b/db/postgres/config/oauth2-service/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-service/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-service/secret.yml b/db/postgres/config/oauth2-service/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-service/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-service/security.yml b/db/postgres/config/oauth2-service/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-service/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-service/server.yml b/db/postgres/config/oauth2-service/server.yml
new file mode 100644
index 00000000..ff558b00
--- /dev/null
+++ b/db/postgres/config/oauth2-service/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6883
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6883
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-service-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-service/swagger.json b/db/postgres/config/oauth2-service/swagger.json
new file mode 100644
index 00000000..c51d14a3
--- /dev/null
+++ b/db/postgres/config/oauth2-service/swagger.json
@@ -0,0 +1,393 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Registration",
+ "description": "OAuth2 Service Registration microservices endpoints.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/service": {
+ "post": {
+ "description": "Return a service object",
+ "operationId": "createService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "description": "Return the updated service",
+ "operationId": "updateService",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Service object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Return all services",
+ "operationId": "getAllService",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "serviceId",
+ "in": "query",
+ "description": "Partial serviceId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Service"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}": {
+ "delete": {
+ "description": "Delete a service by Id",
+ "operationId": "deleteService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get a service by Id",
+ "operationId": "getService",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/Service"
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ },
+ "/oauth2/service/{serviceId}/endpoint": {
+ "post": {
+ "description": "create endpoints for service",
+ "operationId": "createServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "body",
+ "name": "body",
+ "description": "A list of endpoint object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "description": "Delete all endpoints for a service",
+ "operationId": "deleteServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "Service not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.w"
+ ]
+ }
+ ]
+ },
+ "get": {
+ "description": "Get all endpoints for a service",
+ "operationId": "getServiceEndpoint",
+ "parameters": [
+ {
+ "name": "serviceId",
+ "in": "path",
+ "description": "Service Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ServiceEndpoint"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid serviceId supplied"
+ },
+ "404": {
+ "description": "ServiceEndpoint not found"
+ }
+ },
+ "security": [
+ {
+ "service_auth": [
+ "oauth.service.r",
+ "oauth.service.w"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "securityDefinitions": {
+ "service_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.service.w": "write oauth service",
+ "oauth.service.r": "read oauth service"
+ }
+ }
+ },
+ "definitions": {
+ "Service": {
+ "type": "object",
+ "required": [
+ "serviceId",
+ "serviceName",
+ "serviceType",
+ "scope"
+ ],
+ "properties": {
+ "serviceId": {
+ "type": "string",
+ "description": "a unique service id"
+ },
+ "serviceType": {
+ "type": "string",
+ "description": "service type",
+ "enum": [
+ "swagger",
+ "openapi",
+ "graphql",
+ "hybrid"
+ ]
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "service name"
+ },
+ "serviceDesc": {
+ "type": "string",
+ "description": "service description"
+ },
+ "ownerId": {
+ "type": "string",
+ "description": "service owner userId"
+ },
+ "scope": {
+ "type": "string",
+ "description": "service scopes separated by space"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "ServiceEndpoint": {
+ "type": "object",
+ "required": [
+ "endpoint",
+ "operation",
+ "scope"
+ ],
+ "properties": {
+ "endpoint": {
+ "type": "string",
+ "description": "a combination of path and method to uniquely identify an operation"
+ },
+ "operation": {
+ "type": "string",
+ "description": "operationId of the endpoint"
+ },
+ "scope": {
+ "type": "string",
+ "description": "scope associated with the endpoint"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-service/tls/server.keystore b/db/postgres/config/oauth2-service/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-service/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-service/tls/server.truststore b/db/postgres/config/oauth2-service/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-service/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-token/cors.yml b/db/postgres/config/oauth2-token/cors.yml
new file mode 100644
index 00000000..b83eecdf
--- /dev/null
+++ b/db/postgres/config/oauth2-token/cors.yml
@@ -0,0 +1,13 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+description: Cors Http Handler
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-token/jwt.yml b/db/postgres/config/oauth2-token/jwt.yml
new file mode 100644
index 00000000..bc7ae31c
--- /dev/null
+++ b/db/postgres/config/oauth2-token/jwt.yml
@@ -0,0 +1,13 @@
+# This is the default JWT configuration and need to be updated when used to issue JWT tokens. It is a component that used to
+# issue JWT token. Normally, it should be used by light-oauth2 only but can be used to issue tokens distributely.
+---
+# Signature private key that used to sign JWT tokens.
+key:
+ kid: '100' # kid that used to sign the JWT tokens. It will be shown up in the token header.
+ filename: "/config/oauth/primary.jks" # private key that is used to sign JWT tokens.
+ password: password # password for the private key. It should be set during deployment time along with pk
+ keyName: selfsigned # key name that is used to identify the right key in keystore.
+issuer: urn:com:networknt:oauth2:v1 # default issuer of the JWT token
+audience: urn:com.networknt # default audience of the JWT token
+expiredInMinutes: 10 # expired in 10 minutes by default for issued JWT tokens
+version: '1.0' # JWT token version
diff --git a/db/postgres/config/oauth2-token/oauth/primary.crt b/db/postgres/config/oauth2-token/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-token/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-token/oauth/primary.jks b/db/postgres/config/oauth2-token/oauth/primary.jks
new file mode 100644
index 00000000..0d6ae7cf
Binary files /dev/null and b/db/postgres/config/oauth2-token/oauth/primary.jks differ
diff --git a/db/postgres/config/oauth2-token/oauth/secondary.crt b/db/postgres/config/oauth2-token/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-token/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-token/oauth/secondary.jks b/db/postgres/config/oauth2-token/oauth/secondary.jks
new file mode 100644
index 00000000..33824c89
Binary files /dev/null and b/db/postgres/config/oauth2-token/oauth/secondary.jks differ
diff --git a/db/postgres/config/oauth2-token/secret.yml b/db/postgres/config/oauth2-token/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-token/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-token/security.yml b/db/postgres/config/oauth2-token/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-token/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-token/server.yml b/db/postgres/config/oauth2-token/server.yml
new file mode 100644
index 00000000..2a414220
--- /dev/null
+++ b/db/postgres/config/oauth2-token/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6882
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6882
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-token-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-token/swagger.json b/db/postgres/config/oauth2-token/swagger.json
new file mode 100644
index 00000000..b56f7433
--- /dev/null
+++ b/db/postgres/config/oauth2-token/swagger.json
@@ -0,0 +1,125 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "OAuth2 Service Token Service",
+ "description": "OAuth2 Service that issues access tokens.",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/token": {
+ "post": {
+ "description": "JSON object that contains access token",
+ "operationId": "postToken",
+ "parameters": [
+ {
+ "name": "authorization",
+ "description": "encoded client_id and client_secret pair",
+ "in": "header",
+ "type": "string",
+ "required": false
+ },
+ {
+ "name": "grant_type",
+ "type": "string",
+ "enum": [
+ "authorization_code",
+ "client_credentials",
+ "password",
+ "refresh_token",
+ "client_authenticated_user"
+ ],
+ "required": true,
+ "in": "formData"
+ },
+ {
+ "name": "client_id",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "client_secret",
+ "description": "used as alternative to authentication header for client authentication",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code",
+ "description": "used in authorization_code to specify the code",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "username",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "password",
+ "description": "mandatory in password grant type",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "scope",
+ "description": "used by all flows to specify scope in the access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "redirect_uri",
+ "description": "used in authorization code if code endpoint with rediret_uri",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "refresh_token",
+ "description": "refresh token used to get another access token",
+ "type": "string",
+ "in": "formData"
+ },
+ {
+ "name": "code_verifier",
+ "description": "PKCE code verifier",
+ "type": "string",
+ "in": "formData"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Operation"
+ }
+ }
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ }
+}
diff --git a/db/postgres/config/oauth2-token/tls/server.keystore b/db/postgres/config/oauth2-token/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-token/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-token/tls/server.truststore b/db/postgres/config/oauth2-token/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-token/tls/server.truststore differ
diff --git a/db/postgres/config/oauth2-user/cors.yml b/db/postgres/config/oauth2-user/cors.yml
new file mode 100644
index 00000000..99437103
--- /dev/null
+++ b/db/postgres/config/oauth2-user/cors.yml
@@ -0,0 +1,12 @@
+# Cors Http Handler Configuration. This is to support connection
+# from light-portal single page application which might served
+# from another domain/host. You may need to update allowedOrigins.
+---
+enabled: true
+allowedOrigins:
+- http://localhost:8080
+allowedMethods:
+- GET
+- POST
+- PUT
+- DELETE
diff --git a/db/postgres/config/oauth2-user/oauth/primary.crt b/db/postgres/config/oauth2-user/oauth/primary.crt
new file mode 100644
index 00000000..34f9272f
--- /dev/null
+++ b/db/postgres/config/oauth2-user/oauth/primary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIEHnAgtDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEUMBIGA1UEBxMLTWlzc2lzc2F1Z2ExJjAkBgNVBAoTHU5ldHdvcmsgTmV3IFRl
+Y2hub2xvZ2llcyBJbmMuMQwwCgYDVQQLEwNERVYxETAPBgNVBAMTCFN0ZXZlIEh1MB4XDTE2MDkw
+MTE2MTYxNVoXDTI2MDcxMTE2MTYxNVowfjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
+FDASBgNVBAcTC01pc3Npc3NhdWdhMSYwJAYDVQQKEx1OZXR3b3JrIE5ldyBUZWNobm9sb2dpZXMg
+SW5jLjEMMAoGA1UECxMDREVWMREwDwYDVQQDEwhTdGV2ZSBIdTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALrlxMtDb60DogElf4TBz504tRheZimAE0dJL/Yby4nacJdqvc5l4z+WWpDf
+rI9krQ2Yi9yvhwAP+PrR6gWcIqWP4cpNE7XIAUDgr4CtyI7CptT/lpjtbkz4DGCMmaeDn0jqHqJt
+SeSZGfwVu5zAGm8n4sHatjnnxBI/iWzkTII3V4xv0WeK37szNTEd+ly2ag7n2IV5zNnYmqZTeMQm
+J2ENS+IwAG3ENtiVtrVTx/2bGtqutJjtdxsN58/cUG/guRyMT6OPI8Yi3ZzevdvRbxadyhEl/Kaw
+6vJcdxmJI3tp4lx+p6sAxOWa7aapJe4JxutAQqzv0GKdVjoHKQ1wB60CAwEAAaMhMB8wHQYDVR0O
+BBYEFIPF9SBd06RWU1eDL73CKfy01lavMA0GCSqGSIb3DQEBCwUAA4IBAQAoaKZGOak3Upz/ordF
+slZoJuZlCu7jnKQEjYwHf3DNxcd1WmgFPtMcna6pW0VUxPIfidEA6VCMsGoK1RvshB0SjrRdCht6
+5qPXs9kV3NW0WvMiwDSYZZ9HgaZ9efTe5E9Fzc7ltKrE43L6k8NJcaEEWEdpdjFbrAqH4I+j/Vro
+K3OhIo062fXjas5ipL4gF+3ECImjWzirQP8UiAfM0/36x7rtAu3btH/qI9hSyx39LBPPE5AsDJZ4
+dSMwNTW1gqmBAZIj+zQ/RD5dyWfPwON7Q+t96YbK6WBuYo0xy+I+PjcUgrWYWP3N24hlq8ZBIei+
+BudoEVJlIlmS0aRCuP8n
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-user/oauth/secondary.crt b/db/postgres/config/oauth2-user/oauth/secondary.crt
new file mode 100644
index 00000000..215cedb4
--- /dev/null
+++ b/db/postgres/config/oauth2-user/oauth/secondary.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIEUBGbJDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJDQTEQMA4GA1UE
+CBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5v
+bG9naWVzIEluYy4xDDAKBgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwHhcNMTYwOTIyMjI1
+OTIxWhcNMjYwODAxMjI1OTIxWjB6MQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4G
+A1UEBxMHVG9yb250bzEmMCQGA1UEChMdTmV0d29yayBOZXcgVGVjaG5vbG9naWVzIEluYy4xDDAK
+BgNVBAsTA0FQSTERMA8GA1UEAxMIU3RldmUgSHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCqYfarFwug2DwpG/mmcW77OluaHVNsKEVJ/BptLp5suJAH/Z70SS5pwM4x2QwMOVO2ke8U
+rsAws8allxcuKXrbpVt4evpO1Ly2sFwqB1bjN3+VMp6wcT+tSjzYdVGFpQAYHpeA+OLuoHtQyfpB
+0KCveTEe3KAG33zXDNfGKTGmupZ3ZfmBLINoey/X13rY71ITt67AY78VHUKb+D53MBahCcjJ9YpJ
+UHG+Sd3d4oeXiQcqJCBCVpD97awWARf8WYRIgU1xfCe06wQ3CzH3+GyfozLeu76Ni5PwE1tm7Dhg
+EDSSZo5khmzVzo4G0T2sOeshePc5weZBNRHdHlJA0L0fAgMBAAGjITAfMB0GA1UdDgQWBBT9rnek
+spnrFus5wTszjdzYgKll9TANBgkqhkiG9w0BAQsFAAOCAQEAT8udTfUGBgeWbN6ZAXRI64VsSJj5
+1sNUN1GPDADLxZF6jArKU7LjBNXn9bG5VjJqlx8hQ1SNvi/t7FqBRCUt/3MxDmGZrVZqLY1kZ2e7
+x+5RykbspA8neEUtU8sOr/NP3O5jBjU77EVec9hNNT5zwKLevZNL/Q5mfHoc4GrIAolQvi/5fEqC
+8OMdOIWS6sERgjaeI4tXxQtHDcMo5PeLW0/7t5sgEsadZ+pkdeEMVTmLfgf97bpNNI7KF5uEbYnQ
+NpwCT+NNC5ACmJmKidrfW23kml1C7vr7YzTevw9QuH/hN8l/Rh0fr+iPEVpgN6Zv00ymoKGmjuuW
+owVmdKg/0w==
+-----END CERTIFICATE-----
diff --git a/db/postgres/config/oauth2-user/secret.yml b/db/postgres/config/oauth2-user/secret.yml
new file mode 100644
index 00000000..101f36db
--- /dev/null
+++ b/db/postgres/config/oauth2-user/secret.yml
@@ -0,0 +1,46 @@
+# This file contains all the secrets for the server and client in order to manage and
+# secure all of them in the same place. In Kubernetes, this file will be mapped to
+# Secrets and all other config files will be mapped to mapConfig
+
+---
+
+# Sever section
+
+# Key store password, the path of keystore is defined in server.yml
+serverKeystorePass: password
+
+# Key password, the key is in keystore
+serverKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+serverTruststorePass: password
+
+
+# Client section
+
+# Key store password, the path of keystore is defined in server.yml
+clientKeystorePass: password
+
+# Key password, the key is in keystore
+clientKeyPass: password
+
+# Trust store password, the path of truststore is defined in server.yml
+clientTruststorePass: password
+
+# Authorization code client secret for OAuth2 server
+authorizationCodeClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Client credentials client secret for OAuth2 server
+clientCredentialsClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+# Key distribution client secret for OAuth2 server
+keyClientSecret: f6h1FTI8Q3-7UScPZDzfXA
+
+
+# Consul service registry and discovery
+
+# Consul Token for service registry and discovery
+# consulToken: the_one_ring
+
+# EmailSender password
+emailPassword: change-to-real-password
diff --git a/db/postgres/config/oauth2-user/security.yml b/db/postgres/config/oauth2-user/security.yml
new file mode 100644
index 00000000..5125bfa5
--- /dev/null
+++ b/db/postgres/config/oauth2-user/security.yml
@@ -0,0 +1,30 @@
+# Security configuration in light framework.
+---
+# Enable JWT verification flag.
+enableVerifyJwt: false
+
+# Enable JWT scope verification. Only valid when enableVerifyJwt is true.
+enableVerifyScope: true
+
+# User for test only. should be always be false on official environment.
+enableMockJwt: false
+
+# JWT signature public certificates. kid and certificate path mappings.
+jwt:
+ certificate:
+ '100': oauth/primary.crt
+ '101': oauth/secondary.crt
+ clockSkewInSeconds: 60
+
+# Enable or disable JWT token logging
+logJwtToken: true
+
+# Enable or disable client_id, user_id and scope logging.
+logClientUserScope: false
+
+# If OAuth2 provider support http2 protocol. If using light-oauth2, set this to true.
+oauthHttp2Support: true
+
+# Enable JWT token cache to speed up verification. This will only verify expired time
+# and skip the signature verification as it takes more CPU power and long time.
+enableJwtCache: true
diff --git a/db/postgres/config/oauth2-user/server.yml b/db/postgres/config/oauth2-user/server.yml
new file mode 100644
index 00000000..8718d5a6
--- /dev/null
+++ b/db/postgres/config/oauth2-user/server.yml
@@ -0,0 +1,39 @@
+# Server configuration
+---
+# This is the default binding address if the service is dockerized.
+ip: 0.0.0.0
+
+# Http port if enableHttp is true.
+httpPort: 6885
+
+# Enable HTTP should be false on official environment.
+enableHttp: false
+
+# Https port if enableHttps is true.
+httpsPort: 6885
+
+# Enable HTTPS should be true on official environment.
+enableHttps: true
+
+# Enable HTTP2 should be true on official environment.
+enableHttp2: true
+
+# Keystore file name in config folder. KeystorePass is in secret.yml to access it.
+keystoreName: tls/server.keystore
+
+# Flag that indicate if two way TLS is enabled. Not recommended in docker container.
+enableTwoWayTls: false
+
+# Truststore file name in config folder. TruststorePass is in secret.yml to access it.
+truststoreName: tls/server.truststore
+
+# Unique service identifier. Used in service registration and discovery etc.
+serviceId: com.networknt.oauth2-user-1.0.0
+
+# Flag to enable service registration. Only be true if running as standalone Java jar.
+enableRegistry: false
+
+# environment tag that will be registered on consul to support multiple instances per env for testing.
+# https://github.com/networknt/light-doc/blob/master/docs/content/design/env-segregation.md
+# This tag should only be set for testing env, not production. The production certification process will enforce it.
+# environment: test1
diff --git a/db/postgres/config/oauth2-user/swagger.json b/db/postgres/config/oauth2-user/swagger.json
new file mode 100644
index 00000000..9717cc2a
--- /dev/null
+++ b/db/postgres/config/oauth2-user/swagger.json
@@ -0,0 +1,368 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "OAuth2 User Service microservices endpoints.",
+ "version": "1.0.0",
+ "title": "OAuth2 User Service",
+ "contact": {
+ "email": "stevehu@gmail.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "oauth2.networknt.com",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/oauth2/password/{userId}": {
+ "post": {
+ "description": "Reset Password",
+ "operationId": "resetPassword",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Password object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Password"
+ }
+ },
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user": {
+ "get": {
+ "description": "Return all users",
+ "operationId": "getAllUsers",
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number",
+ "required": true,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "pageSize",
+ "in": "query",
+ "description": "Pag size",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Partial userId for filter",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "post": {
+ "description": "Return a user object",
+ "operationId": "createUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "put": {
+ "description": "Return the updated user",
+ "operationId": "updateUser",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "User object that needs to be added",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/oauth2/user/{userId}": {
+ "get": {
+ "description": "Get a user by Id",
+ "operationId": "getUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ },
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.r",
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ },
+ "delete": {
+ "description": "Delete a user by Id",
+ "operationId": "deleteUser",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "description": "User Id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid userId supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "security": [
+ {
+ "user_auth": [
+ "oauth.user.w"
+ ]
+ }
+ ],
+ "x-accepts": "application/json",
+ "x-contentType": "application/json"
+ }
+ },
+ "/server/info": {
+ "get": {
+ "security": [
+ {
+ "service_auth": [
+ "server.info.r"
+ ]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ },
+ "/health": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "successful operation"
+ }
+ },
+ "parameters": []
+ }
+ }
+ },
+ "securityDefinitions": {
+ "user_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://localhost:8888/oauth2/code",
+ "flow": "implicit",
+ "scopes": {
+ "oauth.user.w": "write user",
+ "oauth.user.r": "read user",
+ "server.info.r": "read server info"
+ }
+ }
+ },
+ "definitions": {
+ "User": {
+ "type": "object",
+ "required": [
+ "email",
+ "firstName",
+ "lastName",
+ "userId",
+ "userType"
+ ],
+ "properties": {
+ "userId": {
+ "type": "string",
+ "description": "a unique id"
+ },
+ "userType": {
+ "type": "string",
+ "description": "user type",
+ "enum": [
+ "admin",
+ "employee",
+ "customer",
+ "partner"
+ ]
+ },
+ "firstName": {
+ "type": "string",
+ "description": "first name"
+ },
+ "lastName": {
+ "type": "string",
+ "description": "last name"
+ },
+ "email": {
+ "type": "string",
+ "description": "email address"
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "password"
+ },
+ "passwordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "password confirm"
+ },
+ "createDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "create date time"
+ },
+ "updateDt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "update date time"
+ }
+ }
+ },
+ "Password": {
+ "type": "object",
+ "required": [
+ "newPassword",
+ "newPasswordConfirm",
+ "password"
+ ],
+ "properties": {
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "existing password"
+ },
+ "newPassword": {
+ "type": "string",
+ "format": "password",
+ "description": "new password"
+ },
+ "newPasswordConfirm": {
+ "type": "string",
+ "format": "password",
+ "description": "new password confirm"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/db/postgres/config/oauth2-user/tls/server.keystore b/db/postgres/config/oauth2-user/tls/server.keystore
new file mode 100644
index 00000000..feab9b6d
Binary files /dev/null and b/db/postgres/config/oauth2-user/tls/server.keystore differ
diff --git a/db/postgres/config/oauth2-user/tls/server.truststore b/db/postgres/config/oauth2-user/tls/server.truststore
new file mode 100644
index 00000000..fb0c19cc
Binary files /dev/null and b/db/postgres/config/oauth2-user/tls/server.truststore differ
diff --git a/db/postgres/create_postgres.sql b/db/postgres/create_postgres.sql
index 503e9f7b..46ead571 100644
--- a/db/postgres/create_postgres.sql
+++ b/db/postgres/create_postgres.sql
@@ -1,20 +1,18 @@
-DROP TABLE IF EXISTS users;
-CREATE TABLE users (
+DROP TABLE IF EXISTS user_profile;
+CREATE TABLE user_profile (
user_id VARCHAR(32) NOT NULL,
user_type VARCHAR(16) NOT NULL, -- admin, customer, employee, partner
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
email VARCHAR(64) NOT NULL,
password VARCHAR(1024) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (user_id)
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-DROP TABLE IF EXISTS clients;
-CREATE TABLE clients (
+DROP TABLE IF EXISTS client;
+CREATE TABLE client (
client_id VARCHAR(36) NOT NULL,
client_type VARCHAR(12) NOT NULL, -- public, confidential, trusted
client_profile VARCHAR(10) NOT NULL, -- webserver, mobile, browser, service, batch
@@ -25,32 +23,62 @@ CREATE TABLE clients (
redirect_uri VARCHAR(1024),
authenticate_class VARCHAR(256),
owner_id VARCHAR(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (client_id),
- FOREIGN KEY (owner_id) REFERENCES users(user_id)
+ FOREIGN KEY (owner_id) REFERENCES user_profile(user_id)
);
-DROP TABLE IF EXISTS services;
-CREATE TABLE services (
+DROP TABLE IF EXISTS service;
+CREATE TABLE service (
service_id VARCHAR(32) NOT NULL,
- service_type VARCHAR(8) NOT NULL, -- api, ms
+ service_type VARCHAR(16) NOT NULL, -- swagger, openapi, graphql, hybrid
service_name VARCHAR(32) NOT NULL,
service_desc VARCHAR(1024),
scope VARCHAR(1024),
owner_id VARCHAR(32) NOT NULL,
- create_dt DATE NOT NULL,
- update_dt DATE,
PRIMARY KEY (service_id),
- FOREIGN KEY (owner_id) REFERENCES users(user_id)
+ FOREIGN KEY (owner_id) REFERENCES user_profile(user_id)
);
-INSERT INTO users (user_id, user_type, first_name, last_name, email, password, create_dt)
-VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992', current_timestamp);
+DROP TABLE IF EXISTS service_endpoint;
+CREATE TABLE service_endpoint (
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL, -- different framework will have different endpoint format.
+ operation VARCHAR(256) NOT NULL,
+ scope VARCHAR(64) NOT NULL,
+ PRIMARY KEY (service_id, endpoint),
+ FOREIGN KEY (service_id) REFERENCES service(service_id)
+);
+
+
+DROP TABLE IF EXISTS client_service;
+CREATE TABLE client_service (
+ client_id VARCHAR(36) NOT NULL,
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL, -- different framework will have different endpoint format.
+ PRIMARY KEY (client_id, service_id, endpoint),
+ FOREIGN KEY (service_id, endpoint) REFERENCES service_endpoint(service_id, endpoint),
+ FOREIGN KEY (client_id) REFERENCES client(client_id)
+);
+
+DROP TABLE IF EXISTS audit_log;
+create table audit_log (
+ log_id INT, -- system milliseonds from 1970.
+ service_id VARCHAR(32) NOT NULL,
+ endpoint VARCHAR(256) NOT NULL,
+ request_header VARCHAR(4096),
+ request_body VARCHAR(4096),
+ response_code INT,
+ response_header VARCHAR(4096),
+ response_body VARCHAR(4096)
+);
+
+
+INSERT INTO user_profile (user_id, user_type, first_name, last_name, email, password)
+VALUES('admin', 'admin', 'admin', 'admin', 'admin@cibc.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id, create_dt)
-VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin', current_timestamp);
+INSERT INTO client (client_id, client_secret, client_type, client_profile, client_name, client_desc, scope, redirect_uri, owner_id)
+VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'public', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin');
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id, create_dt)
-VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin', current_timestamp);
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id)
+VALUES ('AACT0001', 'openapi', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/key/docker/Dockerfile b/key/Dockerfile
similarity index 100%
rename from key/docker/Dockerfile
rename to key/Dockerfile
diff --git a/key/build.sh b/key/build.sh
index 939da6b5..0be5cd71 100755
--- a/key/build.sh
+++ b/key/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/key/pom.xml b/key/pom.xml
index 58825b91..fe2042e5 100644
--- a/key/pom.xml
+++ b/key/pom.xml
@@ -4,7 +4,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/key/src/main/resources/logback.xml b/key/src/main/resources/logback.xml
index 6e58aff8..455f137d 100644
--- a/key/src/main/resources/logback.xml
+++ b/key/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
diff --git a/key/src/test/resources/create_h2.sql b/key/src/test/resources/create_h2.sql
index cf8de8a0..52c37cdf 100644
--- a/key/src/test/resources/create_h2.sql
+++ b/key/src/test/resources/create_h2.sql
@@ -1,8 +1,8 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
@@ -13,13 +13,13 @@ create table users (
update_dt DATE
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
- client_type VARCHAR,
client_secret VARCHAR,
- client_profile VARCHAR,
+ client_type VARCHAR, -- public, confidential, trusted
+ client_profile VARCHAR, -- server, mobile, service, batch, browser
client_name VARCHAR,
client_desc VARCHAR,
scope VARCHAR,
@@ -30,7 +30,7 @@ create table clients (
update_dt DATE
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
service_type VARCHAR, -- api, ms
service_name VARCHAR,
@@ -41,10 +41,10 @@ create table services (
update_dt DATE
);
-INSERT INTO users (user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', 'admin');
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO clients (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r');
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/pom.xml b/pom.xml
index 8b329563..3d5a90c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
pom
Parent POM
OAuth2 provider microservices based on Light-Java and SQL
@@ -52,8 +52,8 @@
1.8
UTF-8
- 1.5.7
- 1.5.7
+ 1.5.8
+ 1.5.8
0.1.10
2.9.1
1.7.25
@@ -66,9 +66,9 @@
1.0.32
2.7.1
1.4.196
- 3.6.8
+ 3.9.2
11.2.0.3
- 6.0.4
+ 6.0.5
9.4.1211
2.4
-Xmx512m -XX:MaxPermSize=256m
diff --git a/refresh-token/docker/Dockerfile b/refresh-token/Dockerfile
similarity index 100%
rename from refresh-token/docker/Dockerfile
rename to refresh-token/Dockerfile
diff --git a/refresh-token/build.sh b/refresh-token/build.sh
index e72b9635..bc242fe0 100755
--- a/refresh-token/build.sh
+++ b/refresh-token/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/refresh-token/pom.xml b/refresh-token/pom.xml
index 63ef8b3c..6964f5d9 100644
--- a/refresh-token/pom.xml
+++ b/refresh-token/pom.xml
@@ -3,7 +3,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/refresh-token/src/main/resources/logback.xml b/refresh-token/src/main/resources/logback.xml
index 6e58aff8..455f137d 100644
--- a/refresh-token/src/main/resources/logback.xml
+++ b/refresh-token/src/main/resources/logback.xml
@@ -28,7 +28,7 @@
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %logger{36} - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
@@ -36,7 +36,7 @@
target/test.log
false
- %d{HH:mm:ss.SSS} [%thread] %X{cId} %-5level %class{36}:%L %M - %msg%n
+ %d{HH:mm:ss.SSS} [%thread] %X{sId} %X{cId} %-5level %class{36}:%L %M - %msg%n
diff --git a/refresh-token/src/test/resources/create_h2.sql b/refresh-token/src/test/resources/create_h2.sql
index 423a5fe0..52c37cdf 100644
--- a/refresh-token/src/test/resources/create_h2.sql
+++ b/refresh-token/src/test/resources/create_h2.sql
@@ -1,8 +1,8 @@
-DROP table IF EXISTS users;
-DROP table IF EXISTS clients;
-DROP table IF EXISTS services;
+DROP table IF EXISTS user_profile;
+DROP table IF EXISTS client;
+DROP table IF EXISTS service;
-create table users (
+create table user_profile (
user_id varchar PRIMARY KEY,
user_type varchar, -- admin, customer, employee, partner
first_name varchar,
@@ -13,13 +13,13 @@ create table users (
update_dt DATE
);
-CREATE UNIQUE INDEX email_idx ON users(email);
+CREATE UNIQUE INDEX email_idx ON user_profile(email);
-create table clients (
+create table client (
client_id VARCHAR PRIMARY KEY,
- client_type VARCHAR,
client_secret VARCHAR,
- client_profile VARCHAR,
+ client_type VARCHAR, -- public, confidential, trusted
+ client_profile VARCHAR, -- server, mobile, service, batch, browser
client_name VARCHAR,
client_desc VARCHAR,
scope VARCHAR,
@@ -30,7 +30,7 @@ create table clients (
update_dt DATE
);
-create table services (
+create table service (
service_id VARCHAR PRIMARY KEY,
service_type VARCHAR, -- api, ms
service_name VARCHAR,
@@ -41,12 +41,10 @@ create table services (
update_dt DATE
);
-INSERT INTO users(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
+INSERT INTO user_profile(user_id, user_type, first_name, last_name, email, password) VALUES('admin', 'admin', 'admin', 'admin', 'admin@networknt.com', '1000:5b39342c202d37372c203132302c202d3132302c2034372c2032332c2034352c202d34342c202d31362c2034372c202d35392c202d35362c2039302c202d352c202d38322c202d32385d:949e6fcf9c4bb8a3d6a8c141a3a9182a572fb95fe8ccdc93b54ba53df8ef2e930f7b0348590df0d53f242ccceeae03aef6d273a34638b49c559ada110ec06992');
-INSERT INTO clients (client_id, client_type, client_profile, client_secret, client_name, client_desc, scope, redirect_uri, owner_id)
-VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'trusted', 'mobile', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin' );
-INSERT INTO clients (client_id, client_type, client_profile, client_secret, client_name, client_desc, scope, redirect_uri, owner_id)
-VALUES('6e9d1db3-2feb-4c1f-a5ad-9e93ae8ca59d', 'public', 'mobile', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('f7d42348-c647-4efb-a52d-4c5787421e72', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
+INSERT INTO client (client_id, client_type, client_secret, client_profile, client_name, client_desc, scope, redirect_uri, authenticate_class, owner_id) VALUES('59f347a0-c92d-11e6-9d9d-cec0c932ce01', 'public', '1000:5b37332c202d36362c202d36392c203131362c203132362c2036322c2037382c20342c202d37382c202d3131352c202d35332c202d34352c202d342c202d3132322c203130322c2033325d:29ad1fe88d66584c4d279a6f58277858298dbf9270ffc0de4317a4d38ba4b41f35f122e0825c466f2fa14d91e17ba82b1a2f2a37877a2830fae2973076d93cc2', 'mobile', 'PetStore Web Server', 'PetStore Web Server that calls PetStore API', 'petstore.r petstore.w', 'http://localhost:8080/authorization', null, 'admin' );
-INSERT INTO services (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
+INSERT INTO service (service_id, service_type, service_name, service_desc, scope, owner_id) VALUES ('AACT0001', 'ms', 'Account Service', 'A microservice that serves account information', 'a.r b.r', 'admin');
diff --git a/service/.gitignore b/service/.gitignore
index 3901d721..7883fdc0 100644
--- a/service/.gitignore
+++ b/service/.gitignore
@@ -13,6 +13,7 @@ dist/
*.tmp
*.zip
*.bak
+dependency-reduced-pom.xml
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
diff --git a/service/docker/Dockerfile b/service/Dockerfile
similarity index 100%
rename from service/docker/Dockerfile
rename to service/Dockerfile
diff --git a/service/build.sh b/service/build.sh
index 877b4f6c..9a92f697 100755
--- a/service/build.sh
+++ b/service/build.sh
@@ -42,7 +42,7 @@ cleanup() {
publish() {
echo "Building Docker image with version $VERSION"
- docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./docker/Dockerfile . --no-cache=true
+ docker build -t $IMAGE_NAME:$VERSION -t $IMAGE_NAME:latest -f ./Dockerfile . --no-cache=true
docker build -t $IMAGE_NAME:$VERSION-redhat -f ./docker/Dockerfile-Redhat . --no-cache=true
echo "Images built with version $VERSION"
echo "Pushing image to DockerHub"
diff --git a/service/pom.xml b/service/pom.xml
index 5d65dde3..b7a6bb39 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -4,7 +4,7 @@
com.networknt
light-oauth2
- 1.5.7
+ 1.5.8
..
diff --git a/service/src/main/java/com/networknt/oauth/service/PathHandlerProvider.java b/service/src/main/java/com/networknt/oauth/service/PathHandlerProvider.java
index f49d0603..a93c95e9 100644
--- a/service/src/main/java/com/networknt/oauth/service/PathHandlerProvider.java
+++ b/service/src/main/java/com/networknt/oauth/service/PathHandlerProvider.java
@@ -1,26 +1,41 @@
+
package com.networknt.oauth.service;
-import com.networknt.health.HealthGetHandler;
-import com.networknt.info.ServerInfoGetHandler;
-import com.networknt.oauth.service.handler.*;
+import com.networknt.config.Config;
import com.networknt.server.HandlerProvider;
import io.undertow.Handlers;
import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
import io.undertow.util.Methods;
+import com.networknt.info.ServerInfoGetHandler;
+import com.networknt.health.HealthGetHandler;
+import com.networknt.oauth.service.handler.*;
public class PathHandlerProvider implements HandlerProvider {
@Override
public HttpHandler getHandler() {
- HttpHandler handler = Handlers.routing()
- .add(Methods.GET, "/health", new HealthGetHandler())
- .add(Methods.GET, "/server/info", new ServerInfoGetHandler())
- .add(Methods.GET, "/oauth2/service", new Oauth2ServiceGetHandler())
+ return Handlers.routing()
+
.add(Methods.POST, "/oauth2/service", new Oauth2ServicePostHandler())
+
.add(Methods.PUT, "/oauth2/service", new Oauth2ServicePutHandler())
+
+ .add(Methods.GET, "/oauth2/service", new Oauth2ServiceGetHandler())
+
+ .add(Methods.POST, "/oauth2/service/{serviceId}/endpoint", new Oauth2ServiceServiceIdEndpointPostHandler())
+
+ .add(Methods.DELETE, "/oauth2/service/{serviceId}/endpoint", new Oauth2ServiceServiceIdEndpointDeleteHandler())
+
+ .add(Methods.GET, "/oauth2/service/{serviceId}/endpoint", new Oauth2ServiceServiceIdEndpointGetHandler())
+
.add(Methods.DELETE, "/oauth2/service/{serviceId}", new Oauth2ServiceServiceIdDeleteHandler())
+
.add(Methods.GET, "/oauth2/service/{serviceId}", new Oauth2ServiceServiceIdGetHandler())
+
+ .add(Methods.GET, "/health", new HealthGetHandler())
+
+ .add(Methods.GET, "/server/info", new ServerInfoGetHandler())
+
;
- return handler;
}
}
-
diff --git a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePostHandler.java b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePostHandler.java
index d8ece7a8..9553716c 100644
--- a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePostHandler.java
+++ b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePostHandler.java
@@ -39,8 +39,6 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send(status.toString());
}
}
- // set date here otherwise database will have the date populated but the cache without date
- service.setCreateDt(new Date(System.currentTimeMillis()));
services.set(serviceId, service);
} else {
Status status = new Status(SERVICE_ID_EXISTS, serviceId);
diff --git a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePutHandler.java b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePutHandler.java
index 2d9a2e7b..80b56d04 100644
--- a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePutHandler.java
+++ b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServicePutHandler.java
@@ -44,8 +44,6 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send(status.toString());
}
}
- // set updateDt here
- service.setUpdateDt(new Date(System.currentTimeMillis()));
services.set(serviceId, service);
}
}
diff --git a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointDeleteHandler.java b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointDeleteHandler.java
new file mode 100644
index 00000000..208e546d
--- /dev/null
+++ b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointDeleteHandler.java
@@ -0,0 +1,38 @@
+
+package com.networknt.oauth.service.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Delete all endpoints for a service by serviceId
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ServiceServiceIdEndpointDeleteHandler implements HttpHandler {
+ private static Logger logger = LoggerFactory.getLogger(Oauth2ServiceServiceIdEndpointDeleteHandler.class);
+ private static final String SERVICE_ENDPOINT_NOT_FOUND = "ERR12042";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ String serviceId = exchange.getQueryParameters().get("serviceId").getFirst();
+ if(logger.isDebugEnabled()) logger.debug("Delete service endpoint for serviceId " + serviceId);
+ IMap> serviceEndpoints = CacheStartupHookProvider.hz.getMap("serviceEndpoints");
+ if(serviceEndpoints.get(serviceId) == null || serviceEndpoints.get(serviceId).size() == 0) {
+ Status status = new Status(SERVICE_ENDPOINT_NOT_FOUND, serviceId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ } else {
+ serviceEndpoints.delete(serviceId);
+ }
+
+ }
+}
diff --git a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointGetHandler.java b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointGetHandler.java
new file mode 100644
index 00000000..70d9c94f
--- /dev/null
+++ b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointGetHandler.java
@@ -0,0 +1,46 @@
+
+package com.networknt.oauth.service.handler;
+
+import com.hazelcast.core.IMap;
+import com.hazelcast.query.PagingPredicate;
+import com.hazelcast.query.impl.predicates.LikePredicate;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.oauth.cache.model.ServiceComparator;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * Retrieve all service endpoints for a particular serviceId
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ServiceServiceIdEndpointGetHandler implements HttpHandler {
+ static final Logger logger = LoggerFactory.getLogger(Oauth2ServiceServiceIdEndpointGetHandler.class);
+ static final String SERVICE_ENDPOINT_NOT_FOUND = "ERR12042";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ IMap> serviceEndpoints = CacheStartupHookProvider.hz.getMap("serviceEndpoints");
+
+ String serviceId = exchange.getQueryParameters().get("serviceId").getFirst();
+ List values = serviceEndpoints.get(serviceId);
+
+ if(values == null || values.size() == 0) {
+ Status status = new Status(SERVICE_ENDPOINT_NOT_FOUND, serviceId);
+ exchange.setStatusCode(status.getStatusCode());
+ exchange.getResponseSender().send(status.toString());
+ return;
+ }
+ exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json");
+ exchange.getResponseSender().send(Config.getInstance().getMapper().writeValueAsString(values));
+ }
+}
diff --git a/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointPostHandler.java b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointPostHandler.java
new file mode 100644
index 00000000..77e138fe
--- /dev/null
+++ b/service/src/main/java/com/networknt/oauth/service/handler/Oauth2ServiceServiceIdEndpointPostHandler.java
@@ -0,0 +1,54 @@
+
+package com.networknt.oauth.service.handler;
+
+import com.hazelcast.core.IMap;
+import com.networknt.body.BodyHandler;
+import com.networknt.config.Config;
+import com.networknt.oauth.cache.CacheStartupHookProvider;
+import com.networknt.oauth.cache.model.Service;
+import com.networknt.oauth.cache.model.ServiceEndpoint;
+import com.networknt.oauth.cache.model.User;
+import com.networknt.status.Status;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * create or update a list of endpoints for the serviceId
+ *
+ * @author Steve Hu
+ */
+public class Oauth2ServiceServiceIdEndpointPostHandler implements HttpHandler {
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2ServiceServiceIdEndpointPostHandler.class);
+ static final String SERVICE_NOT_FOUND = "ERR12015";
+
+ @Override
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
+ List