keycloak-uncached

Changes

.gitignore 32(+32 -0)

core/pom.xml 60(+60 -0)

pom.xml 301(+301 -0)

services/pom.xml 111(+111 -0)

Details

.gitignore 32(+32 -0)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54602e4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+# Intellij
+###################
+.idea
+*.iml
+
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+
+

core/pom.xml 60(+60 -0)

diff --git a/core/pom.xml b/core/pom.xml
new file mode 100755
index 0000000..56f2396
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<project>
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-core</artifactId>
+    <name>Keycloak Core</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>tjws</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/core/src/main/java/org/keycloak/AbstractOAuthClient.java b/core/src/main/java/org/keycloak/AbstractOAuthClient.java
new file mode 100755
index 0000000..180bd5f
--- /dev/null
+++ b/core/src/main/java/org/keycloak/AbstractOAuthClient.java
@@ -0,0 +1,162 @@
+package org.keycloak;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.representations.AccessTokenResponse;
+import org.jboss.resteasy.util.BasicAuthHelper;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.security.KeyStore;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AbstractOAuthClient
+{
+   protected String clientId;
+   protected String password;
+   protected KeyStore truststore;
+   protected String authUrl;
+   protected String codeUrl;
+   protected String stateCookieName = "OAuth_Token_Request_State";
+   protected Client client;
+   protected final AtomicLong counter = new AtomicLong();
+
+   protected String getStateCode()
+   {
+      return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
+   }
+
+   public void start()
+   {
+      if (client == null)
+      {
+         client = new ResteasyClientBuilder().trustStore(truststore)
+                 .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
+                 .connectionPoolSize(10)
+                 .build();
+      }
+   }
+
+   public void stop()
+   {
+      client.close();
+   }
+
+   public String getClientId()
+   {
+      return clientId;
+   }
+
+   public void setClientId(String clientId)
+   {
+      this.clientId = clientId;
+   }
+
+   public String getPassword()
+   {
+      return password;
+   }
+
+   public void setPassword(String password)
+   {
+      this.password = password;
+   }
+
+   public KeyStore getTruststore()
+   {
+      return truststore;
+   }
+
+   public void setTruststore(KeyStore truststore)
+   {
+      this.truststore = truststore;
+   }
+
+   public String getAuthUrl()
+   {
+      return authUrl;
+   }
+
+   public void setAuthUrl(String authUrl)
+   {
+      this.authUrl = authUrl;
+   }
+
+   public String getCodeUrl()
+   {
+      return codeUrl;
+   }
+
+   public void setCodeUrl(String codeUrl)
+   {
+      this.codeUrl = codeUrl;
+   }
+
+   public String getStateCookieName()
+   {
+      return stateCookieName;
+   }
+
+   public void setStateCookieName(String stateCookieName)
+   {
+      this.stateCookieName = stateCookieName;
+   }
+
+   public Client getClient()
+   {
+      return client;
+   }
+
+   public void setClient(Client client)
+   {
+      this.client = client;
+   }
+
+   public String resolveBearerToken(String redirectUri, String code)
+   {
+      redirectUri = stripOauthParametersFromRedirect(redirectUri);
+      String authHeader = BasicAuthHelper.createHeader(clientId, password);
+      Form codeForm = new Form()
+              .param("grant_type", "authorization_code")
+              .param("code", code)
+              .param("redirect_uri", redirectUri);
+      Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
+      try
+      {
+         if (res.getStatus() == 400)
+         {
+            throw new BadRequestException();
+         }
+         else if (res.getStatus() != 200)
+         {
+            throw new InternalServerErrorException(new Exception("Unknown error when getting acess token"));
+         }
+         AccessTokenResponse tokenResponse = res.readEntity(AccessTokenResponse.class);
+         return tokenResponse.getToken();
+      }
+      finally
+      {
+         res.close();
+      }
+   }
+
+   protected String stripOauthParametersFromRedirect(String uri)
+   {
+      System.out.println("******************** redirect_uri: " + uri);
+      UriBuilder builder = UriBuilder.fromUri(uri)
+              .replaceQueryParam("code", null)
+              .replaceQueryParam("state", null);
+      return builder.build().toString();
+   }
+
+}
diff --git a/core/src/main/java/org/keycloak/BouncyIntegration.java b/core/src/main/java/org/keycloak/BouncyIntegration.java
new file mode 100755
index 0000000..7d1635b
--- /dev/null
+++ b/core/src/main/java/org/keycloak/BouncyIntegration.java
@@ -0,0 +1,22 @@
+package org.keycloak;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.security.Security;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class BouncyIntegration
+{
+   static
+   {
+      if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
+   }
+
+   public static void init()
+   {
+      // empty, the static class does it
+   }
+}
diff --git a/core/src/main/java/org/keycloak/DerUtils.java b/core/src/main/java/org/keycloak/DerUtils.java
new file mode 100755
index 0000000..ca92418
--- /dev/null
+++ b/core/src/main/java/org/keycloak/DerUtils.java
@@ -0,0 +1,68 @@
+package org.keycloak;
+
+import java.io.DataInputStream;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * Extract PrivateKey, PublicKey, and X509Certificate from a DER encoded byte array or file.  Usually
+ * generated from openssl
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DerUtils
+{
+   static
+   {
+      BouncyIntegration.init();
+   }
+
+   public static PrivateKey decodePrivateKey(InputStream is)
+           throws Exception
+   {
+
+      DataInputStream dis = new DataInputStream(is);
+      byte[] keyBytes = new byte[dis.available()];
+      dis.readFully(keyBytes);
+      dis.close();
+
+      PKCS8EncodedKeySpec spec =
+              new PKCS8EncodedKeySpec(keyBytes);
+      KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
+      return kf.generatePrivate(spec);
+   }
+
+   public static PublicKey decodePublicKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
+   {
+      X509EncodedKeySpec spec =
+              new X509EncodedKeySpec(der);
+      KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
+      return kf.generatePublic(spec);
+   }
+
+   public static X509Certificate decodeCertificate(InputStream is) throws Exception
+   {
+      CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
+      X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+      is.close();
+      return cert;
+   }
+
+   public static PrivateKey decodePrivateKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
+   {
+      PKCS8EncodedKeySpec spec =
+              new PKCS8EncodedKeySpec(der);
+      KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
+      return kf.generatePrivate(spec);
+   }
+}
diff --git a/core/src/main/java/org/keycloak/EnvUtil.java b/core/src/main/java/org/keycloak/EnvUtil.java
new file mode 100755
index 0000000..66917f0
--- /dev/null
+++ b/core/src/main/java/org/keycloak/EnvUtil.java
@@ -0,0 +1,36 @@
+package org.keycloak;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class EnvUtil
+{
+   private static final Pattern p = Pattern.compile("[$][{]([^}]+)[}]");
+
+   /**
+    * Replaces any ${} strings with their corresponding environent variable.
+    *
+    * @param val
+    * @return
+    */
+   public static String replace(String val)
+   {
+      Matcher matcher = p.matcher(val);
+      StringBuffer buf = new StringBuffer();
+      while (matcher.find())
+      {
+         String envVar = matcher.group(1);
+         String envVal = System.getProperty(envVar);
+         if (envVal == null) envVal = "NOT-SPECIFIED";
+         matcher.appendReplacement(buf, envVal.replace("\\", "\\\\"));
+      }
+      matcher.appendTail(buf);
+      return buf.toString();
+   }
+}
+
+
diff --git a/core/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java b/core/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
new file mode 100755
index 0000000..69c7b0f
--- /dev/null
+++ b/core/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
@@ -0,0 +1,129 @@
+package org.keycloak.jaxrs;
+
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeyPrincipal;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import java.io.IOException;
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Priority(Priorities.AUTHENTICATION)
+public class JaxrsBearerTokenFilter implements ContainerRequestFilter
+{
+   protected ResourceMetadata resourceMetadata;
+   private static Logger log = Logger.getLogger(JaxrsBearerTokenFilter.class);
+
+   public JaxrsBearerTokenFilter(ResourceMetadata resourceMetadata)
+   {
+      this.resourceMetadata = resourceMetadata;
+   }
+
+   protected void challengeResponse(ContainerRequestContext request, String error, String description)
+   {
+      StringBuilder header = new StringBuilder("Bearer realm=\"");
+      header.append(resourceMetadata.getRealm()).append("\"");
+      if (error != null)
+      {
+         header.append(", error=\"").append(error).append("\"");
+      }
+      if (description != null)
+      {
+         header.append(", error_description=\"").append(description).append("\"");
+      }
+      request.abortWith(Response.status(401).header("WWW-Authenticate", header.toString()).build());
+      return;
+   }
+
+   @Context
+   protected SecurityContext securityContext;
+
+   @Override
+   public void filter(ContainerRequestContext request) throws IOException
+   {
+      String authHeader = request.getHeaderString(HttpHeaders.AUTHORIZATION);
+      if (authHeader == null)
+      {
+         challengeResponse(request, null, null);
+         return;
+      }
+
+      String[] split = authHeader.trim().split("\\s+");
+      if (split == null || split.length != 2) challengeResponse(request, null, null);
+      if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(request, null, null);
+
+
+      String tokenString = split[1];
+
+
+      try
+      {
+         SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
+         SkeletonKeySession skSession = new SkeletonKeySession(tokenString, resourceMetadata);
+         ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+         String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
+
+         final SkeletonKeyPrincipal principal = new SkeletonKeyPrincipal(token.getPrincipal(), callerPrincipal);
+         final boolean isSecure = securityContext.isSecure();
+         final SkeletonKeyToken.Access access;
+         if (resourceMetadata.getResourceName() != null)
+         {
+            access = token.getResourceAccess(resourceMetadata.getResourceName());
+         }
+         else
+         {
+            access = token.getRealmAccess();
+         }
+         SecurityContext ctx = new SecurityContext()
+         {
+            @Override
+            public Principal getUserPrincipal()
+            {
+               return principal;
+            }
+
+            @Override
+            public boolean isUserInRole(String role)
+            {
+               if (access.getRoles() == null) return false;
+               return access.getRoles().contains(role);
+            }
+
+            @Override
+            public boolean isSecure()
+            {
+               return isSecure;
+            }
+
+            @Override
+            public String getAuthenticationScheme()
+            {
+               return "OAUTH_BEARER";
+            }
+         };
+         request.setSecurityContext(ctx);
+      }
+      catch (VerificationException e)
+      {
+         log.error("Failed to verify token", e);
+         challengeResponse(request, "invalid_token", e.getMessage());
+      }
+   }
+
+}
diff --git a/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java b/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java
new file mode 100755
index 0000000..acc99a5
--- /dev/null
+++ b/core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java
@@ -0,0 +1,55 @@
+package org.keycloak.jaxrs;
+
+import org.keycloak.AbstractOAuthClient;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+/**
+ * Helper code to obtain oauth access tokens via browser redirects
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JaxrsOAuthClient extends AbstractOAuthClient
+{
+   public Response redirect(UriInfo uriInfo, String redirectUri)
+   {
+      String state = getStateCode();
+
+      URI url = UriBuilder.fromUri(authUrl)
+              .queryParam("client_id", clientId)
+              .queryParam("redirect_uri", redirectUri)
+              .queryParam("state", state)
+              .build();
+      NewCookie cookie = new NewCookie(stateCookieName, state, uriInfo.getBaseUri().getPath(), null, null, -1, true);
+      return Response.status(302)
+              .location(url)
+              .cookie(cookie).build();
+   }
+
+   public String getBearerToken(UriInfo uriInfo, HttpHeaders headers) throws BadRequestException, InternalServerErrorException
+   {
+      String error = uriInfo.getQueryParameters().getFirst("error");
+      if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
+      Cookie stateCookie = headers.getCookies().get(stateCookieName);
+      if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));;
+
+      String state = uriInfo.getQueryParameters().getFirst("state");
+      if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
+      if (!state.equals(stateCookie.getValue()))
+      {
+         throw new BadRequestException(new Exception("state parameter invalid"));
+      }
+      String code = uriInfo.getQueryParameters().getFirst("code");
+      if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
+      return resolveBearerToken(uriInfo.getRequestUri().toString(), code);
+   }
+}
diff --git a/core/src/main/java/org/keycloak/PemUtils.java b/core/src/main/java/org/keycloak/PemUtils.java
new file mode 100755
index 0000000..5f2fceb
--- /dev/null
+++ b/core/src/main/java/org/keycloak/PemUtils.java
@@ -0,0 +1,117 @@
+package org.keycloak;
+
+import org.jboss.resteasy.util.Base64;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * Utility classes to extract PublicKey, PrivateKey, and X509Certificate from openssl generated PEM files
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PemUtils
+{
+   static
+   {
+      BouncyIntegration.init();
+   }
+   public static X509Certificate decodeCertificate(InputStream is) throws Exception
+   {
+      byte[] der = pemToDer(is);
+      ByteArrayInputStream bis = new ByteArrayInputStream(der);
+      return DerUtils.decodeCertificate(bis);
+   }
+
+   public static X509Certificate decodeCertificate(String cert) throws Exception
+   {
+      byte[] der = pemToDer(cert);
+      ByteArrayInputStream bis = new ByteArrayInputStream(der);
+      return DerUtils.decodeCertificate(bis);
+   }
+
+
+   /**
+    * Extract a public key from a PEM string
+    *
+    * @param pem
+    * @return
+    * @throws Exception
+    */
+   public static PublicKey decodePublicKey(String pem) throws Exception
+   {
+      byte[] der = pemToDer(pem);
+      return DerUtils.decodePublicKey(der);
+   }
+
+   /**
+    * Extract a private key that is a PKCS8 pem string (base64 encoded PKCS8)
+    *
+    * @param pem
+    * @return
+    * @throws Exception
+    */
+   public static PrivateKey decodePrivateKey(String pem) throws Exception
+   {
+      byte[] der = pemToDer(pem);
+      return DerUtils.decodePrivateKey(der);
+   }
+
+   public static PrivateKey decodePrivateKey(InputStream is) throws Exception
+   {
+      String pem = pemFromStream(is);
+      return decodePrivateKey(pem);
+   }
+
+   /**
+    * Decode a PEM file to DER format
+    *
+    * @param is
+    * @return
+    * @throws java.io.IOException
+    */
+   public static byte[] pemToDer(InputStream is) throws IOException
+   {
+      String pem = pemFromStream(is);
+      byte[] der = pemToDer(pem);
+      return der;
+   }
+
+   /**
+    * Decode a PEM string to DER format
+    *
+    * @param pem
+    * @return
+    * @throws java.io.IOException
+    */
+   public static byte[] pemToDer(String pem) throws IOException
+   {
+      pem = removeBeginEnd(pem);
+      return Base64.decode(pem);
+   }
+
+   public static String removeBeginEnd(String pem)
+   {
+      pem = pem.replaceAll("-----BEGIN (.*)-----", "");
+      pem = pem.replaceAll("-----END (.*)----", "");
+      pem = pem.replaceAll("\r\n", "");
+      pem = pem.replaceAll("\n", "");
+      return pem.trim();
+   }
+
+
+   public static String pemFromStream(InputStream is) throws IOException
+   {
+      DataInputStream dis = new DataInputStream(is);
+      byte[] keyBytes = new byte[dis.available()];
+      dis.readFully(keyBytes);
+      dis.close();
+      return new String(keyBytes, "utf-8");
+   }
+}
diff --git a/core/src/main/java/org/keycloak/RealmConfiguration.java b/core/src/main/java/org/keycloak/RealmConfiguration.java
new file mode 100755
index 0000000..c19c21f
--- /dev/null
+++ b/core/src/main/java/org/keycloak/RealmConfiguration.java
@@ -0,0 +1,98 @@
+package org.keycloak;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmConfiguration
+{
+   protected ResourceMetadata metadata;
+   protected ResteasyClient client;
+   protected UriBuilder authUrl;
+   protected ResteasyWebTarget codeUrl;
+   protected String clientId;
+   protected Form credentials = new Form();
+   protected boolean sslRequired = true;
+   protected String stateCookieName = "OAuth_Token_Request_State";
+
+   public ResourceMetadata getMetadata()
+   {
+      return metadata;
+   }
+
+   public void setMetadata(ResourceMetadata metadata)
+   {
+      this.metadata = metadata;
+   }
+
+   public ResteasyClient getClient()
+   {
+      return client;
+   }
+
+   public void setClient(ResteasyClient client)
+   {
+      this.client = client;
+   }
+
+   public UriBuilder getAuthUrl()
+   {
+      return authUrl;
+   }
+
+   public void setAuthUrl(UriBuilder authUrl)
+   {
+      this.authUrl = authUrl;
+   }
+
+   public String getClientId()
+   {
+      return clientId;
+   }
+
+   public void setClientId(String clientId)
+   {
+      this.clientId = clientId;
+   }
+
+   public Form getCredentials()
+   {
+      return credentials;
+   }
+
+   public ResteasyWebTarget getCodeUrl()
+   {
+      return codeUrl;
+   }
+
+   public void setCodeUrl(ResteasyWebTarget codeUrl)
+   {
+      this.codeUrl = codeUrl;
+   }
+
+   public boolean isSslRequired()
+   {
+      return sslRequired;
+   }
+
+   public void setSslRequired(boolean sslRequired)
+   {
+      this.sslRequired = sslRequired;
+   }
+
+   public String getStateCookieName()
+   {
+      return stateCookieName;
+   }
+
+   public void setStateCookieName(String stateCookieName)
+   {
+      this.stateCookieName = stateCookieName;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
new file mode 100755
index 0000000..a5eec7a
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
@@ -0,0 +1,64 @@
+package org.keycloak.representations;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * OAuth 2.0 Access Token Response json
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AccessTokenResponse
+{
+   @JsonProperty("access_token")
+   protected String token;
+
+   @JsonProperty("expires_in")
+   protected long expiresIn;
+
+   @JsonProperty("refresh_token")
+   protected String refreshToken;
+
+   @JsonProperty("token_type")
+   protected String tokenType;
+
+   public String getToken()
+   {
+      return token;
+   }
+
+   public void setToken(String token)
+   {
+      this.token = token;
+   }
+
+   public long getExpiresIn()
+   {
+      return expiresIn;
+   }
+
+   public void setExpiresIn(long expiresIn)
+   {
+      this.expiresIn = expiresIn;
+   }
+
+   public String getRefreshToken()
+   {
+      return refreshToken;
+   }
+
+   public void setRefreshToken(String refreshToken)
+   {
+      this.refreshToken = refreshToken;
+   }
+
+   public String getTokenType()
+   {
+      return tokenType;
+   }
+
+   public void setTokenType(String tokenType)
+   {
+      this.tokenType = tokenType;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/PublishedRealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/PublishedRealmRepresentation.java
new file mode 100755
index 0000000..7693363
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/PublishedRealmRepresentation.java
@@ -0,0 +1,136 @@
+package org.keycloak.representations.idm;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.PemUtils;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PublishedRealmRepresentation
+{
+   protected String realm;
+   protected String self;
+
+   @JsonProperty("public_key")
+   protected String publicKeyPem;
+
+   @JsonProperty("authorization")
+   protected String authorizationUrl;
+
+   @JsonProperty("codes")
+   protected String codeUrl;
+
+   @JsonProperty("grants")
+   protected String grantUrl;
+
+   @JsonIgnore
+   protected volatile transient PublicKey publicKey;
+
+
+   public String getRealm()
+   {
+      return realm;
+   }
+
+   public void setRealm(String realm)
+   {
+      this.realm = realm;
+   }
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getPublicKeyPem()
+   {
+      return publicKeyPem;
+   }
+
+   public void setPublicKeyPem(String publicKeyPem)
+   {
+      this.publicKeyPem = publicKeyPem;
+      this.publicKey = null;
+   }
+
+
+   @JsonIgnore
+   public PublicKey getPublicKey()
+   {
+      if (publicKey != null) return publicKey;
+      if (publicKeyPem != null)
+      {
+         try
+         {
+            publicKey = PemUtils.decodePublicKey(publicKeyPem);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return publicKey;
+   }
+
+   @JsonIgnore
+   public void setPublicKey(PublicKey publicKey)
+   {
+      this.publicKey = publicKey;
+      StringWriter writer = new StringWriter();
+      PEMWriter pemWriter = new PEMWriter(writer);
+      try
+      {
+         pemWriter.writeObject(publicKey);
+         pemWriter.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      String s = writer.toString();
+      this.publicKeyPem = PemUtils.removeBeginEnd(s);
+   }
+
+
+   public String getAuthorizationUrl()
+   {
+      return authorizationUrl;
+   }
+
+   public void setAuthorizationUrl(String authorizationUrl)
+   {
+      this.authorizationUrl = authorizationUrl;
+   }
+
+   public String getCodeUrl()
+   {
+      return codeUrl;
+   }
+
+   public void setCodeUrl(String codeUrl)
+   {
+      this.codeUrl = codeUrl;
+   }
+
+   public String getGrantUrl()
+   {
+      return grantUrl;
+   }
+
+   public void setGrantUrl(String grantUrl)
+   {
+      this.grantUrl = grantUrl;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
new file mode 100755
index 0000000..17a1bb2
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -0,0 +1,171 @@
+package org.keycloak.representations.idm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmRepresentation
+{
+   protected String self; // link
+   protected String realm;
+   protected long tokenLifespan;
+   protected long accessCodeLifespan;
+   protected boolean enabled;
+   protected boolean sslNotRequired;
+   protected boolean cookieLoginAllowed;
+   protected List<RequiredCredentialRepresentation> requiredCredentials;
+   protected List<UserRepresentation> users;
+   protected List<RoleMappingRepresentation> roleMappings;
+   protected List<ScopeMappingRepresentation> scopeMappings;
+   protected List<ResourceRepresentation> resources;
+
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getRealm()
+   {
+      return realm;
+   }
+
+   public void setRealm(String realm)
+   {
+      this.realm = realm;
+   }
+
+   public List<UserRepresentation> getUsers()
+   {
+      return users;
+   }
+
+   public List<ResourceRepresentation> getResources()
+   {
+      return resources;
+   }
+
+   public ResourceRepresentation resource(String name)
+   {
+      ResourceRepresentation resource = new ResourceRepresentation();
+      if (resources == null) resources = new ArrayList<ResourceRepresentation>();
+      resources.add(resource);
+      resource.setName(name);
+      return resource;
+   }
+
+   public void setUsers(List<UserRepresentation> users)
+   {
+      this.users = users;
+   }
+
+   public UserRepresentation user(String username)
+   {
+      UserRepresentation user = new UserRepresentation();
+      user.setUsername(username);
+      if (users == null) users = new ArrayList<UserRepresentation>();
+      users.add(user);
+      return user;
+   }
+
+   public void setResources(List<ResourceRepresentation> resources)
+   {
+      this.resources = resources;
+   }
+
+   public boolean isEnabled()
+   {
+      return enabled;
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      this.enabled = enabled;
+   }
+
+   public boolean isSslNotRequired()
+   {
+      return sslNotRequired;
+   }
+
+   public void setSslNotRequired(boolean sslNotRequired)
+   {
+      this.sslNotRequired = sslNotRequired;
+   }
+
+   public boolean isCookieLoginAllowed()
+   {
+      return cookieLoginAllowed;
+   }
+
+   public void setCookieLoginAllowed(boolean cookieLoginAllowed)
+   {
+      this.cookieLoginAllowed = cookieLoginAllowed;
+   }
+
+   public long getTokenLifespan()
+   {
+      return tokenLifespan;
+   }
+
+   public void setTokenLifespan(long tokenLifespan)
+   {
+      this.tokenLifespan = tokenLifespan;
+   }
+
+   public List<RoleMappingRepresentation> getRoleMappings()
+   {
+      return roleMappings;
+   }
+
+   public RoleMappingRepresentation roleMapping(String username)
+   {
+      RoleMappingRepresentation mapping = new RoleMappingRepresentation();
+      mapping.setUsername(username);
+      if (roleMappings == null) roleMappings = new ArrayList<RoleMappingRepresentation>();
+      roleMappings.add(mapping);
+      return mapping;
+   }
+
+   public List<ScopeMappingRepresentation> getScopeMappings()
+   {
+      return scopeMappings;
+   }
+
+   public ScopeMappingRepresentation scopeMapping(String username)
+   {
+      ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
+      mapping.setUsername(username);
+      if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
+      scopeMappings.add(mapping);
+      return mapping;
+   }
+
+   public List<RequiredCredentialRepresentation> getRequiredCredentials()
+   {
+      return requiredCredentials;
+   }
+
+   public void setRequiredCredentials(List<RequiredCredentialRepresentation> requiredCredentials)
+   {
+      this.requiredCredentials = requiredCredentials;
+   }
+
+   public long getAccessCodeLifespan()
+   {
+      return accessCodeLifespan;
+   }
+
+   public void setAccessCodeLifespan(long accessCodeLifespan)
+   {
+      this.accessCodeLifespan = accessCodeLifespan;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RequiredCredentialRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RequiredCredentialRepresentation.java
new file mode 100755
index 0000000..763e29c
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/RequiredCredentialRepresentation.java
@@ -0,0 +1,46 @@
+package org.keycloak.representations.idm;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RequiredCredentialRepresentation
+{
+   public static final String PASSWORD = "Password";
+   public static final String TOTP = "TOTP";
+   public static final String CLIENT_CERT = "CLIENT_CERT";
+   public static final String CALLER_PRINCIPAL = "CALLER_PRINCIPAL";
+   protected String type;
+   protected boolean input;
+   protected boolean secret;
+
+   public String getType()
+   {
+      return type;
+   }
+
+   public void setType(String type)
+   {
+      this.type = type;
+   }
+
+   public boolean isInput()
+   {
+      return input;
+   }
+
+   public void setInput(boolean input)
+   {
+      this.input = input;
+   }
+
+   public boolean isSecret()
+   {
+      return secret;
+   }
+
+   public void setSecret(boolean secret)
+   {
+      this.secret = secret;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ResourceRepresentation.java
new file mode 100755
index 0000000..d3d2c5f
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ResourceRepresentation.java
@@ -0,0 +1,97 @@
+package org.keycloak.representations.idm;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class ResourceRepresentation
+{
+   protected String self; // link
+   protected String name;
+   protected boolean surrogateAuthRequired;
+   protected Set<String> roles;
+   protected List<RoleMappingRepresentation> roleMappings;
+   protected List<ScopeMappingRepresentation> scopeMappings;
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+
+   public boolean isSurrogateAuthRequired()
+   {
+      return surrogateAuthRequired;
+   }
+
+   public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
+   {
+      this.surrogateAuthRequired = surrogateAuthRequired;
+   }
+
+   public Set<String> getRoles()
+   {
+      return roles;
+   }
+
+   public void setRoles(Set<String> roles)
+   {
+      this.roles = roles;
+   }
+
+   public ResourceRepresentation role(String role)
+   {
+      if (this.roles == null) this.roles = new HashSet<String>();
+      this.roles.add(role);
+      return this;
+   }
+
+   public List<RoleMappingRepresentation> getRoleMappings()
+   {
+      return roleMappings;
+   }
+
+   public RoleMappingRepresentation roleMapping(String username)
+   {
+      RoleMappingRepresentation mapping = new RoleMappingRepresentation();
+      mapping.setUsername(username);
+      if (roleMappings == null) roleMappings = new ArrayList<RoleMappingRepresentation>();
+      roleMappings.add(mapping);
+      return mapping;
+   }
+
+   public List<ScopeMappingRepresentation> getScopeMappings()
+   {
+      return scopeMappings;
+   }
+
+   public ScopeMappingRepresentation scopeMapping(String username)
+   {
+      ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
+      mapping.setUsername(username);
+      if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
+      scopeMappings.add(mapping);
+      return mapping;
+   }
+
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RoleMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RoleMappingRepresentation.java
new file mode 100755
index 0000000..05915c1
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/RoleMappingRepresentation.java
@@ -0,0 +1,74 @@
+package org.keycloak.representations.idm;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RoleMappingRepresentation
+{
+   protected String self; // link
+   protected String username;
+   protected Set<String> roles;
+   protected Set<String> surrogates;
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getUsername()
+   {
+      return username;
+   }
+
+   public void setUsername(String username)
+   {
+      this.username = username;
+   }
+
+   public Set<String> getRoles()
+   {
+      return roles;
+   }
+
+   public Set<String> getSurrogates()
+   {
+      return surrogates;
+   }
+
+   public void setSurrogates(Set<String> surrogates)
+   {
+      this.surrogates = surrogates;
+   }
+
+   public RoleMappingRepresentation surrogate(String surrogate)
+   {
+      if (this.surrogates == null) this.surrogates = new HashSet<String>();
+      this.surrogates.add(surrogate);
+      return this;
+   }
+
+
+   public void setRoles(Set<String> roles)
+   {
+      this.roles = roles;
+   }
+
+   public RoleMappingRepresentation role(String role)
+   {
+      if (this.roles == null) this.roles = new HashSet<String>();
+      this.roles.add(role);
+      return this;
+   }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java
new file mode 100755
index 0000000..b86e2e2
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java
@@ -0,0 +1,55 @@
+package org.keycloak.representations.idm;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ScopeMappingRepresentation
+{
+   protected String self; // link
+   protected String username;
+   protected Set<String> roles;
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getUsername()
+   {
+      return username;
+   }
+
+   public void setUsername(String username)
+   {
+      this.username = username;
+   }
+
+   public Set<String> getRoles()
+   {
+      return roles;
+   }
+
+   public void setRoles(Set<String> roles)
+   {
+      this.roles = roles;
+   }
+
+   public ScopeMappingRepresentation role(String role)
+   {
+      if (this.roles == null) this.roles = new HashSet<String>();
+      this.roles.add(role);
+      return this;
+   }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
new file mode 100755
index 0000000..4bd0de4
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -0,0 +1,124 @@
+package org.keycloak.representations.idm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class UserRepresentation
+{
+   public static class Credential
+   {
+      protected String type;
+      protected String value;
+      protected boolean hashed;
+
+      public String getType()
+      {
+         return type;
+      }
+
+      public void setType(String type)
+      {
+         this.type = type;
+      }
+
+      public String getValue()
+      {
+         return value;
+      }
+
+      public void setValue(String value)
+      {
+         this.value = value;
+      }
+
+      public boolean isHashed()
+      {
+         return hashed;
+      }
+
+      public void setHashed(boolean hashed)
+      {
+         this.hashed = hashed;
+      }
+   }
+
+   protected String self; // link
+   protected String username;
+   protected boolean enabled;
+   protected Map<String, String> attributes;
+   protected List<Credential> credentials;
+
+   public String getSelf()
+   {
+      return self;
+   }
+
+   public void setSelf(String self)
+   {
+      this.self = self;
+   }
+
+   public String getUsername()
+   {
+      return username;
+   }
+
+   public void setUsername(String username)
+   {
+      this.username = username;
+   }
+
+   public Map<String, String> getAttributes()
+   {
+      return attributes;
+   }
+
+  public void setAttributes(Map<String, String> attributes)
+   {
+      this.attributes = attributes;
+   }
+
+   public List<Credential> getCredentials()
+   {
+      return credentials;
+   }
+
+   public void setCredentials(List<Credential> credentials)
+   {
+      this.credentials = credentials;
+   }
+
+   public UserRepresentation attribute(String name, String value)
+   {
+      if (this.attributes == null) attributes = new HashMap<String, String>();
+      attributes.put(name, value);
+      return this;
+   }
+
+   public UserRepresentation credential(String type, String value, boolean hashed)
+   {
+      if (this.credentials == null) credentials = new ArrayList<Credential>();
+      Credential cred = new Credential();
+      cred.setType(type);
+      cred.setValue(value);
+      cred.setHashed(hashed);
+      credentials.add( cred);
+      return this;
+   }
+
+   public boolean isEnabled()
+   {
+      return enabled;
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      this.enabled = enabled;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/representations/SkeletonKeyScope.java b/core/src/main/java/org/keycloak/representations/SkeletonKeyScope.java
new file mode 100755
index 0000000..8f48d96
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/SkeletonKeyScope.java
@@ -0,0 +1,13 @@
+package org.keycloak.representations;
+
+import javax.ws.rs.core.MultivaluedHashMap;
+
+/**
+ * Key is resource desired.  Values are roles desired for that resource
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SkeletonKeyScope extends MultivaluedHashMap<String, String>
+{
+}
diff --git a/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java b/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java
new file mode 100755
index 0000000..b734781
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java
@@ -0,0 +1,202 @@
+package org.keycloak.representations;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.jboss.resteasy.jwt.JsonWebToken;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SkeletonKeyToken extends JsonWebToken
+{
+   public static class Access
+   {
+      @JsonProperty("roles")
+      protected Set<String> roles;
+      @JsonProperty("verify_caller")
+      protected Boolean verifyCaller;
+
+      public Set<String> getRoles()
+      {
+         return roles;
+      }
+
+      public Access roles(Set<String> roles)
+      {
+         this.roles = roles;
+         return this;
+      }
+
+      @JsonIgnore
+      public boolean isUserInRole(String role)
+      {
+         if (roles == null) return false;
+         return roles.contains(role);
+      }
+
+      public Access addRole(String role)
+      {
+         if (roles == null) roles = new HashSet<String>();
+         roles.add(role);
+         return this;
+      }
+
+      public Boolean getVerifyCaller()
+      {
+         return verifyCaller;
+      }
+
+      public Access verifyCaller(Boolean required)
+      {
+         this.verifyCaller = required;
+         return this;
+      }
+   }
+
+   @JsonProperty("issuedFor")
+   public String issuedFor;
+
+   @JsonProperty("trusted-certs")
+   protected Set<String> trustedCertificates;
+
+
+   @JsonProperty("realm_access")
+   protected Access realmAccess;
+
+   @JsonProperty("resource_access")
+   protected Map<String, Access> resourceAccess = new HashMap<String, Access>();
+
+   public Map<String, Access> getResourceAccess()
+   {
+      return resourceAccess;
+   }
+
+   /**
+    * Does the realm require verifying the caller?
+    *
+    * @return
+    */
+   @JsonIgnore
+   public boolean isVerifyCaller()
+   {
+      if (getRealmAccess() != null && getRealmAccess().getVerifyCaller() != null) return getRealmAccess().getVerifyCaller().booleanValue();
+      return false;
+   }
+
+   /**
+    * Does the resource override the requirement of verifying the caller?
+    *
+    * @param resource
+    * @return
+    */
+   @JsonIgnore
+   public boolean isVerifyCaller(String resource)
+   {
+      Access access = getResourceAccess(resource);
+      if (access != null && access.getVerifyCaller() != null) return access.getVerifyCaller().booleanValue();
+      return false;
+   }
+
+   @JsonIgnore
+   public Access getResourceAccess(String resource)
+   {
+      return resourceAccess.get(resource);
+   }
+
+   public Access addAccess(String service)
+   {
+      Access token = new Access();
+      resourceAccess.put(service, token);
+      return token;
+   }
+
+   @Override
+   public SkeletonKeyToken id(String id)
+   {
+      return (SkeletonKeyToken)super.id(id);
+   }
+
+   @Override
+   public SkeletonKeyToken expiration(long expiration)
+   {
+      return (SkeletonKeyToken)super.expiration(expiration);
+   }
+
+   @Override
+   public SkeletonKeyToken notBefore(long notBefore)
+   {
+      return (SkeletonKeyToken)super.notBefore(notBefore);
+   }
+
+   @Override
+   public SkeletonKeyToken issuedAt(long issuedAt)
+   {
+      return (SkeletonKeyToken)super.issuedAt(issuedAt);
+   }
+
+   @Override
+   public SkeletonKeyToken issuer(String issuer)
+   {
+      return (SkeletonKeyToken)super.issuer(issuer);
+   }
+
+   @Override
+   public SkeletonKeyToken audience(String audience)
+   {
+      return (SkeletonKeyToken)super.audience(audience);
+   }
+
+   @Override
+   public SkeletonKeyToken principal(String principal)
+   {
+      return (SkeletonKeyToken)super.principal(principal);
+   }
+
+   @Override
+   public SkeletonKeyToken type(String type)
+   {
+      return (SkeletonKeyToken)super.type(type);
+   }
+
+   public Access getRealmAccess()
+   {
+      return realmAccess;
+   }
+
+   public void setRealmAccess(Access realmAccess)
+   {
+      this.realmAccess = realmAccess;
+   }
+
+   public Set<String> getTrustedCertificates()
+   {
+      return trustedCertificates;
+   }
+
+   public void setTrustedCertificates(Set<String> trustedCertificates)
+   {
+      this.trustedCertificates = trustedCertificates;
+   }
+
+   /**
+    * OAuth client the token was issued for.
+    *
+    * @return
+    */
+   public String getIssuedFor()
+   {
+      return issuedFor;
+   }
+
+   public SkeletonKeyToken issuedFor(String issuedFor)
+   {
+      this.issuedFor = issuedFor;
+      return this;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/ResourceMetadata.java b/core/src/main/java/org/keycloak/ResourceMetadata.java
new file mode 100755
index 0000000..02e75db
--- /dev/null
+++ b/core/src/main/java/org/keycloak/ResourceMetadata.java
@@ -0,0 +1,94 @@
+package org.keycloak;
+
+import java.security.KeyStore;
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ResourceMetadata
+{
+   protected String resourceName;
+   protected String realm;
+   protected KeyStore clientKeystore;
+   protected String clientKeyPassword;
+   protected KeyStore truststore;
+   protected PublicKey realmKey;
+
+   public String getResourceName()
+   {
+      return resourceName;
+   }
+
+   public String getRealm()
+   {
+      return realm;
+   }
+
+  /**
+    * keystore that contains service's private key and certificate.
+    * Used when making invocations on remote HTTPS endpoints that require client-cert authentication
+    *
+    * @return
+    */
+   public KeyStore getClientKeystore()
+   {
+      return clientKeystore;
+   }
+
+   public String getClientKeyPassword()
+   {
+      return clientKeyPassword;
+   }
+
+   public void setClientKeyPassword(String clientKeyPassword)
+   {
+      this.clientKeyPassword = clientKeyPassword;
+   }
+
+   /**
+    * Truststore to use if this service makes client invocations on remote HTTPS endpoints.
+    *
+    * @return
+    */
+   public KeyStore getTruststore()
+   {
+      return truststore;
+   }
+
+   /**
+    * Public key of the realm.  Used to verify access tokens
+    *
+    * @return
+    */
+   public PublicKey getRealmKey()
+   {
+      return realmKey;
+   }
+
+   public void setResourceName(String resourceName)
+   {
+      this.resourceName = resourceName;
+   }
+
+   public void setRealm(String realm)
+   {
+      this.realm = realm;
+   }
+
+   public void setClientKeystore(KeyStore clientKeystore)
+   {
+      this.clientKeystore = clientKeystore;
+   }
+
+   public void setTruststore(KeyStore truststore)
+   {
+      this.truststore = truststore;
+   }
+
+   public void setRealmKey(PublicKey realmKey)
+   {
+      this.realmKey = realmKey;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/RSATokenVerifier.java b/core/src/main/java/org/keycloak/RSATokenVerifier.java
new file mode 100755
index 0000000..92c23c8
--- /dev/null
+++ b/core/src/main/java/org/keycloak/RSATokenVerifier.java
@@ -0,0 +1,59 @@
+package org.keycloak;
+
+import org.jboss.resteasy.jose.jws.JWSInput;
+import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.representations.SkeletonKeyToken;
+
+import java.io.IOException;
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RSATokenVerifier
+{
+   public static SkeletonKeyToken verifyToken(String tokenString, ResourceMetadata metadata) throws VerificationException
+   {
+      PublicKey realmKey = metadata.getRealmKey();
+      String realm = metadata.getRealm();
+      String resource = metadata.getResourceName();
+      JWSInput input = new JWSInput(tokenString);
+      boolean verified = false;
+      try
+      {
+         verified = RSAProvider.verify(input, realmKey);
+      }
+      catch (Exception ignore)
+      {
+
+      }
+      if (!verified) throw new VerificationException("Token signature not validated");
+
+      SkeletonKeyToken token = null;
+      try
+      {
+         token = JsonSerialization.fromBytes(SkeletonKeyToken.class, input.getContent());
+      }
+      catch (IOException e)
+      {
+         throw new VerificationException(e);
+      }
+      if (!token.isActive())
+      {
+         throw new VerificationException("Token is not active.");
+      }
+      String user = token.getPrincipal();
+      if (user == null)
+      {
+         throw new VerificationException("Token user was null");
+      }
+      if (!realm.equals(token.getAudience()))
+      {
+         throw new VerificationException("Token audience doesn't match domain");
+
+      }
+      return token;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/servlet/ServletOAuthClient.java b/core/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
new file mode 100755
index 0000000..27d4372
--- /dev/null
+++ b/core/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
@@ -0,0 +1,127 @@
+package org.keycloak.servlet;
+
+import org.jboss.resteasy.plugins.server.servlet.ServletUtil;
+import org.keycloak.AbstractOAuthClient;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletOAuthClient extends AbstractOAuthClient
+{
+   /**
+    * Start the process of obtaining an access token by redirecting the browser
+    * to the authentication server
+    *
+    *
+    *
+    * @param relativePath path relative to context root you want auth server to redirect back to
+    * @param request
+    * @param response
+    * @throws IOException
+    */
+   public void redirectRelative(String relativePath, HttpServletRequest request, HttpServletResponse response) throws IOException
+   {
+      ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null);
+      String redirect = uriInfo.getBaseUriBuilder().path(relativePath).toTemplate();
+      redirect(redirect, request, response);
+   }
+
+
+   /**
+    * Start the process of obtaining an access token by redirecting the browser
+    * to the authentication server
+    *
+    * @param redirectUri  full URI you want auth server to redirect back to
+    * @param request
+    * @param response
+    * @throws IOException
+    */
+   public void redirect(String redirectUri, HttpServletRequest request, HttpServletResponse response) throws IOException
+   {
+      String state = getStateCode();
+
+      URI url = UriBuilder.fromUri(authUrl)
+              .queryParam("client_id", clientId)
+              .queryParam("redirect_uri", redirectUri)
+              .queryParam("state", state)
+              .build();
+      String cookiePath = request.getContextPath();
+      if (cookiePath.equals("")) cookiePath = "/";
+
+      Cookie cookie = new Cookie(stateCookieName, state);
+      cookie.setSecure(true);
+      cookie.setPath(cookiePath);
+      response.addCookie(cookie);
+      response.sendRedirect(url.toString());
+   }
+
+   protected String getCookieValue(String name, HttpServletRequest request)
+   {
+      if (request.getCookies() == null) return null;
+
+      for (Cookie cookie : request.getCookies())
+      {
+         if (cookie.getName().equals(name)) return cookie.getValue();
+      }
+      return null;
+   }
+
+   protected String getCode(HttpServletRequest request)
+   {
+      String query = request.getQueryString();
+      if (query == null) return null;
+      String[] params = query.split("&");
+      for (String param : params)
+      {
+         int eq = param.indexOf('=');
+         if (eq == -1) continue;
+         String name = param.substring(0, eq);
+         if (!name.equals("code")) continue;
+         return param.substring(eq + 1);
+      }
+      return null;
+   }
+
+
+   /**
+    * Obtain the code parameter from the url after being redirected back from the auth-server.  Then
+    * do an authenticated request back to the auth-server to turn the access code into an access token.
+    *
+    * @param request
+    * @return
+    * @throws BadRequestException
+    * @throws InternalServerErrorException
+    */
+   public String getBearerToken(HttpServletRequest request) throws BadRequestException, InternalServerErrorException
+   {
+      String error = request.getParameter("error");
+      if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
+      String redirectUri = request.getRequestURL().append("?").append(request.getQueryString()).toString();
+      String stateCookie = getCookieValue(stateCookieName, request);
+      if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));
+      // we can call get parameter as this should be a redirect
+      String state = request.getParameter("state");
+      String code = request.getParameter("code");
+
+      if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
+      if (!state.equals(stateCookie))
+      {
+         throw new BadRequestException(new Exception("state parameter invalid"));
+      }
+      if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
+      return resolveBearerToken(redirectUri, code);
+   }
+
+
+}
diff --git a/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java b/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
new file mode 100755
index 0000000..3bca480
--- /dev/null
+++ b/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
@@ -0,0 +1,42 @@
+package org.keycloak;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Any class with package org.jboss.resteasy.skeleton.key will use NON_DEFAULT inclusion
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Provider
+public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
+{
+   protected ObjectMapper mapper = new ObjectMapper();
+
+   public SkeletonKeyContextResolver()
+   {
+      mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+   }
+
+   public SkeletonKeyContextResolver(boolean indent)
+   {
+      mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+      if (indent)
+      {
+         mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
+      }
+   }
+
+
+   @Override
+   public ObjectMapper getContext(Class<?> type)
+   {
+      if (type.getPackage().getName().startsWith("org.jboss.resteasy.skeleton.key")) return mapper;
+      return null;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/SkeletonKeyPrincipal.java b/core/src/main/java/org/keycloak/SkeletonKeyPrincipal.java
new file mode 100755
index 0000000..bdba805
--- /dev/null
+++ b/core/src/main/java/org/keycloak/SkeletonKeyPrincipal.java
@@ -0,0 +1,58 @@
+package org.keycloak;
+
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SkeletonKeyPrincipal implements Principal
+{
+   protected String name;
+   protected String surrogate;
+
+   public SkeletonKeyPrincipal(String name, String surrogate)
+   {
+      this.name = name;
+      this.surrogate = surrogate;
+   }
+
+   @Override
+   public String getName()
+   {
+      return name;
+   }
+
+   public String getSurrogate()
+   {
+      return surrogate;
+   }
+
+   @Override
+   public boolean equals(Object o)
+   {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      SkeletonKeyPrincipal that = (SkeletonKeyPrincipal) o;
+
+      if (!name.equals(that.name)) return false;
+      if (surrogate != null ? !surrogate.equals(that.surrogate) : that.surrogate != null) return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode()
+   {
+      int result = name.hashCode();
+      result = 31 * result + (surrogate != null ? surrogate.hashCode() : 0);
+      return result;
+   }
+
+   @Override
+   public String toString()
+   {
+      return name;
+   }
+}
diff --git a/core/src/main/java/org/keycloak/SkeletonKeySession.java b/core/src/main/java/org/keycloak/SkeletonKeySession.java
new file mode 100755
index 0000000..e3a2f55
--- /dev/null
+++ b/core/src/main/java/org/keycloak/SkeletonKeySession.java
@@ -0,0 +1,34 @@
+package org.keycloak;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SkeletonKeySession implements Serializable
+{
+   protected String token;
+   protected transient ResourceMetadata metadata;
+
+   public SkeletonKeySession()
+   {
+   }
+
+   public SkeletonKeySession(String token, ResourceMetadata metadata)
+   {
+      this.token = token;
+      this.metadata = metadata;
+   }
+
+   public String getToken()
+   {
+      return token;
+   }
+
+   public ResourceMetadata getMetadata()
+   {
+      return metadata;
+   }
+
+}
diff --git a/core/src/main/java/org/keycloak/VerificationException.java b/core/src/main/java/org/keycloak/VerificationException.java
new file mode 100755
index 0000000..6ef877d
--- /dev/null
+++ b/core/src/main/java/org/keycloak/VerificationException.java
@@ -0,0 +1,27 @@
+package org.keycloak;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class VerificationException extends Exception
+{
+   public VerificationException()
+   {
+   }
+
+   public VerificationException(String message)
+   {
+      super(message);
+   }
+
+   public VerificationException(String message, Throwable cause)
+   {
+      super(message, cause);
+   }
+
+   public VerificationException(Throwable cause)
+   {
+      super(cause);
+   }
+}
diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java
new file mode 100755
index 0000000..8841954
--- /dev/null
+++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java
@@ -0,0 +1,282 @@
+package org.keycloak;
+
+import junit.framework.Assert;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMWriter;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.security.auth.x500.X500Principal;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RSAVerifierTest
+{
+   private static X509Certificate[] idpCertificates;
+   private static KeyPair idpPair;
+   private static KeyPair badPair;
+   private static KeyPair clientPair;
+   private static X509Certificate[] clientCertificateChain;
+   private ResourceMetadata metadata;
+   private SkeletonKeyToken token;
+
+   static
+   {
+      if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
+   }
+
+   public static X509Certificate generateTestCertificate(String subject, String issuer, KeyPair pair) throws InvalidKeyException,
+           NoSuchProviderException, SignatureException
+   {
+
+      X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+
+      certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+      certGen.setIssuerDN(new X500Principal(issuer));
+      certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
+      certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
+      certGen.setSubjectDN(new X500Principal(subject));
+      certGen.setPublicKey(pair.getPublic());
+      certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+      return certGen.generateX509Certificate(pair.getPrivate(), "BC");
+   }
+
+   @BeforeClass
+   public static void setupCerts() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
+   {
+      badPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+      idpPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+      idpCertificates = new X509Certificate[]{generateTestCertificate("CN=IDP", "CN=IDP", idpPair)};
+      clientPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+      clientCertificateChain = new X509Certificate[]{generateTestCertificate("CN=Client", "CN=IDP", idpPair)};
+   }
+
+   @Before
+   public void initTest()
+   {
+      metadata = new ResourceMetadata();
+      metadata.setResourceName("service");
+      metadata.setRealm("domain");
+      metadata.setRealmKey(idpPair.getPublic());
+
+      token = new SkeletonKeyToken();
+      token.principal("CN=Client")
+              .audience("domain")
+              .addAccess("service").addRole("admin");
+   }
+
+   @Test
+   public void testPemWriter() throws Exception
+   {
+      PublicKey realmPublicKey = idpPair.getPublic();
+      StringWriter sw = new StringWriter();
+      PEMWriter writer = new PEMWriter(sw);
+      try
+      {
+         writer.writeObject(realmPublicKey);
+         writer.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      System.out.println(sw.toString());
+   }
+
+
+   @Test
+   public void testSimpleVerification() throws Exception
+   {
+
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+      SkeletonKeyToken token =  RSATokenVerifier.verifyToken(encoded, metadata);
+      Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
+      Assert.assertEquals("CN=Client", token.getPrincipal());
+   }
+
+   /*
+   @Test
+   public void testSpeed() throws Exception
+   {
+
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      long start = System.currentTimeMillis();
+      int count = 10000;
+      for (int i = 0; i < count; i++)
+      {
+         SkeletonKeyTokenVerification v = RSATokenVerifier.verify(null, encoded, metadata);
+
+      }
+      long end = System.currentTimeMillis() - start;
+      System.out.println("rate: " + ((double)end/(double)count));
+   }
+   */
+
+
+   @Test
+   public void testBadSignature() throws Exception
+   {
+
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(badPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+         Assert.fail();
+      }
+      catch (VerificationException ignored)
+      {
+      }
+   }
+
+   @Test
+   public void testNotBeforeGood() throws Exception
+   {
+      token.notBefore((System.currentTimeMillis()/1000) - 100);
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+      }
+      catch (VerificationException ignored)
+      {
+        throw ignored;
+      }
+   }
+
+   @Test
+   public void testNotBeforeBad() throws Exception
+   {
+      token.notBefore((System.currentTimeMillis()/1000) + 100);
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+         Assert.fail();
+      }
+      catch (VerificationException ignored)
+      {
+         System.out.println(ignored.getMessage());
+      }
+   }
+
+   @Test
+   public void testExpirationGood() throws Exception
+   {
+      token.expiration((System.currentTimeMillis()/1000) + 100);
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+      }
+      catch (VerificationException ignored)
+      {
+         throw ignored;
+      }
+   }
+
+   @Test
+   public void testExpirationBad() throws Exception
+   {
+      token.expiration((System.currentTimeMillis()/1000) - 100);
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+         Assert.fail();
+      }
+      catch (VerificationException ignored)
+      {
+         System.out.println(ignored.getMessage());
+      }
+   }
+
+   @Test
+   public void testTokenAuth() throws Exception
+   {
+      token = new SkeletonKeyToken();
+      token.principal("CN=Client")
+              .audience("domain")
+              .addAccess("service").addRole("admin").verifyCaller(true);
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(idpPair.getPrivate());
+
+      SkeletonKeyToken v = null;
+      try
+      {
+         v = RSATokenVerifier.verifyToken(encoded, metadata);
+      }
+      catch (VerificationException ignored)
+      {
+         System.out.println(ignored.getMessage());
+      }
+   }
+
+
+
+}
diff --git a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
new file mode 100755
index 0000000..cf5e459
--- /dev/null
+++ b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
@@ -0,0 +1,78 @@
+package org.keycloak;
+
+import junit.framework.Assert;
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jose.jws.JWSInput;
+import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.representations.SkeletonKeyScope;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.junit.Test;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SkeletonKeyTokenTest
+{
+   @Test
+   public void testScope() throws Exception
+   {
+     SkeletonKeyScope scope2 = new SkeletonKeyScope();
+
+      scope2.add("one", "admin");
+      scope2.add("one", "buyer");
+      scope2.add("two", "seller");
+      String json = JsonSerialization.toString(scope2, true);
+      System.out.println(json);
+
+
+   }
+
+   @Test
+   public void testToken() throws Exception
+   {
+      SkeletonKeyToken token = new SkeletonKeyToken();
+      token.id("111");
+      token.addAccess("foo").addRole("admin");
+      token.addAccess("bar").addRole("user");
+
+      String json = JsonSerialization.toString(token, true);
+      System.out.println(json);
+
+      token = JsonSerialization.fromString(SkeletonKeyToken.class, json);
+      Assert.assertEquals("111", token.getId());
+      SkeletonKeyToken.Access foo = token.getResourceAccess("foo");
+      Assert.assertNotNull(foo);
+      Assert.assertTrue(foo.isUserInRole("admin"));
+
+   }
+
+   @Test
+   public void testRSA() throws Exception
+   {
+      SkeletonKeyToken token = new SkeletonKeyToken();
+      token.id("111");
+      token.addAccess("foo").addRole("admin");
+      token.addAccess("bar").addRole("user");
+
+      KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+      byte[] tokenBytes = JsonSerialization.toByteArray(token, true);
+
+      String encoded = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(keyPair.getPrivate());
+
+      System.out.println(encoded);
+
+      JWSInput input = new JWSInput(encoded);
+      byte[] content = input.getContent();
+
+      token = JsonSerialization.fromBytes(SkeletonKeyToken.class, content);
+      Assert.assertEquals("111", token.getId());
+      Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
+   }
+}

pom.xml 301(+301 -0)

diff --git a/pom.xml b/pom.xml
new file mode 100755
index 0000000..2225e9b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,301 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <name>Identity Guardener</name>
+    <groupId>org.keycloak</groupId>
+    <artifactId>keycloak-parent</artifactId>
+    <version>1.0-alpha-1</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <resteasy.version>3.0.1.Final</resteasy.version>
+    </properties>
+
+    <url>http://keycloak.org</url>
+
+    <licenses>
+        <license>
+            <name>Apache License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <distributionManagement>
+        <repository>
+            <id>jboss-releases-repository</id>
+            <name>JBoss Releases Repository</name>
+            <url>https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/</url>
+        </repository>
+    </distributionManagement>
+
+    <issueManagement>
+        <system>JIRA</system>
+        <url>http://jira.jboss.com/jira/browse/KEYCLOAK</url>
+    </issueManagement>
+
+    <developers>
+        <developer>
+            <id>patriot1burke</id>
+            <name>Bill Burke</name>
+            <email>bburke@redhat.co</email>
+            <organization>Red Hat</organization>
+            <roles>
+                <role>project-owner</role>
+            </roles>
+            <timezone>-5</timezone>
+        </developer>
+    </developers>
+
+    <contributors>
+    </contributors>
+
+    <modules>
+        <module>core</module>
+        <module>services</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk16</artifactId>
+                <version>1.46</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcmail-jdk16</artifactId>
+                <version>1.46</version>
+            </dependency>
+            <dependency>
+                <groupId>org.infinispan</groupId>
+                <artifactId>infinispan-core</artifactId>
+                <version>5.1.6.FINAL</version>
+            </dependency>
+            <dependency>
+                <groupId>org.infinispan</groupId>
+                <artifactId>infinispan-tree</artifactId>
+                <version>5.1.6.FINAL</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>jaxrs-api</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-jaxrs</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-client</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>jose-jwt</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-crypto</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>jose-jwt</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-core-asl</artifactId>
+                <version>1.9.12</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-mapper-asl</artifactId>
+                <version>1.9.12</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-xc</artifactId>
+                <version>1.9.12</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-jaxrs</artifactId>
+                <version>1.9.12</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.servlet</groupId>
+                <artifactId>servlet-api</artifactId>
+                <version>2.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>tjws</artifactId>
+                <version>${resteasy.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.picketlink</groupId>
+                <artifactId>picketlink-idm-api</artifactId>
+                <version>2.5.0-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.picketlink</groupId>
+                <artifactId>picketlink-idm-impl</artifactId>
+                <version>2.5.0-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.picketlink</groupId>
+                <artifactId>picketlink-idm-schema</artifactId>
+                <version>2.5.0-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.picketlink</groupId>
+                <artifactId>picketlink-config</artifactId>
+                <version>2.5.0-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.11</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+    <pluginRepositories>
+    </pluginRepositories>
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <!-- <version>2.5</version> -->
+                    <configuration>
+                        <forkMode>once</forkMode>
+                        <argLine>-Xms512m -Xmx512m</argLine>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>2.3.1</version>
+                    <configuration>
+                        <source>1.6</source>
+                        <target>1.6</target>
+                        <encoding>utf-8</encoding>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>2.8</version>
+                    <configuration>
+                        <minmemory>128m</minmemory>
+                        <maxmemory>1024m</maxmemory>
+                        <quiet>false</quiet>
+                        <aggregate>true</aggregate>
+                        <excludePackageNames>
+                            com.restfully.*:org.jboss.resteasy.examples.*:se.unlogic.*:org.jboss.resteasy.tests.*
+                        </excludePackageNames>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-install-plugin</artifactId>
+                    <version>2.3.1</version>
+                    <configuration>
+                        <createChecksum>true</createChecksum>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>2.1.2</version>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>jar</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.5</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                    <encoding>utf-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8</version>
+                <configuration>
+                    <minmemory>128m</minmemory>
+                    <maxmemory>1024m</maxmemory>
+                    <quiet>false</quiet>
+                    <aggregate>true</aggregate>
+                    <excludePackageNames>
+                        se.unlogic.*:com.restfully.*:org.jboss.resteasy.examples.*:org.jboss.resteasy.tests.*
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <createChecksum>true</createChecksum>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.1.2</version>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.5</version>
+            </plugin>
+            <plugin>
+                <groupId>com.atlassian.maven.plugins</groupId>
+                <artifactId>maven-clover2-plugin</artifactId>
+                <version>3.1.6</version>
+            </plugin>
+        </plugins>
+
+    </build>
+</project>

services/pom.xml 111(+111 -0)

diff --git a/services/pom.xml b/services/pom.xml
new file mode 100755
index 0000000..8c097ae
--- /dev/null
+++ b/services/pom.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0"?>
+<project>
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-services</artifactId>
+    <name>Keycloak REST Services</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-idm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-idm-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-idm-schema</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-crypto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>tjws</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-xc</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/services/src/main/java/org/keycloak/services/IdentityManagerAdapter.java b/services/src/main/java/org/keycloak/services/IdentityManagerAdapter.java
new file mode 100755
index 0000000..0df74ff
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/IdentityManagerAdapter.java
@@ -0,0 +1,84 @@
+package org.keycloak.services;
+
+import org.keycloak.services.model.data.RealmModel;
+import org.keycloak.services.model.data.RequiredCredentialModel;
+import org.keycloak.services.model.data.ResourceModel;
+import org.keycloak.services.model.data.RoleMappingModel;
+import org.keycloak.services.model.data.RoleModel;
+import org.keycloak.services.model.data.ScopeMappingModel;
+import org.keycloak.services.model.data.UserAttributeModel;
+import org.keycloak.services.model.data.UserCredentialModel;
+import org.keycloak.services.model.data.UserModel;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public interface IdentityManagerAdapter
+{
+   RealmModel getRealm(String id);
+   RealmModel create(RealmModel realm);
+   void update(RealmModel realm);
+   void delete(RealmModel realm);
+   List<RequiredCredentialModel> getRequiredCredentials(RealmModel realm);
+   RequiredCredentialModel getRealmCredential(String id);
+   RequiredCredentialModel create(RealmModel realm, RequiredCredentialModel cred);
+   void update(RequiredCredentialModel cred);
+   void delete(RequiredCredentialModel cred);
+
+   UserModel getUser(RealmModel realm, String username);
+   UserModel create(RealmModel realm, UserModel user);
+   void update(RealmModel realm, UserModel user);
+   void delete(RealmModel realm,UserModel user);
+
+   List<UserCredentialModel> getCredentials(UserModel user);
+   UserCredentialModel getCredential(String id);
+   UserCredentialModel create(UserModel user, UserCredentialModel cred);
+   void update(UserCredentialModel cred);
+   void delete(UserCredentialModel cred);
+
+   UserAttributeModel getUserAttribute(String id);
+   UserAttributeModel create(UserModel user, UserAttributeModel attribute);
+   void update(UserAttributeModel attribute);
+   void delete(UserAttributeModel attribute);
+
+   ResourceModel getResource(String resourceid);
+   List<ResourceModel> getResources(RealmModel realm);
+   ResourceModel create(RealmModel realm, ResourceModel resource);
+   void update(ResourceModel resource);
+   void delete(ResourceModel resource);
+
+
+   RoleModel getRoleByName(RealmModel realm, String roleName);
+   RoleModel getRoleByName(ResourceModel resource, String roleName);
+   List<RoleModel> getRoles(RealmModel realm, ResourceModel resource);
+   List<RoleModel> getRoles(RealmModel realm);
+   RoleModel getRole(String id);
+   RoleModel create(RealmModel realm, ResourceModel resource, String role);
+   RoleModel create(RealmModel realm, String role);
+   void delete(RoleModel role);
+
+   List<RoleMappingModel> getRoleMappings(RealmModel realm);
+   List<RoleMappingModel> getRoleMappings(RealmModel realm, ResourceModel resource);
+   RoleMappingModel getRoleMapping(RealmModel realm, UserModel user);
+   RoleMappingModel getRoleMapping(RealmModel realm, ResourceModel resource, UserModel user);
+   RoleMappingModel getRoleMapping(String id);
+   RoleMappingModel create(RealmModel realm, UserModel user, RoleMappingModel mapping);
+   RoleMappingModel create(RealmModel realm, ResourceModel resource, UserModel user, RoleMappingModel mapping);
+   void delete(RoleMappingModel role);
+
+   List<ScopeMappingModel> getScopeMappings(RealmModel realm);
+   List<ScopeMappingModel> getScopeMappings(RealmModel realm, ResourceModel resource);
+   ScopeMappingModel getScopeMapping(RealmModel realm, UserModel user);
+   ScopeMappingModel getScopeMapping(RealmModel realm, ResourceModel resource, UserModel user);
+   ScopeMappingModel getScopeMapping(String id);
+   ScopeMappingModel create(RealmModel realm, UserModel user, ScopeMappingModel mapping);
+   ScopeMappingModel create(RealmModel realm, ResourceModel resource, UserModel user, ScopeMappingModel mapping);
+   void delete(ScopeMappingModel scope);
+
+
+   List<RealmModel> getRealmsByName(String name);
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/RealmModel.java b/services/src/main/java/org/keycloak/services/model/data/RealmModel.java
new file mode 100755
index 0000000..ab3711a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/RealmModel.java
@@ -0,0 +1,194 @@
+package org.keycloak.services.model.data;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.jboss.resteasy.security.PemUtils;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class RealmModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+
+   protected String id;
+   protected String name;
+   protected long tokenLifespan = 3600 * 24; // one day
+   protected long accessCodeLifespan = 300; // 5 minutes
+   protected boolean enabled;
+   protected boolean sslNotRequired;
+   protected boolean cookieLoginAllowed;
+   protected String publicKeyPem;
+   protected String privateKeyPem;
+   protected volatile transient PublicKey publicKey;
+   protected volatile transient PrivateKey privateKey;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+
+   public boolean isEnabled()
+   {
+      return enabled;
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      this.enabled = enabled;
+   }
+
+   public boolean isSslNotRequired()
+   {
+      return sslNotRequired;
+   }
+
+   public void setSslNotRequired(boolean sslNotRequired)
+   {
+      this.sslNotRequired = sslNotRequired;
+   }
+
+   public boolean isCookieLoginAllowed()
+   {
+      return cookieLoginAllowed;
+   }
+
+   public void setCookieLoginAllowed(boolean cookieLoginAllowed)
+   {
+      this.cookieLoginAllowed = cookieLoginAllowed;
+   }
+
+   public long getTokenLifespan()
+   {
+      return tokenLifespan;
+   }
+
+   public void setTokenLifespan(long tokenLifespan)
+   {
+      this.tokenLifespan = tokenLifespan;
+   }
+
+   public long getAccessCodeLifespan()
+   {
+      return accessCodeLifespan;
+   }
+
+   public void setAccessCodeLifespan(long accessCodeLifespan)
+   {
+      this.accessCodeLifespan = accessCodeLifespan;
+   }
+
+   public String getPublicKeyPem()
+   {
+      return publicKeyPem;
+   }
+
+   public void setPublicKeyPem(String publicKeyPem)
+   {
+      this.publicKeyPem = publicKeyPem;
+      this.publicKey = null;
+   }
+
+   public String getPrivateKeyPem()
+   {
+      return privateKeyPem;
+   }
+
+   public void setPrivateKeyPem(String privateKeyPem)
+   {
+      this.privateKeyPem = privateKeyPem;
+      this.privateKey = null;
+   }
+
+   public PublicKey getPublicKey()
+   {
+      if (publicKey != null) return publicKey;
+      if (publicKeyPem != null)
+      {
+         try
+         {
+            publicKey = PemUtils.decodePublicKey(publicKeyPem);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return publicKey;
+   }
+
+   public void setPublicKey(PublicKey publicKey)
+   {
+      this.publicKey = publicKey;
+      StringWriter writer = new StringWriter();
+      PEMWriter pemWriter = new PEMWriter(writer);
+      try
+      {
+         pemWriter.writeObject(publicKey);
+         pemWriter.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      String s = writer.toString();
+      this.publicKeyPem = PemUtils.removeBeginEnd(s);
+   }
+
+   public PrivateKey getPrivateKey()
+   {
+      if (privateKey != null) return privateKey;
+      if (privateKeyPem != null)
+      {
+         try
+         {
+            privateKey = PemUtils.decodePrivateKey(privateKeyPem);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return privateKey;
+   }
+
+   public void setPrivateKey(PrivateKey privateKey)
+   {
+      this.privateKey = privateKey;
+      StringWriter writer = new StringWriter();
+      PEMWriter pemWriter = new PEMWriter(writer);
+      try
+      {
+         pemWriter.writeObject(privateKey);
+         pemWriter.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      String s = writer.toString();
+      this.privateKeyPem = PemUtils.removeBeginEnd(s);
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/RequiredCredentialModel.java b/services/src/main/java/org/keycloak/services/model/data/RequiredCredentialModel.java
new file mode 100755
index 0000000..5ab0159
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/RequiredCredentialModel.java
@@ -0,0 +1,58 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class RequiredCredentialModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+
+   protected String id;
+   protected String type;
+   protected boolean input;
+   protected boolean secret;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getType()
+   {
+      return type;
+   }
+
+   public void setType(String type)
+   {
+      this.type = type;
+   }
+
+   public boolean isInput()
+   {
+      return input;
+   }
+
+   public void setInput(boolean input)
+   {
+      this.input = input;
+   }
+
+   public boolean isSecret()
+   {
+      return secret;
+   }
+
+   public void setSecret(boolean secret)
+   {
+      this.secret = secret;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/ResourceModel.java b/services/src/main/java/org/keycloak/services/model/data/ResourceModel.java
new file mode 100755
index 0000000..e8f2421
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/ResourceModel.java
@@ -0,0 +1,46 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class ResourceModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String name;
+   protected boolean surrogateAuthRequired;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+
+   public boolean isSurrogateAuthRequired()
+   {
+      return surrogateAuthRequired;
+   }
+
+   public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
+   {
+      this.surrogateAuthRequired = surrogateAuthRequired;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/RoleMappingModel.java b/services/src/main/java/org/keycloak/services/model/data/RoleMappingModel.java
new file mode 100755
index 0000000..06e96d8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/RoleMappingModel.java
@@ -0,0 +1,59 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class RoleMappingModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String userid;
+   protected Set<String> roles = new HashSet<String>();
+   protected Set<String> surrogateIds = new HashSet<String>();
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getUserid()
+   {
+      return userid;
+   }
+
+   public void setUserid(String userid)
+   {
+      this.userid = userid;
+   }
+
+   public Set<String> getRoles()
+   {
+      return roles;
+   }
+
+   public void setRoles(Set<String> roles)
+   {
+      this.roles = roles;
+   }
+
+   public Set<String> getSurrogateIds()
+   {
+      return surrogateIds;
+   }
+
+   public void setSurrogateIds(Set<String> surrogateIds)
+   {
+      this.surrogateIds = surrogateIds;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/RoleModel.java b/services/src/main/java/org/keycloak/services/model/data/RoleModel.java
new file mode 100755
index 0000000..a0740d1
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/RoleModel.java
@@ -0,0 +1,35 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class RoleModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String name;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/ScopeMappingModel.java b/services/src/main/java/org/keycloak/services/model/data/ScopeMappingModel.java
new file mode 100755
index 0000000..3e3bc01
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/ScopeMappingModel.java
@@ -0,0 +1,49 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class ScopeMappingModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String userid;
+   protected Set<String> roles = new HashSet<String>();
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getUserid()
+   {
+      return userid;
+   }
+
+   public void setUserid(String userid)
+   {
+      this.userid = userid;
+   }
+
+   public Set<String> getRoles()
+   {
+      return roles;
+   }
+
+   public void setRoles(Set<String> roles)
+   {
+      this.roles = roles;
+   }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/UserAttributeModel.java b/services/src/main/java/org/keycloak/services/model/data/UserAttributeModel.java
new file mode 100755
index 0000000..1f7e2ba
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/UserAttributeModel.java
@@ -0,0 +1,46 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class UserAttributeModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String name;
+   protected String value;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+
+   public String getValue()
+   {
+      return value;
+   }
+
+   public void setValue(String value)
+   {
+      this.value = value;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/UserCredentialModel.java b/services/src/main/java/org/keycloak/services/model/data/UserCredentialModel.java
new file mode 100755
index 0000000..b1cf95a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/UserCredentialModel.java
@@ -0,0 +1,58 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class UserCredentialModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+
+   protected String id;
+   protected String type;
+   protected String value;
+   protected boolean hashed;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getType()
+   {
+      return type;
+   }
+
+   public void setType(String type)
+   {
+      this.type = type;
+   }
+
+   public String getValue()
+   {
+      return value;
+   }
+
+   public void setValue(String value)
+   {
+      this.value = value;
+   }
+
+   public boolean isHashed()
+   {
+      return hashed;
+   }
+
+   public void setHashed(boolean hashed)
+   {
+      this.hashed = hashed;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/data/UserModel.java b/services/src/main/java/org/keycloak/services/model/data/UserModel.java
new file mode 100755
index 0000000..d878348
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/data/UserModel.java
@@ -0,0 +1,46 @@
+package org.keycloak.services.model.data;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Deprecated
+public class UserModel implements Serializable
+{
+   private static final long serialVersionUID = 1L;
+   protected String id;
+   protected String username;
+   protected boolean enabled;
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getUsername()
+   {
+      return username;
+   }
+
+   public void setUsername(String username)
+   {
+      this.username = username;
+   }
+
+   public boolean isEnabled()
+   {
+      return enabled;
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      this.enabled = enabled;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/RealmManager.java b/services/src/main/java/org/keycloak/services/model/RealmManager.java
new file mode 100755
index 0000000..270a86c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/RealmManager.java
@@ -0,0 +1,49 @@
+package org.keycloak.services.model;
+
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.internal.IdentityManagerFactory;
+import org.picketlink.idm.model.Realm;
+import org.picketlink.idm.model.SimpleAgent;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmManager
+{
+   private static AtomicLong counter = new AtomicLong(1);
+
+   public static String generateId()
+   {
+      return counter.getAndIncrement() + "-" + System.currentTimeMillis();
+   }
+
+   protected IdentityManagerFactory factory;
+
+   public RealmManager(IdentityManagerFactory factory)
+   {
+      this.factory = factory;
+   }
+
+   public RealmModel getRealm(String id)
+   {
+      Realm existing = factory.findRealm(id);
+      if (existing == null)
+      {
+         return null;
+      }
+      return new RealmModel(existing, factory);
+   }
+
+   public RealmModel create(String name)
+   {
+      Realm newRealm = factory.createRealm(generateId());
+      IdentityManager idm = factory.createIdentityManager(newRealm);
+      SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID);
+      idm.add(agent);
+      return new RealmModel(newRealm, factory);
+   }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/model/RealmModel.java b/services/src/main/java/org/keycloak/services/model/RealmModel.java
new file mode 100755
index 0000000..1b48554
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/RealmModel.java
@@ -0,0 +1,389 @@
+package org.keycloak.services.model;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.jboss.resteasy.security.PemUtils;
+import org.keycloak.representations.idm.RequiredCredentialRepresentation;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.TOTPCredential;
+import org.picketlink.idm.credential.X509CertificateCredentials;
+import org.picketlink.idm.internal.IdentityManagerFactory;
+import org.picketlink.idm.model.Agent;
+import org.picketlink.idm.model.Attribute;
+import org.picketlink.idm.model.Grant;
+import org.picketlink.idm.model.Realm;
+import org.picketlink.idm.model.Role;
+import org.picketlink.idm.model.SimpleAgent;
+import org.picketlink.idm.model.Tier;
+import org.picketlink.idm.model.User;
+import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.query.RelationshipQuery;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmModel
+{
+   public static final String REALM_AGENT_ID = "_realm_";
+   public static final String REALM_NAME = "name";
+   public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan";
+   public static final String REALM_TOKEN_LIFESPAN = "tokenLifespan";
+   public static final String REALM_PRIVATE_KEY = "privateKey";
+   public static final String REALM_PUBLIC_KEY = "publicKey";
+   public static final String REALM_IS_SSL_NOT_REQUIRED = "isSSLNotRequired";
+   public static final String REALM_IS_COOKIE_LOGIN_ALLOWED = "isCookieLoginAllowed";
+
+   protected Realm realm;
+   protected Agent realmAgent;
+   protected IdentityManagerFactory factory;
+   protected volatile transient PublicKey publicKey;
+   protected volatile transient PrivateKey privateKey;
+
+   public RealmModel(Realm realm, IdentityManagerFactory factory)
+   {
+      this.realm = realm;
+      this.factory = factory;
+      realmAgent = getIdm().getAgent(REALM_AGENT_ID);
+   }
+
+   public IdentityManager getIdm()
+   {
+      return factory.createIdentityManager(realm);
+   }
+
+   public void updateRealm()
+   {
+      getIdm().update(realmAgent);
+   }
+
+   public String getId()
+   {
+      return realm.getId();
+   }
+
+   public String getName()
+   {
+      return (String)realmAgent.getAttribute(REALM_NAME).getValue();
+   }
+
+   public void setName(String name)
+   {
+      realmAgent.setAttribute(new Attribute<String>(REALM_NAME, name));
+   }
+
+   public boolean isEnabled()
+   {
+      return realmAgent.isEnabled();
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      realmAgent.setEnabled(enabled);
+   }
+
+   public boolean isSslNotRequired()
+   {
+      return (Boolean)realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue();
+   }
+
+   public void setSslNotRequired(boolean sslNotRequired)
+   {
+      realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired));
+   }
+
+   public boolean isCookieLoginAllowed()
+   {
+      return (Boolean)realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue();
+   }
+
+   public void setCookieLoginAllowed(boolean cookieLoginAllowed)
+   {
+      realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed));
+   }
+
+   public long getTokenLifespan()
+   {
+      return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
+   }
+
+   public void setTokenLifespan(long tokenLifespan)
+   {
+      realmAgent.setAttribute(new Attribute<Long>(REALM_TOKEN_LIFESPAN,tokenLifespan));
+   }
+
+   public long getAccessCodeLifespan()
+   {
+      return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue();
+   }
+
+   public void setAccessCodeLifespan(long accessCodeLifespan)
+   {
+      realmAgent.setAttribute(new Attribute<Long>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan));
+   }
+
+   public String getPublicKeyPem()
+   {
+      return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue();
+   }
+
+   public void setPublicKeyPem(String publicKeyPem)
+   {
+      realmAgent.setAttribute(new Attribute<String>(REALM_PUBLIC_KEY, publicKeyPem));
+      this.publicKey = null;
+   }
+
+   public String getPrivateKeyPem()
+   {
+      return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue();
+   }
+
+   public void setPrivateKeyPem(String privateKeyPem)
+   {
+      realmAgent.setAttribute(new Attribute<String>(REALM_PRIVATE_KEY, privateKeyPem));
+      this.privateKey = null;
+   }
+
+   public PublicKey getPublicKey()
+   {
+      if (publicKey != null) return publicKey;
+      String pem = getPublicKeyPem();
+      if (pem != null)
+      {
+         try
+         {
+            publicKey = PemUtils.decodePublicKey(pem);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return publicKey;
+   }
+
+   public void setPublicKey(PublicKey publicKey)
+   {
+      this.publicKey = publicKey;
+      StringWriter writer = new StringWriter();
+      PEMWriter pemWriter = new PEMWriter(writer);
+      try
+      {
+         pemWriter.writeObject(publicKey);
+         pemWriter.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      String s = writer.toString();
+      setPublicKeyPem(PemUtils.removeBeginEnd(s));
+   }
+
+   public PrivateKey getPrivateKey()
+   {
+      if (privateKey != null) return privateKey;
+      String pem = getPrivateKeyPem();
+      if (pem != null)
+      {
+         try
+         {
+            privateKey = PemUtils.decodePrivateKey(pem);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return privateKey;
+   }
+
+   public void setPrivateKey(PrivateKey privateKey)
+   {
+      this.privateKey = privateKey;
+      StringWriter writer = new StringWriter();
+      PEMWriter pemWriter = new PEMWriter(writer);
+      try
+      {
+         pemWriter.writeObject(privateKey);
+         pemWriter.flush();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      String s = writer.toString();
+      setPrivateKeyPem(PemUtils.removeBeginEnd(s));
+   }
+
+   public List<RequiredCredentialModel> getRequiredCredentials()
+   {
+      IdentityManager idm = getIdm();
+      Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
+      RelationshipQuery<RequiredCredentialRelationship> query = idm.createRelationshipQuery(RequiredCredentialRelationship.class);
+      query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent);
+      List<RequiredCredentialRelationship> results = query.getResultList();
+      List<RequiredCredentialModel> rtn = new ArrayList<RequiredCredentialModel>();
+      for (RequiredCredentialRelationship relationship : results)
+      {
+         RequiredCredentialModel model = new RequiredCredentialModel();
+         model.setInput(relationship.isInput());
+         model.setSecret(relationship.isSecret());
+         model.setType(relationship.getCredentialType());
+         rtn.add(model);
+      }
+      return rtn;
+   }
+
+   public void addRequiredCredential(RequiredCredentialModel cred)
+   {
+      IdentityManager idm = getIdm();
+      Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
+      RequiredCredentialRelationship relationship = new RequiredCredentialRelationship();
+      relationship.setCredentialType(cred.getType());
+      relationship.setInput(cred.isInput());
+      relationship.setSecret(cred.isSecret());
+      relationship.setRealmAgent(realmAgent);
+      idm.add(relationship);
+   }
+
+   public void updateCredential(User user, UserCredentialModel cred)
+   {
+      IdentityManager idm = getIdm();
+      if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD))
+      {
+         Password password = new Password(cred.getValue());
+         idm.updateCredential(user, password);
+      }
+      else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP))
+      {
+         TOTPCredential totp = new TOTPCredential(cred.getValue());
+         idm.updateCredential(user, totp);
+      }
+      else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT))
+      {
+         X509Certificate cert = null;
+         try
+         {
+            cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+         X509CertificateCredentials creds = new X509CertificateCredentials(cert);
+         idm.updateCredential(user, creds);
+      }
+   }
+
+   public List<Role> getRoles()
+   {
+      IdentityManager idm = getIdm();
+      IdentityQuery<Role> query = idm.createIdentityQuery(Role.class);
+      query.setParameter(Role.PARTITION, realm);
+      return query.getResultList();
+   }
+
+
+   protected ResourceModel loadResource(Agent resource)
+   {
+      Tier tier = factory.findTier(resource.getPartition().getId());
+      return new ResourceModel(tier, resource, this, factory);
+   }
+
+   /**
+    * Key name, value resource
+    *
+    * @return
+    */
+   public Map<String, ResourceModel> getResourceMap()
+   {
+      Map<String, ResourceModel> resourceMap = new HashMap<String, ResourceModel>();
+      for (ResourceModel resource : getResources())
+      {
+         resourceMap.put(resource.getName(), resource);
+      }
+      return resourceMap;
+   }
+
+   public List<ResourceModel> getResources()
+   {
+      IdentityManager idm = getIdm();
+      RelationshipQuery<RealmResourceRelationship> query = idm.createRelationshipQuery(RealmResourceRelationship.class);
+      query.setParameter(RealmResourceRelationship.REALM_AGENT, realmAgent);
+      List<RealmResourceRelationship> results = query.getResultList();
+      List<ResourceModel> resources = new ArrayList<ResourceModel>();
+      for (RealmResourceRelationship relationship : results)
+      {
+         ResourceModel model = loadResource(relationship.getResourceAgent());
+         resources.add(model);
+      }
+
+      return resources;
+   }
+
+   public ResourceModel addResource(String name)
+   {
+      Tier newTier = factory.createTier(RealmManager.generateId());
+      IdentityManager idm = factory.createIdentityManager(newTier);
+      SimpleAgent resourceAgent = new SimpleAgent(ResourceModel.RESOURCE_AGENT_ID);
+      resourceAgent.setAttribute(new Attribute<String>(ResourceModel.RESOURCE_NAME, name));
+      idm.add(resourceAgent);
+      idm = getIdm();
+      RealmResourceRelationship relationship = new RealmResourceRelationship();
+      relationship.setRealmAgent(realmAgent);
+      relationship.setResourceAgent(resourceAgent);
+      idm.add(relationship);
+      return new ResourceModel(newTier, resourceAgent, this, factory);
+   }
+
+   public Set<String> getRoleMappings(User user)
+   {
+      RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
+      query.setParameter(Grant.ASSIGNEE, user);
+      List<Grant> grants = query.getResultList();
+      HashSet<String> set = new HashSet<String>();
+      for (Grant grant : grants)
+      {
+         if (grant.getRole().getPartition().getId().equals(realm.getId()))set.add(grant.getRole().getName());
+      }
+      return set;
+   }
+
+   public void addScope(Agent agent, String roleName)
+   {
+      IdentityManager idm = getIdm();
+      Role role = idm.getRole(roleName);
+      if (role == null) throw new RuntimeException("role not found");
+      ScopeRelationship scope = new ScopeRelationship();
+      scope.setClient(agent);
+      scope.setScope(role);
+
+   }
+
+
+   public Set<String> getScope(Agent agent)
+   {
+      RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
+      query.setParameter(ScopeRelationship.CLIENT, agent);
+      List<ScopeRelationship> scope = query.getResultList();
+      HashSet<String> set = new HashSet<String>();
+      for (ScopeRelationship rel : scope)
+      {
+         if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName());
+      }
+      return set;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/RealmResourceRelationship.java b/services/src/main/java/org/keycloak/services/model/RealmResourceRelationship.java
new file mode 100755
index 0000000..b601160
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/RealmResourceRelationship.java
@@ -0,0 +1,57 @@
+package org.keycloak.services.model;
+
+import org.picketlink.idm.model.AbstractAttributedType;
+import org.picketlink.idm.model.Agent;
+import org.picketlink.idm.model.Relationship;
+import org.picketlink.idm.model.annotation.IdentityProperty;
+import org.picketlink.idm.query.RelationshipQueryParameter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmResourceRelationship extends AbstractAttributedType implements Relationship
+{
+   private static final long serialVersionUID = 1L;
+
+   public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() {
+
+      @Override
+      public String getName() {
+         return "realmAgent";
+      }
+   };
+
+   public static final RelationshipQueryParameter RESOURCE_AGENT = new RelationshipQueryParameter() {
+
+      @Override
+      public String getName() {
+         return "resourceAgent";
+      }
+   };
+
+   protected Agent realmAgent;
+   protected Agent resourceAgent;
+
+   @IdentityProperty
+   public Agent getRealmAgent()
+   {
+      return realmAgent;
+   }
+
+   public void setRealmAgent(Agent realmAgent)
+   {
+      this.realmAgent = realmAgent;
+   }
+
+   @IdentityProperty
+   public Agent getResourceAgent()
+   {
+      return resourceAgent;
+   }
+
+   public void setResourceAgent(Agent resourceAgent)
+   {
+      this.resourceAgent = resourceAgent;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/RequiredCredentialModel.java b/services/src/main/java/org/keycloak/services/model/RequiredCredentialModel.java
new file mode 100755
index 0000000..8506e44
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/RequiredCredentialModel.java
@@ -0,0 +1,42 @@
+package org.keycloak.services.model;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RequiredCredentialModel
+{
+   protected String type;
+   protected boolean input;
+   protected boolean secret;
+
+   public String getType()
+   {
+      return type;
+   }
+
+   public void setType(String type)
+   {
+      this.type = type;
+   }
+
+   public boolean isInput()
+   {
+      return input;
+   }
+
+   public void setInput(boolean input)
+   {
+      this.input = input;
+   }
+
+   public boolean isSecret()
+   {
+      return secret;
+   }
+
+   public void setSecret(boolean secret)
+   {
+      this.secret = secret;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/RequiredCredentialRelationship.java b/services/src/main/java/org/keycloak/services/model/RequiredCredentialRelationship.java
new file mode 100755
index 0000000..c2e94db
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/RequiredCredentialRelationship.java
@@ -0,0 +1,79 @@
+package org.keycloak.services.model;
+
+import org.picketlink.idm.model.AbstractAttributedType;
+import org.picketlink.idm.model.Agent;
+import org.picketlink.idm.model.Relationship;
+import org.picketlink.idm.model.annotation.AttributeProperty;
+import org.picketlink.idm.model.annotation.IdentityProperty;
+import org.picketlink.idm.query.RelationshipQueryParameter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RequiredCredentialRelationship extends AbstractAttributedType implements Relationship
+{
+   private static final long serialVersionUID = 1L;
+
+   public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() {
+
+      @Override
+      public String getName() {
+         return "realmAgent";
+      }
+   };
+
+
+   protected Agent realmAgent;
+   protected String credentialType;
+   protected boolean input;
+   protected boolean secret;
+
+   public RequiredCredentialRelationship()
+   {
+   }
+
+   @IdentityProperty
+   public Agent getRealmAgent()
+   {
+      return realmAgent;
+   }
+
+   public void setRealmAgent(Agent realmAgent)
+   {
+      this.realmAgent = realmAgent;
+   }
+
+   @AttributeProperty
+   public String getCredentialType()
+   {
+      return credentialType;
+   }
+
+   public void setCredentialType(String credentialType)
+   {
+      this.credentialType = credentialType;
+   }
+
+   @AttributeProperty
+   public boolean isInput()
+   {
+      return input;
+   }
+
+   public void setInput(boolean input)
+   {
+      this.input = input;
+   }
+
+   @AttributeProperty
+   public boolean isSecret()
+   {
+      return secret;
+   }
+
+   public void setSecret(boolean secret)
+   {
+      this.secret = secret;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/ResourceModel.java b/services/src/main/java/org/keycloak/services/model/ResourceModel.java
new file mode 100755
index 0000000..a50dcda
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/ResourceModel.java
@@ -0,0 +1,134 @@
+package org.keycloak.services.model;
+
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.internal.IdentityManagerFactory;
+import org.picketlink.idm.model.Agent;
+import org.picketlink.idm.model.Attribute;
+import org.picketlink.idm.model.Grant;
+import org.picketlink.idm.model.Role;
+import org.picketlink.idm.model.Tier;
+import org.picketlink.idm.model.User;
+import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.query.RelationshipQuery;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ResourceModel
+{
+   public static final String RESOURCE_AGENT_ID = "_resource_";
+   public static final String RESOURCE_NAME = "name";
+   public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth";
+
+   protected Tier tier;
+   protected Agent agent;
+   protected RealmModel realm;
+   protected IdentityManagerFactory factory;
+
+   public ResourceModel(Tier tier, Agent agent, RealmModel realm, IdentityManagerFactory factory)
+   {
+      this.tier = tier;
+      this.agent = agent;
+      this.realm = realm;
+      this.factory = factory;
+   }
+
+   public IdentityManager getIdm()
+   {
+      return factory.createIdentityManager(tier);
+   }
+
+   public void updateResource()
+   {
+      getIdm().update(agent);
+   }
+
+   public String getId()
+   {
+      return tier.getId();
+   }
+
+   public String getName()
+   {
+      return (String)agent.getAttribute(RESOURCE_NAME).getValue();
+   }
+
+   public void setName(String name)
+   {
+      agent.setAttribute(new Attribute<String>(RESOURCE_NAME, name));
+      getIdm().update(agent);
+   }
+
+   public boolean isEnabled()
+   {
+      return agent.isEnabled();
+   }
+
+   public void setEnabled(boolean enabled)
+   {
+      agent.setEnabled(enabled);
+   }
+
+   public boolean isSurrogateAuthRequired()
+   {
+      return (Boolean)agent.getAttribute(RESOURCE_SURROGATE_AUTH).getValue();
+   }
+
+   public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
+   {
+      agent.setAttribute(new Attribute<Boolean>(RESOURCE_SURROGATE_AUTH, surrogateAuthRequired));
+   }
+
+   public List<Role> getRoles()
+   {
+      IdentityQuery<Role> query = getIdm().createIdentityQuery(Role.class);
+      query.setParameter(Role.PARTITION, tier);
+      return query.getResultList();
+   }
+
+   public Set<String> getRoleMappings(User user)
+   {
+      RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
+      query.setParameter(Grant.ASSIGNEE, user);
+      List<Grant> grants = query.getResultList();
+      HashSet<String> set = new HashSet<String>();
+      for (Grant grant : grants)
+      {
+         if (grant.getRole().getPartition().getId().equals(tier.getId()))set.add(grant.getRole().getName());
+      }
+      return set;
+   }
+
+   public void addScope(Agent agent, String roleName)
+   {
+      IdentityManager idm = getIdm();
+      Role role = idm.getRole(roleName);
+      if (role == null) throw new RuntimeException("role not found");
+      ScopeRelationship scope = new ScopeRelationship();
+      scope.setClient(agent);
+      scope.setScope(role);
+
+   }
+
+
+   public Set<String> getScope(Agent agent)
+   {
+      RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
+      query.setParameter(ScopeRelationship.CLIENT, agent);
+      List<ScopeRelationship> scope = query.getResultList();
+      HashSet<String> set = new HashSet<String>();
+      for (ScopeRelationship rel : scope)
+      {
+         if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName());
+      }
+      return set;
+   }
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/model/ScopeRelationship.java b/services/src/main/java/org/keycloak/services/model/ScopeRelationship.java
new file mode 100755
index 0000000..0f01be7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/ScopeRelationship.java
@@ -0,0 +1,50 @@
+package org.keycloak.services.model;
+
+import org.picketlink.idm.model.AbstractAttributedType;
+import org.picketlink.idm.model.Agent;
+import org.picketlink.idm.model.Relationship;
+import org.picketlink.idm.model.Role;
+import org.picketlink.idm.model.annotation.IdentityProperty;
+import org.picketlink.idm.query.RelationshipQueryParameter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ScopeRelationship extends AbstractAttributedType implements Relationship
+{
+   private static final long serialVersionUID = 1L;
+
+   public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() {
+
+      @Override
+      public String getName() {
+         return "client";
+      }
+   };
+
+   protected Agent client;
+   protected Role scope;
+
+   @IdentityProperty
+   public Agent getClient()
+   {
+      return client;
+   }
+
+   public void setClient(Agent client)
+   {
+      this.client = client;
+   }
+
+   @IdentityProperty
+   public Role getScope()
+   {
+      return scope;
+   }
+
+   public void setScope(Role scope)
+   {
+      this.scope = scope;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/model/UserCredentialModel.java b/services/src/main/java/org/keycloak/services/model/UserCredentialModel.java
new file mode 100755
index 0000000..ee8c7e7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/model/UserCredentialModel.java
@@ -0,0 +1,32 @@
+package org.keycloak.services.model;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserCredentialModel
+{
+
+   protected String type;
+   protected String value;
+
+   public String getType()
+   {
+      return type;
+   }
+
+   public void setType(String type)
+   {
+      this.type = type;
+   }
+
+   public String getValue()
+   {
+      return value;
+   }
+
+   public void setValue(String value)
+   {
+      this.value = value;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/service/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/service/AuthenticationManager.java
new file mode 100755
index 0000000..6412afd
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/AuthenticationManager.java
@@ -0,0 +1,87 @@
+package org.keycloak.services.service;
+
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.representations.idm.RequiredCredentialRepresentation;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.RequiredCredentialModel;
+import org.picketlink.idm.credential.Credentials;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.TOTPCredentials;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
+import org.picketlink.idm.model.User;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationManager
+{
+   protected Logger logger = Logger.getLogger(AuthenticationManager.class);
+   public static final String FORM_USERNAME = "username";
+   protected RealmManager adapter;
+
+   public AuthenticationManager(RealmManager adapter)
+   {
+      this.adapter = adapter;
+   }
+
+   public boolean authenticate(RealmModel realm, User user, MultivaluedMap<String, String> formData)
+   {
+      String username = user.getLoginName();
+      Set<String> types = new HashSet<String>();
+
+      for (RequiredCredentialModel credential : realm.getRequiredCredentials())
+      {
+         types.add(credential.getType());
+      }
+
+      if (types.contains(RequiredCredentialRepresentation.PASSWORD))
+      {
+         String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD);
+         if (password == null)
+         {
+            logger.warn("Password not provided");
+            return false;
+         }
+
+         if (types.contains(RequiredCredentialRepresentation.TOTP))
+         {
+            String token = formData.getFirst(RequiredCredentialRepresentation.TOTP);
+            if (token == null)
+            {
+               logger.warn("TOTP token not provided");
+               return false;
+            }
+            TOTPCredentials creds = new TOTPCredentials();
+            creds.setToken(token);
+            creds.setUsername(username);
+            creds.setPassword(new Password(password));
+            realm.getIdm().validateCredentials(creds);
+            if (creds.getStatus() != Credentials.Status.VALID)
+            {
+               return false;
+            }
+         }
+         else
+         {
+            UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password));
+            realm.getIdm().validateCredentials(creds);
+            if (creds.getStatus() != Credentials.Status.VALID)
+            {
+               return false;
+            }
+         }
+      }
+      else
+      {
+         logger.warn("Do not know how to authenticate user");
+         return false;
+      }
+      return true;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/service/RealmFactory.java b/services/src/main/java/org/keycloak/services/service/RealmFactory.java
new file mode 100755
index 0000000..821acc5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/RealmFactory.java
@@ -0,0 +1,354 @@
+package org.keycloak.services.service;
+
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.model.RealmModel;
+import org.picketlink.idm.model.User;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("/realmfactory")
+public class RealmFactory
+{
+   protected RealmManager adapter;
+
+   @Context
+   protected UriInfo uriInfo;
+
+   public RealmFactory(RealmManager adapter)
+   {
+      this.adapter = adapter;
+   }
+
+
+   @POST
+   @Consumes("application/json")
+   public Response importDomain(RealmRepresentation rep)
+   {
+      RealmModel realm = createRealm(rep);
+      UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId());
+      return Response.created(builder.build())
+              //.entity(RealmResource.realmRep(realm, uriInfo))
+              .type(MediaType.APPLICATION_JSON_TYPE).build();
+   }
+
+   protected RealmModel createRealm(RealmRepresentation rep)
+   {
+      //verifyRealmRepresentation(rep);
+
+      RealmModel realm = adapter.create(rep.getRealm());
+      KeyPair keyPair = null;
+      try
+      {
+         keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+      }
+      catch (NoSuchAlgorithmException e)
+      {
+         throw new RuntimeException(e);
+      }
+      realm.setPrivateKey(keyPair.getPrivate());
+      realm.setPublicKey(keyPair.getPublic());
+      realm.setName(rep.getRealm());
+      realm.setEnabled(rep.isEnabled());
+      realm.setTokenLifespan(rep.getTokenLifespan());
+      realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
+      realm.setSslNotRequired(rep.isSslNotRequired());
+      realm.updateRealm();
+
+
+      Map<String, User> userMap = new HashMap<String, User>();
+      return null;
+   }/*
+      RoleModel adminRole = identityManager.create(realm, "admin");
+
+      for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials())
+      {
+         RequiredCredentialModel credential = new RequiredCredentialModel();
+         credential.setType(requiredCred.getType());
+         credential.setInput(requiredCred.isInput());
+         credential.setSecret(requiredCred.isSecret());
+         identityManager.create(realm, credential);
+      }
+
+      for (UserRepresentation userRep : rep.getUsers())
+      {
+         UserModel user = new UserModel();
+         user.setUsername(userRep.getUsername());
+         user.setEnabled(userRep.isEnabled());
+         user = identityManager.create(realm, user);
+         userMap.put(user.getUsername(), user);
+         if (userRep.getCredentials() != null)
+         {
+            for (UserRepresentation.Credential cred : userRep.getCredentials())
+            {
+               UserCredentialModel credential = new UserCredentialModel();
+               credential.setType(cred.getType());
+               credential.setValue(cred.getValue());
+               credential.setHashed(cred.isHashed());
+               identityManager.create(user, credential);
+            }
+         }
+
+         if (userRep.getAttributes() != null)
+         {
+            for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet())
+            {
+               UserAttributeModel attribute = new UserAttributeModel();
+               attribute.setName(entry.getKey());
+               attribute.setValue(entry.getValue());
+               identityManager.create(user, attribute);
+            }
+         }
+      }
+
+      for (RoleMappingRepresentation mapping : rep.getRoleMappings())
+      {
+         RoleMappingModel roleMapping = createRoleMapping(userMap, mapping);
+         UserModel user = userMap.get(mapping.getUsername());
+         identityManager.create(realm, user, roleMapping);
+      }
+
+      if (rep.getScopeMappings() != null)
+      {
+         for (ScopeMappingRepresentation scope : rep.getScopeMappings())
+         {
+            ScopeMappingModel scopeMapping = createScopeMapping(userMap, scope);
+            UserModel user = userMap.get(scope.getUsername());
+            identityManager.create(realm, user, scopeMapping);
+
+         }
+      }
+
+      if (rep.getResources() != null)
+      {
+         for (ResourceRepresentation resourceRep : rep.getResources())
+         {
+            ResourceModel resource = new ResourceModel();
+            resource.setName(resourceRep.getName());
+            resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
+            resource = identityManager.create(realm, resource);
+            if (resourceRep.getRoles() != null)
+            {
+               for (String role : resourceRep.getRoles())
+               {
+                  RoleModel r = identityManager.create(realm, resource, role);
+               }
+            }
+            if (resourceRep.getRoleMappings() != null)
+            {
+               for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings())
+               {
+                  RoleMappingModel roleMapping = createRoleMapping(userMap, mapping);
+                  UserModel user = userMap.get(mapping.getUsername());
+                  identityManager.create(realm, resource, user, roleMapping);
+               }
+            }
+            if (resourceRep.getScopeMappings() != null)
+            {
+               for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings())
+               {
+                  ScopeMappingModel scopeMapping = createScopeMapping(userMap, mapping);
+                  UserModel user = userMap.get(mapping.getUsername());
+                  identityManager.create(realm, resource, user, scopeMapping);
+               }
+            }
+
+         }
+      }
+      return realm;
+   }
+
+   protected RoleMappingModel createRoleMapping(Map<String, UserModel> userMap, RoleMappingRepresentation mapping)
+   {
+      RoleMappingModel roleMapping = new RoleMappingModel();
+      UserModel user = userMap.get(mapping.getUsername());
+      roleMapping.setUserid(user.getId());
+      if (mapping.getSurrogates() != null)
+      {
+         for (String s : mapping.getSurrogates())
+         {
+            UserModel surrogate = userMap.get(s);
+            roleMapping.getSurrogateIds().add(surrogate.getId());
+
+         }
+      }
+      for (String role : mapping.getRoles())
+      {
+         roleMapping.getRoles().add(role);
+      }
+      return roleMapping;
+   }
+
+   protected ScopeMappingModel createScopeMapping(Map<String, UserModel> userMap, ScopeMappingRepresentation mapping)
+   {
+      ScopeMappingModel scopeMapping = new ScopeMappingModel();
+      UserModel user = userMap.get(mapping.getUsername());
+      scopeMapping.setUserid(user.getId());
+      for (String role : mapping.getRoles())
+      {
+         scopeMapping.getRoles().add(role);
+      }
+      return scopeMapping;
+   }
+
+
+   protected void verifyRealmRepresentation(RealmRepresentation rep)
+   {
+      if (rep.getUsers() == null)
+      {
+         throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                 .entity("No realm admin users defined for realm").type("text/plain").build());
+      }
+
+      if (rep.getRequiredCredentials() == null)
+      {
+         throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                 .entity("Realm credential requirements not defined").type("text/plain").build());
+
+      }
+
+      if (rep.getRoleMappings() == null)
+      {
+         throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                 .entity("No realm admin users defined for realm").type("text/plain").build());
+      }
+
+      HashMap<String, UserRepresentation> userReps = new HashMap<String, UserRepresentation>();
+      for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep);
+
+      // make sure there is a user that has admin privileges for the realm
+      Set<UserRepresentation> admins = new HashSet<UserRepresentation>();
+      for (RoleMappingRepresentation mapping : rep.getRoleMappings())
+      {
+         if (!userReps.containsKey(mapping.getUsername()))
+         {
+            throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                    .entity("No users declared for role mapping").type("text/plain").build());
+
+         }
+         for (String role : mapping.getRoles())
+         {
+            if (!role.equals("admin"))
+            {
+               throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                       .entity("There is only an 'admin' role for realms").type("text/plain").build());
+
+            }
+            else
+            {
+               admins.add(userReps.get(mapping.getUsername()));
+            }
+         }
+      }
+      if (admins.size() == 0)
+      {
+         throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                 .entity("No realm admin users defined for realm").type("text/plain").build());
+      }
+
+      // override enabled to false if user does not have at least all of browser or client credentials
+      for (UserRepresentation userRep : rep.getUsers())
+      {
+         if (!userRep.isEnabled())
+         {
+            admins.remove(userRep);
+            continue;
+         }
+         if (userRep.getCredentials() == null)
+         {
+            admins.remove(userRep);
+            userRep.setEnabled(false);
+         }
+         else
+         {
+            boolean hasBrowserCredentials = true;
+            for (RequiredCredentialRepresentation credential : rep.getRequiredCredentials())
+            {
+               boolean hasCredential = false;
+               for (UserRepresentation.Credential cred : userRep.getCredentials())
+               {
+                  if (cred.getType().equals(credential.getType()))
+                  {
+                     hasCredential = true;
+                     break;
+                  }
+               }
+               if (!hasCredential)
+               {
+                  hasBrowserCredentials = false;
+                  break;
+               }
+            }
+            if (!hasBrowserCredentials)
+            {
+               userRep.setEnabled(false);
+               admins.remove(userRep);
+            }
+
+         }
+      }
+
+      if (admins.size() == 0)
+      {
+         throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                 .entity("No realm admin users are enabled or have appropriate credentials").type("text/plain").build());
+      }
+
+      if (rep.getResources() != null)
+      {
+         // check mappings
+         for (ResourceRepresentation resourceRep : rep.getResources())
+         {
+            if (resourceRep.getRoleMappings() != null)
+            {
+               for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings())
+               {
+                  if (!userReps.containsKey(mapping.getUsername()))
+                  {
+                     throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                             .entity("No users declared for role mapping").type("text/plain").build());
+
+                  }
+                  if (mapping.getSurrogates() != null)
+                  {
+                     for (String surrogate : mapping.getSurrogates())
+                     {
+                        if (!userReps.containsKey(surrogate))
+                        {
+                           throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                                   .entity("No users declared for role mapping surrogate").type("text/plain").build());
+                        }
+                     }
+                  }
+                  for (String role : mapping.getRoles())
+                  {
+                     if (!resourceRep.getRoles().contains(role))
+                     {
+                        throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                                .entity("No resource role for role mapping").type("text/plain").build());
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+   */
+
+}
diff --git a/services/src/main/java/org/keycloak/services/service/RealmResource.java b/services/src/main/java/org/keycloak/services/service/RealmResource.java
new file mode 100755
index 0000000..a5baf40
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/RealmResource.java
@@ -0,0 +1,158 @@
+package org.keycloak.services.service;
+
+import org.keycloak.services.IdentityManagerAdapter;
+import org.keycloak.services.model.data.RealmModel;
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.representations.idm.PublishedRealmRepresentation;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("/")
+public class RealmResource
+{
+   protected Logger logger = Logger.getLogger(RealmResource.class);
+   protected IdentityManagerAdapter identityManager;
+   @Context
+   protected UriInfo uriInfo;
+
+   public RealmResource(IdentityManagerAdapter identityManager)
+   {
+      this.identityManager = identityManager;
+   }
+
+   @GET
+   @Path("realms/{realm}")
+   @Produces("application/json")
+   public PublishedRealmRepresentation getRealm(@PathParam("realm") String id)
+   {
+      RealmModel realm = identityManager.getRealm(id);
+      if (realm == null)
+      {
+         logger.debug("realm not found");
+         throw new NotFoundException();
+      }
+      return realmRep(realm, uriInfo);
+   }
+
+   @GET
+   @Path("realms/{realm}.html")
+   @Produces("text/html")
+   public String getRealmHtml(@PathParam("realm") String id)
+   {
+      RealmModel realm = identityManager.getRealm(id);
+      if (realm == null)
+      {
+         logger.debug("realm not found");
+         throw new NotFoundException();
+      }
+      return realmHtml(realm);
+   }
+
+   private String realmHtml(RealmModel realm)
+   {
+      StringBuffer html = new StringBuffer();
+
+      UriBuilder auth = uriInfo.getBaseUriBuilder();
+      auth.path(TokenService.class)
+              .path(TokenService.class, "requestAccessCode");
+      String authUri = auth.build(realm.getId()).toString();
+
+      UriBuilder code = uriInfo.getBaseUriBuilder();
+      code.path(TokenService.class).path(TokenService.class, "accessRequest");
+      String codeUri = code.build(realm.getId()).toString();
+
+      UriBuilder grant = uriInfo.getBaseUriBuilder();
+      grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant");
+      String grantUrl = grant.build(realm.getId()).toString();
+
+      html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>");
+      html.append("<p>auth: ").append(authUri).append("</p>");
+      html.append("<p>code: ").append(codeUri).append("</p>");
+      html.append("<p>grant: ").append(grantUrl).append("</p>");
+      html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>");
+      html.append("</body></html>");
+
+      return html.toString();
+   }
+
+
+   @GET
+   @Path("realms")
+   @Produces("application/json")
+   public Response getRealmsByName(@QueryParam("name") String name)
+   {
+      if (name == null) return Response.noContent().build();
+      List<RealmModel> realms = identityManager.getRealmsByName(name);
+      if (realms.size() == 0) return Response.noContent().build();
+
+      List<PublishedRealmRepresentation> list = new ArrayList<PublishedRealmRepresentation>();
+      for (RealmModel realm : realms)
+      {
+         list.add(realmRep(realm, uriInfo));
+      }
+      GenericEntity<List<PublishedRealmRepresentation>> entity = new GenericEntity<List<PublishedRealmRepresentation>>(list){};
+      return Response.ok(entity).type(MediaType.APPLICATION_JSON_TYPE).build();
+   }
+
+   @GET
+   @Path("realms.html")
+   @Produces("text/html")
+   public String getRealmsByNameHtml(@QueryParam("name") String name)
+   {
+      if (name == null) return "<html><body><h1>No realms with that name</h1></body></html>";
+      List<RealmModel> realms = identityManager.getRealmsByName(name);
+      if (realms.size() == 0) return "<html><body><h1>No realms with that name</h1></body></html>";
+      if (realms.size() == 1) return realmHtml(realms.get(0));
+
+      StringBuffer html = new StringBuffer();
+      html.append("<html><body><h1>Realms</h1>");
+      for (RealmModel realm : realms)
+      {
+         html.append("<p><a href=\"").append(uriInfo.getBaseUriBuilder().path("realms").path(realm.getId() + ".html"))
+                 .append("\">").append(realm.getId()).append("</a></p>");
+      }
+      html.append("</body></html>");
+      return html.toString();
+   }
+
+
+   public static PublishedRealmRepresentation realmRep(RealmModel realm, UriInfo uriInfo)
+   {
+      PublishedRealmRepresentation rep = new PublishedRealmRepresentation();
+      rep.setRealm(realm.getName());
+      rep.setSelf(uriInfo.getRequestUri().toString());
+      rep.setPublicKeyPem(realm.getPublicKeyPem());
+
+      UriBuilder auth = uriInfo.getBaseUriBuilder();
+      auth.path(TokenService.class)
+              .path(TokenService.class, "requestAccessCode");
+      rep.setAuthorizationUrl(auth.build(realm.getId()).toString());
+
+      UriBuilder code = uriInfo.getBaseUriBuilder();
+      code.path(TokenService.class).path(TokenService.class, "accessRequest");
+      rep.setCodeUrl(code.build(realm.getId()).toString());
+
+      UriBuilder grant = uriInfo.getBaseUriBuilder();
+      grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant");
+      String grantUrl = grant.build(realm.getId()).toString();
+      rep.setGrantUrl(grantUrl);
+      return rep;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/service/RegistrationService.java b/services/src/main/java/org/keycloak/services/service/RegistrationService.java
new file mode 100755
index 0000000..e8ff93d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/RegistrationService.java
@@ -0,0 +1,65 @@
+package org.keycloak.services.service;
+
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.UserCredentialModel;
+import org.picketlink.idm.model.Realm;
+import org.picketlink.idm.model.SimpleUser;
+import org.picketlink.idm.model.User;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("/registrations")
+public class RegistrationService
+{
+   protected RealmManager adapter;
+   protected RealmModel defaultRealm;
+
+   @Context
+   protected UriInfo uriInfo;
+
+   public RegistrationService(RealmManager adapter)
+   {
+      this.adapter = adapter;
+      defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM);
+   }
+
+
+
+   @POST
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Response register(UserRepresentation newUser)
+   {
+      User user = defaultRealm.getIdm().getUser(newUser.getUsername());
+      if (user != null)
+      {
+         return Response.status(400).type("text/plain").entity("user exists").build();
+      }
+
+      user = new SimpleUser(newUser.getUsername());
+      defaultRealm.getIdm().add(user);
+      for (UserRepresentation.Credential cred : newUser.getCredentials())
+      {
+         UserCredentialModel credModel = new UserCredentialModel();
+         credModel.setType(cred.getType());
+         credModel.setValue(cred.getValue());
+         defaultRealm.updateCredential(user, credModel);
+      }
+      URI uri = uriInfo.getBaseUriBuilder().path(RealmFactory.class).path(user.getLoginName()).build();
+      return Response.created(uri).build();
+   }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/service/SkeletonKeyApplication.java b/services/src/main/java/org/keycloak/services/service/SkeletonKeyApplication.java
new file mode 100755
index 0000000..96ead5e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/SkeletonKeyApplication.java
@@ -0,0 +1,57 @@
+package org.keycloak.services.service;
+
+import org.infinispan.Cache;
+import org.infinispan.manager.DefaultCacheManager;
+import org.keycloak.SkeletonKeyContextResolver;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@ApplicationPath("/")
+public class SkeletonKeyApplication extends Application
+{
+   protected Set<Object> singletons = new HashSet<Object>();
+   protected Set<Class<?>> classes = new HashSet<Class<?>>();
+
+   public SkeletonKeyApplication()
+   {
+      Cache cache = getCache();
+      singletons.add(new TokenService(null));
+      singletons.add(new RealmFactory(null));
+      singletons.add(new RealmResource(null));
+      classes.add(SkeletonKeyContextResolver.class);
+   }
+
+   @Override
+   public Set<Class<?>> getClasses()
+   {
+      return classes;
+   }
+
+   @Override
+   public Set<Object> getSingletons()
+   {
+      return singletons;
+   }
+
+   protected Cache getCache()
+   {
+      try
+      {
+         InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("skeleton-key.xml");
+         return new DefaultCacheManager(is).getCache("skeleton-key");
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/service/TokenManager.java b/services/src/main/java/org/keycloak/services/service/TokenManager.java
new file mode 100755
index 0000000..b258031
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/TokenManager.java
@@ -0,0 +1,176 @@
+package org.keycloak.services.service;
+
+import org.jboss.resteasy.jose.Base64Url;
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.representations.SkeletonKeyScope;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.ResourceModel;
+import org.picketlink.idm.model.User;
+
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class TokenManager
+{
+   protected RealmManager adapter;
+
+   public TokenManager(RealmManager adapter)
+   {
+      this.adapter = adapter;
+   }
+
+   public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user)
+   {
+      SkeletonKeyToken token = new SkeletonKeyToken();
+      token.id(adapter.generateId());
+      token.principal(user.getLoginName());
+      token.audience(realm.getName());
+      token.issuedNow();
+      token.issuedFor(client.getLoginName());
+      if (realm.getTokenLifespan() > 0)
+      {
+         token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
+      }
+      Map<String, ResourceModel> resourceMap = realm.getResourceMap();
+
+      for (String res : scope.keySet())
+      {
+         ResourceModel resource = resourceMap.get(res);
+         Set<String> scopeMapping = resource.getScope(client);
+         Set<String> roleMapping = resource.getRoleMappings(user);
+         SkeletonKeyToken.Access access = token.addAccess(resource.getName());
+         for (String role : scope.get(res))
+         {
+            if (!scopeMapping.contains("*") && !scopeMapping.contains(role))
+            {
+               throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build());
+            }
+            if (!roleMapping.contains(role))
+            {
+               throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build());
+
+            }
+            access.addRole(role);
+         }
+      }
+      return token;
+   }
+
+   public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user)
+   {
+      SkeletonKeyScope scope = decodeScope(scopeParam);
+      return createScopedToken(scope, realm, client, user);
+   }
+
+   public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user)
+   {
+      Set<String> mapping = realm.getScope(client);
+      if (!mapping.contains("*"))
+      {
+         throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized to request a user login.</p>").type("text/html").build());
+      }
+      SkeletonKeyToken token = createAccessToken(realm, user);
+      token.issuedFor(client.getLoginName());
+      return token;
+
+   }
+
+   public SkeletonKeyScope decodeScope(String scopeParam)
+   {
+      SkeletonKeyScope scope = null;
+      byte[] bytes = Base64Url.decode(scopeParam);
+      try
+      {
+         scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes);
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      return scope;
+   }
+
+
+   public SkeletonKeyToken createAccessToken(RealmModel realm, User user)
+   {
+      List<ResourceModel> resources = realm.getResources();
+      SkeletonKeyToken token = new SkeletonKeyToken();
+      token.id(adapter.generateId());
+      token.issuedNow();
+      token.principal(user.getLoginName());
+      token.audience(realm.getId());
+      if (realm.getTokenLifespan() > 0)
+      {
+         token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
+      }
+
+      Set<String> realmMapping = realm.getRoleMappings(user);
+
+      if (realmMapping != null && realmMapping.size() > 0)
+      {
+         SkeletonKeyToken.Access access = new SkeletonKeyToken.Access();
+         for (String role : realmMapping)
+         {
+            access.addRole(role);
+         }
+         token.setRealmAccess(access);
+      }
+      if (resources != null)
+      {
+         for (ResourceModel resource : resources)
+         {
+            Set<String> mapping = resource.getRoleMappings(user);
+            if (mapping == null) continue;
+            SkeletonKeyToken.Access access = token.addAccess(resource.getName())
+                    .verifyCaller(resource.isSurrogateAuthRequired());
+            for (String role : mapping)
+            {
+               access.addRole(role);
+            }
+         }
+      }
+      return token;
+   }
+
+   public SkeletonKeyToken createIdentityToken(RealmModel realm, String username)
+   {
+      SkeletonKeyToken token = new SkeletonKeyToken();
+      token.id(adapter.generateId());
+      token.issuedNow();
+      token.principal(username);
+      token.audience(realm.getId());
+      if (realm.getTokenLifespan() > 0)
+      {
+         token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
+      }
+      return token;
+   }
+
+   public String encodeToken(RealmModel realm, SkeletonKeyToken token)
+   {
+      byte[] tokenBytes = null;
+      try
+      {
+         tokenBytes = JsonSerialization.toByteArray(token, false);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException(e);
+      }
+      String encodedToken = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(realm.getPrivateKey());
+      return encodedToken;
+   }
+}
diff --git a/services/src/main/java/org/keycloak/services/service/TokenService.java b/services/src/main/java/org/keycloak/services/service/TokenService.java
new file mode 100755
index 0000000..6edd0af
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/service/TokenService.java
@@ -0,0 +1,593 @@
+package org.keycloak.services.service;
+
+import org.jboss.resteasy.jose.Base64Url;
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jose.jws.JWSInput;
+import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.SkeletonKeyScope;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.RequiredCredentialModel;
+import org.keycloak.services.model.ResourceModel;
+import org.picketlink.idm.model.User;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Providers;
+import java.io.UnsupportedEncodingException;
+import java.security.PrivateKey;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("/realms")
+public class TokenService
+{
+   public static class AccessCode
+   {
+      protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
+      protected long expiration;
+      protected SkeletonKeyToken token;
+      protected User client;
+
+      public boolean isExpired()
+      {
+         return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
+      }
+
+      public String getId()
+      {
+         return id;
+      }
+
+      public long getExpiration()
+      {
+         return expiration;
+      }
+
+      public void setExpiration(long expiration)
+      {
+         this.expiration = expiration;
+      }
+
+      public SkeletonKeyToken getToken()
+      {
+         return token;
+      }
+
+      public void setToken(SkeletonKeyToken token)
+      {
+         this.token = token;
+      }
+
+      public User getClient()
+      {
+         return client;
+      }
+
+      public void setClient(User client)
+      {
+         this.client = client;
+      }
+   }
+
+   protected RealmManager adapter;
+   protected TokenManager tokenManager;
+   protected AuthenticationManager authManager;
+   protected Logger logger = Logger.getLogger(TokenService.class);
+   protected Map<String, AccessCode> accessCodeMap = new HashMap<String, AccessCode>();
+   @Context
+   protected UriInfo uriInfo;
+   @Context
+   protected Providers providers;
+   @Context
+   protected SecurityContext securityContext;
+   @Context
+   protected HttpHeaders headers;
+
+   private static AtomicLong counter = new AtomicLong(1);
+   private static String generateId()
+   {
+      return counter.getAndIncrement() + "." + UUID.randomUUID().toString();
+   }
+
+   public TokenService(RealmManager adapter)
+   {
+      this.adapter = adapter;
+      this.tokenManager = new TokenManager(adapter);
+      this.authManager = new AuthenticationManager(adapter);
+   }
+
+   @Path("{realm}/grants/identity-token")
+   @POST
+   @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+   @Produces(MediaType.APPLICATION_JSON)
+   public Response identityTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form)
+   {
+      String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
+      if (username == null)
+      {
+         throw new NotAuthorizedException("No user");
+      }
+      RealmModel realm = adapter.getRealm(realmId);
+      if (realm == null)
+      {
+         throw new NotFoundException("Realm not found");
+      }
+      if (!realm.isEnabled())
+      {
+         throw new NotAuthorizedException("Disabled realm");
+      }
+      User user = realm.getIdm().getUser(username);
+      if (user == null)
+      {
+         throw new NotAuthorizedException("No user");
+      }
+      if (!user.isEnabled())
+      {
+         throw new NotAuthorizedException("Disabled user.");
+      }
+      SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username);
+      String encoded = tokenManager.encodeToken(realm, token);
+      AccessTokenResponse res = accessTokenResponse(token, encoded);
+      return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
+   }
+
+   @Path("{realm}/grants/access")
+   @POST
+   @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+   @Produces(MediaType.APPLICATION_JSON)
+   public Response accessTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form)
+   {
+      String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
+      if (username == null)
+      {
+         throw new NotAuthorizedException("No user");
+      }
+      RealmModel realm = adapter.getRealm(realmId);
+      if (realm == null)
+      {
+         throw new NotFoundException("Realm not found");
+      }
+      if (!realm.isEnabled())
+      {
+         throw new NotAuthorizedException("Disabled realm");
+      }
+      User user = realm.getIdm().getUser(username);
+      if (user == null)
+      {
+         throw new NotAuthorizedException("No user");
+      }
+      if (!user.isEnabled())
+      {
+         throw new NotAuthorizedException("Disabled user.");
+      }
+      if (authManager.authenticate(realm, user, form))
+      {
+         throw new NotAuthorizedException("Auth failed");
+      }
+      SkeletonKeyToken token = tokenManager.createAccessToken(realm, user);
+      String encoded = tokenManager.encodeToken(realm, token);
+      AccessTokenResponse res = accessTokenResponse(token, encoded);
+      return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
+   }
+
+   @Path("{realm}/auth/request/login")
+   @POST
+   @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+   public Response login(@PathParam("realm") String realmId,
+                         MultivaluedMap<String, String> formData)
+   {
+      String clientId = formData.getFirst("client_id");
+      String scopeParam = formData.getFirst("scope");
+      String state = formData.getFirst("state");
+      String redirect = formData.getFirst("redirect_uri");
+
+      RealmModel realm = adapter.getRealm(realmId);
+      if (realm == null)
+      {
+         throw new NotFoundException("Realm not found");
+      }
+      if (!realm.isEnabled())
+      {
+         return Response.ok("Realm not enabled").type("text/html").build();
+      }
+      User client = realm.getIdm().getUser(clientId);
+      if (client == null)
+      {
+         throw new NotAuthorizedException("No client");
+      }
+      if (!client.isEnabled())
+      {
+         return Response.ok("Requester not enabled").type("text/html").build();
+      }
+      String username = formData.getFirst("username");
+      User user = realm.getIdm().getUser(username);
+      if (user == null)
+      {
+         logger.debug("user not found");
+         return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client);
+      }
+      if (!user.isEnabled())
+      {
+         return Response.ok("Your account is not enabled").type("text/html").build();
+
+      }
+      boolean authenticated = authManager.authenticate(realm, user, formData);
+      if (!authenticated) return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client);
+
+      SkeletonKeyToken token = null;
+      if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user);
+      else token = tokenManager.createLoginToken(realm, client, user);
+
+      AccessCode code = new AccessCode();
+      code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
+      code.setToken(token);
+      code.setClient(client);
+      synchronized (accessCodeMap)
+      {
+         accessCodeMap.put(code.getId(), code);
+      }
+      String accessCode = null;
+      try
+      {
+         accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
+      }
+      catch (UnsupportedEncodingException e)
+      {
+         throw new RuntimeException(e);
+      }
+      UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode);
+      if (state != null) redirectUri.queryParam("state", state);
+      return Response.status(302).location(redirectUri.build()).build();
+   }
+
+   @Path("{realm}/access/codes")
+   @POST
+   @Produces("application/json")
+   public Response accessRequest(@PathParam("realm") String realmId,
+                                 MultivaluedMap<String, String> formData)
+   {
+      RealmModel realm = adapter.getRealm(realmId);
+      if (realm == null)
+      {
+         throw new NotFoundException("Realm not found");
+      }
+      if (!realm.isEnabled())
+      {
+         throw new NotAuthorizedException("Realm not enabled");
+      }
+
+      String code = formData.getFirst("code");
+      if (code == null)
+      {
+         logger.debug("code not specified");
+         Map<String, String> error = new HashMap<String, String>();
+         error.put("error", "invalid_request");
+         error.put("error_description", "code not specified");
+         return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+
+      }
+      String client_id = formData.getFirst("client_id");
+      if (client_id == null)
+      {
+         logger.debug("client_id not specified");
+         Map<String, String> error = new HashMap<String, String>();
+         error.put("error", "invalid_request");
+         error.put("error_description", "client_id not specified");
+         return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+      }
+      User client = realm.getIdm().getUser(client_id);
+      if (client == null)
+      {
+         logger.debug("Could not find user");
+         Map<String, String> error = new HashMap<String, String>();
+         error.put("error", "invalid_client");
+         error.put("error_description", "Could not find user");
+         return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+      }
+
+      if (!client.isEnabled())
+      {
+         logger.debug("user is not enabled");
+         Map<String, String> error = new HashMap<String, String>();
+         error.put("error", "invalid_client");
+         error.put("error_description", "User is not enabled");
+         return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+      }
+
+      boolean authenticated = authManager.authenticate(realm, client, formData);
+      if (!authenticated)
+      {
+         Map<String, String> error = new HashMap<String, String>();
+         error.put("error", "unauthorized_client");
+         return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+      }
+
+
+
+      JWSInput input = new JWSInput(code, providers);
+      boolean verifiedCode = false;
+      try
+      {
+         verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
+      }
+      catch (Exception ignored)
+      {
+         logger.debug("Failed to verify signature", ignored);
+      }
+      if (!verifiedCode)
+      {
+         Map<String, String> res = new HashMap<String, String>();
+         res.put("error", "invalid_grant");
+         res.put("error_description", "Unable to verify code signature");
+         return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+      }
+      String key = input.readContent(String.class);
+      AccessCode accessCode = null;
+      synchronized (accessCodeMap)
+      {
+         accessCode = accessCodeMap.remove(key);
+      }
+      if (accessCode == null)
+      {
+         Map<String, String> res = new HashMap<String, String>();
+         res.put("error", "invalid_grant");
+         res.put("error_description", "Code not found");
+         return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+      }
+      if (accessCode.isExpired())
+      {
+         Map<String, String> res = new HashMap<String, String>();
+         res.put("error", "invalid_grant");
+         res.put("error_description", "Code is expired");
+         return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+      }
+      if (!accessCode.getToken().isActive())
+      {
+         Map<String, String> res = new HashMap<String, String>();
+         res.put("error", "invalid_grant");
+         res.put("error_description", "Token expired");
+         return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+      }
+      if (!client.getId().equals(accessCode.getClient().getId()))
+      {
+         Map<String, String> res = new HashMap<String, String>();
+         res.put("error", "invalid_grant");
+         res.put("error_description", "Auth error");
+         return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+      }
+      AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
+      return Response.ok(res).build();
+
+   }
+
+   protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token)
+   {
+      byte[] tokenBytes = null;
+      try
+      {
+         tokenBytes = JsonSerialization.toByteArray(token, false);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException(e);
+      }
+      String encodedToken = new JWSBuilder()
+              .content(tokenBytes)
+              .rsa256(privateKey);
+
+      return accessTokenResponse(token, encodedToken);
+   }
+
+   protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken)
+   {
+      AccessTokenResponse res = new AccessTokenResponse();
+      res.setToken(encodedToken);
+      res.setTokenType("bearer");
+      if (token.getExpiration() != 0)
+      {
+         long time = token.getExpiration() - (System.currentTimeMillis() / 1000);
+         res.setExpiresIn(time);
+      }
+      return res;
+   }
+
+   @Path("{realm}/auth/request")
+   @GET
+   public Response requestAccessCode(@PathParam("realm") String realmId,
+                                     @QueryParam("response_type") String responseType,
+                                     @QueryParam("redirect_uri") String redirect,
+                                     @QueryParam("client_id") String clientId,
+                                     @QueryParam("scope") String scopeParam,
+                                     @QueryParam("state") String state)
+   {
+      RealmModel realm = adapter.getRealm(realmId);
+      if (realm == null)
+      {
+         throw new NotFoundException("Realm not found");
+      }
+      if (!realm.isEnabled())
+      {
+         throw new NotAuthorizedException("Realm not enabled");
+      }
+      User client = realm.getIdm().getUser(clientId);
+      if (client == null)
+         return Response.ok("<h1>Security Alert</h1><p>Unknown client trying to get access to your account.</p>").type("text/html").build();
+
+      return loginForm(null, redirect, clientId, scopeParam, state, realm, client);
+   }
+
+   private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client)
+   {
+      StringBuffer html = new StringBuffer();
+      if (scopeParam != null)
+      {
+         html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
+         if (validationError != null)
+         {
+            try
+            {
+               Thread.sleep(1000); // put in a delay
+            }
+            catch (InterruptedException e)
+            {
+               throw new RuntimeException(e);
+            }
+            html.append("<p/><p><b>").append(validationError).append("</b></p>");
+         }
+         html.append("<p>A Third Party is requesting access to the following resources</p>");
+         html.append("<table>");
+         SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam);
+         Map<String, ResourceModel> resourceMap = realm.getResourceMap();
+
+         for (String res : scope.keySet())
+         {
+            ResourceModel resource = resourceMap.get(res);
+            html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
+            Set<String> scopeMapping = resource.getScope(client);
+            for (String role : scope.get(res))
+            {
+               html.append(" ").append(role);
+               if (!scopeMapping.contains("*") && !scopeMapping.contains(role))
+               {
+                  return Response.ok("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build();
+               }
+            }
+            html.append("</td></tr>");
+         }
+         html.append("</table><p>To Authorize, please login below</p>");
+      }
+      else
+      {
+         Set<String> scopeMapping = realm.getScope(client);
+         if (scopeMapping.contains("*"))
+         {
+            html.append("<h1>Login For ").append(realm.getName()).append(" Realm</h1>");
+            if (validationError != null)
+            {
+               try
+               {
+                  Thread.sleep(1000); // put in a delay
+               }
+               catch (InterruptedException e)
+               {
+                  throw new RuntimeException(e);
+               }
+               html.append("<p/><p><b>").append(validationError).append("</b></p>");
+            }
+         }
+         else
+         {
+            html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
+            if (validationError != null)
+            {
+               try
+               {
+                  Thread.sleep(1000); // put in a delay
+               }
+               catch (InterruptedException e)
+               {
+                  throw new RuntimeException(e);
+               }
+               html.append("<p/><p><b>").append(validationError).append("</b></p>");
+            }
+            SkeletonKeyScope scope = new SkeletonKeyScope();
+            List<ResourceModel> resources = realm.getResources();
+            boolean found = false;
+            for (ResourceModel resource : resources)
+            {
+               Set<String> resourceScope = resource.getScope(client);
+               if (resourceScope == null) continue;
+               if (resourceScope.size() == 0) continue;
+               if (!found)
+               {
+                  found = true;
+                  html.append("<p>A Third Party is requesting access to the following resources</p>");
+                  html.append("<table>");
+               }
+               html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
+               // todo add description of role
+               for (String role : resourceScope)
+               {
+                  html.append(" ").append(role);
+                  scope.add(resource.getName(), role);
+               }
+            }
+            if (!found)
+            {
+               return Response.ok("<h1>Security Alert</h1><p>Known client not authorized to access this realm.</p>").type("text/html").build();
+            }
+            html.append("</table>");
+            try
+            {
+               String json = JsonSerialization.toString(scope, false);
+               scopeParam = Base64Url.encode(json.getBytes("UTF-8"));
+            }
+            catch (Exception e)
+            {
+               throw new RuntimeException(e);
+            }
+
+         }
+      }
+
+      UriBuilder formActionUri = uriInfo.getBaseUriBuilder().path(TokenService.class).path(TokenService.class, "login");
+      String action = formActionUri.build(realm.getId()).toString();
+      html.append("<form action=\"").append(action).append("\" method=\"POST\">");
+      html.append("Username: <input type=\"text\" name=\"username\" size=\"20\"><br>");
+
+      for (RequiredCredentialModel credential : realm.getRequiredCredentials())
+      {
+         if (!credential.isInput()) continue;
+         html.append(credential.getType()).append(": ");
+         if (credential.isSecret())
+         {
+            html.append("<input type=\"password\" name=\"").append(credential.getType()).append("\"  size=\"20\"><br>");
+
+         } else
+         {
+            html.append("<input type=\"text\" name=\"").append(credential.getType()).append("\"  size=\"20\"><br>");
+         }
+      }
+      html.append("<input type=\"hidden\" name=\"client_id\" value=\"").append(clientId).append("\">");
+      if (scopeParam != null)
+      {
+         html.append("<input type=\"hidden\" name=\"scope\" value=\"").append(scopeParam).append("\">");
+      }
+      if (state != null) html.append("<input type=\"hidden\" name=\"state\" value=\"").append(state).append("\">");
+      html.append("<input type=\"hidden\" name=\"redirect_uri\" value=\"").append(redirect).append("\">");
+      html.append("<input type=\"submit\" value=\"");
+      if (scopeParam == null) html.append("Login");
+      else html.append("Grant Access");
+      html.append("\">");
+      html.append("</form>");
+      return Response.ok(html.toString()).type("text/html").build();
+   }
+}
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
new file mode 100755
index 0000000..998bedd
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -0,0 +1,159 @@
+package org.keycloak.test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.keycloak.representations.idm.RequiredCredentialRepresentation;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.RealmResourceRelationship;
+import org.keycloak.services.model.RequiredCredentialModel;
+import org.keycloak.services.model.RequiredCredentialRelationship;
+import org.keycloak.services.model.ScopeRelationship;
+import org.keycloak.services.model.UserCredentialModel;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.config.IdentityConfigurationBuilder;
+import org.picketlink.idm.credential.Credentials;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
+import org.picketlink.idm.file.internal.FileUtils;
+import org.picketlink.idm.internal.IdentityManagerFactory;
+import org.picketlink.idm.model.Realm;
+import org.picketlink.idm.model.Role;
+import org.picketlink.idm.model.SimpleRole;
+import org.picketlink.idm.model.SimpleUser;
+import org.picketlink.idm.model.User;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AdapterTest
+{
+   private static IdentityManagerFactory factory;
+   public static final String WORKING_DIRECTORY = "/tmp/keycloak";
+   public RealmManager adapter;
+   public RealmModel realmModel;
+   @Before
+   public void before() throws Exception
+   {
+      after();
+      factory = createFactory();
+      adapter = new RealmManager(factory);
+   }
+
+   private static IdentityManagerFactory createFactory() {
+      IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
+
+      builder
+              .stores()
+              .file()
+              .addRealm(Realm.DEFAULT_REALM)
+              .workingDirectory(WORKING_DIRECTORY)
+              .preserveState(true)
+              .supportAllFeatures()
+              .supportRelationshipType(RealmResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class);
+
+      return new IdentityManagerFactory(builder.build());
+   }
+
+   @After
+   public void after() throws Exception
+   {
+      File file = new File(WORKING_DIRECTORY);
+      FileUtils.delete(file);
+      Thread.sleep(10); // my windows machine seems to have delays on deleting files sometimes
+   }
+
+   @Test
+   public void test1CreateRealm() throws Exception
+   {
+      realmModel = adapter.create("JUGGLER");
+      realmModel.setAccessCodeLifespan(100);
+      realmModel.setCookieLoginAllowed(true);
+      realmModel.setEnabled(true);
+      realmModel.setName("JUGGLER");
+      realmModel.setPrivateKeyPem("0234234");
+      realmModel.setPublicKeyPem("0234234");
+      realmModel.setTokenLifespan(1000);
+      realmModel.updateRealm();
+
+      System.out.println(realmModel.getId());
+      realmModel = adapter.getRealm(realmModel.getId());
+      Assert.assertNotNull(realmModel);
+      Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
+      Assert.assertEquals(realmModel.getTokenLifespan(), 1000);
+      Assert.assertEquals(realmModel.isEnabled(), true);
+      Assert.assertEquals(realmModel.getName(), "JUGGLER");
+      Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
+      Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
+   }
+
+   @Test
+   public void test2RequiredCredential() throws Exception
+   {
+      test1CreateRealm();
+      RequiredCredentialModel creds = new RequiredCredentialModel();
+      creds.setSecret(true);
+      creds.setType(RequiredCredentialRepresentation.PASSWORD);
+      creds.setInput(true);
+      realmModel.addRequiredCredential(creds);
+      creds = new RequiredCredentialModel();
+      creds.setSecret(true);
+      creds.setType(RequiredCredentialRepresentation.TOTP);
+      creds.setInput(true);
+      realmModel.addRequiredCredential(creds);
+      List<RequiredCredentialModel> storedCreds = realmModel.getRequiredCredentials();
+      Assert.assertEquals(2, storedCreds.size());
+      boolean totp = false;
+      boolean password = false;
+      for (RequiredCredentialModel cred : storedCreds)
+      {
+         if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true;
+         else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true;
+      }
+      Assert.assertTrue(totp);
+      Assert.assertTrue(password);
+   }
+
+   @Test
+   public void testCredentialValidation() throws Exception
+   {
+      test1CreateRealm();
+      User user = new SimpleUser("bburke");
+      realmModel.getIdm().add(user);
+      UserCredentialModel cred = new UserCredentialModel();
+      cred.setType(RequiredCredentialRepresentation.PASSWORD);
+      cred.setValue("geheim");
+      realmModel.updateCredential(user, cred);
+      IdentityManager idm = realmModel.getIdm();
+      UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user.getLoginName(), new Password("geheim"));
+      idm.validateCredentials(creds);
+      Assert.assertEquals(creds.getStatus(), Credentials.Status.VALID);
+   }
+
+   @Test
+   public void testRoles() throws Exception
+   {
+      test1CreateRealm();
+      IdentityManager idm = realmModel.getIdm();
+      idm.add(new SimpleRole("admin"));
+      idm.add(new SimpleRole("user"));
+      List<Role> roles = realmModel.getRoles();
+      Assert.assertEquals(2, roles.size());
+      SimpleUser user = new SimpleUser("bburke");
+      idm.add(user);
+      Role role = idm.getRole("user");
+      idm.grantRole(user, role);
+      Assert.assertTrue(idm.hasRole(user, role));
+   }
+
+
+}
diff --git a/services/src/test/resources/META-INF/persistence.xml b/services/src/test/resources/META-INF/persistence.xml
new file mode 100755
index 0000000..1e5524f
--- /dev/null
+++ b/services/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,29 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+    version="1.0">
+    <persistence-unit name="identitydb" transaction-type="RESOURCE_LOCAL">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+        
+        <class>org.picketlink.idm.jpa.schema.IdentityObject</class>
+        <class>org.picketlink.idm.jpa.schema.PartitionObject</class>
+        <class>org.picketlink.idm.jpa.schema.RelationshipObject</class>
+        <class>org.picketlink.idm.jpa.schema.RelationshipIdentityObject</class>
+        <class>org.picketlink.idm.jpa.schema.RelationshipIdentityWeakObject</class>
+        <class>org.picketlink.idm.jpa.schema.RelationshipObjectAttribute</class>
+        <class>org.picketlink.idm.jpa.schema.IdentityObjectAttribute</class>
+        <class>org.picketlink.idm.jpa.schema.CredentialObject</class>
+        <class>org.picketlink.idm.jpa.schema.CredentialObjectAttribute</class>
+        
+        <properties>
+            <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+            <property name="hibernate.connection.username" value="sa"/>
+            <property name="hibernate.connection.password" value=""/>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+			<property name="hibernate.show_sql" value="true" />
+			<property name="hibernate.format_sql" value="true" />
+        </properties>
+    </persistence-unit>
+
+</persistence>
diff --git a/services/src/test/resources/testrealm.json b/services/src/test/resources/testrealm.json
new file mode 100644
index 0000000..6002c79
--- /dev/null
+++ b/services/src/test/resources/testrealm.json
@@ -0,0 +1,101 @@
+{
+   "realm" : "test-realm",
+   "enabled" : true,
+   "tokenLifespan" : 6000,
+   "accessCodeLifespan" : 30,
+   "requiredCredentials" : [
+        {
+            "type" : "Password",
+            "input" : true,
+            "secret" : true
+        }
+   ],
+   "users" : [
+      {
+            "username" : "wburke",
+            "enabled" : true,
+            "attributes" : {
+                "email" : "bburke@redhat.com"
+            },
+            "credentials" : [
+                { "type" : "Password",
+                 "value" : "userpassword" }
+            ]
+      },
+      {
+            "username" : "loginclient",
+            "enabled" : true,
+            "credentials" : [
+                { "type" : "Password",
+                 "value" : "clientpassword" }
+            ]
+      },
+      {
+            "username" : "admin",
+            "enabled" : true,
+            "credentials" : [
+                { "type" : "Password",
+                 "value" : "adminpassword" }
+            ]
+      },
+      {
+            "username" : "oauthclient",
+            "enabled" : true,
+            "credentials" : [
+                { "type" : "Password",
+                 "value" : "clientpassword" }
+            ]
+      }
+   ],
+   "roleMappings" : [
+       {
+          "username" : "admin",
+          "roles" : ["admin"]
+       }
+   ],
+   "scopeMappings" : [
+       {
+          "username" : "loginclient",
+          "roles" : ["login"]
+       }
+   ],
+   "resources" : [
+       {
+          "name" : "Application",
+          "roles" : ["admin", "user"],
+          "roleMappings" : [
+             {
+                "username" : "wburke",
+                "roles" : ["user"]
+             },
+             {
+                "username" : "admin",
+                "roles" : ["admin"]
+             }
+          ],
+          "scopeMappings" : [
+             {
+                "username" : "oauthclient",
+                "roles" : ["user"]
+             }
+          ]
+       },
+              {
+                 "name" : "OtherApp",
+                 "roles" : ["admin", "user"],
+                 "roleMappings" : [
+                    {
+                       "username" : "wburke",
+                       "roles" : ["user"]
+                    },
+                    {
+                       "username" : "admin",
+                       "roles" : ["admin"]
+                    }
+                 ]
+              }
+
+   ]
+
+
+}
\ No newline at end of file