You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2019/06/18 12:39:49 UTC

[servicecomb-fence] 04/09: [SCB-1293]1. add TokenStore to support stateless or stateful session management 2. refactor test cases and change developers guide

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

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-fence.git

commit 1d2e250c22aa1f09f87cdbb0b1e22833c5b0bed7
Author: liubao <bi...@qq.com>
AuthorDate: Tue May 28 15:09:55 2019 +0800

    [SCB-1293]1. add TokenStore to support stateless or stateful session management 2. refactor test cases and change developers guide
---
 .../server/PasswordTokenGranter.java               |  51 ++++-----
 .../server/RefreshTokenTokenGranter.java           |  77 ++++++--------
 .../authentication/server/TokenEndpoint.java       |   6 +-
 .../authentication/server/TokenGranter.java        |   4 +-
 api/authentication-server/service/pom.xml          |   8 ++
 .../server/{Token.java => TokenResponse.java}      |  13 ++-
 .../authentication/server/TokenService.java        |   2 +-
 .../token/AbstractSessionIDTokenStore.java}        |   7 +-
 .../token/InMemorySessionIDTokenStore.java}        |  32 +++---
 .../servicecomb/authentication/token/JWTToken.java |  85 +++++++++++++++
 .../authentication/token/JWTTokenStore.java        |  71 +++++++++++++
 .../authentication/token/SessionIDToken.java       |  76 +++++++++++++
 .../servicecomb/authentication/token/Token.java}   |  18 +++-
 .../authentication/token/TokenStore.java}          |  10 +-
 .../servicecomb/authentication/util/Constants.java |   5 +-
 .../authentication/edge/AuthHandler.java           |  17 +--
 .../authentication/edge/AuthenticationFilter.java  |   0
 .../edge/AuthenticationServerTokenEndpoint.java}   |  27 ++---
 .../edge/CustomVertxRestDispatcher.java            |   1 +
 .../authentication/edge/EdgeTokenStore.java}       |  10 +-
 .../edge/InMemoryEdgeTokenStore.java}              |  28 +++--
 .../authentication/edge/InternalAccessHandler.java |   0
 .../authentication/edge/TokenEndpoint.java         |  65 ++++++++++++
 ...servicecomb.common.rest.filter.HttpServerFilter |   0
 ...cecomb.transport.rest.vertx.VertxHttpDispatcher |   2 +-
 .../src/main/resources/config/cse.handler.xml      |   0
 api/edge-service/service/pom.xml                   |   3 +-
 .../authentication/edge}/TokenService.java         |   8 +-
 .../resource/ResourceAuthHandler.java              |  24 ++---
 docs/zh_CN/developersGuide.md                      |  97 ++++++++++++-----
 .../AuthenticationConfiguration.java               |  30 ++++--
 .../authentication/AuthenticationTestCase.java     | 117 ++++++++-------------
 .../authentication/BootEventListener.java          |   7 +-
 .../authentication/GateRestTemplate.java           |   9 ++
 .../gateway/AuthenticationConfiguration.java       |  10 +-
 .../resource/AuthenticationConfiguration.java      |  19 +++-
 36 files changed, 652 insertions(+), 287 deletions(-)

diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java
index 7d16268..a1538ff 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java
+++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java
@@ -18,18 +18,13 @@
 package org.apache.servicecomb.authentication.server;
 
 import java.util.Map;
-import java.util.UUID;
 
-import org.apache.servicecomb.authentication.jwt.JWTClaims;
-import org.apache.servicecomb.authentication.jwt.JsonParser;
+import org.apache.servicecomb.authentication.token.TokenStore;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.jwt.Jwt;
-import org.springframework.security.jwt.JwtHelper;
-import org.springframework.security.jwt.crypto.sign.Signer;
 import org.springframework.stereotype.Component;
 
 import com.netflix.config.DynamicPropertyFactory;
@@ -45,40 +40,34 @@ public class PasswordTokenGranter implements TokenGranter {
   private PasswordEncoder passwordEncoder;
 
   @Autowired
-  @Qualifier("authSigner")
-  private Signer signer;
+  @Qualifier("authAccessTokenStore")
+  private TokenStore accessTokenStore;
+
+  @Autowired
+  @Qualifier("authRefreshTokenStore")
+  private TokenStore refreshTokenStore;
+
+  @Autowired
+  @Qualifier("authIDTokenStore")
+  private TokenStore idTokenStore;
 
   @Override
-  public Token grant(Map<String, String> parameters) {
+  public TokenResponse grant(Map<String, String> parameters) {
     String username = parameters.get(TokenConst.PARAM_USERNAME);
     String password = parameters.get(TokenConst.PARAM_PASSWORD);
 
     UserDetails userDetails = userDetailsService.loadUserByUsername(username);
     if (passwordEncoder.matches(password, userDetails.getPassword())) {
-      JWTClaims claims = new JWTClaims();
-      if (userDetails.getAuthorities() != null) {
-        userDetails.getAuthorities().forEach(authority -> claims.addAuthority(authority.getAuthority()));
-      }
-      claims.setJti(UUID.randomUUID().toString());
-      claims.setIat(System.currentTimeMillis());
-      claims.setExp(5 * 60);
-      
-      String content = JsonParser.unparse(claims);
-      Jwt accessToken = JwtHelper.encode(content, signer);
-
-      Token token = new Token();
-      token.setScope(claims.getScope());
+      TokenResponse token = new TokenResponse();
+      token.setAccess_token(accessTokenStore.createToken(userDetails).getValue());
+      token.setRefresh_token(refreshTokenStore.createToken(userDetails).getValue());
+      token.setId_token(idTokenStore.createToken(userDetails).getValue());
+
+      //TODO add parameters.
+      token.setScope(null);
       token.setExpires_in(10 * 60);
       token.setToken_type("bearer");
-      token.setAccess_token(accessToken.getEncoded());
-      
-      JWTClaims accessTokenClaims = new JWTClaims();
-      accessTokenClaims.setJti(UUID.randomUUID().toString());
-      accessTokenClaims.setIat(System.currentTimeMillis());
-      accessTokenClaims.setExp(60 * 60);
-      Jwt refreshToken = JwtHelper.encode(JsonParser.unparse(claims), signer);
-      token.setRefresh_token(refreshToken.getEncoded());
-      
+
       return token;
     } else {
       return null;
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java
index 7a41e90..6980287 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java
+++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java
@@ -19,14 +19,12 @@ package org.apache.servicecomb.authentication.server;
 
 import java.util.Map;
 
-import org.apache.servicecomb.authentication.jwt.JWTClaims;
-import org.apache.servicecomb.authentication.jwt.JsonParser;
+import org.apache.servicecomb.authentication.token.Token;
+import org.apache.servicecomb.authentication.token.TokenStore;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.security.jwt.Jwt;
-import org.springframework.security.jwt.JwtHelper;
-import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
-import org.springframework.security.jwt.crypto.sign.Signer;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.stereotype.Component;
 
 import com.netflix.config.DynamicPropertyFactory;
@@ -34,12 +32,20 @@ import com.netflix.config.DynamicPropertyFactory;
 @Component(value = "fefreshTokenTokenGranter")
 public class RefreshTokenTokenGranter implements TokenGranter {
   @Autowired
-  @Qualifier("authSignatureVerifier")
-  private SignatureVerifier signerVerifier;
+  @Qualifier("authUserDetailsService")
+  private UserDetailsService userDetailsService;
 
   @Autowired
-  @Qualifier("authSigner")
-  private Signer signer;
+  @Qualifier("authAccessTokenStore")
+  private TokenStore accessTokenStore;
+
+  @Autowired
+  @Qualifier("authRefreshTokenStore")
+  private TokenStore refreshTokenStore;
+
+  @Autowired
+  @Qualifier("authIDTokenStore")
+  private TokenStore idTokenStore;
 
   @Override
   public boolean enabled() {
@@ -54,42 +60,27 @@ public class RefreshTokenTokenGranter implements TokenGranter {
   }
 
   @Override
-  public Token grant(Map<String, String> parameters) {
-    String refreshToken = parameters.get(TokenConst.PARAM_REFRESH_TOKEN);
-    String accessToken = parameters.get(TokenConst.PARAM_ACCESS_TOKEN);
-
-    // verify refresh tokens
-    Jwt jwt = JwtHelper.decode(refreshToken);
-    JWTClaims claims;
-    try {
-      jwt.verifySignature(signerVerifier);
-      claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class);
-      // TODO: verify claims.
-    } catch (Exception e) {
-      return null;
-    }
+  public TokenResponse grant(Map<String, String> parameters) {
+    String refreshTokenValue = parameters.get(TokenConst.PARAM_REFRESH_TOKEN);
 
-    // verify access tokens
-    jwt = JwtHelper.decode(accessToken);
-    claims = null;
-    try {
-      jwt.verifySignature(signerVerifier);
-      claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class);
-      // TODO: verify claims.
-    } catch (Exception e) {
-      return null;
-    }
+    Token refreshToken = refreshTokenStore.readTokenByValue(refreshTokenValue);
 
-    claims.setIat(System.currentTimeMillis());
-    String content = JsonParser.unparse(claims);
-    Jwt newAccessToken = JwtHelper.encode(content, signer);
+    if (refreshToken != null && !refreshToken.isExpired()) {
+      UserDetails userDetails = userDetailsService.loadUserByUsername(refreshToken.username());
 
-    Token token = new Token();
-    token.setScope(claims.getScope());
-    token.setExpires_in(10 * 60);
-    token.setToken_type("bearer");
-    token.setAccess_token(newAccessToken.getEncoded());
-    return token;
+      TokenResponse token = new TokenResponse();
+      token.setAccess_token(accessTokenStore.createToken(userDetails).getValue());
+      // refresh token is not generated 
+      token.setRefresh_token(refreshTokenValue);
+      token.setId_token(idTokenStore.createToken(userDetails).getValue());
+
+      //TODO add parameters.
+      token.setScope(null);
+      token.setExpires_in(10 * 60);
+      token.setToken_type("bearer");
+      return token;
+    }
+    return null;
   }
 
 }
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenEndpoint.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenEndpoint.java
index 775d332..fa2ca32 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenEndpoint.java
+++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenEndpoint.java
@@ -29,19 +29,19 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 @RestSchema(schemaId = "TokenEndpoint")
-@RequestMapping(path = "/v1/oauth/token")
+@RequestMapping(path = "/v1/token")
 public class TokenEndpoint implements TokenService {
   @Autowired
   private List<TokenGranter> granters;
 
   @Override
   @PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED)
-  public Token getAccessToken(@RequestBody Map<String, String> parameters) {
+  public TokenResponse getToken(@RequestBody Map<String, String> parameters) {
     String grantType = parameters.get(TokenConst.PARAM_GRANT_TYPE);
 
     for (TokenGranter granter : granters) {
       if (granter.enabled()) {
-        Token token = granter.grant(grantType, parameters);
+        TokenResponse token = granter.grant(grantType, parameters);
         if (token != null) {
           return token;
         }
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
index 9ba0861..e5f600b 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
+++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
@@ -29,12 +29,12 @@ public interface TokenGranter {
 
   String grantType();
 
-  default Token grant(String grantType, Map<String, String> parameters) {
+  default TokenResponse grant(String grantType, Map<String, String> parameters) {
     if (grantType().equals(grantType)) {
       return grant(parameters);
     }
     return null;
   }
 
-  Token grant(Map<String, String> parameters);
+  TokenResponse grant(Map<String, String> parameters);
 }
diff --git a/api/authentication-server/service/pom.xml b/api/authentication-server/service/pom.xml
index 4433ff6..8f9b5e2 100644
--- a/api/authentication-server/service/pom.xml
+++ b/api/authentication-server/service/pom.xml
@@ -28,4 +28,12 @@
   <artifactId>authentication-server-api-service</artifactId>
   <packaging>jar</packaging>
 
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.servicecomb.authentication</groupId>
+      <artifactId>authentication-common-api-service</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+  </dependencies>
+
 </project>
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/Token.java b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java
similarity index 88%
rename from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/Token.java
rename to api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java
index 8758bd1..9bdc6d7 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/Token.java
+++ b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java
@@ -20,7 +20,7 @@ package org.apache.servicecomb.authentication.server;
 import java.util.Map;
 import java.util.Set;
 
-public class Token {
+public class TokenResponse {
   // Naming conventions https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00#section-3.1
   private String token_type;
 
@@ -28,6 +28,9 @@ public class Token {
 
   private String refresh_token;
 
+  // Naming conventions https://openid.net/specs/openid-connect-basic-1_0.html#ObtainingTokens
+  private String id_token;
+
   private int expires_in;
 
   private Set<String> scope;
@@ -69,6 +72,14 @@ public class Token {
     this.expires_in = expires_in;
   }
 
+  public String getId_token() {
+    return id_token;
+  }
+
+  public void setId_token(String id_token) {
+    this.id_token = id_token;
+  }
+
   public Set<String> getScope() {
     return scope;
   }
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
index b0cea22..8dbd197 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
@@ -20,5 +20,5 @@ package org.apache.servicecomb.authentication.server;
 import java.util.Map;
 
 public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+  TokenResponse getToken(Map<String, String> parameters);
 }
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java
similarity index 83%
copy from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java
index b0cea22..cb507a3 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java
@@ -15,10 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.token;
 
-import java.util.Map;
-
-public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+public abstract class AbstractSessionIDTokenStore implements TokenStore {
 }
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java
similarity index 55%
copy from api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java
index 9ba0861..5bac8f0 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java
@@ -15,26 +15,28 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.token;
 
+import java.util.HashMap;
 import java.util.Map;
 
-/**
- * Token granter is used to grant access tokens. 
- * @author Administrator
- *
- */
-public interface TokenGranter {
-  boolean enabled();
+import org.springframework.security.core.userdetails.UserDetails;
 
-  String grantType();
+public class InMemorySessionIDTokenStore extends AbstractSessionIDTokenStore {
+  private Map<String, SessionIDToken> tokens = new HashMap<>();
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T extends Token> T createToken(UserDetails userDetails) {
+    SessionIDToken token = new SessionIDToken(userDetails.getUsername());
+    tokens.put(token.getValue(), token);
+    return (T) token;
+  }
 
-  default Token grant(String grantType, Map<String, String> parameters) {
-    if (grantType().equals(grantType)) {
-      return grant(parameters);
-    }
-    return null;
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T extends Token> T readTokenByValue(String value) {
+    return (T) tokens.get(value);
   }
 
-  Token grant(Map<String, String> parameters);
 }
diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java
new file mode 100644
index 0000000..e989ae5
--- /dev/null
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java
@@ -0,0 +1,85 @@
+/*
+ * 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.servicecomb.authentication.token;
+
+import java.util.Map;
+
+import org.apache.servicecomb.authentication.jwt.JWTClaims;
+import org.apache.servicecomb.authentication.jwt.JsonParser;
+import org.springframework.security.jwt.Jwt;
+import org.springframework.security.jwt.JwtHelper;
+import org.springframework.security.jwt.crypto.sign.Signer;
+
+public class JWTToken implements Token {
+  private JWTClaims claims;
+
+  private boolean valueCalculated = false;
+
+  private String value;
+
+  private Signer signer;
+
+  public JWTToken(JWTClaims claims, Signer signer) {
+    this.claims = claims;
+    this.signer = signer;
+  }
+
+  @Override
+  public boolean isExpired() {
+    return System.currentTimeMillis() - this.getIssueAt() > this.getExpiration() * 1000;
+  }
+
+  @Override
+  public long getIssueAt() {
+    return this.claims.getIat();
+  }
+
+  @Override
+  public long getExpiration() {
+    return this.claims.getExp();
+  }
+
+  @Override
+  public long getNotBefore() {
+    return this.claims.getNbf();
+  }
+
+  @Override
+  public String getValue() {
+    if (!this.valueCalculated) {
+      String content = JsonParser.unparse(claims);
+      Jwt jwtToken = JwtHelper.encode(content, signer);
+      this.value = jwtToken.getEncoded();
+    }
+    return this.value;
+  }
+
+  @Override
+  public Map<String, Object> getAdditionalInformation() {
+    return this.claims.getAdditionalInformation();
+  }
+
+  @Override
+  public String username() {
+    return this.claims.getSub();
+  }
+  
+  public JWTClaims getClaims() {
+    return this.claims;
+  }
+}
diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java
new file mode 100644
index 0000000..2f394e7
--- /dev/null
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java
@@ -0,0 +1,71 @@
+/*
+ * 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.servicecomb.authentication.token;
+
+import java.util.UUID;
+
+import org.apache.servicecomb.authentication.jwt.JWTClaims;
+import org.apache.servicecomb.authentication.jwt.JsonParser;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.jwt.Jwt;
+import org.springframework.security.jwt.JwtHelper;
+import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
+import org.springframework.security.jwt.crypto.sign.Signer;
+
+public class JWTTokenStore implements TokenStore {
+  private Signer signer;
+
+  private SignatureVerifier signerVerifier;
+
+  public JWTTokenStore(Signer signer, SignatureVerifier signerVerifier) {
+    this.signer = signer;
+    this.signerVerifier = signerVerifier;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T extends Token> T createToken(UserDetails userDetails) {
+    JWTClaims claims = new JWTClaims();
+    claims.setSub(userDetails.getUsername());
+    if (userDetails.getAuthorities() != null) {
+      userDetails.getAuthorities().forEach(authority -> claims.addAuthority(authority.getAuthority()));
+    }
+
+    // TODO : set other parameters.
+    claims.setJti(UUID.randomUUID().toString());
+    claims.setIat(System.currentTimeMillis());
+    claims.setExp(5 * 60);
+
+    return (T) new JWTToken(claims, signer);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T extends Token> T readTokenByValue(String value) {
+    Jwt jwt = JwtHelper.decode(value);
+    JWTClaims claims;
+    try {
+      jwt.verifySignature(signerVerifier);
+      claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class);
+    } catch (Exception e) {
+      return null;
+    }
+    return (T) new JWTToken(claims, signer);
+  }
+
+}
diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java
new file mode 100644
index 0000000..06a6f96
--- /dev/null
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java
@@ -0,0 +1,76 @@
+/*
+ * 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.servicecomb.authentication.token;
+
+import java.util.Map;
+import java.util.UUID;
+
+public class SessionIDToken implements Token {
+  private String value;
+
+  private long issueAt;
+
+  // in seconds
+  private long expiration;
+
+  private String username;
+
+  public SessionIDToken(String username) {
+    this.value = UUID.randomUUID().toString();
+    this.issueAt = System.currentTimeMillis();
+    // TODO add a configuration
+    this.expiration = 600;
+    this.username = username;
+  }
+
+  @Override
+  public boolean isExpired() {
+    return System.currentTimeMillis() - this.issueAt > this.expiration * 1000;
+  }
+
+  @Override
+  public long getIssueAt() {
+    return this.issueAt;
+  }
+
+  @Override
+  public long getExpiration() {
+    return this.expiration;
+  }
+
+  @Override
+  public long getNotBefore() {
+    // TODO add a configuration
+    return 0;
+  }
+
+  @Override
+  public String getValue() {
+    return this.value;
+  }
+
+  @Override
+  public Map<String, Object> getAdditionalInformation() {
+    // TODO add a configuration
+    return null;
+  }
+
+  public String username() {
+    return this.username;
+  }
+}
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java
similarity index 75%
copy from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java
index b0cea22..21b06d5 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java
@@ -15,10 +15,22 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.token;
 
 import java.util.Map;
 
-public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+public interface Token {
+  String username();
+
+  boolean isExpired();
+
+  long getIssueAt();
+
+  long getExpiration();
+
+  long getNotBefore();
+
+  String getValue();
+
+  Map<String, Object> getAdditionalInformation();
 }
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java
similarity index 75%
copy from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java
index b0cea22..1f26610 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java
@@ -15,10 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.token;
 
-import java.util.Map;
+import org.springframework.security.core.userdetails.UserDetails;
 
-public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+public interface TokenStore {
+  <T extends Token> T createToken(UserDetails userDetails);
+  
+  <T extends Token> T readTokenByValue(String value);
 }
diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java
index 96333aa..4dcc8e7 100644
--- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java
+++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java
@@ -21,6 +21,9 @@ public final class Constants {
   public static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
 
   public static final String CONTEXT_HEADER_AUTHORIZATION = "Authorization";
-  
+
   public static final String CONTEXT_HEADER_CLAIMS = "Claims";
+
+  public static final String TOKEN_TYPE_BEARER = "Bearer";
+
 }
diff --git a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java
similarity index 78%
rename from api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java
rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java
index abf4587..d209be4 100644
--- a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java
@@ -17,32 +17,35 @@
 
 package org.apache.servicecomb.authentication.edge;
 
+import org.apache.servicecomb.authentication.server.TokenResponse;
 import org.apache.servicecomb.authentication.util.Constants;
 import org.apache.servicecomb.core.Handler;
 import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.foundation.common.utils.BeanUtils;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
-import org.springframework.security.jwt.Jwt;
-import org.springframework.security.jwt.JwtHelper;
-import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
 
 
 public class AuthHandler implements Handler {
   @Override
   public void handle(Invocation invocation, AsyncResponse asyncResponse) throws Exception {
+    EdgeTokenStore edgeTokenStore = BeanUtils.getBean("authEdgeTokenStore");
+
     String token = invocation.getContext(Constants.CONTEXT_HEADER_AUTHORIZATION);
     if (token == null) {
       asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated"));
       return;
     }
-    Jwt jwt = JwtHelper.decode(token);
-    try {
-      jwt.verifySignature(BeanUtils.getBean("authSignatureVerifier"));
-    } catch (InvalidSignatureException e) {
+
+    TokenResponse tokenResonse = edgeTokenStore.readTokenResponse(token);
+    if (tokenResonse == null) {
+      // TODO : check token validity by expiration time
       asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated"));
       return;
     }
+
+    // send id_token to services to apply state less validation
+    invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION, tokenResonse.getId_token());
     invocation.next(asyncResponse);
   }
 }
diff --git a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java
similarity index 100%
rename from api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java
rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationServerTokenEndpoint.java
similarity index 59%
copy from api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
copy to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationServerTokenEndpoint.java
index 9ba0861..50c29d3 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationServerTokenEndpoint.java
@@ -15,26 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.edge;
 
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 
-/**
- * Token granter is used to grant access tokens. 
- * @author Administrator
- *
- */
-public interface TokenGranter {
-  boolean enabled();
-
-  String grantType();
-
-  default Token grant(String grantType, Map<String, String> parameters) {
-    if (grantType().equals(grantType)) {
-      return grant(parameters);
-    }
-    return null;
-  }
+import org.apache.servicecomb.authentication.server.TokenResponse;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 
-  Token grant(Map<String, String> parameters);
+public interface AuthenticationServerTokenEndpoint {
+  @PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+  public CompletableFuture<TokenResponse> getToken(@RequestBody Map<String, String> parameters);
 }
diff --git a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java
similarity index 99%
rename from api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java
rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java
index 521b71c..a08873d 100644
--- a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/CustomVertxRestDispatcher.java
@@ -63,6 +63,7 @@ public class CustomVertxRestDispatcher extends AbstractVertxHttpDispatcher {
 
   @Override
   public void init(Router router) {
+    // TODO: regex configuration
     String regex = "(/v1/log|/inspector|/v1/auth)/(.*)";
     router.routeWithRegex(regex).handler(CookieHandler.create());
     router.routeWithRegex(regex).handler(createBodyHandler());
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java
similarity index 73%
copy from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
copy to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java
index b0cea22..92bbbaf 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java
@@ -15,10 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.edge;
 
-import java.util.Map;
+import org.apache.servicecomb.authentication.server.TokenResponse;
 
-public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+public interface EdgeTokenStore {
+  TokenResponse readTokenResponse(String accessTokenValue);
+
+  void saveTokenResponse(String accessTokenValue, TokenResponse tokenResponse);
 }
diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java
similarity index 60%
copy from api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
copy to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java
index 9ba0861..03a7baa 100644
--- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/TokenGranter.java
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java
@@ -15,26 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.edge;
 
+import java.util.HashMap;
 import java.util.Map;
 
-/**
- * Token granter is used to grant access tokens. 
- * @author Administrator
- *
- */
-public interface TokenGranter {
-  boolean enabled();
+import org.apache.servicecomb.authentication.server.TokenResponse;
 
-  String grantType();
+public class InMemoryEdgeTokenStore implements EdgeTokenStore {
+  private Map<String, TokenResponse> tokens = new HashMap<>();
+
+  @Override
+  public TokenResponse readTokenResponse(String accessTokenValue) {
+    return tokens.get(accessTokenValue);
+  }
 
-  default Token grant(String grantType, Map<String, String> parameters) {
-    if (grantType().equals(grantType)) {
-      return grant(parameters);
-    }
-    return null;
+  @Override
+  public void saveTokenResponse(String accessTokenValue, TokenResponse tokenResponse) {
+    tokens.put(accessTokenValue, tokenResponse);
   }
 
-  Token grant(Map<String, String> parameters);
 }
diff --git a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/InternalAccessHandler.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InternalAccessHandler.java
similarity index 100%
rename from api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/InternalAccessHandler.java
rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InternalAccessHandler.java
diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java
new file mode 100644
index 0000000..87dcbb0
--- /dev/null
+++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java
@@ -0,0 +1,65 @@
+/*
+ * 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.servicecomb.authentication.edge;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.servicecomb.authentication.server.TokenResponse;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@RestSchema(schemaId = "TokenEndpoint")
+@RequestMapping(path = "/v1/token")
+public class TokenEndpoint implements TokenService {
+  @RpcReference(microserviceName = "authentication-server", schemaId = "TokenEndpoint")
+  private AuthenticationServerTokenEndpoint authenticationSererTokenEndpoint;
+
+  @Autowired
+  @Qualifier("authEdgeTokenStore")
+  private EdgeTokenStore edgeTokenStore;
+
+
+  @Override
+  @PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+  public CompletableFuture<TokenResponse> getToken(@RequestBody Map<String, String> parameters) {
+    CompletableFuture<TokenResponse> result = new CompletableFuture<>();
+
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+
+    CompletableFuture<TokenResponse> response =
+        authenticationSererTokenEndpoint.getToken(parameters);
+    response.whenComplete((tokenResonse, ex) -> {
+      if (!response.isCompletedExceptionally()) {
+        result.complete(tokenResonse);
+        edgeTokenStore.saveTokenResponse(tokenResonse.getAccess_token(), tokenResonse);
+      } else {
+        result.completeExceptionally(ex);
+      }
+    });
+    return result;
+  }
+}
diff --git a/api/edge-service/service/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/api/edge-service/endpoint/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter
similarity index 100%
rename from api/edge-service/service/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter
rename to api/edge-service/endpoint/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter
diff --git a/api/edge-service/service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/api/edge-service/endpoint/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
similarity index 91%
rename from api/edge-service/service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
rename to api/edge-service/endpoint/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
index 32c1583..53481f1 100644
--- a/api/edge-service/service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
+++ b/api/edge-service/endpoint/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
@@ -15,4 +15,4 @@
 # limitations under the License.
 #
 
-# org.apache.servicecomb.authentication.edge.CustomVertxRestDispatcher
\ No newline at end of file
+org.apache.servicecomb.authentication.edge.CustomVertxRestDispatcher
\ No newline at end of file
diff --git a/api/edge-service/service/src/main/resources/config/cse.handler.xml b/api/edge-service/endpoint/src/main/resources/config/cse.handler.xml
similarity index 100%
rename from api/edge-service/service/src/main/resources/config/cse.handler.xml
rename to api/edge-service/endpoint/src/main/resources/config/cse.handler.xml
diff --git a/api/edge-service/service/pom.xml b/api/edge-service/service/pom.xml
index 8d86c00..487f146 100644
--- a/api/edge-service/service/pom.xml
+++ b/api/edge-service/service/pom.xml
@@ -36,7 +36,8 @@
   <dependencies>
     <dependency>
       <groupId>org.apache.servicecomb.authentication</groupId>
-      <artifactId>authentication-common-api-endpoint</artifactId>
+      <artifactId>authentication-server-api-service</artifactId>
+      <version>${project.parent.version}</version>
     </dependency>
   </dependencies>
 </project>
diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java b/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/TokenService.java
similarity index 77%
copy from api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
copy to api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/TokenService.java
index b0cea22..5e12a45 100644
--- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenService.java
+++ b/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/TokenService.java
@@ -15,10 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.authentication.server;
+package org.apache.servicecomb.authentication.edge;
 
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.servicecomb.authentication.server.TokenResponse;
+
 
 public interface TokenService {
-  Token getAccessToken(Map<String, String> parameters);
+  CompletableFuture<TokenResponse> getToken(Map<String, String> parameters);
 }
diff --git a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java
index 68c6960..5c81139 100644
--- a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java
+++ b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java
@@ -21,8 +21,8 @@ import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.servicecomb.authentication.jwt.JWTClaims;
-import org.apache.servicecomb.authentication.jwt.JsonParser;
+import org.apache.servicecomb.authentication.token.JWTToken;
+import org.apache.servicecomb.authentication.token.JWTTokenStore;
 import org.apache.servicecomb.authentication.util.Constants;
 import org.apache.servicecomb.core.Handler;
 import org.apache.servicecomb.core.Invocation;
@@ -35,8 +35,6 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextImpl;
-import org.springframework.security.jwt.Jwt;
-import org.springframework.security.jwt.JwtHelper;
 
 public class ResourceAuthHandler implements Handler {
 
@@ -56,23 +54,19 @@ public class ResourceAuthHandler implements Handler {
       return;
     }
     // verify tokens
-    Jwt jwt = JwtHelper.decode(token);
-    JWTClaims claims;
-    try {
-      jwt.verifySignature(BeanUtils.getBean("authSignatureVerifier"));
-      claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class);
-      // TODO: verify claims.
-    } catch (Exception e) {
+    JWTTokenStore store = BeanUtils.getBean("authIDTokenStore");
+    JWTToken t = store.readTokenByValue(token);
+    if(t == null) {
       asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated"));
       return;
     }
-
+    
     // check roles
     if (!StringUtils.isEmpty(config.roles)) {
       String[] roles = config.roles.split(",");
       if (roles.length > 0) {
         boolean valid = false;
-        Set<String> authorities = claims.getAuthorities();
+        Set<String> authorities = t.getClaims().getAuthorities();
         for (String role : roles) {
           if (authorities.contains(role)) {
             valid = true;
@@ -87,8 +81,8 @@ public class ResourceAuthHandler implements Handler {
     }
 
     // pre method authentiation
-    Set<GrantedAuthority> grantedAuthorities = new HashSet<>(claims.getAuthorities().size());
-    claims.getAuthorities().forEach(v -> grantedAuthorities.add(new SimpleGrantedAuthority(v)));
+    Set<GrantedAuthority> grantedAuthorities = new HashSet<>(t.getClaims().getAuthorities().size());
+    t.getClaims().getAuthorities().forEach(v -> grantedAuthorities.add(new SimpleGrantedAuthority(v)));
     SecurityContext sc = new SecurityContextImpl();
     Authentication authentication = new SimpleAuthentication(true, grantedAuthorities);
     sc.setAuthentication(authentication);
diff --git a/docs/zh_CN/developersGuide.md b/docs/zh_CN/developersGuide.md
index 013f98f..3d67833 100644
--- a/docs/zh_CN/developersGuide.md
+++ b/docs/zh_CN/developersGuide.md
@@ -11,15 +11,37 @@
 ```
 ** HTTP Request **
 
-POST http://localhost:9090/api/authentication-server/v1/oauth/token HTTP/1.1
+POST http://localhost:9090/v1/token HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 
 grant_type=password&username=admin&password=changeMyPassword
+
+** HTTP Response **
+
+{
+    "token_type": "bearer",
+    "access_token": "SlAV32hkKG",
+    "refresh_token": "8xLOxBtZp8",
+    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
+     yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
+     NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
+     fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
+     AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
+     Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
+     NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
+     QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
+     K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
+     XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg",
+    "expires_in": 600,
+    "scope": null,
+    "jti": null,
+    "additionalInformation": null
+}
 ```
 
   * Authentication Server 发送 Token 给 Client 。
 
-  * Client 携带 Token 请求 Resource Server 。
+  * Client 携带  Access Token 请求 Edge Service 。
 
 ```
 ** HTTP Request **
@@ -29,6 +51,7 @@ Content-Type: application/x-www-form-urlencoded
 Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
 ```
 
+  * Edge Service 将 Access Token 转换为对应的 ID Token , 然后将请求转发给Resource Server。
   * Resource Server 返回对应的资源给 Client 。 
 
 ## 开发 Authentication Server
@@ -48,31 +71,38 @@ Authentication Server 主要提供认证和授权等接口。
 
 * 配置
 
-Authentication Server 需要配置 PasswordEncoder、Signer、SignerVerifier、UserDetailsService 等。这些对象和 Spring Security的概念一样。
+Authentication Server 需要配置 PasswordEncoder、Signer、SignerVerifier、TokenStore、UserDetailsService 等。
 ```
 @Configuration
 public class AuthenticationConfiguration {
-  @Autowired
-  @Qualifier("authPasswordEncoder")
-  private PasswordEncoder passwordEncoder;
-
   @Bean(name = "authPasswordEncoder")
   public PasswordEncoder authPasswordEncoder() {
     return new Pbkdf2PasswordEncoder();
   }
 
-  @Bean(name = "authSigner")
-  public Signer authSigner() {
-    return authSignerVerifier();
-  }
-  
-  @Bean(name = "authSignerVerifier")
+  @Bean(name = {"authSigner", "authSignatureVerifier"})
   public SignerVerifier authSignerVerifier() {
+    // If using RSA, need to configure authSigner and authSignatureVerifier separately. 
+    // If using MacSigner, need to protect the shared key by properly encryption.
     return new MacSigner("Please change this key.");
   }
 
+  @Bean(name = {"authAccessTokenStore", "authRefreshTokenStore"})
+  public TokenStore sessionIDTokenStore() {
+    // Use in memory store for testing. Need to implement JDBC or Redis SessionIDTokenStore in product. 
+    return new InMemorySessionIDTokenStore();
+  }
+
+  @Bean(name = "authIDTokenStore")
+  public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer,
+      @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) {
+    return new JWTTokenStore(signer, signerVerifier);
+  }
+
   @Bean(name = "authUserDetailsService")
-  public UserDetailsService authUserDetailsService() {
+  public UserDetailsService authUserDetailsService(
+      @Autowired @Qualifier("authPasswordEncoder") PasswordEncoder passwordEncoder) {
+    // Use in memory UserDetails, need to implement JDBC or others in product
     InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
     UserDetails uAdmin = new User("admin", passwordEncoder.encode("changeMyPassword"),
         Arrays.asList(new SimpleGrantedAuthority("ADMIN")));
@@ -93,6 +123,9 @@ public class AuthenticationConfiguration {
 
 生成 Token 和对 Token 进行校验。Singer 和  SignatureVerifier 是配套使用的, 在 Authentication Server , 生成 Token 的时候,需要使用 Singer 。 验证 Token 的有效性 (比如查询 userDetails 等场景), 需要使用  SignatureVerifier 。 通常有两种方式进行签名和校验, 一种是基于对称秘钥的机制,比如MacSigner,即是 Singer, 也是 SignatureVerifier (SignerVerifier); 一种是基于非对称秘钥的机制, 比如 RsaSigner 和  RsaVerifier , 生成 Token 和校验 Token 的秘钥是不同的。
 
+* TokenStore
+在Authentication Server,TokenStore主要用来生成Access Token, Refresh Token和ID Token, 默认情况下, Access Token和Refresh Token都使用AbstractSessionIDTokenStore(本例子使用了InMemorySessionIDTokenStore,业务代码通常需要换为JDBC、Redis等实现), ID Token使用JWTTokenStore。 JWTTokenStore是一个无状态的会话机制,Authentication Server的任何一个实例都可以独立生成。
+
 * PasswordEncoder 
 
 从 UserDetailsService 校验用户密码的时候需要使用。 开发者需要在加密性能和保密程度方面选择合适的算法。 常用的有 Pbkdf2PasswordEncoder , 它可以设置迭代次数,能够更好的保护密码不被暴力破解。 
@@ -114,17 +147,30 @@ Resource Server 对 Client 的访问进行认证, 并进行权限控制。
 
 * 配置
 
-Resource Server 需要配置 SignatureVerifier 等, 对用户会话进行认证。 
+Resource Server 需要配置 Signer、SignatureVerifier、TokenStore 等, 对用户会话进行认证。 
+
 ```
-@Configuration
-public class AuthenticationConfiguration {
-  @Bean(name = "authSignatureVerifier")
-  public SignerVerifier authSignatureVerifier() {
+  @Bean(name = {"authSigner", "authSignatureVerifier"})
+  public SignerVerifier authSignerVerifier() {
+    // If using RSA, need to configure authSigner and authSignatureVerifier separately. 
+    // If using MacSigner, need to protect the shared key by properly encryption.
     return new MacSigner("Please change this key.");
   }
-}
+
+  @Bean(name = "authIDTokenStore")
+  public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer,
+      @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) {
+    return new JWTTokenStore(signer, signerVerifier);
+  }
 ```
 
+* Signer、SignatureVerifier
+对Token进行校验需要,实际上Resource Server只需要使用SignatureVerifier。
+
+* TokenStore
+默认情况下, Edge Service将ID Token传递给Resource Server,所以只需要配置authIDTokenStore。
+
+
 * 权限配置
 
 fence 提供了两种方式进行权限配置。 一种是基于配置文件的,一种是基于 Annotation 。
@@ -205,15 +251,14 @@ Edge Service 是微服务接入层。 在[单体应用微服务改造](https://b
 
 * 配置
 
-Edge Service 需要配置 SignatureVerifier 等, 对用户会话进行认证。 
-```
+Edge Service 需要配置 EdgeTokenStore 等, 对用户会话进行认证。Edge Service 从HTTP头里面读取Access Token, 然后通过 EdgeTokenStore比对是否Access Token有效,如果有效,将对应的 ID Token传递到 Resource Server。 这里使用了 InMemoryEdgeTokenStore, 产品代码会多实例部署 Edge Service, 需要将其替换为 JDBC 或者 Redis 等实现。 
 
+```
 @Configuration
 public class AuthenticationConfiguration {
-  @Bean(name = "authSignatureVerifier")
-  public SignerVerifier authSignatureVerifier() {
-    return new MacSigner("Please change this key.");
+  @Bean(name = "authEdgeTokenStore")
+  public EdgeTokenStore authEdgeTokenStore() {
+    return new InMemoryEdgeTokenStore();
   }
 }
-
 ```
diff --git a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java
index b70273f..20e9f68 100644
--- a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java
+++ b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java
@@ -19,6 +19,9 @@ package org.apache.servicecomb.authentication;
 
 import java.util.Arrays;
 
+import org.apache.servicecomb.authentication.token.InMemorySessionIDTokenStore;
+import org.apache.servicecomb.authentication.token.JWTTokenStore;
+import org.apache.servicecomb.authentication.token.TokenStore;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Bean;
@@ -30,28 +33,41 @@ import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
 import org.springframework.security.jwt.crypto.sign.MacSigner;
+import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
+import org.springframework.security.jwt.crypto.sign.Signer;
 import org.springframework.security.jwt.crypto.sign.SignerVerifier;
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 
 @Configuration
 public class AuthenticationConfiguration {
-  @Autowired
-  @Qualifier("authPasswordEncoder")
-  private PasswordEncoder passwordEncoder;
-
   @Bean(name = "authPasswordEncoder")
   public PasswordEncoder authPasswordEncoder() {
     return new Pbkdf2PasswordEncoder();
   }
 
-  // If using RSA, need to configure authSigner and authSignatureVerifier separately. 
-  @Bean(name = {"authSigner", "authSignatureVerifier" })
+  @Bean(name = {"authSigner", "authSignatureVerifier"})
   public SignerVerifier authSignerVerifier() {
+    // If using RSA, need to configure authSigner and authSignatureVerifier separately. 
+    // If using MacSigner, need to protect the shared key by properly encryption.
     return new MacSigner("Please change this key.");
   }
 
+  @Bean(name = {"authAccessTokenStore", "authRefreshTokenStore"})
+  public TokenStore sessionIDTokenStore() {
+    // Use in memory store for testing. Need to implement JDBC or Redis SessionIDTokenStore in product. 
+    return new InMemorySessionIDTokenStore();
+  }
+
+  @Bean(name = "authIDTokenStore")
+  public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer,
+      @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) {
+    return new JWTTokenStore(signer, signerVerifier);
+  }
+
   @Bean(name = "authUserDetailsService")
-  public UserDetailsService authUserDetailsService() {
+  public UserDetailsService authUserDetailsService(
+      @Autowired @Qualifier("authPasswordEncoder") PasswordEncoder passwordEncoder) {
+    // Use in memory UserDetails, need to implement JDBC or others in product
     InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
     UserDetails uAdmin = new User("admin", passwordEncoder.encode("changeMyPassword"),
         Arrays.asList(new SimpleGrantedAuthority("ADMIN")));
diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java
index 13bfaba..e05545f 100644
--- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java
+++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java
@@ -17,13 +17,10 @@
 
 package org.apache.servicecomb.authentication;
 
-import org.apache.servicecomb.authentication.jwt.JWTClaims;
-import org.apache.servicecomb.authentication.jwt.JsonParser;
-import org.apache.servicecomb.authentication.server.Token;
+import org.apache.servicecomb.authentication.server.TokenResponse;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
-import org.springframework.security.jwt.JwtHelper;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
@@ -33,12 +30,16 @@ import org.springframework.web.client.HttpClientErrorException;
 public class AuthenticationTestCase implements TestCase {
   @Override
   public void run() {
-    testHanlderAuth();
-    testMethodAuth();
-  }
+    String accessToken = accessToken();
+    testHanlderAuth(accessToken);
+    testMethodAuth(accessToken);
 
+    accessToken = accessTokenByRefreshToken();
+    testHanlderAuth(accessToken);
+    testMethodAuth(accessToken);
+  }
 
-  private void testHanlderAuth() {
+  private String accessToken() {
     // get token
     MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
     map.add("grant_type", "password");
@@ -47,68 +48,55 @@ public class AuthenticationTestCase implements TestCase {
     HttpHeaders headers = new HttpHeaders();
     headers.setContentType(MediaType.MULTIPART_FORM_DATA);
 
-    Token token =
-        BootEventListener.authenticationServerTokenEndpoint.postForObject("/",
+    TokenResponse token =
+        BootEventListener.edgeServiceTokenEndpoint.postForObject("/",
             new HttpEntity<>(map, headers),
-            Token.class);
+            TokenResponse.class);
     TestMgr.check("bearer", token.getToken_type());
     TestMgr.check(true, token.getAccess_token().length() > 10);
+    return token.getAccess_token();
+  }
 
-    // get resources
-    headers = new HttpHeaders();
-    headers.add("Authorization", "Bearer " + token.getAccess_token());
-    headers.setContentType(MediaType.APPLICATION_JSON);
-    String name;
-    name = BootEventListener.resouceServerHandlerAuthEndpoint.postForObject("/everyoneSayHello?name=Hi",
-        new HttpEntity<>(headers),
-        String.class);
-    TestMgr.check("Hi", name);
-
-    name = BootEventListener.resouceServerHandlerAuthEndpoint.postForObject("/adminSayHello?name=Hi",
-        new HttpEntity<>(headers),
-        String.class);
-    TestMgr.check("Hi", name);
+  private String accessTokenByRefreshToken() {
+    // get token
+    MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
+    map.add("grant_type", "password");
+    map.add("username", "admin");
+    map.add("password", "changeMyPassword");
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
 
-    name = BootEventListener.resouceServerHandlerAuthEndpoint.postForObject("/guestOrAdminSayHello?name=Hi",
-        new HttpEntity<>(headers),
-        String.class);
-    TestMgr.check("Hi", name);
+    TokenResponse token =
+        BootEventListener.edgeServiceTokenEndpoint.postForObject("/",
+            new HttpEntity<>(map, headers),
+            TokenResponse.class);
+    TestMgr.check("bearer", token.getToken_type());
+    TestMgr.check(true, token.getAccess_token().length() > 10);
 
-    name = null;
-    try {
-      name = BootEventListener.resouceServerHandlerAuthEndpoint.postForObject("/guestSayHello?name=Hi",
-          new HttpEntity<>(headers),
-          String.class);
-    } catch (HttpClientErrorException e) {
-      TestMgr.check(403, e.getStatusCode().value());
-    }
-    TestMgr.check(null, name);
-    
     // refresh token
-    // get token
     map = new LinkedMultiValueMap<>();
     map.add("grant_type", "refresh_token");
     map.add("refresh_token", token.getRefresh_token());
-    map.add("access_token", token.getAccess_token());
-    headers = new HttpHeaders();
-    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
 
-    Token tokenNew =
-        BootEventListener.authenticationServerTokenEndpoint.postForObject("/",
+    TokenResponse tokenNew =
+        BootEventListener.edgeServiceTokenEndpoint.postForObject("/",
             new HttpEntity<>(map, headers),
-            Token.class);
+            TokenResponse.class);
     TestMgr.check(token.getToken_type(), tokenNew.getToken_type());
-   
-    JWTClaims claims = JsonParser.parse(JwtHelper.decode(token.getAccess_token()).getClaims(), JWTClaims.class);
-    JWTClaims newClaims = JsonParser.parse(JwtHelper.decode(tokenNew.getAccess_token()).getClaims(), JWTClaims.class);
-    TestMgr.check(claims.getJti(), newClaims.getJti());
-    TestMgr.check(claims.getIat() < newClaims.getIat(), true);
+    TestMgr.check(token.getRefresh_token(), tokenNew.getRefresh_token());
+    TestMgr.check(token.getAccess_token().equals(tokenNew.getAccess_token()), false);
+    TestMgr.check(token.getId_token().equals(tokenNew.getId_token()), false);
+
+    return tokenNew.getAccess_token();
+  }
 
+  private void testHanlderAuth(String accessToken) {
     // get resources
+    HttpHeaders headers = new HttpHeaders();
     headers = new HttpHeaders();
-    headers.add("Authorization", "Bearer " + tokenNew.getAccess_token());
+    headers.add("Authorization", "Bearer " + accessToken);
     headers.setContentType(MediaType.APPLICATION_JSON);
-
+    String name;
     name = BootEventListener.resouceServerHandlerAuthEndpoint.postForObject("/everyoneSayHello?name=Hi",
         new HttpEntity<>(headers),
         String.class);
@@ -135,27 +123,10 @@ public class AuthenticationTestCase implements TestCase {
     TestMgr.check(null, name);
   }
 
-
-  private void testMethodAuth() {
-    // get token
-    MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
-    map.add("grant_type", "password");
-    map.add("username", "admin");
-    map.add("password", "changeMyPassword");
-    HttpHeaders headers = new HttpHeaders();
-    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
-
-    Token token =
-        BootEventListener.authenticationServerTokenEndpoint.postForObject("/",
-            new HttpEntity<>(map, headers),
-            Token.class);
-    TestMgr.check("bearer", token.getToken_type());
-    TestMgr.check(true, token.getAccess_token().length() > 10);
-    TestMgr.check(true, token.getRefresh_token().length() > 10);
-
+  private void testMethodAuth(String accessToken) {
     // get resources
-    headers = new HttpHeaders();
-    headers.add("Authorization", "Bearer " + token.getAccess_token());
+    HttpHeaders headers = new HttpHeaders();
+    headers.add("Authorization", "Bearer " + accessToken);
     headers.setContentType(MediaType.APPLICATION_JSON);
     String name;
     name = BootEventListener.resouceServerMethodAuthEndpoint.postForObject("/everyoneSayHello?name=Hi",
diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/BootEventListener.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/BootEventListener.java
index 08d1bb3..74db45a 100644
--- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/BootEventListener.java
+++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/BootEventListener.java
@@ -23,7 +23,8 @@ import org.springframework.stereotype.Component;
 @Component
 public class BootEventListener implements BootListener {
   public static GateRestTemplate authenticationServerTokenEndpoint;
-  public static GateRestTemplate gateEndpoint;
+  public static GateRestTemplate edgeService;
+  public static GateRestTemplate edgeServiceTokenEndpoint;
   public static GateRestTemplate resouceServerHandlerAuthEndpoint;
   public static GateRestTemplate resouceServerMethodAuthEndpoint;
   
@@ -32,8 +33,10 @@ public class BootEventListener implements BootListener {
     if (EventType.AFTER_REGISTRY.equals(event.getEventType())) {
       authenticationServerTokenEndpoint =
           GateRestTemplate.createEdgeRestTemplate("edge-service", "authentication-server", "TokenEndpoint").init();
-      gateEndpoint =
+      edgeService =
           GateRestTemplate.createEdgeRestTemplate("edge-service", null, null).init();
+      edgeServiceTokenEndpoint =
+          GateRestTemplate.createEdgeRestTemplate("edge-service", "edge-service", "TokenEndpoint").init();
       resouceServerHandlerAuthEndpoint =
           GateRestTemplate.createEdgeRestTemplate("edge-service", "resource-server", "HandlerAuthEndpoint").init();
       resouceServerMethodAuthEndpoint =
diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/GateRestTemplate.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/GateRestTemplate.java
index 7a6709f..df981e5 100644
--- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/GateRestTemplate.java
+++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/GateRestTemplate.java
@@ -100,6 +100,15 @@ public class GateRestTemplate extends RestTemplate {
             DefinitionConst.VERSION_RULE_ALL);
     MicroserviceVersionMeta microserviceVersionMeta = microserviceVersionRule.getLatestMicroserviceVersion();
     SchemaMeta schemaMeta = microserviceVersionMeta.getMicroserviceMeta().ensureFindSchemaMeta(schemaId);
+
+    if(producerName.equals(gateName)) {
+      return String
+          .format("%s://%s:%d/%s",
+              urlSchema,
+              edgeAddress.getHostOrIp(),
+              edgeAddress.getPort(),
+              schemaMeta.getSwagger().getBasePath());
+    }
     return String
         .format("%s://%s:%d/api/%s%s",
             urlSchema,
diff --git a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java
index 510d821..e9187b7 100644
--- a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java
+++ b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java
@@ -17,15 +17,15 @@
 
 package org.apache.servicecomb.authentication.gateway;
 
+import org.apache.servicecomb.authentication.edge.EdgeTokenStore;
+import org.apache.servicecomb.authentication.edge.InMemoryEdgeTokenStore;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.security.jwt.crypto.sign.MacSigner;
-import org.springframework.security.jwt.crypto.sign.SignerVerifier;
 
 @Configuration
 public class AuthenticationConfiguration {
-  @Bean(name = "authSignatureVerifier")
-  public SignerVerifier authSignatureVerifier() {
-    return new MacSigner("Please change this key.");
+  @Bean(name = "authEdgeTokenStore")
+  public EdgeTokenStore authEdgeTokenStore() {
+    return new InMemoryEdgeTokenStore();
   }
 }
diff --git a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java
index 2074321..e69217f 100644
--- a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java
+++ b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java
@@ -17,15 +17,30 @@
 
 package org.apache.servicecomb.authentication.resource;
 
+import org.apache.servicecomb.authentication.token.JWTTokenStore;
+import org.apache.servicecomb.authentication.token.TokenStore;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.jwt.crypto.sign.MacSigner;
 import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
+import org.springframework.security.jwt.crypto.sign.Signer;
+import org.springframework.security.jwt.crypto.sign.SignerVerifier;
 
 @Configuration
 public class AuthenticationConfiguration {
-  @Bean(name = "authSignatureVerifier")
-  public SignatureVerifier authSignatureVerifier() {
+  @Bean(name = {"authSigner", "authSignatureVerifier"})
+  public SignerVerifier authSignerVerifier() {
+    // If using RSA, need to configure authSigner and authSignatureVerifier separately. 
+    // If using MacSigner, need to protect the shared key by properly encryption.
     return new MacSigner("Please change this key.");
   }
+
+  @Bean(name = "authIDTokenStore")
+  public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer,
+      @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) {
+    return new JWTTokenStore(signer, signerVerifier);
+  }
+
 }