keycloak-aplcache

IDToken setup

2/26/2014 9:04:42 PM

Details

diff --git a/core/src/main/java/org/keycloak/KeycloakAuthenticatedSession.java b/core/src/main/java/org/keycloak/KeycloakAuthenticatedSession.java
index 658e19a..6593f2d 100755
--- a/core/src/main/java/org/keycloak/KeycloakAuthenticatedSession.java
+++ b/core/src/main/java/org/keycloak/KeycloakAuthenticatedSession.java
@@ -2,6 +2,7 @@ package org.keycloak;
 
 import org.keycloak.adapters.ResourceMetadata;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
 
 import java.io.Serializable;
 
@@ -12,14 +13,18 @@ import java.io.Serializable;
 public class KeycloakAuthenticatedSession implements Serializable {
     protected String tokenString;
     protected AccessToken token;
+    protected IDToken idToken;
+    protected String idTokenString;
     protected transient ResourceMetadata metadata;
 
     public KeycloakAuthenticatedSession() {
     }
 
-    public KeycloakAuthenticatedSession(String tokenString, AccessToken token, ResourceMetadata metadata) {
+    public KeycloakAuthenticatedSession(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata) {
         this.tokenString = tokenString;
         this.token = token;
+        this.idToken = idToken;
+        this.idTokenString = idTokenString;
         this.metadata = metadata;
     }
 
@@ -38,4 +43,12 @@ public class KeycloakAuthenticatedSession implements Serializable {
     public void setMetadata(ResourceMetadata metadata) {
         this.metadata = metadata;
     }
+
+    public IDToken getIdToken() {
+        return idToken;
+    }
+
+    public String getIdTokenString() {
+        return idTokenString;
+    }
 }
diff --git a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
index 196579d..16b6a96 100755
--- a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
+++ b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
@@ -6,6 +6,7 @@ import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.keycloak.KeycloakAuthenticatedSession;
 import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.representations.IDToken;
 import org.keycloak.util.JsonSerialization;
 
 import javax.servlet.http.HttpServletRequest;
@@ -35,6 +36,12 @@ public class CustomerDatabaseClient {
         }
     }
 
+    public static IDToken getIDToken(HttpServletRequest req) {
+        KeycloakAuthenticatedSession session = (KeycloakAuthenticatedSession) req.getAttribute(KeycloakAuthenticatedSession.class.getName());
+        return session.getIdToken();
+
+    }
+
     public static List<String> getCustomers(HttpServletRequest req) throws Failure {
         KeycloakAuthenticatedSession session = (KeycloakAuthenticatedSession) req.getAttribute(KeycloakAuthenticatedSession.class.getName());
 
diff --git a/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp b/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp
index 0e9ab0e..f966321 100755
--- a/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp
+++ b/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp
@@ -2,6 +2,7 @@
          pageEncoding="ISO-8859-1" %>
 <%@ page import="org.keycloak.example.CustomerDatabaseClient" %>
 <%@ page import="org.keycloak.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.representations.IDToken" %>
 <html>
 <head>
     <title>Customer View Page</title>
@@ -11,11 +12,18 @@
     String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth/rest/realms/demo/tokens/logout")
             .queryParam("redirect_uri", "http://localhost:8080/customer-portal").build().toString();
     String acctUri = "http://localhost:8080/auth/rest/realms/demo/account";
+    IDToken idToken = CustomerDatabaseClient.getIDToken(request);
 %>
 <p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="<%=logoutUri%>">logout</a> | <a
         href="<%=acctUri%>">manage acct</a></p>
-User <b><%=request.getUserPrincipal().getName()%>
+Servlet User Principal <b><%=request.getUserPrincipal().getName()%>
 </b> made this request.
+<p><b>Caller IDToken values</b> (<i>You can specify what is returned in IDToken in the customer-portal claims page in the admin console</i>:</p>
+<p>Username: <%=idToken.getPreferredUsername()%></p>
+<p>Email: <%=idToken.getEmail()%></p>
+<p>Full Name: <%=idToken.getName()%></p>
+<p>First: <%=idToken.getGivenName()%></p>
+<p>Last: <%=idToken.getFamilyName()%></p>
 <h2>Customer Listing</h2>
 <%
     java.util.List<String> list = null;
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
index 8716e0e..7e14a79 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
@@ -7,6 +7,7 @@ import org.keycloak.adapters.config.RealmConfiguration;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
 import org.jboss.logging.Logger;
+import org.keycloak.representations.IDToken;
 
 import java.io.IOException;
 
@@ -24,8 +25,8 @@ public class RefreshableKeycloakSession extends KeycloakAuthenticatedSession {
     public RefreshableKeycloakSession() {
     }
 
-    public RefreshableKeycloakSession(String tokenString, AccessToken token, ResourceMetadata metadata, RealmConfiguration realmConfiguration, String refreshToken) {
-        super(tokenString, token, metadata);
+    public RefreshableKeycloakSession(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata, RealmConfiguration realmConfiguration, String refreshToken) {
+        super(tokenString, token, idTokenString, idToken, metadata);
         this.realmConfiguration = realmConfiguration;
         this.refreshToken = refreshToken;
     }
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
index 86933f0..93be4f4 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
@@ -106,7 +106,7 @@ public class CatalinaBearerTokenAuthenticator {
         principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
         request.setUserPrincipal(principal);
         request.setAuthType("OAUTH_BEARER");
-        KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, resourceMetadata);
+        KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, null, null, resourceMetadata);
         request.setAttribute(KeycloakAuthenticatedSession.class.getName(), skSession);
 
         return true;
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index 430b3bd..b6e8af9 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -262,7 +262,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
             Session session = request.getSessionInternal(true);
             session.setPrincipal(principal);
             session.setAuthType("OAUTH");
-            KeycloakAuthenticatedSession skSession = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), resourceMetadata, realmConfiguration, oauth.getRefreshToken());
+            KeycloakAuthenticatedSession skSession = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfiguration, oauth.getRefreshToken());
             session.setNote(KeycloakAuthenticatedSession.class.getName(), skSession);
 
             String username = token.getSubject();
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
index 35cb609..1cec19f 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
@@ -5,8 +5,10 @@ import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
 import org.keycloak.adapters.TokenGrantRequest;
 import org.keycloak.adapters.config.RealmConfiguration;
+import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.IDToken;
 import org.keycloak.util.KeycloakUriBuilder;
 
 import javax.servlet.http.Cookie;
@@ -28,6 +30,8 @@ public class ServletOAuthLogin {
     protected RealmConfiguration realmInfo;
     protected int redirectPort;
     protected String tokenString;
+    protected String idTokenString;
+    protected IDToken idToken;
     protected AccessToken token;
     protected String refreshToken;
 
@@ -50,6 +54,14 @@ public class ServletOAuthLogin {
         return refreshToken;
     }
 
+    public String getIdTokenString() {
+        return idTokenString;
+    }
+
+    public IDToken getIdToken() {
+        return idToken;
+    }
+
     public RealmConfiguration getRealmInfo() {
         return realmInfo;
     }
@@ -246,8 +258,17 @@ public class ServletOAuthLogin {
         }
 
         tokenString = tokenResponse.getToken();
+        idTokenString = tokenResponse.getIdToken();
         try {
             token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
+            if (idTokenString != null) {
+                JWSInput input = new JWSInput(idTokenString);
+                try {
+                    idToken = input.readJsonContent(IDToken.class);
+                } catch (IOException e) {
+                    throw new VerificationException();
+                }
+            }
             log.debug("Token Verification succeeded!");
         } catch (VerificationException e) {
             log.error("failed verification of token");
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
index 6e22636..88d29c4 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
@@ -67,7 +67,7 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
 
         try {
             AccessToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
-            KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, resourceMetadata);
+            KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, null, null, resourceMetadata);
             ResteasyProviderFactory.pushContext(KeycloakAuthenticatedSession.class, skSession);
             String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
 
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
index 5b9ef86..ffa9505 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
@@ -94,7 +94,7 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism 
 
     protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, OAuthAuthenticator oauth) {
         final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), null);
-        RefreshableKeycloakSession session = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), resourceMetadata, realmConfig, oauth.getRefreshToken());
+        RefreshableKeycloakSession session = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfig, oauth.getRefreshToken());
         KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
         securityContext.authenticationComplete(account, "KEYCLOAK", true);
         login(exchange, account);
@@ -107,7 +107,7 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism 
 
     protected void completeAuthentication(SecurityContext securityContext, BearerTokenAuthenticator bearer) {
         final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), bearer.getSurrogate());
-        RefreshableKeycloakSession session = new RefreshableKeycloakSession(bearer.getTokenString(), bearer.getToken(), resourceMetadata, realmConfig, null);
+        RefreshableKeycloakSession session = new RefreshableKeycloakSession(bearer.getTokenString(), bearer.getToken(), null, null, resourceMetadata, realmConfig, null);
         KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
         securityContext.authenticationComplete(account, "KEYCLOAK", false);
     }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
index d21e5d3..29bb28a 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
@@ -12,8 +12,10 @@ import org.keycloak.RSATokenVerifier;
 import org.keycloak.adapters.config.RealmConfiguration;
 import org.keycloak.VerificationException;
 import org.keycloak.adapters.TokenGrantRequest;
+import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.IDToken;
 import org.keycloak.util.KeycloakUriBuilder;
 
 import java.io.IOException;
@@ -31,6 +33,8 @@ public class OAuthAuthenticator {
     protected RealmConfiguration realmInfo;
     protected int sslRedirectPort;
     protected String tokenString;
+    protected String idTokenString;
+    protected IDToken idToken;
     protected AccessToken token;
     protected HttpServerExchange exchange;
     protected KeycloakChallenge challenge;
@@ -58,6 +62,22 @@ public class OAuthAuthenticator {
         return refreshToken;
     }
 
+    public String getIdTokenString() {
+        return idTokenString;
+    }
+
+    public void setIdTokenString(String idTokenString) {
+        this.idTokenString = idTokenString;
+    }
+
+    public IDToken getIdToken() {
+        return idToken;
+    }
+
+    public void setIdToken(IDToken idToken) {
+        this.idToken = idToken;
+    }
+
     protected String getRequestUrl() {
         KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
                 .replaceQuery(exchange.getQueryString());
@@ -255,8 +275,17 @@ public class OAuthAuthenticator {
 
         tokenString = tokenResponse.getToken();
         refreshToken = tokenResponse.getRefreshToken();
+        idTokenString = tokenResponse.getIdToken();
         try {
             token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
+            if (idTokenString != null) {
+                JWSInput input = new JWSInput(idTokenString);
+                try {
+                    idToken = input.readJsonContent(IDToken.class);
+                } catch (IOException e) {
+                    throw new VerificationException();
+                }
+            }
             log.debug("Token Verification succeeded!");
         } catch (VerificationException e) {
             log.error("failed verification of token");
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 080d52f..8b4e8a6 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -6,6 +6,8 @@ import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClaimMask;
+import org.keycloak.models.ClaimRequesterModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
@@ -13,7 +15,9 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.AccessScope;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.IDToken;
 import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.ClaimRepresentation;
 import org.keycloak.util.Base64Url;
 import org.keycloak.util.JsonSerialization;
 
@@ -178,7 +182,9 @@ public class TokenManager {
 
             }
         }
-        AccessToken accessToken = initToken(realm, client, user);
+        ClaimRequesterModel claimRequesterModel = getClaimRequester(realm, client);
+
+        AccessToken accessToken = initToken(realm, claimRequesterModel, client, user);
         accessToken.setRealmAccess(refreshToken.getRealmAccess());
         accessToken.setResourceAccess(refreshToken.getResourceAccess());
         return accessToken;
@@ -188,6 +194,12 @@ public class TokenManager {
         return createClientAccessToken(scopeParam, realm, client, user, new LinkedList<RoleModel>(), new MultivaluedHashMap<String, RoleModel>());
     }
 
+    protected ClaimRequesterModel getClaimRequester(RealmModel realm, UserModel client) {
+        ClaimRequesterModel model = realm.getApplicationByName(client.getLoginName());
+        if (model != null) return model;
+        return realm.getOAuthClient(client.getLoginName());
+    }
+
 
     public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, UserModel client, UserModel user, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
         AccessScope scopeMap = null;
@@ -196,6 +208,7 @@ public class TokenManager {
 
         Set<RoleModel> roleMappings = realm.getRoleMappings(user);
         Set<RoleModel> scopeMappings = realm.getScopeMappings(client);
+        ClaimRequesterModel claimRequesterModel = getClaimRequester(realm, client);
         ApplicationModel clientApp = realm.getApplicationByName(client.getLoginName());
         Set<RoleModel> clientAppRoles = clientApp == null ? null : clientApp.getRoles();
         if (clientAppRoles != null) scopeMappings.addAll(clientAppRoles);
@@ -222,7 +235,7 @@ public class TokenManager {
             }
         }
 
-        AccessToken token = initToken(realm, client, user);
+        AccessToken token = initToken(realm, claimRequesterModel, client, user);
 
         if (realmRolesRequested.size() > 0) {
             for (RoleModel role : realmRolesRequested) {
@@ -240,7 +253,42 @@ public class TokenManager {
         return token;
     }
 
-    protected AccessToken initToken(RealmModel realm, UserModel client, UserModel user) {
+    public void initClaims(IDToken token, ClaimRequesterModel model, UserModel user) {
+        if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) {
+            token.setPreferredUsername(user.getLoginName());
+        }
+        if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
+            token.setEmail(user.getEmail());
+            token.setEmailVerified(user.isEmailVerified());
+        }
+        if (ClaimMask.hasName(model.getAllowedClaimsMask())) {
+            token.setFamilyName(user.getLastName());
+            token.setGivenName(user.getFirstName());
+            StringBuilder fullName = new StringBuilder();
+            if (user.getFirstName() != null) fullName.append(user.getFirstName()).append(" ");
+            if (user.getLastName() != null) fullName.append(user.getLastName());
+            token.setName(fullName.toString());
+        }
+    }
+
+    protected IDToken initIDToken(RealmModel realm, ClaimRequesterModel claimer, UserModel client, UserModel user) {
+        IDToken token = new IDToken();
+        token.id(KeycloakModelUtils.generateId());
+        token.subject(user.getId());
+        token.audience(realm.getName());
+        token.issuedNow();
+        token.issuedFor(client.getLoginName());
+        token.issuer(realm.getName());
+        if (realm.getAccessTokenLifespan() > 0) {
+            token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
+        }
+        initClaims(token, claimer, user);
+        return token;
+    }
+
+
+
+    protected AccessToken initToken(RealmModel realm, ClaimRequesterModel claimer, UserModel client, UserModel user) {
         AccessToken token = new AccessToken();
         token.id(KeycloakModelUtils.generateId());
         token.subject(user.getId());
@@ -250,12 +298,12 @@ public class TokenManager {
         token.issuer(realm.getName());
         if (realm.getAccessTokenLifespan() > 0) {
             token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
-            logger.info("Access Token expiration: " + token.getExpiration());
         }
         Set<String> allowedOrigins = client.getWebOrigins();
         if (allowedOrigins != null) {
             token.setAllowedOrigins(allowedOrigins);
         }
+        initClaims(token, claimer, user);
         return token;
     }
 
@@ -324,6 +372,7 @@ public class TokenManager {
         RealmModel realm;
         AccessToken accessToken;
         RefreshToken refreshToken;
+        IDToken idToken;
 
         public AccessTokenResponseBuilder(RealmModel realm) {
             this.realm = realm;
@@ -354,8 +403,53 @@ public class TokenManager {
             return this;
         }
 
+        public AccessTokenResponseBuilder generateIDToken() {
+            if (accessToken == null) {
+                throw new IllegalStateException("accessToken not set");
+            }
+            idToken = new IDToken();
+            idToken.id(KeycloakModelUtils.generateId());
+            idToken.subject(accessToken.getSubject());
+            idToken.audience(realm.getName());
+            idToken.issuedNow();
+            idToken.issuedFor(accessToken.getIssuedFor());
+            idToken.issuer(accessToken.getIssuer());
+            if (realm.getAccessTokenLifespan() > 0) {
+                idToken.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
+            }
+            idToken.setPreferredUsername(accessToken.getPreferredUsername());
+            idToken.setGivenName(accessToken.getGivenName());
+            idToken.setMiddleName(accessToken.getMiddleName());
+            idToken.setFamilyName(accessToken.getFamilyName());
+            idToken.setName(accessToken.getName());
+            idToken.setNickName(accessToken.getNickName());
+            idToken.setGender(accessToken.getGender());
+            idToken.setPicture(accessToken.getPicture());
+            idToken.setProfile(accessToken.getProfile());
+            idToken.setWebsite(accessToken.getWebsite());
+            idToken.setBirthdate(accessToken.getBirthdate());
+            idToken.setEmail(accessToken.getEmail());
+            idToken.setEmailVerified(accessToken.getEmailVerified());
+            idToken.setLocale(accessToken.getLocale());
+            idToken.setFormattedAddress(accessToken.getFormattedAddress());
+            idToken.setAddress(accessToken.getAddress());
+            idToken.setStreetAddress(accessToken.getStreetAddress());
+            idToken.setLocality(accessToken.getLocality());
+            idToken.setRegion(accessToken.getRegion());
+            idToken.setPostalCode(accessToken.getPostalCode());
+            idToken.setCountry(accessToken.getCountry());
+            idToken.setPhoneNumber(accessToken.getPhoneNumber());
+            idToken.setPhoneNumberVerified(accessToken.getPhoneNumberVerified());
+            idToken.setZoneinfo(accessToken.getZoneinfo());
+            return this;
+        }
+
         public AccessTokenResponse build() {
             AccessTokenResponse res = new AccessTokenResponse();
+            if (idToken != null) {
+                String encodedToken = new JWSBuilder().jsonContent(idToken).rsa256(realm.getPrivateKey());
+                res.setIdToken(encodedToken);
+            }
             if (accessToken != null) {
                 String encodedToken = new JWSBuilder().jsonContent(accessToken).rsa256(realm.getPrivateKey());
                 res.setToken(encodedToken);
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 44ac6e2..ba6ce97 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -159,7 +159,9 @@ public class TokenService {
         }
         String scope = form.getFirst("scope");
         AccessTokenResponse res = tokenManager.responseBuilder(realm)
-                .generateAccessToken(scope, client, user).build();
+                .generateAccessToken(scope, client, user)
+                .generateIDToken()
+                .build();
         return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
     }
 
@@ -188,6 +190,7 @@ public class TokenService {
 
         AccessTokenResponse res = tokenManager.responseBuilder(realm)
                                               .accessToken(accessToken)
+                                              .generateIDToken()
                                               .generateRefreshToken().build();
         return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
     }
@@ -410,6 +413,7 @@ public class TokenService {
         logger.debug("accessRequest SUCCESS");
         AccessTokenResponse res = tokenManager.responseBuilder(realm)
                                               .accessToken(accessCode.getToken())
+                                              .generateIDToken()
                                               .generateRefreshToken().build();
 
         return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build();