keycloak-aplcache

KEYCLOAK-75 Retrieve user profile

11/3/2013 11:33:22 AM

Details

diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 8ffbf94..5fe4475 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -52,24 +52,27 @@ public class AuthenticationManager {
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
         String cookiePath = uri.getPath();
-        return createLoginCookie(realm, user, cookieName, cookiePath);
+        return createLoginCookie(realm, user, null, cookieName, cookiePath);
     }
 
     public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
         String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
         URI uri = SaasService.saasCookiePath(uriInfo).build();
         String cookiePath = uri.getPath();
-        return createLoginCookie(realm, user, cookieName, cookiePath);
+        return createLoginCookie(realm, user, null, cookieName, cookiePath);
     }
 
-    public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, URI uri) {
+    public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, UserModel client, URI uri) {
         String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE;
         String cookiePath = uri.getPath();
-        return createLoginCookie(realm, user, cookieName, cookiePath);
+        return createLoginCookie(realm, user, client, cookieName, cookiePath);
     }
 
-    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, String cookieName, String cookiePath) {
+    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserModel client, String cookieName, String cookiePath) {
         SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
+        if (client != null) {
+            identityToken.issuedFor(client.getLoginName());
+        }
         String encoded = encodeToken(realm, identityToken);
         boolean secureOnly = !realm.isSslNotRequired();
         logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
@@ -123,15 +126,17 @@ public class AuthenticationManager {
 
     public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
-        return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+        Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+        return auth != null ? auth.getUser() : null;
     }
 
     public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
         String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
-        return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+        Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+        return auth != null ? auth.getUser() : null;
     }
 
-    public UserModel authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+    public Auth authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
         String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE;
         return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
     }
@@ -140,11 +145,19 @@ public class AuthenticationManager {
         UserModel user = authenticateSaasIdentityCookie(realm, uriInfo, headers);
         if (user != null) return user;
 
+        Auth auth = authenticateBearerToken(realm, headers);
+        return auth != null ? auth.getUser() : null;
+    }
+
+    public Auth authenticateAccountIdentity(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+        Auth auth = authenticateAccountIdentityCookie(realm, uriInfo, headers);
+        if (auth != null) return auth;
+
         return authenticateBearerToken(realm, headers);
     }
 
 
-    protected UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) {
+    protected Auth authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) {
         Cookie cookie = headers.getCookies().get(cookieName);
         if (cookie == null) {
             logger.debug("authenticateCookie could not find cookie: {0}", cookieName);
@@ -159,13 +172,28 @@ public class AuthenticationManager {
                 expireIdentityCookie(realm, uriInfo);
                 return null;
             }
+
+            Auth auth = new Auth();
+
             UserModel user = realm.getUser(token.getPrincipal());
             if (user == null || !user.isEnabled()) {
                 logger.debug("Unknown user in identity cookie");
                 expireIdentityCookie(realm, uriInfo);
                 return null;
             }
-            return user;
+            auth.setUser(user);
+
+            if (token.getIssuedFor() != null) {
+                UserModel client = realm.getUser(token.getIssuedFor());
+                if (client == null || !client.isEnabled()) {
+                    logger.debug("Unknown client in identity cookie");
+                    expireIdentityCookie(realm, uriInfo);
+                    return null;
+                }
+                auth.setClient(client);
+            }
+
+            return auth;
         } catch (VerificationException e) {
             logger.debug("Failed to verify identity cookie", e);
             expireIdentityCookie(realm, uriInfo);
@@ -173,11 +201,11 @@ public class AuthenticationManager {
         return null;
     }
 
-    public UserModel authenticateBearerToken(RealmModel realm, HttpHeaders headers) {
+    public Auth authenticateBearerToken(RealmModel realm, HttpHeaders headers) {
         String tokenString = null;
         String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
         if (authHeader == null) {
-            throw new NotAuthorizedException("Bearer");
+            return null;
         } else {
             String[] split = authHeader.trim().split("\\s+");
             if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer");
@@ -191,11 +219,24 @@ public class AuthenticationManager {
             if (!token.isActive()) {
                 throw new NotAuthorizedException("token_expired");
             }
+
+            Auth auth = new Auth();
+
             UserModel user = realm.getUser(token.getPrincipal());
             if (user == null || !user.isEnabled()) {
                 throw new NotAuthorizedException("invalid_user");
             }
-            return user;
+            auth.setUser(user);
+
+            if (token.getIssuedFor() != null) {
+                UserModel client = realm.getUser(token.getIssuedFor());
+                if (client == null || !client.isEnabled()) {
+                    throw new NotAuthorizedException("invalid_user");
+                }
+                auth.setClient(client);
+            }
+
+            return auth;
         } catch (VerificationException e) {
             logger.error("Failed to verify token", e);
             throw new NotAuthorizedException("invalid_token");
@@ -267,4 +308,25 @@ public class AuthenticationManager {
         SUCCESS, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED
     }
 
+    public static class Auth {
+        private UserModel user;
+        private UserModel client;
+
+        public UserModel getUser() {
+            return user;
+        }
+
+        public UserModel getClient() {
+            return client;
+        }
+
+        void setUser(UserModel user) {
+            this.user = user;
+        }
+
+        void setClient(UserModel client) {
+            this.client = client;
+        }
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 1387f12..14220b2 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -388,9 +388,11 @@ public class RealmManager {
         rep.setLastName(user.getLastName());
         rep.setFirstName(user.getFirstName());
         rep.setEmail(user.getEmail());
-        Map<String, String> attrs = new HashMap<String, String>();
-        attrs.putAll(user.getAttributes());
-        rep.setAttributes(attrs);
+        if (user.getAttributes() != null && !user.getAttributes().isEmpty()) {
+            Map<String, String> attrs = new HashMap<String, String>();
+            attrs.putAll(user.getAttributes());
+            rep.setAttributes(attrs);
+        }
         return rep;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 8857271..ac4fabf 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -23,6 +23,7 @@ package org.keycloak.services.resources;
 
 import java.net.URI;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import javax.ws.rs.*;
@@ -39,6 +40,7 @@ import org.keycloak.AbstractOAuthClient;
 import org.keycloak.jaxrs.JaxrsOAuthClient;
 import org.keycloak.models.*;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.services.email.EmailSender;
 import org.keycloak.services.managers.AccessCodeEntry;
 import org.keycloak.services.managers.AuthenticationManager;
@@ -89,18 +91,32 @@ public class AccountService {
     }
 
     private Response forwardToPage(String path, String template) {
-        UserModel user = getUser(false);
-        if (user != null) {
-            return Flows.forms(realm, request, uriInfo).setUser(user).forwardToForm(template);
+        AuthenticationManager.Auth auth = getAuth(false);
+        if (auth != null) {
+            return Flows.forms(realm, request, uriInfo).setUser(auth.getUser()).forwardToForm(template);
         } else {
             return login(path);
         }
     }
 
     @Path("")
+    @OPTIONS
+    public Response accountPreflight() {
+        return Cors.add(request, Response.ok()).auth().preflight().build();
+    }
+
+    @Path("")
     @GET
     public Response accountPage() {
-        return forwardToPage(null, Pages.ACCOUNT);
+        List<MediaType> types = headers.getAcceptableMediaTypes();
+        if (types.contains(MediaType.WILDCARD_TYPE) || (types.contains(MediaType.TEXT_HTML_TYPE))) {
+            return forwardToPage(null, Pages.ACCOUNT);
+        } else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) {
+            AuthenticationManager.Auth auth = getAuth(true);
+            return Cors.add(request, Response.ok(RealmManager.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getClient()).build();
+        } else {
+            return Response.notAcceptable(Variant.VariantListBuilder.newInstance().mediaTypes(MediaType.TEXT_HTML_TYPE, MediaType.APPLICATION_JSON_TYPE).build()).build();
+        }
     }
 
     @Path("social")
@@ -131,8 +147,8 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processAccountUpdate(final MultivaluedMap<String, String> formData) {
-
-        UserModel user = getUser(true);
+        AuthenticationManager.Auth auth = getAuth(true);
+        UserModel user = auth.getUser();
 
         String error = Validation.validateUpdateProfileForm(formData);
         if (error != null) {
@@ -150,7 +166,9 @@ public class AccountService {
     @Path("totp-remove")
     @GET
     public Response processTotpRemove() {
-        UserModel user = getUser(true);
+        AuthenticationManager.Auth auth = getAuth(true);
+        UserModel user = auth.getUser();
+
         user.setTotp(false);
         return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.MessageType.SUCCESS)
                 .setUser(user).forwardToTotp();
@@ -160,7 +178,8 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processTotpUpdate(final MultivaluedMap<String, String> formData) {
-        UserModel user = getUser(true);
+        AuthenticationManager.Auth auth = getAuth(true);
+        UserModel user = auth.getUser();
 
         String totp = formData.getFirst("totp");
         String totpSecret = formData.getFirst("totpSecret");
@@ -187,7 +206,8 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
-        UserModel user = getUser(true);
+        AuthenticationManager.Auth auth = getAuth(true);
+        UserModel user = auth.getUser();
 
         FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user);
 
@@ -288,7 +308,7 @@ public class AccountService {
             }
             URI redirectUri = redirectBuilder.build(realm.getId());
 
-            NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId()));
+            NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), client, Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId()));
             return Response.status(302).cookie(cookie).location(redirectUri).build();
         } finally {
             authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath());
@@ -318,11 +338,12 @@ public class AccountService {
         return oauth.redirect(uriInfo, accountUri.toString(), path);
     }
 
-    private UserModel getUser(boolean required) {
-        UserModel user = authManager.authenticateAccountIdentityCookie(realm, uriInfo, headers);
-        if (user == null && required) {
+    private AuthenticationManager.Auth getAuth(boolean required) {
+        AuthenticationManager.Auth auth = authManager.authenticateAccountIdentity(realm, uriInfo, headers);
+        if (auth == null && required) {
             throw new ForbiddenException();
         }
-        return user;
+        return auth;
     }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java
index 8c28d6f..42c34d1 100644
--- a/services/src/main/java/org/keycloak/services/resources/Cors.java
+++ b/services/src/main/java/org/keycloak/services/resources/Cors.java
@@ -1,20 +1,37 @@
 package org.keycloak.services.resources;
 
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 
 import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.models.UserModel;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class Cors {
 
+    public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1);
+    public static final String DEFAULT_ALLOW_METHODS = "GET, OPTIONS";
+
+    public static final String ORIGIN = "Origin";
+
+    public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
+    public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
+    public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
+    public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
+    public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
+
     private HttpRequest request;
     private ResponseBuilder response;
     private Set<String> allowedOrigins;
+    private String[] allowedMethods;
+
+    private boolean preflight;
+    private boolean auth;
 
     public Cors(HttpRequest request, ResponseBuilder response) {
         this.request = request;
@@ -25,18 +42,60 @@ public class Cors {
         return new Cors(request, response);
     }
 
-    public Cors allowedOrigins(Set<String> allowedOrigins) {
-        this.allowedOrigins = allowedOrigins;
+    public Cors preflight() {
+        preflight = true;
+        return this;
+    }
+
+    public Cors auth() {
+        auth = true;
+        return this;
+    }
+
+    public Cors allowedOrigins(UserModel client) {
+        if (client != null) {
+            allowedOrigins = client.getWebOrigins();
+        }
+        return this;
+    }
+
+    public Cors allowedMethods(String... allowedMethods) {
+        this.allowedMethods = allowedMethods;
         return this;
     }
 
     public Response build() {
-        String origin = request.getHttpHeaders().getHeaderString("Origin");
-        if (origin == null || allowedOrigins == null || (!allowedOrigins.contains(origin))) {
+        String origin = request.getHttpHeaders().getHeaderString(ORIGIN);
+        if (origin == null) {
+            return response.build();
+        }
+
+        if (!preflight && (allowedOrigins == null || !allowedOrigins.contains(origin))) {
             return response.build();
         }
 
-        response.header("Access-Control-Allow-Origin", origin);
+        response.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+
+        if (allowedMethods != null) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < allowedMethods.length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(allowedMethods[i]);
+            }
+            response.header(ACCESS_CONTROL_ALLOW_METHODS, sb.toString());
+        } else {
+            response.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
+        }
+
+        response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
+        if (auth) {
+            response.header(ACCESS_CONTROL_ALLOW_HEADERS, "Authorization");
+        }
+
+        response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
+
         return response.build();
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index f118bc4..7388b61 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -413,7 +413,7 @@ public class TokenService {
         logger.debug("accessRequest SUCCESS");
         AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
 
-        return Cors.add(request, Response.ok(res)).allowedOrigins(client.getWebOrigins()).build();
+        return Cors.add(request, Response.ok(res)).allowedOrigins(client).build();
     }
 
     protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) {
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 0e121c7..a411b48 100644
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -190,6 +190,10 @@
             <artifactId>selenium-java</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.seleniumhq.selenium</groupId>
+            <artifactId>selenium-chrome-driver</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.mongodb</groupId>
             <artifactId>mongo-java-driver</artifactId>
         </dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
new file mode 100644
index 0000000..cdcef48
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
@@ -0,0 +1,174 @@
+package org.keycloak.testsuite.account;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONObject;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.Constants;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ProfileTest {
+
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+        @Override
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            UserModel user = appRealm.getUser("test-user@localhost");
+            user.setFirstName("First");
+            user.setLastName("Last");
+            user.setAttribute("key1", "value1");
+            user.setAttribute("key2", "value2");
+
+            for (ApplicationModel app : appRealm.getApplications()) {
+                if (app.getName().equals("test-app")) {
+                    app.getApplicationUser().addWebOrigin("http://localtest.me:8081");
+                }
+            }
+        }
+    });
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    protected WebDriver driver;
+
+    @WebResource
+    protected OAuthClient oauth;
+
+    @WebResource
+    protected AccountUpdateProfilePage profilePage;
+
+    @WebResource
+    protected LoginPage loginPage;
+
+    @Test
+    public void getProfile() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        String code = oauth.getCurrentQuery().get("code");
+        String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+
+        HttpResponse response = doGetProfile(token, null);
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        JSONObject profile = new JSONObject(IOUtils.toString(response.getEntity().getContent()));
+
+        assertEquals("test-user@localhost", profile.getString("username"));
+        assertEquals("test-user@localhost", profile.getString("email"));
+        assertEquals("First", profile.getString("firstName"));
+        assertEquals("Last", profile.getString("lastName"));
+
+        JSONObject attributes = profile.getJSONObject("attributes");
+        assertEquals(2, attributes.length());
+        assertEquals("value1", attributes.getString("key1"));
+        assertEquals("value2", attributes.getString("key2"));
+    }
+
+    @Test
+    public void getProfileCors() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        String code = oauth.getCurrentQuery().get("code");
+        String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+
+        driver.navigate().to("http://localtest.me:8081/app");
+
+        String[] response = doGetProfileJs(token);
+        assertEquals("200", response[0]);
+    }
+
+    @Test
+     public void getProfileCorsInvalidOrigin() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        String code = oauth.getCurrentQuery().get("code");
+        String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+
+        driver.navigate().to("http://invalid.localtest.me:8081");
+
+        try {
+            doGetProfileJs(token);
+            fail("Expected failure");
+        } catch (Throwable t) {
+        }
+    }
+
+    @Test
+    public void getProfileCookieAuth() throws Exception {
+        profilePage.open();
+        loginPage.login("test-user@localhost", "password");
+
+        String[] response = doGetProfileJs(null);
+        assertEquals("200", response[0]);
+
+        JSONObject profile = new JSONObject(response[1]);
+        assertEquals("test-user@localhost", profile.getString("username"));
+    }
+
+    @Test
+    public void getProfileNoAuth() throws Exception {
+        HttpResponse response = doGetProfile(null, null);
+        assertEquals(403, response.getStatusLine().getStatusCode());
+    }
+
+    private URI getAccountURI() {
+        return UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT + "/rest/realms/" + oauth.getRealm() + "/account").build();
+    }
+
+    private HttpResponse doGetProfile(String token, String origin) throws IOException {
+        HttpClient client = new DefaultHttpClient();
+        HttpGet get = new HttpGet(UriBuilder.fromUri(getAccountURI()).build());
+        if (token != null) {
+            get.setHeader(HttpHeaders.AUTHORIZATION, "bearer " + token);
+        }
+        if (origin != null) {
+            get.setHeader("Origin", origin);
+        }
+        get.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
+        return client.execute(get);
+    }
+
+    private String[] doGetProfileJs(String token) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("var req = new XMLHttpRequest();\n");
+        sb.append("req.open('GET', '" + getAccountURI().toString() + "', false);\n");
+        if (token != null) {
+            sb.append("req.setRequestHeader('Authorization', 'Bearer " + token + "');\n");
+        }
+        sb.append("req.setRequestHeader('Accept', 'application/json');\n");
+        sb.append("req.send(null);\n");
+        sb.append("return req.status + '///' + req.responseText;\n");
+
+        JavascriptExecutor js  = (JavascriptExecutor) driver;
+        String response = (String) js.executeScript(sb.toString());
+        return response.split("///");
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java
index 88301c5..ccdca44 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/WebRule.java
@@ -55,6 +55,7 @@ public class WebRule extends ExternalResource {
 
         if (browser.equals("htmlunit")) {
             HtmlUnitDriver d = new HtmlUnitDriver();
+            d.getWebClient().getOptions().setJavaScriptEnabled(true);
             d.getWebClient().getOptions().setCssEnabled(false);
             driver = d;
         } else if (browser.equals("chrome")) {