You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by pi...@apache.org on 2022/12/26 05:16:21 UTC

[submarine] branch master updated: SUBMARINE-1138. New SSO function based on OIDC

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

pingsutw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new 9347fe5d SUBMARINE-1138. New SSO function based on OIDC
9347fe5d is described below

commit 9347fe5df6ac0e579df7b75421578eedb5342545
Author: cdmikechen <cd...@apache.org>
AuthorDate: Sun Dec 11 13:56:03 2022 +0800

    SUBMARINE-1138. New SSO function based on OIDC
    
    ### What is this PR for?
    Use pac4j to support OIDC and default login action, and fix some user rest api question.
    Currently, it is a preview version, which is mainly modified for the background and adapted to the front-end processing.
    
    The current purpose is to summarize the core of the modification and test cicd. So please do not merge the current changes!
    
    ### What type of PR is it?
    Improvement
    
    ### Todos
    * [x] - User `pac4j-oidc` to support OIDC SSO based on cookie/session
    * [x] - Support rest api with header token
    * [x] - Front end modification. The 302 redirection of httpclient is not handled at present
    * [x] - Remove jdk1.8 support
    * [x] - Optimized cookie configuration
    * [x] - automatically create new user when logged in
    * [x] - Support clustering session by jdbc
    * [x] - Change mybatis log to SLF4J
    * [x] - Add some tests
    * [x] - Add some more documents about oidc support
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/SUBMARINE-1138
    
    ### How should this be tested?
    Need to add some test later.
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Do the license files need updating? No
    * Are there breaking changes for older versions? Yes
    * Does this need new documentation? Yes
    
    Author: cdmikechen <cd...@apache.org>
    
    Signed-off-by: Kevin <pi...@apache.org>
    
    Closes #1019 from cdmikechen/SUBMARINE-1138-0.8.0-pacj4j5.7 and squashes the following commits:
    
    359109b8 [cdmikechen] SysUserService singleton
    3501790e [cdmikechen] Add cookie document
    0a455761 [cdmikechen] Fix document
    ad9d1902 [cdmikechen] Add test
    d1a3304e [cdmikechen] revert authType
    b5752418 [cdmikechen] remove SUBMARINE_AUTH_TYPE in image
    c5526736 [cdmikechen] Fix test error
    2e296587 [cdmikechen] Remove derby and upgrade jdk11 version
    c8644cea [cdmikechen] update jdk11
    2803bda4 [cdmikechen] Adjustment code
    e9a1b8ac [cdmikechen] Support jdk11 and pac4j 5.6.1 Add cookie samesite/httponly/securite
    eef13732 [cdmikechen] Test python-sdk
    2ce98c1c [cdmikechen] Dealing with automatic user creation
    1c98d2fc [cdmikechen] Commit for python check fix
    9ecb7cbc [cdmikechen] Add api paths auth checks
    220c49a0 [cdmikechen] Change auth type to flow type
    3df16b4b [cdmikechen] Use servlet to replace static auth type check js
    94099147 [cdmikechen] Handle front-end workbench oidc support
    8b786954 [cdmikechen] deal with 401
    16fe1a17 [cdmikechen] Add @Context to fix error
    90eb5c5c [cdmikechen] Add token to rest api header
    0f8f2636 [cdmikechen] Add oidc backend support(excluding the addition of oidc users)
---
 .github/workflows/deploy_docker_images.yml         |   4 +-
 .github/workflows/master.yml                       |  44 +++---
 conf/log4j.properties                              |   3 +
 dev-support/docker-images/submarine/Dockerfile     |   6 +-
 pom.xml                                            |   8 +-
 .../submarine/commons/utils/SubmarineConfVars.java |   5 +
 .../submarine/server/api/workbench/UserInfo.java   |  66 ++++++++-
 submarine-server/server-core/pom.xml               |  53 +++++++-
 .../apache/submarine/server/SubmarineServer.java   |  81 +++++++++--
 .../server/rest/workbench/LoginRestApi.java        |  20 +--
 .../server/rest/workbench/SysUserRestApi.java      | 105 ++++++++++++---
 .../server/rest/workbench/SystemRestApi.java       |   2 +-
 .../workbench/annotation/NoneAuth.java}            |  28 ++--
 .../submarine/server/security/SecurityFactory.java |  10 +-
 .../server/security/SecurityProvider.java          | 102 ++++++++++++--
 .../{CommonConfig.java => AuthFlowType.java}       |  24 ++--
 .../server/security/common/CommonConfig.java       |   5 +
 .../server/security/common/CommonFilter.java       | 139 ++++++++++++++++---
 .../security/common/RegistryUserActionAdapter.java |  86 ++++++++++++
 .../server/security/oidc/OidcCallbackResource.java |  49 +++++++
 .../submarine/server/security/oidc/OidcConfig.java |  54 ++++++++
 .../SimpleFilter.java => oidc/OidcFilter.java}     |  58 ++++----
 .../server/security/oidc/OidcSecurityProvider.java | 149 +++++++++++++++++++++
 .../server/security/simple/SimpleFilter.java       |  21 ++-
 .../security/simple/SimpleSecurityProvider.java    |  46 ++-----
 .../server/utils/response/DictAnnotation.java      |   4 +-
 .../src/main/resources/log4j.properties            |   3 +
 .../server/security/MockHttpServletRequest.java    |   2 +-
 .../security/oidc/MockOidcHttpServletRequest.java} |  24 ++--
 .../SubmarineAuthOidcTest.java}                    | 130 ++++++++++++------
 .../{ => simple}/SubmarineAuthSimpleTest.java      |   8 +-
 .../resources/security/openid-configuration.json   | 145 ++++++++++++++++++++
 .../src/test/resources/security/user-info.json     |  12 ++
 .../server/database/utils/MyBatisUtil.java         |  13 +-
 .../database/workbench/entity/SysUserEntity.java   |  24 ++++
 .../database/workbench/service/SysUserService.java |  52 ++++++-
 .../src/main/resources/mybatis-config.xml          |   3 +-
 .../server-submitter/submitter-k8s/pom.xml         |   4 -
 .../workbench-web/src/WEB-INF/web.xml              |   6 -
 .../workbench-web/src/app/app.component.ts         |   1 -
 .../workbench-web/src/app/app.module.ts            |  10 +-
 .../src/app/core/auth/api-token-injector.ts        |  60 +++++++++
 .../workbench-web/src/app/core/auth/auth.guard.ts  |   2 +-
 .../pages/workbench/workbench-routing.module.ts    |   2 +-
 .../workbench-web/src/app/services/auth.service.ts |  50 +++++--
 .../workbench-web/src/assets/security/provider.js  |  25 +---
 submarine-workbench/workbench-web/src/index.html   |   1 +
 .../workbench-web/src/types/index.d.ts             |  23 +---
 submarine-workbench/workbench-web/tsconfig.json    |   2 +-
 .../wip-designs/security-implementation.md         |  52 +++----
 50 files changed, 1466 insertions(+), 360 deletions(-)

diff --git a/.github/workflows/deploy_docker_images.yml b/.github/workflows/deploy_docker_images.yml
index 4955dc49..3fa0f010 100644
--- a/.github/workflows/deploy_docker_images.yml
+++ b/.github/workflows/deploy_docker_images.yml
@@ -35,10 +35,10 @@ jobs:
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_PASSWORD }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 1b0745f3..ab8c7ac8 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -67,10 +67,10 @@ jobs:
       - uses: actions/checkout@v2
         with:
           fetch-depth: 50
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -146,10 +146,10 @@ jobs:
       - uses: actions/checkout@v2
         with:
           fetch-depth: 50
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -247,10 +247,10 @@ jobs:
         with:
           path: ./submarine-test/test-e2e/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -293,10 +293,10 @@ jobs:
         with:
           path: ./submarine-test/test-k8s/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -367,10 +367,10 @@ jobs:
         with:
           path: ./submarine-commons/commons-runtime/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -403,10 +403,10 @@ jobs:
         with:
           path: ./submarine-client/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -472,10 +472,10 @@ jobs:
           path: |
             ./submarine-serve/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -588,10 +588,10 @@ jobs:
           path: |
             ./submarine-server/server-submitter/submitter-k8s/target/jacoco.exec
           key: ${{ runner.os }}-docker-${{ github.sha }}
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -620,7 +620,7 @@ jobs:
       - uses: actions/checkout@v2
       - uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - run: mvn org.apache.rat:apache-rat-plugin:check
   linter:
     name: Check Style
@@ -628,10 +628,10 @@ jobs:
     timeout-minutes: 30
     steps:
       - uses: actions/checkout@v2
-      - name: Set up JDK 1.8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: "1.8"
+          java-version: "11"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
@@ -754,16 +754,10 @@ jobs:
           path: ~/.sonar/cache
           key: ${{ runner.os }}-sonar
           restore-keys: ${{ runner.os }}-sonar
-      - name: Set up JDK 1.8
-        uses: actions/setup-java@v1
-        with:
-          java-version: "1.8"
       - name: Set up Maven 3.6.3
         uses: stCarolas/setup-maven@v4
         with:
           maven-version: 3.6.3
-      - name: Build the project with JDK 8
-        run: mvn install -DskipTests
       - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
diff --git a/conf/log4j.properties b/conf/log4j.properties
index 2f44c721..125e24f8 100644
--- a/conf/log4j.properties
+++ b/conf/log4j.properties
@@ -57,3 +57,6 @@ log4j.appender.console.target=System.err
 log4j.appender.console.layout=org.apache.log4j.PatternLayout
 log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} [%t]: %p %c{2}: %m%n
 log4j.appender.console.encoding=UTF-8
+
+# mybatis sql debug
+log4j.logger.org.apache.submarine.server.database=DEBUG
diff --git a/dev-support/docker-images/submarine/Dockerfile b/dev-support/docker-images/submarine/Dockerfile
index ca76d318..507c7d8d 100644
--- a/dev-support/docker-images/submarine/Dockerfile
+++ b/dev-support/docker-images/submarine/Dockerfile
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM alpine:3.10
+FROM alpine:3.16.3
 MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
 
 # If you are in China, enabling the following two lines of code can speed up the build of the image, but it may cause failure in travis.
@@ -23,10 +23,10 @@ MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
 
 # INSTALL openjdk
 RUN apk update && \
-    apk add --no-cache openjdk8 bash tini && \
+    apk add --no-cache openjdk11 bash tini && \
     rm -rf /tmp/* /var/cache/apk/*
 
-ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk/jre
+ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk/jre
 
 # Install Submarine
 ADD ./tmp/submarine-dist-*.tar.gz /opt/
diff --git a/pom.xml b/pom.xml
index 66a266a6..b1df264f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
 
   <properties>
     <!-- language versions -->
-    <java.version>1.8</java.version>
+    <java.version>11</java.version>
     <go.version>1.11.8</go.version>
 
     <!-- plugin versions -->
@@ -127,9 +127,8 @@
     <commons-text.version>1.4</commons-text.version>
     <zip4j.version>1.3.2</zip4j.version>
     <commons-collections.version>3.2.2</commons-collections.version>
-    <nimbus-jose-jwt.version>7.9</nimbus-jose-jwt.version>
+    <nimbus-jose-jwt.version>9.21</nimbus-jose-jwt.version>
     <mybatis-generator.version>1.3.7</mybatis-generator.version>
-    <derby.version>10.15.1.3</derby.version>
     <zeppelin.version>0.9.0-preview1</zeppelin.version>
     <jgit.version>5.13.0.202109080827-r</jgit.version>
     <atomix.version>3.1.5</atomix.version>
@@ -149,7 +148,8 @@
     <protobuf-java.version>3.14.0</protobuf-java.version>
     <joda-time.version>2.10.8</joda-time.version>
     <!--  pac4j  -->
-    <pac4j.version>4.5.6</pac4j.version>
+    <pac4j.version>5.6.1</pac4j.version>
+    <reflections.version>0.10.2</reflections.version>
   </properties>
 
   <modules>
diff --git a/submarine-commons/commons-utils/src/main/java/org/apache/submarine/commons/utils/SubmarineConfVars.java b/submarine-commons/commons-utils/src/main/java/org/apache/submarine/commons/utils/SubmarineConfVars.java
index 6b0b9e45..6dbe18e4 100644
--- a/submarine-commons/commons-utils/src/main/java/org/apache/submarine/commons/utils/SubmarineConfVars.java
+++ b/submarine-commons/commons-utils/src/main/java/org/apache/submarine/commons/utils/SubmarineConfVars.java
@@ -86,6 +86,11 @@ public class SubmarineConfVars {
     ENVIRONMENT_CONDA_MIN_VERSION("environment.conda.min.version", "4.0.1"),
     ENVIRONMENT_CONDA_MAX_VERSION("environment.conda.max.version", "4.11.10"),
 
+    /* cookie setting */
+    SUBMARINE_COOKIE_HTTP_ONLY("submarine.cookie.http.only", false),
+    SUBMARINE_COOKIE_SECURE("submarine.cookie.secure", false),
+    SUBMARINE_COOKIE_SAMESITE("submarine.cookie.samesite", ""),
+
     /* auth */
     SUBMARINE_AUTH_TYPE("submarine.auth.type", "none"),
     SUBMARINE_AUTH_DEFAULT_SECRET("submarine.auth.default.secret", "SUBMARINE_SECRET_12345678901234567890"),
diff --git a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/workbench/UserInfo.java b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/workbench/UserInfo.java
index c14eb865..edbdaaa2 100644
--- a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/workbench/UserInfo.java
+++ b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/workbench/UserInfo.java
@@ -24,7 +24,7 @@ public class UserInfo {
   private final String username;
   private final String password;
   private final String avatar;
-  private final int status;
+  private final String status;
   private final String telephone;
   private final String lastLoginIp;
   private final long lastLoginTime;
@@ -60,7 +60,7 @@ public class UserInfo {
     private String username;
     private String password;
     private String avatar;
-    private int status = 0;
+    private String status;
     private String telephone;
     private String lastLoginIp;
     private long lastLoginTime;
@@ -91,7 +91,7 @@ public class UserInfo {
       return this;
     }
 
-    public Builder status(int status) {
+    public Builder status(String status) {
       this.status = status;
       return this;
     }
@@ -146,6 +146,66 @@ public class UserInfo {
     }
   }
 
+  public String getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public String getAvatar() {
+    return avatar;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public String getTelephone() {
+    return telephone;
+  }
+
+  public String getLastLoginIp() {
+    return lastLoginIp;
+  }
+
+  public long getLastLoginTime() {
+    return lastLoginTime;
+  }
+
+  public String getCreatorId() {
+    return creatorId;
+  }
+
+  public long getCreateTime() {
+    return createTime;
+  }
+
+  public String getMerchantCode() {
+    return merchantCode;
+  }
+
+  public int getDeleted() {
+    return deleted;
+  }
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public Role getRole() {
+    return role;
+  }
+
   @Override
   public String toString() {
     return "User{" +
diff --git a/submarine-server/server-core/pom.xml b/submarine-server/server-core/pom.xml
index 311ee5a6..f582c69b 100644
--- a/submarine-server/server-core/pom.xml
+++ b/submarine-server/server-core/pom.xml
@@ -258,12 +258,6 @@
       <!-- mysql-connector-java uses the GPL license. When we release the version in Submarine-dist, we exclude mysql-connector-java jar -->
     </dependency>
 
-    <dependency>
-      <groupId>org.apache.derby</groupId>
-      <artifactId>derby</artifactId>
-      <version>${derby.version}</version>
-    </dependency>
-
     <dependency>
       <groupId>org.json</groupId>
       <artifactId>json</artifactId>
@@ -465,6 +459,53 @@
       </exclusions>
     </dependency>
 
+    <dependency>
+      <groupId>org.pac4j</groupId>
+      <artifactId>pac4j-oidc</artifactId>
+      <version>${pac4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>com.fasterxml.jackson.core</groupId>
+          <artifactId>jackson-databind</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <dependency>
+      <groupId>org.pac4j</groupId>
+      <artifactId>pac4j-javaee</artifactId>
+      <version>${pac4j.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.reflections</groupId>
+      <artifactId>reflections</artifactId>
+      <version>${reflections.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.javassist</groupId>
+          <artifactId>javassist</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>${mockito.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.github.tomakehurst</groupId>
+      <artifactId>wiremock-jre8-standalone</artifactId>
+      <version>${wiremock.version}</version>
+      <scope>test</scope>
+    </dependency>
+
   </dependencies>
 
   <build>
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/SubmarineServer.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/SubmarineServer.java
index 44498802..5a3f9b8b 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/SubmarineServer.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/SubmarineServer.java
@@ -18,12 +18,16 @@
  */
 package org.apache.submarine.server;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.PropertyConfigurator;
+import org.apache.submarine.server.database.utils.MyBatisUtil;
 import org.apache.submarine.server.rest.provider.YamlEntityProvider;
 import org.apache.submarine.server.security.SecurityFactory;
 import org.apache.submarine.server.security.SecurityProvider;
+import org.apache.submarine.server.security.common.AuthFlowType;
 import org.apache.submarine.server.workbench.websocket.NotebookServer;
 import org.apache.submarine.server.websocket.WebSocketServer;
+import org.eclipse.jetty.http.HttpCookie;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -33,6 +37,8 @@ import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.session.DatabaseAdaptor;
+import org.eclipse.jetty.server.session.JDBCSessionDataStoreFactory;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
@@ -119,6 +125,9 @@ public class SubmarineServer extends ResourceConfig {
 
     setupRestApiContextHandler(webApp, conf);
 
+    // Cookie config
+    setCookieConfig(webApp);
+
     // Notebook server
     setupNotebookServer(webApp, conf, sharedServiceLocator);
 
@@ -199,6 +208,51 @@ public class SubmarineServer extends ResourceConfig {
       webApp.setTempDirectory(warTempDirectory);
     }
 
+    // add security filter
+    Optional<SecurityProvider> securityProvider = SecurityFactory.getSecurityProvider();
+    if (securityProvider.isPresent()) {
+      SecurityProvider provider = securityProvider.get();
+      Class<Filter> filterClass = provider.getFilterClass();
+      // add filter
+      LOG.info("Add {} to support auth", filterClass);
+      webApp.addFilter(filterClass, "/*", EnumSet.of(DispatcherType.REQUEST));
+      // add flow type result to front end
+      AuthFlowType type = provider.getAuthFlowType();
+      // If using session, we can add JDBCSessionDataStoreFactory to support clustering session
+      // This solves two problems:
+      // 1. session loss after service restart
+      // 2. session sharing when multiple replicas
+      if (type == AuthFlowType.SESSION) {
+        // Configure a JDBCSessionDataStoreFactory.
+        JDBCSessionDataStoreFactory sessionDataStoreFactory = new JDBCSessionDataStoreFactory();
+        sessionDataStoreFactory.setGracePeriodSec(3600);
+        sessionDataStoreFactory.setSavePeriodSec(0);
+        // add datasource (current mybatis) to factory
+        DatabaseAdaptor adaptor = new DatabaseAdaptor();
+        adaptor.setDatasource(MyBatisUtil.getDatasource());
+        sessionDataStoreFactory.setDatabaseAdaptor(adaptor);
+        // Add the SessionDataStoreFactory as a bean on the server.
+        jettyWebServer.addBean(sessionDataStoreFactory);
+      }
+
+      ServletHolder authProviderServlet = new ServletHolder(new HttpServlet() {
+        private static final long serialVersionUID = 1L;
+        private final String staticProviderJs = String.format(
+            "(function () { window.GLOBAL_CONFIG = { \"type\": \"%s\" }; })();", type.getType()
+        );
+        private static final String contentType = "application/javascript";
+        private static final String encoding = "UTF-8";
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+          resp.setContentType(contentType);
+          resp.setCharacterEncoding(encoding);
+          resp.getWriter().write(staticProviderJs);
+        }
+      });
+      webApp.addServlet(authProviderServlet, "/assets/security/provider.js");
+    }
+
     webApp.addServlet(new ServletHolder(new DefaultServlet()), "/");
     // When requesting the workbench page, the content of index.html needs to be returned,
     // otherwise a 404 error will be displayed
@@ -207,19 +261,30 @@ public class SubmarineServer extends ResourceConfig {
     webApp.addServlet(new ServletHolder(RefreshServlet.class), "/user/*");
     webApp.addServlet(new ServletHolder(RefreshServlet.class), "/workbench/*");
 
-    // add security filter
-    Optional<SecurityProvider> securityProvider = SecurityFactory.getSecurityProvider();
-    if (securityProvider.isPresent()) {
-      Class<Filter> filterClass = securityProvider.get().getFilterClass();
-      LOG.info("Add {} to support auth", filterClass);
-      webApp.addFilter(filterClass, "/*", EnumSet.of(DispatcherType.REQUEST));
-    }
-
     handlers.setHandlers(new Handler[]{webApp});
 
     return webApp;
   }
 
+  /**
+   * Session cookie config
+   */
+  public static void setCookieConfig(WebAppContext webapp) {
+    // http only
+    webapp.getSessionHandler().getSessionCookieConfig().setHttpOnly(
+        conf.getBoolean(SubmarineConfVars.ConfVars.SUBMARINE_COOKIE_HTTP_ONLY)
+    );
+    // same site: NONE("None"), STRICT("Strict"), LAX("Lax");
+    String sameSite = conf.getString(SubmarineConfVars.ConfVars.SUBMARINE_COOKIE_SAMESITE);
+    if (StringUtils.isNoneBlank(sameSite)) {
+      webapp.getSessionHandler().setSameSite(HttpCookie.SameSite.valueOf(sameSite.toUpperCase()));
+    }
+    // secure
+    webapp.getSessionHandler().getSessionCookieConfig().setSecure(
+        conf.getBoolean(SubmarineConfVars.ConfVars.SUBMARINE_COOKIE_SECURE)
+    );
+  }
+
   private static Server setupJettyServer(SubmarineConfiguration conf) {
     ThreadPool threadPool =
         new QueuedThreadPool(conf.getInt(SubmarineConfVars.ConfVars.SUBMARINE_SERVER_JETTY_THREAD_POOL_MAX),
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/LoginRestApi.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/LoginRestApi.java
index f385a997..4feb6dd2 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/LoginRestApi.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/LoginRestApi.java
@@ -21,6 +21,7 @@ package org.apache.submarine.server.rest.workbench;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 import org.apache.ibatis.session.SqlSession;
+import org.apache.submarine.server.rest.workbench.annotation.NoneAuth;
 import org.apache.submarine.server.rest.workbench.annotation.SubmarineApi;
 import org.apache.submarine.server.database.workbench.entity.SysUserEntity;
 import org.apache.submarine.server.database.workbench.mappers.SysUserMapper;
@@ -54,6 +55,7 @@ public class LoginRestApi {
   @POST
   @Path("/login")
   @SubmarineApi
+  @NoneAuth
   public Response login(String loginParams) {
     HashMap<String, String> mapParams
         = gson.fromJson(loginParams, new TypeToken<HashMap<String, String>>() {}.getType());
@@ -80,7 +82,8 @@ public class LoginRestApi {
         claimsMap.put("exp", new Date().getTime() + CommonConfig.MAX_AGE);
         claimsMap.put("sub", "submarine");
         claimsMap.put("jti", sysUser.getId());
-
+        // TODO(cdmikechen) By default the simple token is used,
+        //  in other cases such as ldap it may need to be returned as an interface
         String token = SimpleLoginConfig.getJwtGenerator().generate(claimsMap);
         sysUser.setToken(token);
       } else {
@@ -105,21 +108,6 @@ public class LoginRestApi {
         .build();
   }
 
-  /**
-   * Get user by unique name
-   */
-  public SysUserEntity getUserByName(String name) throws Exception {
-    SysUserEntity sysUser = null;
-    try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
-      SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
-      sysUser = sysUserMapper.getUserByUniqueName(name);
-    } catch (Exception e) {
-      LOG.error(e.getMessage(), e);
-      throw new Exception(e);
-    }
-    return sysUser;
-  }
-
   @POST
   @Path("/2step-code")
   @SubmarineApi
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SysUserRestApi.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SysUserRestApi.java
index d8e41e75..b4d053f1 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SysUserRestApi.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SysUserRestApi.java
@@ -19,8 +19,10 @@
 package org.apache.submarine.server.rest.workbench;
 
 import com.github.pagehelper.PageInfo;
-import com.google.gson.Gson;
 
+import org.apache.submarine.server.rest.workbench.annotation.NoneAuth;
+import org.apache.submarine.server.security.SecurityFactory;
+import org.apache.submarine.server.security.SecurityProvider;
 import org.apache.submarine.server.utils.response.JsonResponse;
 import org.apache.submarine.server.utils.response.JsonResponse.ListResult;
 import org.apache.submarine.server.rest.workbench.annotation.SubmarineApi;
@@ -30,11 +32,14 @@ import org.apache.submarine.server.api.workbench.Action;
 import org.apache.submarine.server.api.workbench.Permission;
 import org.apache.submarine.server.api.workbench.Role;
 import org.apache.submarine.server.api.workbench.UserInfo;
+import org.pac4j.core.profile.CommonProfile;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -42,18 +47,22 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
+
+import static org.apache.submarine.server.database.workbench.service.SysUserService.DEFAULT_ADMIN_UID;
 
 @Path("/sys/user")
 @Produces("application/json")
 @Singleton
 public class SysUserRestApi {
+
   private static final Logger LOG = LoggerFactory.getLogger(SysUserRestApi.class);
 
-  private SysUserService userService = new SysUserService();
-  private static final Gson gson = new Gson();
+  private static final SysUserService userService = SysUserService.INSTANCE;
 
   @Inject
   public SysUserRestApi() {
@@ -69,7 +78,7 @@ public class SysUserRestApi {
                                 @QueryParam("field") String field,
                                 @QueryParam("pageNo") int pageNo,
                                 @QueryParam("pageSize") int pageSize) {
-    LOG.info("queryDictList userName:{}, email:{}, deptCode:{}, " +
+    LOG.debug("queryDictList userName:{}, email:{}, deptCode:{}, " +
             "column:{}, field:{}, pageNo:{}, pageSize:{}",
         userName, email, deptCode, column, field, pageNo, pageSize);
 
@@ -107,6 +116,8 @@ public class SysUserRestApi {
   @POST
   @Path("/add")
   @SubmarineApi
+  /* This is temporarily marked as not requiring validation in the way that on the login page failed */
+  @NoneAuth
   public Response add(SysUserEntity sysUser) {
     LOG.info("add({})", sysUser.toString());
 
@@ -159,7 +170,53 @@ public class SysUserRestApi {
   @GET
   @Path("/info")
   @SubmarineApi
-  public Response info() {
+  public Response info(@Context HttpServletRequest hsRequest, @Context HttpServletResponse hsResponse) {
+    UserInfo userInfo = null;
+    // get SecurityProvider to use perform method to get user info
+    Optional<SecurityProvider> securityProvider = SecurityFactory.getSecurityProvider();
+    if (securityProvider.isPresent()) {
+      Optional<CommonProfile> profileOpt = securityProvider.get().perform(hsRequest, hsResponse);
+      if (profileOpt.isPresent()) {
+        // Get user information
+        SysUserEntity sysUser = userService.getUserByName(profileOpt.get().getUsername());
+        if (sysUser != null) {
+          // Create user info
+          UserInfo.Builder userInfoBuilder = new UserInfo.Builder(sysUser.getId(), sysUser.getUserName());
+          userInfo = userInfoBuilder
+              .username(sysUser.getUserName())
+              .password("******")
+              .avatar(sysUser.getAvatar())
+              .status(sysUser.getStatus())
+              .telephone(sysUser.getPhone())
+              .lastLoginIp("******")
+              .lastLoginTime(System.currentTimeMillis())
+              .creatorId(sysUser.getUserName())
+              .createTime(sysUser.getCreateTime().getTime())
+              .merchantCode("")
+              .deleted(0)
+              .roleId("default")
+              .role(createDefaultRole()).build();
+        }
+      }
+    }
+    if (userInfo == null) { // user not found
+      return new JsonResponse.Builder<>(Response.Status.OK).
+              success(false)
+              .message("User can not be found!")
+              .build();
+    } else {
+      return new JsonResponse.Builder<UserInfo>(Response.Status.OK)
+              .success(true)
+              .result(userInfo)
+              .build();
+    }
+  }
+
+  /**
+   * Create default role
+   */
+  private Role createDefaultRole() {
+    // TODO(cdmikechen): Will do after the role function is completed
     List<Action> actions = new ArrayList<Action>();
     Action action1 = new Action("add", false, "add");
     Action action2 = new Action("query", false, "query");
@@ -223,17 +280,35 @@ public class SysUserRestApi {
     permissions.add(permission12);
 
     Role.Builder roleBuilder = new Role.Builder("admin", "admin");
-    Role role = roleBuilder.describe("Permission").status(1).creatorId("system")
-        .createTime(1497160610259L).deleted(0).permissions(permissions).build();
-
-    UserInfo.Builder userInfoBuilder = new UserInfo.Builder("4291d7da9005377ec9aec4a71ea837f", "admin");
-    UserInfo userInfo = userInfoBuilder.username("admin").password("")
-        .avatar("/avatar2.jpg").status(1).telephone("").lastLoginIp("27.154.74.117")
-        .lastLoginTime(1534837621348L).creatorId("admin")
-        .createTime(1497160610259L).merchantCode("TLif2btpzg079h15bk")
-        .deleted(0).roleId("admin").role(role).build();
+    return roleBuilder.describe("Permission")
+            .status(1)
+            .creatorId("system")
+            .createTime(System.currentTimeMillis())
+            .deleted(0)
+            .permissions(permissions)
+            .build();
+  }
 
-    return new JsonResponse.Builder<UserInfo>(Response.Status.OK).success(true).result(userInfo).build();
+  /**
+   * Create default user
+   */
+  private UserInfo createDefaultUser() {
+    LOG.warn("Can not get user info, use a default admin user");
+    UserInfo.Builder userInfoBuilder = new UserInfo.Builder(DEFAULT_ADMIN_UID, "admin");
+    return userInfoBuilder.username("admin")
+            .password("")
+            .avatar("/avatar2.jpg")
+            .status("1")
+            .telephone("")
+            .lastLoginIp("******")
+            .lastLoginTime(System.currentTimeMillis())
+            .creatorId("admin")
+            .createTime(System.currentTimeMillis())
+            .merchantCode("TLif2btpzg079h15bk")
+            .deleted(0)
+            .roleId("admin")
+            .role(createDefaultRole())
+            .build();
   }
 
   @POST
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SystemRestApi.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SystemRestApi.java
index 733dd846..9e5a8d2c 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SystemRestApi.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/SystemRestApi.java
@@ -49,7 +49,7 @@ import java.util.Map;
 public class SystemRestApi {
   private static final Logger LOG = LoggerFactory.getLogger(SystemRestApi.class);
 
-  private SysUserService userService = new SysUserService();
+  private static final SysUserService userService = SysUserService.INSTANCE;
 
   @Inject
   public SystemRestApi() {
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/annotation/NoneAuth.java
similarity index 57%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
copy to submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/annotation/NoneAuth.java
index 83f1bb84..ca6b1cee 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/workbench/annotation/NoneAuth.java
@@ -17,23 +17,17 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security.common;
+package org.apache.submarine.server.rest.workbench.annotation;
 
-import org.apache.submarine.commons.utils.SubmarineConfiguration;
-
-import static org.apache.submarine.commons.utils.SubmarineConfVars.ConfVars;
-
-public class CommonConfig {
-
-  public static final String LOGOUT_ENDPOINT = "/auth/logout";
-  public static final String AUTH_HEADER = "Authorization";
-  public static final String BEARER_HEADER_PREFIX = "Bearer ";
-
-  public static final int MAX_AGE;
-
-  static {
-    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
-    MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
-  }
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
+/**
+ * Identifies methods that do not require auth checks
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface NoneAuth {
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityFactory.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityFactory.java
index d2232443..01fd6f2e 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityFactory.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityFactory.java
@@ -21,6 +21,7 @@ package org.apache.submarine.server.security;
 
 import org.apache.submarine.commons.utils.SubmarineConfVars;
 import org.apache.submarine.commons.utils.SubmarineConfiguration;
+import org.apache.submarine.server.security.oidc.OidcSecurityProvider;
 import org.apache.submarine.server.security.simple.SimpleSecurityProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,10 +40,15 @@ public class SecurityFactory {
     return (SimpleSecurityProvider) providerMap.get("simple");
   }
 
+  public static OidcSecurityProvider getPac4jSecurityProvider() {
+    return (OidcSecurityProvider) providerMap.get("oidc");
+  }
+
   static {
-    // int provider map
+    // init provider map
     providerMap = new HashMap<>();
     providerMap.put("simple", new SimpleSecurityProvider());
+    providerMap.put("oidc", new OidcSecurityProvider());
   }
 
   public static void addProvider(String name, SecurityProvider provider) {
@@ -51,7 +57,7 @@ public class SecurityFactory {
 
   public static Optional<SecurityProvider> getSecurityProvider() {
     String authType = SubmarineConfiguration.getInstance()
-        .getString(SubmarineConfVars.ConfVars.SUBMARINE_AUTH_TYPE);
+            .getString(SubmarineConfVars.ConfVars.SUBMARINE_AUTH_TYPE);
     if (providerMap.containsKey(authType)) {
       return Optional.ofNullable(providerMap.get(authType));
     } else {
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityProvider.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityProvider.java
index c775705b..ee78d1b3 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityProvider.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/SecurityProvider.java
@@ -19,8 +19,22 @@
 
 package org.apache.submarine.server.security;
 
+import org.apache.submarine.server.security.common.AuthFlowType;
 import org.pac4j.core.config.Config;
+import org.pac4j.core.context.WebContext;
+import org.pac4j.core.context.session.SessionStore;
+import org.pac4j.core.engine.CallbackLogic;
+import org.pac4j.core.engine.DefaultCallbackLogic;
+import org.pac4j.core.engine.DefaultLogoutLogic;
+import org.pac4j.core.engine.DefaultSecurityLogic;
+import org.pac4j.core.engine.LogoutLogic;
+import org.pac4j.core.engine.SecurityLogic;
+import org.pac4j.core.http.adapter.HttpActionAdapter;
 import org.pac4j.core.profile.CommonProfile;
+import org.pac4j.core.util.FindBest;
+import org.pac4j.jee.context.JEEContextFactory;
+import org.pac4j.jee.context.session.JEESessionStoreFactory;
+import org.pac4j.jee.http.adapter.JEEHttpActionAdapter;
 
 import javax.servlet.Filter;
 import javax.servlet.http.HttpServletRequest;
@@ -30,32 +44,104 @@ import java.util.Optional;
 /**
  * Provide security methods for different authentication types
  */
-public interface SecurityProvider<T extends Filter, R extends CommonProfile> {
+public abstract class SecurityProvider<T extends Filter, R extends CommonProfile> {
 
-  String DEFAULT_AUTHORIZER = "isAuthenticated";
+  protected final String DEFAULT_AUTHORIZER = "isAuthenticated";
+
+  protected Config pac4jConfig;
+
+  /**
+   * Get authentication flow type
+   */
+  public AuthFlowType getAuthFlowType() {
+    return AuthFlowType.TOKEN;
+  }
 
   /**
    * Get filter class
    */
-  Class<T> getFilterClass();
+  public abstract Class<T> getFilterClass();
 
   /**
    * Get pac4j config
    */
-  Config getConfig();
+  public Config getConfig() {
+    if (this.pac4jConfig == null) this.pac4jConfig = createConfig();
+    return pac4jConfig;
+  }
+
+  /**
+   * Create pac4j config
+   */
+  protected abstract Config createConfig();
 
   /**
    * Get pac4j client
    */
-  String getClient(HttpServletRequest httpServletRequest);
+  public abstract String getClient(HttpServletRequest httpServletRequest);
+
+  /**
+   * Create {@link WebContext}
+   */
+  protected WebContext createWebContext(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
+    return FindBest.webContextFactory(null, getConfig(), JEEContextFactory.INSTANCE)
+        .newContext(hsRequest, hsResponse);
+  }
+
+  /**
+   * Create {@link SessionStore}
+   */
+  protected SessionStore createSessionStore(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
+    return FindBest.sessionStoreFactory(null, getConfig(), JEESessionStoreFactory.INSTANCE)
+        .newSessionStore(hsRequest, hsResponse);
+  }
+
+  /**
+   * Create {@link HttpActionAdapter}
+   */
+  protected HttpActionAdapter createHttpActionAdapter() {
+    return FindBest.httpActionAdapter(null, getConfig(), JEEHttpActionAdapter.INSTANCE);
+  }
+
+  /**
+   * Create {@link SecurityLogic}
+   */
+  protected SecurityLogic createSecurityLogic() {
+    return FindBest.securityLogic(null, getConfig(), DefaultSecurityLogic.INSTANCE);
+  }
+
+  /**
+   * Create {@link CallbackLogic}
+   */
+  protected CallbackLogic createCallbackLogic() {
+    return FindBest.callbackLogic(null, getConfig(), DefaultCallbackLogic.INSTANCE);
+  }
+
+  /**
+   * Create {@link LogoutLogic}
+   */
+  protected LogoutLogic createLogoutLogic() {
+    return FindBest.logoutLogic(null, this.pac4jConfig, DefaultLogoutLogic.INSTANCE);
+  }
 
   /**
    * Process authentication information and return user profile
    */
-  Optional<R> perform(HttpServletRequest hsRequest, HttpServletResponse hsResponse);
+  public abstract Optional<R> perform(HttpServletRequest hsRequest, HttpServletResponse hsResponse);
+
+  /**
+   * Handling login perform
+   */
+  public void login(HttpServletRequest hsRequest, HttpServletResponse hsResponse) { }
 
   /**
-   * Get user profile
+   * Handling callback perform
    */
-  Optional<R> getProfile(HttpServletRequest hsRequest, HttpServletResponse hsResponse);
+  public void callback(HttpServletRequest hsRequest, HttpServletResponse hsResponse) { }
+
+  /**
+   * Handling logout perform
+   */
+  public void logout(HttpServletRequest hsRequest, HttpServletResponse hsResponse) { }
+
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/AuthFlowType.java
similarity index 61%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
copy to submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/AuthFlowType.java
index 83f1bb84..af7600d1 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/AuthFlowType.java
@@ -19,21 +19,25 @@
 
 package org.apache.submarine.server.security.common;
 
-import org.apache.submarine.commons.utils.SubmarineConfiguration;
+/**
+ * Type of the authentication flow
+ */
+public enum AuthFlowType {
 
-import static org.apache.submarine.commons.utils.SubmarineConfVars.ConfVars;
+  /* Use header token to pass authentication information by default */
+  TOKEN("token"),
 
-public class CommonConfig {
+  /* Using session to pass authentication information is generally suitable for sso */
+  SESSION("session");
 
-  public static final String LOGOUT_ENDPOINT = "/auth/logout";
-  public static final String AUTH_HEADER = "Authorization";
-  public static final String BEARER_HEADER_PREFIX = "Bearer ";
+  private final String type;
 
-  public static final int MAX_AGE;
+  AuthFlowType(String type) {
+    this.type = type;
+  }
 
-  static {
-    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
-    MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
+  public String getType() {
+    return type;
   }
 
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
index 83f1bb84..118018aa 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
@@ -31,6 +31,11 @@ public class CommonConfig {
 
   public static final int MAX_AGE;
 
+  public static final String AGENT_HEADER = "User-Agent";
+  // python sdk agent header (submarine-sdk/pysubmarine/submarine/client/api_client.py#93)
+  // We only deal with front and server, py-sdk is not dealt with now
+  public static final String PYTHON_USER_AGENT_REGREX = "^OpenAPI-Generator/[\\w\\-\\.]+/python$";
+
   static {
     SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
     MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonFilter.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonFilter.java
index a0a0fdc6..aa0f0d89 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonFilter.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonFilter.java
@@ -19,28 +19,133 @@
 
 package org.apache.submarine.server.security.common;
 
-import org.pac4j.core.context.JEEContext;
-import org.pac4j.core.context.session.JEESessionStore;
-import org.pac4j.core.context.session.SessionStore;
-import org.pac4j.core.engine.DefaultCallbackLogic;
-import org.pac4j.core.engine.DefaultLogoutLogic;
-import org.pac4j.core.engine.DefaultSecurityLogic;
-import org.pac4j.core.http.adapter.HttpActionAdapter;
-import org.pac4j.core.http.adapter.JEEHttpActionAdapter;
-import org.pac4j.core.profile.CommonProfile;
-import org.pac4j.core.profile.UserProfile;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.submarine.server.rest.workbench.annotation.NoneAuth;
+import org.reflections.Reflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PATCH;
+import javax.ws.rs.PUT;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static org.apache.submarine.server.security.common.CommonConfig.PYTHON_USER_AGENT_REGREX;
+import static org.reflections.scanners.Scanners.SubTypes;
+import static org.reflections.scanners.Scanners.TypesAnnotated;
+
 
 public class CommonFilter {
 
-  public static final HttpActionAdapter DEFAULT_HTTP_ACTION_ADAPTER = JEEHttpActionAdapter.INSTANCE;
+  private static final Logger LOG = LoggerFactory.getLogger(CommonFilter.class);
+
+  /* Supported http method */
+  protected final Set<Class<? extends Annotation>> SUPPORT_HTTP_METHODS =
+      new HashSet<Class<? extends Annotation>>() {{
+        add(GET.class);
+        add(PUT.class);
+        add(POST.class);
+        add(DELETE.class);
+        add(PATCH.class);
+        add(OPTIONS.class);
+        add(HEAD.class);
+      }};
+
+  /* api with the full path */
+  protected final Set<String> REST_API_PATHS = new HashSet<>(16);
+  /* api with the regrex path */
+  protected final Set<String> REST_REGREX_API_PATHS = new HashSet<>(16);
+
+  /**
+   * Filter init
+   */
+  public void init(FilterConfig filterConfig) throws ServletException {
+    // Scan rest api class by annotations @Path
+    Reflections reflections = new Reflections("org.apache.submarine.server.rest");
+    Set<Class<?>> rests = reflections.get(SubTypes.of(TypesAnnotated.with(Path.class)).asClass());
+    for (Class<?> rest : rests) {
+      // get path
+      Path pathAnno = rest.getAnnotation(Path.class);
+      String path = pathAnno.value();
+      if (path.startsWith("/")) path = path.substring(1);
+      if (path.endsWith("/")) path = path.substring(0, path.length() - 1);
+      // loop method annotations
+      Method[] methods = rest.getDeclaredMethods();
+      for (Method method : methods) {
+        addSupportedApiPath(path, method);
+      }
+    }
+    LOG.info("Get security filter rest api path = {} and regrex api path = {}",
+        REST_API_PATHS, REST_REGREX_API_PATHS);
+  }
 
-  public static final DefaultCallbackLogic<CommonProfile, JEEContext> CALLBACK_LOGIC =
-          new DefaultCallbackLogic<>();
+  /**
+   * Add supported api path
+   */
+  private void addSupportedApiPath(String path, Method method) {
+    Stream<Annotation> annotations = Arrays.stream(method.getAnnotations());
+    // Only methods marked as REST http method
+    if (annotations.anyMatch(annotation -> SUPPORT_HTTP_METHODS.contains(annotation.annotationType()))) {
+      // Methods with the @NoneAuth require no authentication
+      if (method.getAnnotation(NoneAuth.class) != null) return;
+      Path pathAnno = method.getAnnotation(Path.class);
+      String endpoint = pathAnno == null ? "" : pathAnno.value();
 
-  public static final DefaultSecurityLogic<UserProfile, JEEContext> SECURITY_LOGIC =
-          new DefaultSecurityLogic<>();
+      // If endpoint is empty, the api is used as the path
+      if ("".equals(endpoint) || "/".equals(endpoint)) {
+        REST_API_PATHS.add(String.format("/api/%s", path));
+      } else {
+        if (endpoint.startsWith("/")) endpoint = endpoint.substring(1);
+        if (endpoint.endsWith("/")) endpoint = endpoint.substring(0, endpoint.length() - 1);
+        String api = String.format("/api/%s/%s", path, endpoint);
+        if (api.matches("(.*)\\{\\w+\\}(.*)")) {
+          REST_REGREX_API_PATHS.add(api.replaceAll("\\{\\w+\\}", "((?!\\/).)*"));
+        } else {
+          REST_API_PATHS.add(api);
+        }
+      }
+    }
+  }
 
-  public static final DefaultLogoutLogic<UserProfile, JEEContext> LOGOUT_LOGIC = new DefaultLogoutLogic<>();
+  /**
+   * Check if uri is in the list of known apis
+   */
+  private boolean isSupportedRest(String uri) {
+    // Return true if found in the full path
+    if (REST_API_PATHS.contains(uri)) return true;
+    // Otherwise, do a match on the regrex path
+    for (String api : REST_REGREX_API_PATHS) {
+      if (Pattern.matches(api, uri)) {
+        return true;
+      }
+    }
+    return false;
+  }
 
-  public static final SessionStore<JEEContext> SESSION_STORE = new JEESessionStore();
+  /**
+   * Check whether the endpoint requires authorization verification
+   */
+  protected boolean isProtectedApi(HttpServletRequest httpServletRequest) {
+    // If it is called by python, temporarily passed
+    String agentHeader = httpServletRequest.getHeader(CommonConfig.AGENT_HEADER);
+    if (StringUtils.isNoneBlank(agentHeader) && agentHeader.matches(PYTHON_USER_AGENT_REGREX)) {
+      return false;
+    }
+    // Now we just verify the api
+    return isSupportedRest(httpServletRequest.getRequestURI());
+  }
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/RegistryUserActionAdapter.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/RegistryUserActionAdapter.java
new file mode 100644
index 00000000..8ff0b1b9
--- /dev/null
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/RegistryUserActionAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.submarine.server.security.common;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.submarine.server.database.workbench.entity.SysUserEntity;
+import org.apache.submarine.server.database.workbench.service.SysUserService;
+import org.pac4j.core.context.WebContext;
+import org.pac4j.core.exception.http.HttpAction;
+import org.pac4j.core.profile.ProfileManager;
+import org.pac4j.core.profile.UserProfile;
+import org.pac4j.jee.context.session.JEESessionStore;
+import org.pac4j.jee.http.adapter.JEEHttpActionAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+import java.util.Optional;
+
+import static org.apache.submarine.server.database.workbench.service.SysUserService.DEFAULT_ADMIN_UID;
+import static org.apache.submarine.server.database.workbench.service.SysUserService.DEFAULT_CREATE_USER_PASSWORD;
+
+/**
+ * Triggers automatic creation of non-existent users
+ * when authenticating third party logins to the adapter at the same time
+ */
+public class RegistryUserActionAdapter extends JEEHttpActionAdapter {
+
+  private static final SysUserService userService = SysUserService.INSTANCE;
+
+  private final Logger LOG = LoggerFactory.getLogger(RegistryUserActionAdapter.class);
+
+  @Override
+  public Object adapt(HttpAction action, WebContext context) {
+    super.adapt(action, context);
+    // get profile
+    //final SessionStore store = FindBest.sessionStore(null, Config.INSTANCE, JEESessionStore.INSTANCE);
+    ProfileManager manager = new ProfileManager(context, JEESessionStore.INSTANCE);
+    Optional<UserProfile> profile = manager.getProfile();
+    // every time call back, check if this user is exists
+    profile.ifPresent(this::createUndefinedUser);
+    return null;
+  }
+
+  /**
+   * Create a user that does not exist
+   */
+  public void createUndefinedUser(UserProfile profile) {
+    LOG.trace("Check user if exists ...");
+    try {
+      // If the user does not exist then create
+      userService.getOrCreateUser(profile.getUsername(), () -> {
+        SysUserEntity entity = new SysUserEntity();
+        entity.setUserName(profile.getUsername());
+        entity.setRealName(profile.getUsername());
+        entity.setPassword(DEFAULT_CREATE_USER_PASSWORD);
+        entity.setEmail(ObjectUtils.identityToString(profile.getAttribute("email")));
+        entity.setPhone(ObjectUtils.identityToString(profile.getAttribute("phone")));
+        entity.setAvatar(ObjectUtils.identityToString(profile.getAttribute("picture")));
+        entity.setDeleted(0);
+        entity.setCreateBy(DEFAULT_ADMIN_UID);
+        entity.setCreateTime(new Date());
+        return entity;
+      });
+    } catch (Exception e) {
+      LOG.error("Get error when creating user, skip ...", e);
+    }
+  }
+}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcCallbackResource.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcCallbackResource.java
new file mode 100644
index 00000000..1e6162f6
--- /dev/null
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcCallbackResource.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.submarine.server.security.oidc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ * Fixed Callback endpoint used after successful login with Identity Provider e.g. OAuth server.
+ * See https://www.pac4j.org/blog/understanding-the-callback-endpoint.html
+ */
+@Path(OidcCallbackResource.SELF_URL)
+public class OidcCallbackResource {
+
+  public static final String SELF_URL = "/auth/oidc/callback";
+
+  private static final Logger LOG = LoggerFactory.getLogger(OidcCallbackResource.class);
+
+  @GET
+  public Response callback() {
+    LOG.error(
+            "This endpoint is to be handled by the pac4j filter to redirect users," +
+                    " request should never reach here.",
+            new RuntimeException()
+    );
+    return Response.serverError().build();
+  }
+}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcConfig.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcConfig.java
new file mode 100644
index 00000000..1a8fb9d2
--- /dev/null
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.submarine.server.security.oidc;
+
+import org.apache.submarine.commons.utils.SubmarineConfiguration;
+import org.apache.submarine.server.security.common.CommonConfig;
+
+public class OidcConfig extends CommonConfig {
+
+  public static final String CLIENT_ID_ENV = "SUBMARINE_AUTH_OIDC_CLIENT_ID";
+  public static final String CLIENT_ID_PROP = "submarine.auth.oidc.client.id";
+
+  public static final String CLIENT_SECRET_ENV = "SUBMARINE_AUTH_OIDC_CLIENT_SECRET";
+  public static final String CLIENT_SECRET_PROP = "submarine.auth.oidc.client.secret";
+
+  public static final String DISCOVER_URI_ENV = "SUBMARINE_AUTH_OIDC_DISCOVER_URI";
+  public static final String DISCOVER_URI_PROP = "submarine.auth.oidc.discover.uri";
+
+  public static final String LOGOUT_REDIRECT_URI_ENV = "SUBMARINE_AUTH_OIDC_LOGOUT_URI";
+  public static final String LOGOUT_REDIRECT_URI_PROP = "submarine.auth.oidc.logout.uri";
+
+  public static final String CLIENT_ID;
+
+  public static final String CLIENT_SECRET;
+
+  public static final String DISCOVER_URI;
+
+  public static final String LOGOUT_REDIRECT_URI;
+
+  static  {
+    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
+    CLIENT_ID = conf.getString(CLIENT_ID_ENV, CLIENT_ID_PROP, "");
+    CLIENT_SECRET = conf.getString(CLIENT_SECRET_ENV, CLIENT_SECRET_PROP, "");
+    DISCOVER_URI = conf.getString(DISCOVER_URI_ENV, DISCOVER_URI_PROP, "");
+    LOGOUT_REDIRECT_URI = conf.getString(LOGOUT_REDIRECT_URI_ENV, LOGOUT_REDIRECT_URI_PROP, "");
+  }
+}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcFilter.java
similarity index 52%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java
copy to submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcFilter.java
index 4bb91989..58f392de 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcFilter.java
@@ -17,17 +17,16 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security.simple;
+package org.apache.submarine.server.security.oidc;
 
 import org.apache.submarine.server.security.SecurityFactory;
 import org.apache.submarine.server.security.common.CommonFilter;
-import org.pac4j.jwt.profile.JwtProfile;
+import org.pac4j.oidc.profile.OidcProfile;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -36,40 +35,45 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Optional;
 
-/**
- * Simple authentication
- * Only users in submarine sys_user table can log in, and user is verified based on token
- */
-public class SimpleFilter extends CommonFilter implements Filter {
+public class OidcFilter extends CommonFilter implements Filter {
 
-  private static final Logger LOG = LoggerFactory.getLogger(SimpleFilter.class);
+  private static final Logger LOG = LoggerFactory.getLogger(OidcFilter.class);
 
-  private final SimpleSecurityProvider provider;
+  private final OidcSecurityProvider provider;
 
-  public SimpleFilter() {
-    this.provider = SecurityFactory.getSimpleSecurityProvider();
+  public OidcFilter() {
+    this.provider = SecurityFactory.getPac4jSecurityProvider();
   }
 
   @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-  }
-
-  @Override
-  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
-                       FilterChain filterChain) throws IOException, ServletException {
-    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
-    HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
-    // check header token
-    Optional<JwtProfile> profile = provider.perform(httpServletRequest, httpServletResponse);
-    // If the token can be correctly parsed then continue processing, otherwise return 401
-    if (profile.isPresent()) {
-      filterChain.doFilter(servletRequest, servletResponse);
-    } else {
-      httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+      throws IOException, ServletException {
+    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+    switch (httpServletRequest.getRequestURI()) {
+      case OidcCallbackResource.SELF_URL:
+        provider.callback(httpServletRequest, httpServletResponse);
+        break;
+      case OidcConfig.LOGOUT_ENDPOINT:
+        provider.logout(httpServletRequest, httpServletResponse);
+        break;
+      default:
+        Optional<OidcProfile> profile = provider.perform(httpServletRequest, httpServletResponse);
+        if (profile.isPresent()) {
+          chain.doFilter(request, response);
+        } else {
+          // There are some static resources that are also within the service,
+          // so we need to filter out the api
+          if (isProtectedApi(httpServletRequest)) {
+            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
+                    "The token/session is not valid.");
+          }
+        }
     }
   }
 
   @Override
   public void destroy() {
+
   }
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcSecurityProvider.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcSecurityProvider.java
new file mode 100644
index 00000000..faa65eab
--- /dev/null
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/oidc/OidcSecurityProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.submarine.server.security.oidc;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.submarine.commons.utils.SubmarineConfVars;
+import org.apache.submarine.commons.utils.SubmarineConfiguration;
+import org.apache.submarine.server.security.SecurityProvider;
+import org.apache.submarine.server.security.common.AuthFlowType;
+import org.apache.submarine.server.security.common.RegistryUserActionAdapter;
+import org.pac4j.core.config.Config;
+import org.pac4j.core.context.WebContext;
+import org.pac4j.core.context.session.SessionStore;
+import org.pac4j.core.http.callback.NoParameterCallbackUrlResolver;
+import org.pac4j.core.http.url.DefaultUrlResolver;
+import org.pac4j.core.matching.matcher.csrf.CsrfTokenGeneratorMatcher;
+import org.pac4j.core.matching.matcher.csrf.DefaultCsrfTokenGenerator;
+import org.pac4j.core.profile.UserProfile;
+import org.pac4j.http.client.direct.HeaderClient;
+import org.pac4j.oidc.client.OidcClient;
+import org.pac4j.oidc.config.OidcConfiguration;
+import org.pac4j.oidc.credentials.authenticator.UserInfoOidcAuthenticator;
+import org.pac4j.oidc.profile.OidcProfile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.pac4j.core.matching.matcher.DefaultMatchers.CSRF_TOKEN;
+
+public class OidcSecurityProvider extends SecurityProvider<OidcFilter, OidcProfile> {
+
+  private static final Logger LOG = LoggerFactory.getLogger(OidcSecurityProvider.class);
+
+  private final RegistryUserActionAdapter userActionAdapter = new RegistryUserActionAdapter();
+
+  @Override
+  public AuthFlowType getAuthFlowType() {
+    return AuthFlowType.SESSION;
+  }
+
+
+  @Override
+  public Class<OidcFilter> getFilterClass() {
+    return OidcFilter.class;
+  }
+
+  @Override
+  public Config createConfig() {
+    // oidc config
+    OidcConfiguration oidcConf = new OidcConfiguration();
+    oidcConf.setClientId(OidcConfig.CLIENT_ID);
+    oidcConf.setSecret(OidcConfig.CLIENT_SECRET);
+    oidcConf.setDiscoveryURI(OidcConfig.DISCOVER_URI);
+    oidcConf.setExpireSessionWithToken(true);
+    oidcConf.setUseNonce(true);
+    oidcConf.setReadTimeout(5000);
+    oidcConf.setMaxAge(OidcConfig.MAX_AGE);
+    // oidc client
+    OidcClient oidcClient = new OidcClient(oidcConf);
+    oidcClient.setUrlResolver(new DefaultUrlResolver(true));
+    oidcClient.setCallbackUrlResolver(new NoParameterCallbackUrlResolver());
+    // header client
+    HeaderClient headerClient = new HeaderClient(OidcConfig.AUTH_HEADER,
+            OidcConfig.BEARER_HEADER_PREFIX, new UserInfoOidcAuthenticator(oidcConf));
+    Config config = new Config(OidcCallbackResource.SELF_URL, oidcClient, headerClient);
+    // add csrfToken matcher
+    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
+    CsrfTokenGeneratorMatcher csrftgm = new CsrfTokenGeneratorMatcher(new DefaultCsrfTokenGenerator());
+    csrftgm.setSecure(conf.getBoolean(SubmarineConfVars.ConfVars.SUBMARINE_COOKIE_SECURE));
+    csrftgm.setHttpOnly(conf.getBoolean(SubmarineConfVars.ConfVars.SUBMARINE_COOKIE_HTTP_ONLY));
+    config.setMatchers(Map.of(CSRF_TOKEN, csrftgm));
+    return config;
+  }
+
+  @Override
+  public String getClient(HttpServletRequest httpServletRequest) {
+    return "OidcClient,HeaderClient";
+  }
+
+  @Override
+  public Optional<OidcProfile> perform(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
+    // perform get profile
+    UserProfile profile = (UserProfile) createSecurityLogic().perform(
+        createWebContext(hsRequest, hsResponse),
+        createSessionStore(hsRequest, hsResponse),
+        getConfig(),
+        (WebContext ctx, SessionStore store, Collection<UserProfile> profiles, Object... parameters) -> {
+          if (profiles.isEmpty()) {
+            LOG.warn("No profiles found after OIDC auth.");
+            return null;
+          } else {
+            return profiles.iterator().next();
+          }
+        },
+        createHttpActionAdapter(),
+        getClient(hsRequest), DEFAULT_AUTHORIZER, ""
+    );
+    return Optional.ofNullable((OidcProfile) profile);
+  }
+
+  @Override
+  public void callback(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
+    // perform callback
+    createCallbackLogic().perform(
+        createWebContext(hsRequest, hsResponse),
+        createSessionStore(hsRequest, hsResponse),
+        getConfig(), userActionAdapter, "/", false, "oidcClient"
+    );
+  }
+
+  @Override
+  public void logout(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
+    String redirectUrl = OidcConfig.LOGOUT_REDIRECT_URI;
+    if (StringUtils.isBlank(redirectUrl)) {
+      redirectUrl = hsRequest.getParameter("redirect_url");
+    }
+    // perform logout
+    createLogoutLogic().perform(
+        createWebContext(hsRequest, hsResponse),
+        createSessionStore(hsRequest, hsResponse),
+        getConfig(),
+        createHttpActionAdapter(),
+        redirectUrl, "/", true, true, true
+    );
+  }
+
+}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java
index 4bb91989..1a3d4250 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleFilter.java
@@ -27,7 +27,6 @@ import org.slf4j.LoggerFactory;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -50,22 +49,22 @@ public class SimpleFilter extends CommonFilter implements Filter {
     this.provider = SecurityFactory.getSimpleSecurityProvider();
   }
 
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-  }
-
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                        FilterChain filterChain) throws IOException, ServletException {
     HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
     HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
-    // check header token
-    Optional<JwtProfile> profile = provider.perform(httpServletRequest, httpServletResponse);
-    // If the token can be correctly parsed then continue processing, otherwise return 401
-    if (profile.isPresent()) {
-      filterChain.doFilter(servletRequest, servletResponse);
+    if (isProtectedApi(httpServletRequest)) {
+      // check header token
+      Optional<JwtProfile> profile = provider.perform(httpServletRequest, httpServletResponse);
+      // If the token can be correctly parsed then continue processing, otherwise return 401
+      if (profile.isPresent()) {
+        filterChain.doFilter(servletRequest, servletResponse);
+      } else {
+        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
+      }
     } else {
-      httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
+      filterChain.doFilter(servletRequest, servletResponse);
     }
   }
 
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleSecurityProvider.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleSecurityProvider.java
index 3a351665..0a10846d 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleSecurityProvider.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/simple/SimpleSecurityProvider.java
@@ -21,11 +21,9 @@ package org.apache.submarine.server.security.simple;
 
 import org.apache.submarine.server.security.SecurityProvider;
 import org.apache.submarine.server.security.common.CommonConfig;
-import org.apache.submarine.server.security.common.CommonFilter;
 import org.pac4j.core.config.Config;
-import org.pac4j.core.context.JEEContext;
-import org.pac4j.core.matching.matcher.PathMatcher;
-import org.pac4j.core.profile.ProfileManager;
+import org.pac4j.core.context.WebContext;
+import org.pac4j.core.context.session.SessionStore;
 import org.pac4j.core.profile.UserProfile;
 import org.pac4j.http.client.direct.HeaderClient;
 import org.pac4j.jwt.profile.JwtProfile;
@@ -37,36 +35,24 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.Collection;
 import java.util.Optional;
 
-public class SimpleSecurityProvider implements SecurityProvider<SimpleFilter, JwtProfile> {
+public class SimpleSecurityProvider extends SecurityProvider<SimpleFilter, JwtProfile> {
 
   private static final Logger LOG = LoggerFactory.getLogger(SimpleSecurityProvider.class);
 
-  private Config pac4jConfig;
-
   @Override
   public Class<SimpleFilter> getFilterClass() {
     return SimpleFilter.class;
   }
 
   @Override
-  public Config getConfig() {
+  public Config createConfig() {
     if (pac4jConfig != null) {
       return pac4jConfig;
     }
-
     // header client
     HeaderClient headerClient = new HeaderClient(CommonConfig.AUTH_HEADER, CommonConfig.BEARER_HEADER_PREFIX,
             SimpleLoginConfig.getJwtAuthenticator());
-
-    Config pac4jConfig = new Config(headerClient);
-    // skip web static resources
-    pac4jConfig.addMatcher("static", new PathMatcher().excludeRegex(
-            "^/.*(\\.map|\\.js|\\.css|\\.ico|\\.svg|\\.png|\\.html|\\.htm)$"));
-    // skip login rest api
-    pac4jConfig.addMatcher("api", new PathMatcher().excludeRegex("^/api/auth/login$"));
-    this.pac4jConfig = pac4jConfig;
-
-    return pac4jConfig;
+    return new Config(headerClient);
   }
 
   @Override
@@ -76,11 +62,11 @@ public class SimpleSecurityProvider implements SecurityProvider<SimpleFilter, Jw
 
   @Override
   public Optional<JwtProfile> perform(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
-    JEEContext context = new JEEContext(hsRequest, hsResponse, CommonFilter.SESSION_STORE);
-    UserProfile profile = CommonFilter.SECURITY_LOGIC.perform(
-        context,
-        pac4jConfig,
-        (JEEContext ctx, Collection<UserProfile> profiles, Object... parameters) -> {
+    UserProfile profile = (UserProfile) createSecurityLogic().perform(
+        createWebContext(hsRequest, hsResponse),
+        createSessionStore(hsRequest, hsResponse),
+        getConfig(),
+        (WebContext ctx, SessionStore store, Collection<UserProfile> profiles, Object... parameters) -> {
           if (profiles.isEmpty()) {
             LOG.warn("No profiles found with default auth.");
             return null;
@@ -88,15 +74,9 @@ public class SimpleSecurityProvider implements SecurityProvider<SimpleFilter, Jw
             return profiles.iterator().next();
           }
         },
-        CommonFilter.DEFAULT_HTTP_ACTION_ADAPTER,
-        getClient(hsRequest), DEFAULT_AUTHORIZER, "static,api", null);
+        createHttpActionAdapter(),
+        getClient(hsRequest), DEFAULT_AUTHORIZER, null
+    );
     return Optional.ofNullable((JwtProfile) profile);
   }
-
-  @Override
-  public Optional<JwtProfile> getProfile(HttpServletRequest hsRequest, HttpServletResponse hsResponse) {
-    JEEContext context = new JEEContext(hsRequest, hsResponse, CommonFilter.SESSION_STORE);
-    ProfileManager<JwtProfile> manager = new ProfileManager<>(context);
-    return manager.get(true);
-  }
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/utils/response/DictAnnotation.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/utils/response/DictAnnotation.java
index 110cc0df..3d73eb83 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/utils/response/DictAnnotation.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/utils/response/DictAnnotation.java
@@ -173,7 +173,9 @@ public class DictAnnotation {
 
       return true;
     } else {
-      LOG.warn("Unsupported parse {} Dict Annotation!", result.getClass());
+      // When it contains lists, mostly arraylists and the like,
+      // we should do as a trace/debug level as it does not affect the data returned
+      LOG.trace("Unsupported parse {} Dict Annotation!", result.getClass());
     }
 
     return false;
diff --git a/submarine-server/server-core/src/main/resources/log4j.properties b/submarine-server/server-core/src/main/resources/log4j.properties
index 55e02b6d..5e71979a 100644
--- a/submarine-server/server-core/src/main/resources/log4j.properties
+++ b/submarine-server/server-core/src/main/resources/log4j.properties
@@ -15,3 +15,6 @@ log4j.appender.stdout = org.apache.log4j.ConsoleAppender
 log4j.appender.stdout.Target = System.out
 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
 log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
+
+# mybatis sql debug
+log4j.logger.org.apache.submarine.server.database=DEBUG
diff --git a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/MockHttpServletRequest.java b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/MockHttpServletRequest.java
index 49d8e040..ad8a198c 100644
--- a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/MockHttpServletRequest.java
+++ b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/MockHttpServletRequest.java
@@ -134,7 +134,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
 
   @Override
   public String getRequestURI() {
-    return null;
+    return "/api/sys/user/info";
   }
 
   private StringBuffer requestUrl;
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/MockOidcHttpServletRequest.java
similarity index 57%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
copy to submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/MockOidcHttpServletRequest.java
index 83f1bb84..887ac549 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/MockOidcHttpServletRequest.java
@@ -17,23 +17,21 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security.common;
+package org.apache.submarine.server.security.oidc;
 
-import org.apache.submarine.commons.utils.SubmarineConfiguration;
+import org.apache.submarine.server.security.MockHttpServletRequest;
+import org.mockito.Mockito;
 
-import static org.apache.submarine.commons.utils.SubmarineConfVars.ConfVars;
+import javax.servlet.http.HttpSession;
 
-public class CommonConfig {
+import static org.mockito.Mockito.when;
 
-  public static final String LOGOUT_ENDPOINT = "/auth/logout";
-  public static final String AUTH_HEADER = "Authorization";
-  public static final String BEARER_HEADER_PREFIX = "Bearer ";
+public class MockOidcHttpServletRequest extends MockHttpServletRequest {
 
-  public static final int MAX_AGE;
-
-  static {
-    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
-    MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
+  @Override
+  public HttpSession getSession(boolean create) {
+    HttpSession session = Mockito.mock(HttpSession.class);
+    when(session.getId()).thenReturn("id");
+    return session;
   }
-
 }
diff --git a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/SubmarineAuthOidcTest.java
similarity index 51%
copy from submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java
copy to submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/SubmarineAuthOidcTest.java
index 284362f4..7a83a36b 100644
--- a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java
+++ b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/oidc/SubmarineAuthOidcTest.java
@@ -17,26 +17,32 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security;
+package org.apache.submarine.server.security.oidc;
 
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.reflect.TypeToken;
 import org.apache.submarine.commons.utils.SubmarineConfVars;
 import org.apache.submarine.commons.utils.SubmarineConfiguration;
+
 import org.apache.submarine.server.api.environment.EnvironmentId;
-import org.apache.submarine.server.database.workbench.entity.SysUserEntity;
-import org.apache.submarine.server.rest.workbench.LoginRestApi;
+import org.apache.submarine.server.api.workbench.UserInfo;
 import org.apache.submarine.server.rest.workbench.SysUserRestApi;
-import org.apache.submarine.server.security.simple.SimpleFilter;
+import org.apache.submarine.server.security.SecurityFactory;
+import org.apache.submarine.server.security.SecurityProvider;
+import org.apache.submarine.server.security.common.RegistryUserActionAdapter;
 import org.apache.submarine.server.utils.gson.EnvironmentIdDeserializer;
 import org.apache.submarine.server.utils.gson.EnvironmentIdSerializer;
 import org.apache.submarine.server.utils.response.JsonResponse;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.pac4j.core.config.Config;
+import org.pac4j.core.profile.UserProfile;
 import org.pac4j.core.util.Pac4jConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,35 +51,53 @@ import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
+import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static org.apache.submarine.server.security.oidc.OidcConfig.CLIENT_ID_PROP;
+import static org.apache.submarine.server.security.oidc.OidcConfig.CLIENT_SECRET_PROP;
+import static org.apache.submarine.server.security.oidc.OidcConfig.DISCOVER_URI_PROP;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class SubmarineAuthSimpleTest {
+public class SubmarineAuthOidcTest {
+
+  private static final Logger LOG = LoggerFactory.getLogger(SubmarineAuthOidcTest.class);
 
   private static final SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
 
+  private SysUserRestApi sysUserRestApi;
+
+  private RegistryUserActionAdapter userActionAdapter;
+
   private static final GsonBuilder gsonBuilder = new GsonBuilder()
           .registerTypeAdapter(EnvironmentId.class, new EnvironmentIdSerializer())
           .registerTypeAdapter(EnvironmentId.class, new EnvironmentIdDeserializer());
   private static final Gson gson = gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss").create();
 
-  private static final Logger LOG = LoggerFactory.getLogger(SubmarineAuthSimpleTest.class);
-
-  private static LoginRestApi loginRestApi;
-  private static SysUserRestApi sysUserRestApi;
+  @Rule
+  public final WireMockRule wireMockRule = new WireMockRule(8080);
 
   @Before
   public void before() {
-    conf.updateConfiguration("submarine.auth.type", "simple");
+    conf.updateConfiguration("submarine.auth.type", "oidc");
+    conf.updateConfiguration(CLIENT_ID_PROP, "test");
+    conf.updateConfiguration(CLIENT_SECRET_PROP, "secret");
+    conf.updateConfiguration(DISCOVER_URI_PROP,
+        "http://localhost:8080/auth/realms/test-login/.well-known/openid-configuration");
     conf.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/submarine_test?" +
             "useUnicode=true&" +
             "characterEncoding=UTF-8&" +
@@ -83,50 +107,66 @@ public class SubmarineAuthSimpleTest {
             "useSSL=false");
     conf.setJdbcUserName("submarine_test");
     conf.setJdbcPassword("password_test");
-    loginRestApi = new LoginRestApi();
-    // add a test user
+
     sysUserRestApi = new SysUserRestApi();
-    SysUserEntity user = new SysUserEntity();
-    user.setUserName("test");
-    user.setRealName("test");
-    user.setPassword("test");
-    user.setDeleted(0);
-    sysUserRestApi.add(user);
+    userActionAdapter = new RegistryUserActionAdapter();
+
+    // Add oidc mock endpoint
+    // Based on the token, we currently use the following two endpoints:
+    // 1. openid-configuration
+    String openidConfig = getResourceFileContent("security/openid-configuration.json");
+    wireMockRule.stubFor(
+        WireMock.get(urlEqualTo("/auth/realms/test-login/.well-known/openid-configuration"))
+            .willReturn(aResponse().withHeader("Content-Type", "application/json")
+                .withBody(openidConfig)
+            )
+    );
+    // 2. userinfo
+    String userInfo = getResourceFileContent("security/user-info.json");
+    wireMockRule.stubFor(
+        WireMock.get(urlEqualTo("/auth/realms/test-login/protocol/openid-connect/userinfo"))
+            .willReturn(aResponse().withHeader("Content-Type", "application/json")
+                .withBody(userInfo)
+            )
+    );
+  }
+
+  public static String getResourceFileContent(String resource) {
+    File file = new File(Objects.requireNonNull(
+        SubmarineAuthOidcTest.class.getClassLoader().getResource(resource)).getPath()
+    );
+    try {
+      return new String(Files.readAllBytes(Paths.get(file.toString())));
+    } catch (IOException e) {
+      LOG.error("Can not find file: " + resource, e);
+      return null;
+    }
   }
 
   @Test
-  public void testSimpleType() throws ServletException, IOException {
+  public void testOidcType() throws ServletException, IOException {
     // test auth type config
     String authType = conf.getString(SubmarineConfVars.ConfVars.SUBMARINE_AUTH_TYPE);
-    assertEquals(authType, "simple");
+    assertEquals(authType, "oidc");
 
     // test provider
     Optional<SecurityProvider> providerOptional = SecurityFactory.getSecurityProvider();
     SecurityProvider provider = providerOptional.get();
     assertNotNull(provider);
-    assertEquals(provider.getFilterClass(), SimpleFilter.class);
+    assertEquals(provider.getFilterClass(), OidcFilter.class);
     Config config = provider.getConfig();
     assertTrue(config.getClients().findClient("headerClient").isPresent());
-
-    // test login api
-    String testUsrJson = "{\"username\":\"test\",\"password\":\"test\"}";
-    Response loginResp = loginRestApi.login(testUsrJson);
-    assertEquals(loginResp.getStatus(), Response.Status.OK.getStatusCode());
-    String entity = (String) loginResp.getEntity();
-    Type type = new TypeToken<JsonResponse<SysUserEntity>>() { }.getType();
-    JsonResponse<SysUserEntity> jsonResponse = gson.fromJson(entity, type);
-    String token = jsonResponse.getResult().getToken();
-    LOG.info("Get user token: " + token);
+    assertTrue(config.getClients().findClient("oidcClient").isPresent());
 
     // create filter involved objects
     // 1. test filter
-    SimpleFilter filterTest = new SimpleFilter();
+    OidcFilter filterTest = new OidcFilter();
     filterTest.init(null);
     // 2. filter chain
     FilterChain mockFilterChain = Mockito.mock(FilterChain.class);
     // 3. http request
-    MockHttpServletRequest mockRequest = new MockHttpServletRequest();
-    mockRequest.setRequestURL(new StringBuffer("/test/url"));
+    MockOidcHttpServletRequest mockRequest = new MockOidcHttpServletRequest();
+    mockRequest.setRequestURL(new StringBuffer("/api/sys/user/info"));
     // 4. http response
     HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class);
     StringWriter out = new StringWriter();
@@ -135,18 +175,32 @@ public class SubmarineAuthSimpleTest {
 
     // test no header
     filterTest.doFilter(mockRequest, mockResponse, mockFilterChain);
-    verify(mockResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
+    verify(mockResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED,
+        "The token/session is not valid.");
 
-    // test header
-    mockRequest.setHeader("Authorization", "Bearer " + token);
+    // test header, here we use a fake Token to simulate login
+    mockRequest.setHeader("Authorization", "Bearer XXX");
     filterTest.doFilter(mockRequest, mockResponse, mockFilterChain);
     verify(mockFilterChain).doFilter(mockRequest, mockResponse);
     assertNotNull(mockRequest.getAttribute(Pac4jConstants.USER_PROFILES));
+
+    // Since we are not callback user, we can simulate creating oidc user
+    Map<String, UserProfile> profiles = (Map<String, UserProfile>)
+        mockRequest.getAttribute(Pac4jConstants.USER_PROFILES);
+    UserProfile profile = profiles.get("HeaderClient");
+    userActionAdapter.createUndefinedUser(profile);
+
+    // test get user info
+    Response response = sysUserRestApi.info(mockRequest, mockResponse);
+    assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+    String entity = (String) response.getEntity();
+    Type type = new TypeToken<JsonResponse<UserInfo>>() { }.getType();
+    JsonResponse<UserInfo> jsonResponse = gson.fromJson(entity, type);
+    assertEquals(jsonResponse.getResult().getName(), "oidc_test");
   }
 
   @After
   public void after() {
     conf.updateConfiguration("submarine.auth.type", "none");
   }
-
 }
diff --git a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/simple/SubmarineAuthSimpleTest.java
similarity index 95%
rename from submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java
rename to submarine-server/server-core/src/test/java/org/apache/submarine/server/security/simple/SubmarineAuthSimpleTest.java
index 284362f4..05b8d103 100644
--- a/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/SubmarineAuthSimpleTest.java
+++ b/submarine-server/server-core/src/test/java/org/apache/submarine/server/security/simple/SubmarineAuthSimpleTest.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security;
+package org.apache.submarine.server.security.simple;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -28,7 +28,9 @@ import org.apache.submarine.server.api.environment.EnvironmentId;
 import org.apache.submarine.server.database.workbench.entity.SysUserEntity;
 import org.apache.submarine.server.rest.workbench.LoginRestApi;
 import org.apache.submarine.server.rest.workbench.SysUserRestApi;
-import org.apache.submarine.server.security.simple.SimpleFilter;
+import org.apache.submarine.server.security.MockHttpServletRequest;
+import org.apache.submarine.server.security.SecurityFactory;
+import org.apache.submarine.server.security.SecurityProvider;
 import org.apache.submarine.server.utils.gson.EnvironmentIdDeserializer;
 import org.apache.submarine.server.utils.gson.EnvironmentIdSerializer;
 import org.apache.submarine.server.utils.response.JsonResponse;
@@ -126,7 +128,7 @@ public class SubmarineAuthSimpleTest {
     FilterChain mockFilterChain = Mockito.mock(FilterChain.class);
     // 3. http request
     MockHttpServletRequest mockRequest = new MockHttpServletRequest();
-    mockRequest.setRequestURL(new StringBuffer("/test/url"));
+    mockRequest.setRequestURL(new StringBuffer("/api/sys/user/info"));
     // 4. http response
     HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class);
     StringWriter out = new StringWriter();
diff --git a/submarine-server/server-core/src/test/resources/security/openid-configuration.json b/submarine-server/server-core/src/test/resources/security/openid-configuration.json
new file mode 100644
index 00000000..021eca04
--- /dev/null
+++ b/submarine-server/server-core/src/test/resources/security/openid-configuration.json
@@ -0,0 +1,145 @@
+{
+  "issuer": "http://localhost:8080/auth/realms/test-login",
+  "authorization_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/auth",
+  "token_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/token",
+  "token_introspection_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/token/introspect",
+  "userinfo_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/userinfo",
+  "end_session_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/logout",
+  "jwks_uri": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/certs",
+  "check_session_iframe": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/login-status-iframe.html",
+  "grant_types_supported": [
+    "authorization_code",
+    "implicit",
+    "refresh_token",
+    "password",
+    "client_credentials"
+  ],
+  "response_types_supported": [
+    "code",
+    "none",
+    "id_token",
+    "token",
+    "id_token token",
+    "code id_token",
+    "code token",
+    "code id_token token"
+  ],
+  "subject_types_supported": [
+    "public",
+    "pairwise"
+  ],
+  "id_token_signing_alg_values_supported": [
+    "PS384",
+    "ES384",
+    "RS384",
+    "HS256",
+    "HS512",
+    "ES256",
+    "RS256",
+    "HS384",
+    "ES512",
+    "PS256",
+    "PS512",
+    "RS512"
+  ],
+  "id_token_encryption_alg_values_supported": [
+    "RSA-OAEP",
+    "RSA1_5"
+  ],
+  "id_token_encryption_enc_values_supported": [
+    "A128GCM",
+    "A128CBC-HS256"
+  ],
+  "userinfo_signing_alg_values_supported": [
+    "PS384",
+    "ES384",
+    "RS384",
+    "HS256",
+    "HS512",
+    "ES256",
+    "RS256",
+    "HS384",
+    "ES512",
+    "PS256",
+    "PS512",
+    "RS512",
+    "none"
+  ],
+  "request_object_signing_alg_values_supported": [
+    "PS384",
+    "ES384",
+    "RS384",
+    "HS256",
+    "HS512",
+    "ES256",
+    "RS256",
+    "HS384",
+    "ES512",
+    "PS256",
+    "PS512",
+    "RS512",
+    "none"
+  ],
+  "response_modes_supported": [
+    "query",
+    "fragment",
+    "form_post"
+  ],
+  "registration_endpoint": "http://localhost:8080/auth/realms/test-login/clients-registrations/openid-connect",
+  "token_endpoint_auth_methods_supported": [
+    "private_key_jwt",
+    "client_secret_basic",
+    "client_secret_post",
+    "tls_client_auth",
+    "client_secret_jwt"
+  ],
+  "token_endpoint_auth_signing_alg_values_supported": [
+    "PS384",
+    "ES384",
+    "RS384",
+    "HS256",
+    "HS512",
+    "ES256",
+    "RS256",
+    "HS384",
+    "ES512",
+    "PS256",
+    "PS512",
+    "RS512"
+  ],
+  "claims_supported": [
+    "aud",
+    "sub",
+    "iss",
+    "auth_time",
+    "name",
+    "given_name",
+    "family_name",
+    "preferred_username",
+    "email",
+    "acr"
+  ],
+  "claim_types_supported": [
+    "normal"
+  ],
+  "claims_parameter_supported": false,
+  "scopes_supported": [
+    "openid",
+    "offline_access",
+    "microprofile-jwt",
+    "profile",
+    "email",
+    "address",
+    "phone",
+    "roles",
+    "web-origins"
+  ],
+  "request_parameter_supported": true,
+  "request_uri_parameter_supported": true,
+  "code_challenge_methods_supported": [
+    "plain",
+    "S256"
+  ],
+  "tls_client_certificate_bound_access_tokens": true,
+  "introspection_endpoint": "http://localhost:8080/auth/realms/test-login/protocol/openid-connect/token/introspect"
+}
diff --git a/submarine-server/server-core/src/test/resources/security/user-info.json b/submarine-server/server-core/src/test/resources/security/user-info.json
new file mode 100644
index 00000000..3bbb8d32
--- /dev/null
+++ b/submarine-server/server-core/src/test/resources/security/user-info.json
@@ -0,0 +1,12 @@
+{
+  "uid": "oidc_test",
+  "sub": "677ecaf0-2d94-4fa5-8cc1-f3caecac09f9",
+  "email_verified": false,
+  "displayName": "oidc_test",
+  "name": "oidc_test",
+  "preferred_username": "oidc_test",
+  "given_name": "oidc_test",
+  "family_name": "0",
+  "userId": "ffa47159-a80c-450c-999a-dd9420d393da",
+  "policy": "public_read_write_policy"
+}
diff --git a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/utils/MyBatisUtil.java b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/utils/MyBatisUtil.java
index 69ab6c1e..35907810 100755
--- a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/utils/MyBatisUtil.java
+++ b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/utils/MyBatisUtil.java
@@ -26,6 +26,7 @@ import org.apache.submarine.commons.utils.SubmarineConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.sql.DataSource;
 import java.io.IOException;
 import java.io.Reader;
 import java.util.Properties;
@@ -46,8 +47,9 @@ public class MyBatisUtil {
       String jdbcUrl = conf.getJdbcUrl();
       String jdbcUserName = conf.getJdbcUserName();
       String jdbcPassword = conf.getJdbcPassword();
-      LOG.info("MyBatisUtil -> jdbcClassName: {}, jdbcUrl: {}, jdbcUserName: {}, jdbcPassword: {}",
-              jdbcClassName, jdbcUrl, jdbcUserName, jdbcPassword);
+      // We need to protect the password in logging
+      LOG.info("MyBatisUtil -> jdbcClassName: {}, jdbcUrl: {}, jdbcUserName: {}, jdbcPassword: ****",
+              jdbcClassName, jdbcUrl, jdbcUserName);
 
       Properties props = new Properties();
       props.setProperty("jdbc.driverClassName", jdbcClassName);
@@ -71,6 +73,13 @@ public class MyBatisUtil {
     return sqlSessionFactory.openSession();
   }
 
+  /**
+   * Get datasource {@link org.apache.ibatis.datasource.pooled.PooledDataSource}
+   */
+  public static DataSource getDatasource() {
+    return sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();
+  }
+
   private static void checkCalledByTestMethod() {
     StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
     for (StackTraceElement element : stackTraceElements) {
diff --git a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/entity/SysUserEntity.java b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/entity/SysUserEntity.java
index 203ea713..cb14a6fb 100644
--- a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/entity/SysUserEntity.java
+++ b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/entity/SysUserEntity.java
@@ -161,4 +161,28 @@ public class SysUserEntity extends BaseEntity {
     this.birthday = birthday;
   }
 
+  @Override
+  public String toString() {
+    return "SysUserEntity{" +
+            "userName='" + userName + '\'' +
+            ", realName='" + realName + '\'' +
+            ", password='" + password + '\'' +
+            ", avatar='" + avatar + '\'' +
+            ", sex='" + sex + '\'' +
+            ", status='" + status + '\'' +
+            ", phone='" + phone + '\'' +
+            ", email='" + email + '\'' +
+            ", deptCode='" + deptCode + '\'' +
+            ", deptName='" + deptName + '\'' +
+            ", roleCode='" + roleCode + '\'' +
+            ", birthday=" + birthday +
+            ", deleted=" + deleted +
+            ", token='" + token + '\'' +
+            ", id='" + id + '\'' +
+            ", createBy='" + createBy + '\'' +
+            ", createTime=" + createTime +
+            ", updateBy='" + updateBy + '\'' +
+            ", updateTime=" + updateTime +
+            '}';
+  }
 }
diff --git a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/service/SysUserService.java b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/service/SysUserService.java
index 91d2c12e..47b2db00 100755
--- a/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/service/SysUserService.java
+++ b/submarine-server/server-database/src/main/java/org/apache/submarine/server/database/workbench/service/SysUserService.java
@@ -29,13 +29,22 @@ import org.slf4j.LoggerFactory;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 public class SysUserService {
+
+  public static final SysUserService INSTANCE = new SysUserService();
+
   private static final Logger LOG = LoggerFactory.getLogger(SysUserService.class);
 
-  private static String GET_USER_BY_NAME_STATEMENT
+  private static final String GET_USER_BY_NAME_STATEMENT
       = "org.apache.submarine.server.database.workbench.mappers.SysUserMapper.getUserByName";
 
+  // default user is admin
+  public static final String DEFAULT_ADMIN_UID = "e9ca23d68d884d4ebb19d07889727dae";
+  // default password is `password` by angular markAsDirty method
+  public static final String DEFAULT_CREATE_USER_PASSWORD = "5f4dcc3b5aa765d61d8327deb882cf99";
+
   public SysUserEntity getUserByName(String name, String password) throws Exception {
     SysUserEntity sysUser = null;
     try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
@@ -54,6 +63,47 @@ public class SysUserService {
     return sysUser;
   }
 
+  /**
+   * Get user by unique name
+   */
+  public SysUserEntity getUserByName(String name) {
+    try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
+      SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
+      return sysUserMapper.getUserByUniqueName(name);
+    } catch (Exception e) {
+      LOG.error(e.getMessage(), e);
+      throw e;
+    }
+  }
+
+  /**
+   * Get or create undefined user:
+   * 1. If present, determine if reactivation is required
+   * 1. If not present, create user
+   */
+  public SysUserEntity getOrCreateUser(String username, Supplier<SysUserEntity> entitySupplier) {
+    LOG.trace("Check user if exists ...");
+    try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
+      SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
+      SysUserEntity sysUser = sysUserMapper.getUserByUniqueName(username);
+      if (sysUser == null) {
+        // if user is undefined, create this user
+        sysUser = entitySupplier.get();
+        LOG.info("Can not find this user, need to create! User entity: {}", sysUser);
+        sysUserMapper.add(sysUser);
+        sqlSession.commit();
+      } else if (sysUser.getDeleted() == 1) {
+        LOG.info("Reset this user {} to active", username);
+        sysUserMapper.activeUser(sysUser.getId());
+        sqlSession.commit();
+      }
+      return sysUser;
+    } catch (Exception e) {
+      LOG.error("Get error when creating user, skip ...", e);
+      return null;
+    }
+  }
+
   public SysUserEntity login(HashMap<String, String> mapParams) throws Exception {
     SysUserEntity sysUser = null;
     try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
diff --git a/submarine-server/server-database/src/main/resources/mybatis-config.xml b/submarine-server/server-database/src/main/resources/mybatis-config.xml
index ecdc40ba..89700039 100755
--- a/submarine-server/server-database/src/main/resources/mybatis-config.xml
+++ b/submarine-server/server-database/src/main/resources/mybatis-config.xml
@@ -20,11 +20,12 @@
 <!DOCTYPE configuration PUBLIC '-//mybatis.org//DTD Config 3.0//EN'
     'http://mybatis.org/dtd/mybatis-3-config.dtd'>
 <configuration>
+  <!-- More details can reference https://mybatis.org/mybatis-3/logging.html -->
   <settings>
     <setting name="cacheEnabled" value="true"/>
     <setting name="lazyLoadingEnabled" value="false"/>
     <setting name="aggressiveLazyLoading" value="true"/>
-    <setting name="logImpl" value="STDOUT_LOGGING"/>
+    <setting name="logImpl" value="SLF4J"/>
   </settings>
 
   <typeAliases>
diff --git a/submarine-server/server-submitter/submitter-k8s/pom.xml b/submarine-server/server-submitter/submitter-k8s/pom.xml
index 609126e3..d04b3096 100644
--- a/submarine-server/server-submitter/submitter-k8s/pom.xml
+++ b/submarine-server/server-submitter/submitter-k8s/pom.xml
@@ -87,10 +87,6 @@
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
         </exclusion>
-        <exclusion>
-          <groupId>org.apache.derby</groupId>
-          <artifactId>derby</artifactId>
-        </exclusion>
       </exclusions>
     </dependency>
 
diff --git a/submarine-workbench/workbench-web/src/WEB-INF/web.xml b/submarine-workbench/workbench-web/src/WEB-INF/web.xml
index 63f9a23c..d1b256b6 100644
--- a/submarine-workbench/workbench-web/src/WEB-INF/web.xml
+++ b/submarine-workbench/workbench-web/src/WEB-INF/web.xml
@@ -56,10 +56,4 @@
     <load-on-startup>2</load-on-startup>
   </servlet>
 
-  <session-config>
-    <cookie-config>
-      <http-only>true</http-only>
-      <secure>true</secure>
-    </cookie-config>
-  </session-config>
 </web-app>
diff --git a/submarine-workbench/workbench-web/src/app/app.component.ts b/submarine-workbench/workbench-web/src/app/app.component.ts
index 833cdaee..0f29e6f9 100644
--- a/submarine-workbench/workbench-web/src/app/app.component.ts
+++ b/submarine-workbench/workbench-web/src/app/app.component.ts
@@ -33,7 +33,6 @@ export class AppComponent implements OnInit {
   ngOnInit(): void {
     this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
       const paths = this.router.url.split('/');
-
       this.title.setTitle(`Submarine - ${paths[paths.length - 1]}`);
     });
   }
diff --git a/submarine-workbench/workbench-web/src/app/app.module.ts b/submarine-workbench/workbench-web/src/app/app.module.ts
index 77c99373..209d3727 100644
--- a/submarine-workbench/workbench-web/src/app/app.module.ts
+++ b/submarine-workbench/workbench-web/src/app/app.module.ts
@@ -21,10 +21,11 @@ import { NgModule } from '@angular/core';
 import { BrowserModule } from '@angular/platform-browser';
 
 import { registerLocaleData } from '@angular/common';
-import { HttpClientModule } from '@angular/common/http';
+import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
 import en from '@angular/common/locales/en';
 import { FormsModule } from '@angular/forms';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { ApiTokenInjector } from "@submarine/core/auth/api-token-injector";
 import { LocalStorageService } from '@submarine/services';
 import { en_US, NgZorroAntdModule, NZ_I18N } from 'ng-zorro-antd';
 import { AppRoutingModule } from './app-routing.module';
@@ -44,7 +45,12 @@ registerLocaleData(en);
     HttpClientModule,
     BrowserAnimationsModule
   ],
-  providers: [{ provide: NZ_I18N, useValue: en_US }, LocalStorageService],
+  providers: [
+    { provide: NZ_I18N, useValue: en_US },
+    // add injector to set header a token when calling rest api
+    { provide: HTTP_INTERCEPTORS, useClass: ApiTokenInjector, multi: true },
+    LocalStorageService
+  ],
   bootstrap: [AppComponent]
 })
 export class AppModule {}
diff --git a/submarine-workbench/workbench-web/src/app/core/auth/api-token-injector.ts b/submarine-workbench/workbench-web/src/app/core/auth/api-token-injector.ts
new file mode 100644
index 00000000..c214065b
--- /dev/null
+++ b/submarine-workbench/workbench-web/src/app/core/auth/api-token-injector.ts
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
+import {Injectable} from '@angular/core';
+import {Router} from '@angular/router';
+import {of, throwError, Observable} from 'rxjs';
+import {catchError} from "rxjs/operators";
+import {AuthService} from "../../services";
+
+@Injectable()
+export class ApiTokenInjector implements HttpInterceptor {
+
+  constructor(private authService: AuthService, private router: Router) { }
+
+  private handleAuthError(err: HttpErrorResponse): Observable<any> {
+    // handle auth error or rethrow
+    if (err.status === 401 || err.status === 403) {
+      if ("session" !== this.authService.getFlowType()) {
+        // remove token cache
+        this.authService.removeToken();
+        // navigate to login
+        this.router.navigate(['/user/login']);
+      }
+      return of(err.message);
+    }
+    return throwError(err);
+  }
+
+  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+    // If there is a token in localstorage and not with session, set the token into the header
+    const checkToken = "session" !== this.authService.getFlowType() && !!this.authService.getToken();
+    let handler;
+    if (checkToken) {
+      handler = next.handle(request.clone({
+        setHeaders: {Authorization: `Bearer ${this.authService.getToken()}`}
+      }));
+    } else {
+      handler = next.handle(request);
+    }
+    // handle unauthorized exception (like 401)
+    return handler.pipe(catchError(x => this.handleAuthError(x)));
+  }
+}
diff --git a/submarine-workbench/workbench-web/src/app/core/auth/auth.guard.ts b/submarine-workbench/workbench-web/src/app/core/auth/auth.guard.ts
index 63a9a8b0..783a2612 100644
--- a/submarine-workbench/workbench-web/src/app/core/auth/auth.guard.ts
+++ b/submarine-workbench/workbench-web/src/app/core/auth/auth.guard.ts
@@ -34,7 +34,7 @@ export class AuthGuard implements CanActivate {
   }
 
   checkLogin(url: string): boolean {
-    if (this.authService.isLoggedIn) {
+    if (this.authService.isLoggedIn || this.authService.getFlowType() === "session") {
       return true;
     }
 
diff --git a/submarine-workbench/workbench-web/src/app/pages/workbench/workbench-routing.module.ts b/submarine-workbench/workbench-web/src/app/pages/workbench/workbench-routing.module.ts
index e872c1c6..4e24b296 100644
--- a/submarine-workbench/workbench-web/src/app/pages/workbench/workbench-routing.module.ts
+++ b/submarine-workbench/workbench-web/src/app/pages/workbench/workbench-routing.module.ts
@@ -97,7 +97,7 @@ const routes: Routes = [
       useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
         const disablePaths = ['home', 'data', 'workspace', 'interpreter'];
         let currentPage = state.url.split('/')[2];
-        console.log('currentPage', currentPage);
+        // console.log('currentPage', currentPage);
         if (disablePaths.includes(currentPage)) return false;
         else return true;
       },
diff --git a/submarine-workbench/workbench-web/src/app/services/auth.service.ts b/submarine-workbench/workbench-web/src/app/services/auth.service.ts
index e7ad4080..76bf583a 100644
--- a/submarine-workbench/workbench-web/src/app/services/auth.service.ts
+++ b/submarine-workbench/workbench-web/src/app/services/auth.service.ts
@@ -32,17 +32,37 @@ import { LocalStorageService } from './local-storage.service';
 export class AuthService {
   isLoggedIn = false;
   authTokenKey = 'auth_token';
-
   // store the URL so we can redirect after logging in
   redirectUrl: string;
+  // auth flow type: token, session
+  flowType: string = "token";
 
   constructor(
     private localStorageService: LocalStorageService,
     private baseApi: BaseApiService,
     private httpClient: HttpClient
   ) {
-    const authToken = this.localStorageService.get<string>(this.authTokenKey);
-    this.isLoggedIn = !!authToken;
+    this.flowType = window.GLOBAL_CONFIG.type
+    // console.log(`auth type = ${this.authType}`)
+    if (this.flowType === "session") {
+      this.isLoggedIn = true;
+    } else {
+      const authToken = this.localStorageService.get<string>(this.authTokenKey);
+      this.isLoggedIn = !!authToken;
+    }
+  }
+
+  getFlowType(): string {
+    return this.flowType;
+  }
+
+  getToken() {
+    return this.localStorageService.get<string>(this.authTokenKey);
+  }
+
+  removeToken() {
+    this.isLoggedIn = false;
+    this.localStorageService.remove(this.authTokenKey);
   }
 
   login(userForm: { userName: string; password: string }): Observable<SysUser> {
@@ -66,15 +86,19 @@ export class AuthService {
   }
 
   logout() {
-    return this.httpClient.post<Rest<boolean>>(this.baseApi.getRestApi('/auth/logout'), {}).pipe(
-      map((res) => {
-        if (res.result) {
-          this.isLoggedIn = false;
-          this.localStorageService.remove(this.authTokenKey);
-        }
-
-        return res.result;
-      })
-    );
+    if (this.flowType === "session") {
+      this.removeToken();
+      const url = window.location.origin + window.location.pathname
+      window.location.href = '/auth/logout?redirect_url=' + url;
+    } else {
+      return this.httpClient.post<Rest<boolean>>(this.baseApi.getRestApi('/auth/logout'), {}).pipe(
+        map((res) => {
+          if (res.result) {
+            this.removeToken();
+          }
+          return res.result;
+        })
+      );
+    }
   }
 }
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-workbench/workbench-web/src/assets/security/provider.js
similarity index 57%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
copy to submarine-workbench/workbench-web/src/assets/security/provider.js
index 83f1bb84..eadb66e5 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-workbench/workbench-web/src/assets/security/provider.js
@@ -17,23 +17,8 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security.common;
-
-import org.apache.submarine.commons.utils.SubmarineConfiguration;
-
-import static org.apache.submarine.commons.utils.SubmarineConfVars.ConfVars;
-
-public class CommonConfig {
-
-  public static final String LOGOUT_ENDPOINT = "/auth/logout";
-  public static final String AUTH_HEADER = "Authorization";
-  public static final String BEARER_HEADER_PREFIX = "Bearer ";
-
-  public static final int MAX_AGE;
-
-  static {
-    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
-    MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
-  }
-
-}
+(function () {
+  window.GLOBAL_CONFIG = {
+    "type": "simple"
+  };
+})();
diff --git a/submarine-workbench/workbench-web/src/index.html b/submarine-workbench/workbench-web/src/index.html
index a88d57fc..f1595b78 100644
--- a/submarine-workbench/workbench-web/src/index.html
+++ b/submarine-workbench/workbench-web/src/index.html
@@ -25,6 +25,7 @@
     <base href="/" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
     <link rel="icon" type="image/x-icon" href="favicon.ico" />
+    <script src="/assets/security/provider.js"></script>
   </head>
   <body>
     <submarine-root></submarine-root>
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java b/submarine-workbench/workbench-web/src/types/index.d.ts
similarity index 57%
copy from submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
copy to submarine-workbench/workbench-web/src/types/index.d.ts
index 83f1bb84..aaf9ca74 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/security/common/CommonConfig.java
+++ b/submarine-workbench/workbench-web/src/types/index.d.ts
@@ -17,23 +17,12 @@
  * under the License.
  */
 
-package org.apache.submarine.server.security.common;
+export {};
 
-import org.apache.submarine.commons.utils.SubmarineConfiguration;
-
-import static org.apache.submarine.commons.utils.SubmarineConfVars.ConfVars;
-
-public class CommonConfig {
-
-  public static final String LOGOUT_ENDPOINT = "/auth/logout";
-  public static final String AUTH_HEADER = "Authorization";
-  public static final String BEARER_HEADER_PREFIX = "Bearer ";
-
-  public static final int MAX_AGE;
-
-  static {
-    SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
-    MAX_AGE = conf.getInt(ConfVars.SUBMARINE_AUTH_MAX_AGE_ENV);
+declare global {
+  interface Window {
+    GLOBAL_CONFIG: {
+      type: string
+    };
   }
-
 }
diff --git a/submarine-workbench/workbench-web/tsconfig.json b/submarine-workbench/workbench-web/tsconfig.json
index cc513fce..670584ae 100644
--- a/submarine-workbench/workbench-web/tsconfig.json
+++ b/submarine-workbench/workbench-web/tsconfig.json
@@ -18,7 +18,7 @@
     "importHelpers": true,
     "target": "es5",
     "typeRoots": [
-      "node_modules/@types"
+      "node_modules/@types", "./src/types"
     ],
     "lib": [
       "es2018",
diff --git a/website/docs/designDocs/wip-designs/security-implementation.md b/website/docs/designDocs/wip-designs/security-implementation.md
index b799f2e8..c4e129f0 100644
--- a/website/docs/designDocs/wip-designs/security-implementation.md
+++ b/website/docs/designDocs/wip-designs/security-implementation.md
@@ -107,28 +107,28 @@ The field names and values are defined in the OpenID Connect Discovery Specifica
 
 ### Configuration
 
-|  Attribute   | Description  | Type | Default | Comment |
-|  ----  | ----  | ---- | ---- | ---- |
-| submarine.auth.type  | Supported authentication types, currently available are: none, simple, oauth2/oidc, ldap, kerberos, saml, cas | string | none | Only one authentication method can be supported at any one time |
-| submarine.auth.token.maxAge  | Expiry time of the token (minite) | int | 1 day | |
-| submarine.auth.refreshToken.maxAge  | Expiry time of the refresh token (minite) | int | 1 hour | |
-| submarine.auth.oauth2.client.id  | OAuth2 client id | string |  | |
-| submarine.auth.oauth2.client.secret  | OAuth2 client secret| string |  | |
-| submarine.auth.oauth2.client.flows  | OAuth2 flows, can be: authorizationCode, implicit, password or clientCredentials | string |  | |
-| submarine.auth.oauth2.scopes  | The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it. | string |  | |
-| submarine.auth.oauth2.token.uri  | OAuth2 access token uri | string |  | |
-| submarine.auth.oauth2.refresh.uri  | OAuth2 refresh token uri | string |  | |
-| submarine.auth.oauth2.authorization.uri  | OAuth2 authorization uri | string |  | |
-| submarine.auth.oauth2.logout.uri  | OAuth2 logout uri | string |  | |
-| submarine.auth.oidc.client.id  | OIDC client id | string |  | |
-| submarine.auth.oidc.client.secret  | OIDC client Secret| string |  | |
-| submarine.auth.oidc.client.scopes  | The available scopes for the OIDC security scheme. A map between the scope name and a short description for it.| string |  | |
-| submarine.auth.oidc.useNonce  | Whether to use nonce during login process | string |  | |
-| submarine.auth.oidc.discover.uri  | OIDC discovery uri | string |  | |
-| submarine.auth.oidc.logout.uri  | OIDC logout uri | string |  | |
-| submarine.auth.ladp.provider.uri  | LDAP provider uri | string |  | |
-| submarine.auth.ladp.baseDn  | LDAP base DN | string |  | base DN is the base LDAP distinguished name for your LDAP server. For example, ou=dev,dc=xyz,dc=com |
-| submarine.auth.ladp.domain  | LDAP AD domain | string |  | AD domain is the domain name of the AD server. For example, corp.domain.com |
+| Attribute                               | Description                                                                                                       | Type    | Default | Comment                                                                                             |
+|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------|---------|---------|-----------------------------------------------------------------------------------------------------|
+| submarine.auth.type                     | Supported authentication types, currently available are: none, simple, oauth2/oidc, ldap, kerberos, saml, cas     | string  | none    | Only one authentication method can be supported at any one time                                     |
+| submarine.auth.token.maxAge             | Expiry time of the token (minute)                                                                                 | int     | 1 day   |                                                                                                     |
+| submarine.auth.refreshToken.maxAge      | Expiry time of the refresh token (minute)                                                                         | int     | 1 hour  |                                                                                                     |
+| submarine.cookie.http.only              | HttpOnly Cookie                                                                                                   | boolean | false   |                                                                                                     |
+| submarine.cookie.secure                 | Secure Cookie                                                                                                     | boolean | false   |                                                                                                     |
+| submarine.cookie.samesite               | SameSite Cookie, can be Lax, Strict, None(or empty)                                                               | string  |         | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite                       |
+| submarine.auth.oauth2.client.id         | OAuth2 client id                                                                                                  | string  |         |                                                                                                     |
+| submarine.auth.oauth2.client.secret     | OAuth2 client secret                                                                                              | string  |         |                                                                                                     |
+| submarine.auth.oauth2.client.flows      | OAuth2 flows, can be: authorizationCode, implicit, password or clientCredentials                                  | string  |         |                                                                                                     |
+| submarine.auth.oauth2.scopes            | The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it. | string  |         |                                                                                                     |
+| submarine.auth.oauth2.token.uri         | OAuth2 access token uri                                                                                           | string  |         |                                                                                                     |
+| submarine.auth.oauth2.refresh.uri       | OAuth2 refresh token uri                                                                                          | string  |         |                                                                                                     |
+| submarine.auth.oauth2.authorization.uri | OAuth2 authorization uri                                                                                          | string  |         |                                                                                                     |
+| submarine.auth.oauth2.logout.uri        | OAuth2 logout uri                                                                                                 | string  |         |                                                                                                     |
+| submarine.auth.oidc.client.id           | OIDC client id                                                                                                    | string  |         |                                                                                                     |
+| submarine.auth.oidc.client.secret       | OIDC client Secret                                                                                                | string  |         |                                                                                                     |
+| submarine.auth.oidc.discover.uri        | OIDC discovery uri                                                                                                | string  |         |                                                                                                     |
+| submarine.auth.ladp.provider.uri        | LDAP provider uri                                                                                                 | string  |         |                                                                                                     |
+| submarine.auth.ladp.baseDn              | LDAP base DN                                                                                                      | string  |         | base DN is the base LDAP distinguished name for your LDAP server. For example, ou=dev,dc=xyz,dc=com |
+| submarine.auth.ladp.domain              | LDAP AD domain                                                                                                    | string  |         | AD domain is the domain name of the AD server. For example, corp.domain.com                         |
 
 ### Design and implementation
 
@@ -147,10 +147,10 @@ Describe the design of relevant user tables, user registration/modification/dele
 and the processing logic associated with authenticated login
 (including the mapping of attributes for automatically registered users when integrating with other authentication platforms, etc.).
 
-We use `sys_user` table to store user information for submarines.  
-When `submarine.auth.type` is `simple`, the user's login operation will match `user_name` and `password` (encrypted) in `sys_user`. Only when the user name and password match will the login succeed.  
-When `submarine.auth.type` is `ldap`, the user's login will operation request the LDAP and verify that the username and password are correct. A new record will be added to the `sys_user` table if the logged-in user does not exist.  
-When logging in using other third-party authentication (OAuth2/OpenID Connect (OIDC), SAML, CAS etc.), the login page will automatically jump to the third-party service and revert back to the submarine after a successful login. A new record will be added to the `sys_user` table if the logged-in user does not exist.  
+We use `sys_user` table to store user information for submarines.
+When `submarine.auth.type` is `simple`, the user's login operation will match `user_name` and `password` (encrypted) in `sys_user`. Only when the user name and password match will the login succeed.
+When `submarine.auth.type` is `ldap`, the user's login will operation request the LDAP and verify that the username and password are correct. A new record will be added to the `sys_user` table if the logged-in user does not exist.
+When logging in using other third-party authentication (OAuth2/OpenID Connect (OIDC), SAML, CAS etc.), the login page will automatically jump to the third-party service and revert back to the submarine after a successful login. A new record will be added to the `sys_user` table if the logged-in user does not exist.
 
 #### Department
 [TODO]


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org