From 2d5e6520dbc25a5787ad20f5bd97fa918cef572f Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Sat, 15 Mar 2014 12:53:52 +0000 Subject: [PATCH] Updates to keycloak.js --- examples/js-console/example-realm.json | 73 +++ examples/js-console/pom.xml | 42 ++ .../js-console/src/main/webapp/index.html | 97 ++++ .../js-console/src/main/webapp/keycloak.json | 8 + examples/test-cordova.json | 27 - .../META-INF/resources/js/keycloak.js | 481 +++++++++++------- .../resources/META-INF/resources/js/test.html | 24 - .../server/KeycloakServerApplication.java | 5 +- 8 files changed, 514 insertions(+), 243 deletions(-) create mode 100755 examples/js-console/example-realm.json create mode 100755 examples/js-console/pom.xml create mode 100644 examples/js-console/src/main/webapp/index.html create mode 100644 examples/js-console/src/main/webapp/keycloak.json delete mode 100644 examples/test-cordova.json delete mode 100644 integration/js/src/main/resources/META-INF/resources/js/test.html diff --git a/examples/js-console/example-realm.json b/examples/js-console/example-realm.json new file mode 100755 index 0000000000..537d120392 --- /dev/null +++ b/examples/js-console/example-realm.json @@ -0,0 +1,73 @@ +{ + "realm": "example", + "enabled": true, + "sslNotRequired": true, + "registrationAllowed": true, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ "password" ], + "users" : [ + { + "username" : "user", + "enabled": true, + "email" : "sample-user@example", + "firstName": "Sample", + "lastName": "User", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ] + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + }, + { + "name": "admin", + "description": "Administrator privileges" + } + ] + }, + "roleMappings": [ + { + "username": "user", + "roles": ["user"] + } + ], + "scopeMappings": [ + { + "client": "js-console", + "roles": ["user"] + } + ], + "applications": [ + { + "name": "js-console", + "enabled": true, + "publicClient": true, + "baseUrl": "http://localhost:8080/js-console", + "redirectUris": [ + "http://localhost:8080/js-console/*" + ] + } + ], + "applicationRoleMappings": { + "account": [ + { + "username": "user", + "roles": ["view-profile", "manage-account"] + } + ] + }, + "applicationScopeMappings": { + "account": [ + { + "client": "js-console", + "roles": ["view-profile"] + } + ] + } +} diff --git a/examples/js-console/pom.xml b/examples/js-console/pom.xml new file mode 100755 index 0000000000..1e8dc6a0f8 --- /dev/null +++ b/examples/js-console/pom.xml @@ -0,0 +1,42 @@ + + + + keycloak-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + ../../../pom.xml + + 4.0.0 + org.keycloak.example.demo + js-console + war + JS Console + + + + js-console + + + org.jboss.as.plugins + jboss-as-maven-plugin + 7.4.Final + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html new file mode 100644 index 0000000000..58513adc65 --- /dev/null +++ b/examples/js-console/src/main/webapp/index.html @@ -0,0 +1,97 @@ + + + + + + +
+ + + + + + + + + + + + +
+ +

Result

+

+
+

Events

+

+
+
+
+
+
diff --git a/examples/js-console/src/main/webapp/keycloak.json b/examples/js-console/src/main/webapp/keycloak.json
new file mode 100644
index 0000000000..f166f87106
--- /dev/null
+++ b/examples/js-console/src/main/webapp/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm" : "example",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8080/auth",
+  "ssl-not-required" : true,
+  "resource" : "js-console",
+  "public-client" : true
+}
\ No newline at end of file
diff --git a/examples/test-cordova.json b/examples/test-cordova.json
deleted file mode 100644
index 9de2ee35b8..0000000000
--- a/examples/test-cordova.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-    "id": "test",
-    "realm": "test",
-    "enabled": true,
-    "sslNotRequired": true,
-    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
-    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
-    "requiredCredentials": [ "password" ],
-    "users" : [
-        {
-            "username" : "test",
-            "enabled": true,
-            "email" : "test-user@localhost",
-            "credentials" : [
-                { "type" : "password",
-                    "value" : "test" }
-            ]
-        }
-    ],
-    "applications": [
-        {
-            "name": "test",
-            "enabled": true,
-            "secret": "password"
-         }
-    ]
-}
\ No newline at end of file
diff --git a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
index b3a6af4871..6860b78f51 100755
--- a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
+++ b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
@@ -1,58 +1,108 @@
-var Keycloak = function (options) {
-    options = options || {};
-
+var Keycloak = function (config) {
     if (!(this instanceof Keycloak)) {
-        return new Keycloak(options);
+        return new Keycloak(config);
     }
 
     var kc = this;
+    kc.authenticated = false;
 
-    if (!options.url) {
-        var scripts = document.getElementsByTagName('script');
-        for (var i = 0; i < scripts.length; i++) {
-            if (scripts[i].src.match(/.*keycloak\.js/)) {
-                options.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/auth/js/keycloak.js'));
-                break;
+    var configPromise = createPromise();
+    configPromise.name = 'config';
+
+    if (!config) {
+        loadConfig('keycloak.json', configPromise);
+    } else if (typeof config === 'string') {
+        loadConfig(config, configPromise);
+    } else {
+        if (!config['url']) {
+            var scripts = document.getElementsByTagName('script');
+            for (var i = 0; i < scripts.length; i++) {
+                if (scripts[i].src.match(/.*keycloak\.js/)) {
+                    config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
+                    break;
+                }
             }
         }
-    }
 
-    if (!options.url) {
-        throw 'url missing';
-    }
-
-    if (!options.realm) {
-        throw 'realm missing';
-    }
-
-    if (!options.clientId) {
-        throw 'clientId missing';
-    }
-
-    kc.init = function (successCallback, errorCallback) {
-        if (window.oauth.callback) {
-            processCallback(successCallback, errorCallback);
-        } else if (options.token) {
-            kc.setToken(options.token, successCallback);
-        } else if (options.onload) {
-            switch (options.onload) {
-                case 'login-required' :
-                    window.location = kc.createLoginUrl(true);
-                    break;
-                case 'check-sso' :
-                    window.location = kc.createLoginUrl(false);
-                    break;
-            }
+        if (!config.realm) {
+            throw 'realm missing';
         }
+
+        if (!config.clientId) {
+            throw 'clientId missing';
+        }
+
+        kc.authServerUrl = config.url;
+        kc.realm = config.realm;
+        kc.clientId = config.clientId;
+
+        configPromise.setSuccess();
     }
 
-    kc.login = function () {
-        window.location.href = kc.createLoginUrl(true);
+    kc.init = function (init) {
+        var promise = createPromise();
+        var callback = parseCallback(window.location.href);
+
+        function processInit() {
+            if (callback) {
+                window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
+                processCallback(callback, promise);
+                return;
+            } else if (init) {
+                if (init.code || init.error) {
+                    processCallback(init, promise);
+                    return;
+                } else if (init.token || init.refreshToken) {
+                    setToken(init.token, init.refreshToken);
+                } else if (init == 'login-required') {
+                    kc.login();
+                    return;
+                } else if (init == 'check-sso') {
+                    window.location = kc.createLoginUrl() + '&prompt=none';
+                    return;
+                }
+            }
+
+            promise.setSuccess(false);
+        }
+
+        configPromise.promise.success(processInit);
+
+        return promise.promise;
     }
 
-    kc.logout = function () {
-        kc.setToken(undefined);
-        window.location.href = kc.createLogoutUrl();
+    kc.login = function (redirectUri) {
+        window.location.href = kc.createLoginUrl(redirectUri);
+    }
+
+    kc.createLoginUrl = function(redirectUri) {
+        var state = createUUID();
+
+        sessionStorage.oauthState = state;
+        var url = getRealmUrl()
+            + '/tokens/login'
+            + '?client_id=' + encodeURIComponent(kc.clientId)
+            + '&redirect_uri=' + getEncodedRedirectUri(redirectUri)
+            + '&state=' + encodeURIComponent(state)
+            + '&response_type=code';
+
+        return url;
+    }
+
+    kc.logout = function(redirectUri) {
+        setToken(null, null);
+        window.location.href = kc.createLogoutUrl(redirectUri);
+    }
+
+    kc.clearToken = function() {
+        setToken(null, null);
+    }
+
+    kc.createLogoutUrl = function(redirectUri) {
+        var url = getRealmUrl()
+            + '/tokens/logout'
+            + '?redirect_uri=' + getEncodedRedirectUri(redirectUri);
+        return url;
     }
 
     kc.hasRealmRole = function (role) {
@@ -60,122 +110,131 @@ var Keycloak = function (options) {
         return access && access.roles.indexOf(role) >= 0 || false;
     }
 
-    kc.hasResourceRole = function (role, resource) {
+    kc.hasResourceRole = function(role, resource) {
         if (!kc.resourceAccess) {
             return false;
         }
 
-        var access = kc.resourceAccess[resource || options.clientId];
+        var access = kc.resourceAccess[resource || kc.clientId];
         return access && access.roles.indexOf(role) >= 0 || false;
     }
 
-    kc.loadUserProfile = function (success, error) {
-        var url = kc.getRealmUrl() + '/account';
+    kc.loadUserProfile = function() {
+        var url = getRealmUrl() + '/account';
         var req = new XMLHttpRequest();
         req.open('GET', url, true);
         req.setRequestHeader('Accept', 'application/json');
         req.setRequestHeader('Authorization', 'bearer ' + kc.token);
 
+        var promise = createPromise();
+
         req.onreadystatechange = function () {
             if (req.readyState == 4) {
                 if (req.status == 200) {
                     kc.profile = JSON.parse(req.responseText);
-                    success && success(kc.profile)
+                    promise.setSuccess(kc.profile);
                 } else {
-                    var response = { status: req.status, statusText: req.status };
-                    if (req.responseText) {
-                        response.data = JSON.parse(req.responseText);
-                    }
-                    error && error(response);
+                    promise.setError();
                 }
             }
         }
 
         req.send();
+
+        return promise.promise;
     }
 
-    /**
-     * checks to make sure token is valid.  If it is, it calls successCallback with no parameters.
-     * If it isn't valid, it tries to refresh the access token.  On successful refresh, it calls successCallback.
-     *
-     * @param successCallback
-     * @param errorCallback
-     */
-    kc.onValidAccessToken = function(successCallback, errorCallback) {
-        if (!kc.tokenParsed) {
-            console.log('no token');
-            errorCallback();
-            return;
+    kc.refreshAccessToken = function(minValidity) {
+        if (!kc.tokenParsed || !kc.refreshToken) {
+            throw 'Not authenticated';
         }
-        var currTime = new Date().getTime() / 1000;
-        if (currTime > kc.tokenParsed['exp']) {
-            if (!kc.refreshToken) {
-                console.log('no refresh token');
-                errorCallback();
-                return;
+
+        var promise = createPromise();
+
+        if (minValidity) {
+            var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+            if (expiresIn > minValidity) {
+                promise.setSuccess(false);
+                return promise.promise;
             }
-            console.log('calling refresh');
-            var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
-            var url = kc.getRealmUrl() + '/tokens/refresh';
-
-            var req = new XMLHttpRequest();
-            req.open('POST', url, true, options.clientId, options.clientSecret);
-            req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
-
-            req.onreadystatechange = function () {
-                if (req.readyState == 4) {
-                    if (req.status == 200) {
-                        console.log('Refresh Success');
-                        var tokenResponse = JSON.parse(req.responseText);
-                        kc.refreshToken = tokenResponse['refresh_token'];
-                        kc.setToken(tokenResponse['access_token'], successCallback);
-                    } else {
-                        console.log('error on refresh HTTP invoke: ' + req.status);
-                        errorCallback && errorCallback({ authenticated: false, status: req.status, statusText: req.statusText });
-                    }
-                }
-            };
-            req.send(params);
-        } else {
-            console.log('Token is still valid');
-            successCallback();
         }
 
+        var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
+        var url = getRealmUrl() + '/tokens/refresh';
+
+        var req = new XMLHttpRequest();
+        req.open('POST', url, true);
+        req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+        if (kc.clientId && kc.clientSecret) {
+            req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+        } else {
+            params += '&client_id=' + encodeURIComponent(kc.clientId);
+        }
+
+        req.onreadystatechange = function() {
+            if (req.readyState == 4) {
+                if (req.status == 200) {
+                    var tokenResponse = JSON.parse(req.responseText);
+                    setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
+                    kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
+                    promise.setSuccess(true);
+                } else {
+                    kc.onAuthRefreshError && kc.onAuthRefreshError();
+                    promise.setError();
+                }
+            }
+        };
+
+        req.send(params);
+
+        return promise.promise;
     }
 
-    kc.getRealmUrl = function() {
-        return options.url + '/auth/rest/realms/' + encodeURIComponent(options.realm);
+    kc.processCallback = function(url) {
+        var callback = parseCallback(url);
+        if (callback) {
+            var promise = createPromise();
+            processCallback(callback, promise);
+            return promise;
+        }
     }
 
-    function processCallback(successCallback, errorCallback) {
-        var code = window.oauth.code;
-        var error = window.oauth.error;
-        var prompt = window.oauth.prompt;
+    function getRealmUrl() {
+        return kc.authServerUrl + '/rest/realms/' + encodeURIComponent(kc.realm);
+    }
+
+    function processCallback(oauth, promise) {
+        var code = oauth.code;
+        var error = oauth.error;
+        var prompt = oauth.prompt;
 
         if (code) {
             var params = 'code=' + code;
-            var url = kc.getRealmUrl() + '/tokens/access/codes';
+            var url = getRealmUrl() + '/tokens/access/codes';
 
             var req = new XMLHttpRequest();
             req.open('POST', url, true);
             req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
 
-            if (options.clientId && options.clientSecret) {
-                req.setRequestHeader('Authorization', 'Basic ' + btoa(options.clientId + ':' + options.clientSecret));
+            if (kc.clientId && kc.clientSecret) {
+                req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
             } else {
-                params += '&client_id=' + encodeURIComponent(options.clientId);
+                params += '&client_id=' + encodeURIComponent(kc.clientId);
             }
 
             req.withCredentials = true;
 
-            req.onreadystatechange = function () {
+            req.onreadystatechange = function() {
                 if (req.readyState == 4) {
                     if (req.status == 200) {
                         var tokenResponse = JSON.parse(req.responseText);
-                        kc.refreshToken = tokenResponse['refresh_token'];
-                        kc.setToken(tokenResponse['access_token'], successCallback);
+                        setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
+                        kc.onAuthSuccess && kc.onAuthSuccess();
+                        promise.setSuccess(true);
                     } else {
-                        errorCallback && errorCallback({ authenticated: false, status: req.status, statusText: req.statusText });
+                        kc.onAuthError && kc.onAuthError();
+                        promise.setError();
                     }
                 }
             };
@@ -183,16 +242,38 @@ var Keycloak = function (options) {
             req.send(params);
         } else if (error) {
             if (prompt != 'none') {
-                setTimeout(function() {
-                    errorCallback && errorCallback({  authenticated: false, error: error })
-                }, 0);
+                kc.onAuthError && kc.onAuthError();
+                promise.setError();
             }
         }
     }
 
-    kc.setToken = function(token, successCallback) {
+    function loadConfig(url, configPromise) {
+        var req = new XMLHttpRequest();
+        req.open('GET', url, true);
+        req.setRequestHeader('Accept', 'application/json');
+
+        req.onreadystatechange = function () {
+            if (req.readyState == 4) {
+                if (req.status == 200) {
+                    var config = JSON.parse(req.responseText);
+
+                    kc.authServerUrl = config['auth-server-url'];
+                    kc.realm = config['realm'];
+                    kc.clientId = config['resource'];
+
+                    configPromise.setSuccess();
+                } else {
+                    configPromise.setError();
+                }
+            }
+        };
+
+        req.send();
+    }
+
+    function setToken(token, refreshToken) {
         if (token) {
-            window.oauth.token = token;
             kc.token = token;
             kc.tokenParsed = JSON.parse(atob(token.split('.')[1]));
             kc.authenticated = true;
@@ -209,45 +290,32 @@ var Keycloak = function (options) {
                     kc.idToken[n] = kc.tokenParsed[n];
                 }
             }
-
-            setTimeout(function() {
-                successCallback && successCallback({ authenticated: kc.authenticated, subject: kc.subject });
-            }, 0);
         } else {
-            delete window.oauth.token;
             delete kc.token;
+            delete kc.tokenParsed;
+            delete kc.subject;
+            delete kc.realmAccess;
+            delete kc.resourceAccess;
+            delete kc.idToken;
+
+            kc.authenticated = false;
+        }
+
+        if (refreshToken) {
+            kc.refreshToken = refreshToken;
+            kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
+        } else {
+            delete kc.refreshToken;
+            delete kc.refreshTokenParsed;
         }
     }
 
-    kc.createLoginUrl = function(prompt) {
-        var state = createUUID();
-
-        sessionStorage.oauthState = state;
-        var url = kc.getRealmUrl()
-            + '/tokens/login'
-            + '?client_id=' + encodeURIComponent(options.clientId)
-            + '&redirect_uri=' + getEncodedRedirectUri()
-            + '&state=' + encodeURIComponent(state)
-            + '&response_type=code';
-
-        if (prompt == false) {
-            url += '&prompt=none';
-        }
-
-        return url;
-    }
-
-    kc.createLogoutUrl = function() {
-        var url = kc.getRealmUrl()
-            + '/tokens/logout'
-            + '?redirect_uri=' + getEncodedRedirectUri();
-        return url;
-    }
-
-    function getEncodedRedirectUri() {
+    function getEncodedRedirectUri(redirectUri) {
         var url;
-        if (options.redirectUri) {
-            url = options.redirectUri;
+        if (redirectUri) {
+            url = redirectUri;
+        } else if (kc.redirectUri) {
+            url = kc.redirectUri;
         } else {
             url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
             if (location.hash) {
@@ -269,7 +337,81 @@ var Keycloak = function (options) {
         var uuid = s.join('');
         return uuid;
     }
-    
+
+    function parseCallback(url) {
+
+        if (url.indexOf('?') != -1) {
+            var oauth = {};
+
+            var params = url.split('?')[1].split('&');
+            for (var i = 0; i < params.length; i++) {
+                var p = params[i].split('=');
+                switch (decodeURIComponent(p[0])) {
+                    case 'code':
+                        oauth.code = p[1];
+                        break;
+                    case 'error':
+                        oauth.error = p[1];
+                        break;
+                    case 'state':
+                        oauth.state = decodeURIComponent(p[1]);
+                        break;
+                    case 'redirect_fragment':
+                        oauth.fragment = decodeURIComponent(p[1]);
+                        break;
+                    case 'prompt':
+                        oauth.prompt = p[1];
+                        break;
+                }
+            }
+
+            if (oauth.state && oauth.state == sessionStorage.oauthState) {
+                delete sessionStorage.oauthState;
+                return oauth;
+            }
+        }
+    }
+
+    function createPromise() {
+        var p = {
+            setSuccess: function(result) {
+                p.success = true;
+                p.result = result;
+                if (p.successCallback) {
+                    p.successCallback(result);
+                }
+            },
+
+            setError: function(result) {
+                p.error = true;
+                p.result = result;
+                if (p.errorCallback) {
+                    p.errorCallback(result);
+                }
+            },
+
+            promise: {
+                success: function(callback) {
+                    if (p.success) {
+                        callback(p.result);
+                    } else if (!p.error) {
+                        p.successCallback = callback;
+                    }
+                    return p.promise;
+                },
+                error: function(callback) {
+                    if (p.error) {
+                        callback(p.result);
+                    } else if (!p.success) {
+                        p.errorCallback = callback;
+                    }
+                    return p.promise;
+                }
+            }
+        }
+        return p;
+    }
+
     var idTokenProperties = [
         "name", 
         "given_name", 
@@ -298,45 +440,4 @@ var Keycloak = function (options) {
         "country", 
         "claims_locales"
     ]
-}
-
-window.oauth = (function () {
-    var oauth = {};
-
-    var params = window.location.search.substring(1).split('&');
-    for (var i = 0; i < params.length; i++) {
-        var p = params[i].split('=');
-        switch (decodeURIComponent(p[0])) {
-            case 'code':
-                oauth.code = p[1];
-                break;
-            case 'error':
-                oauth.error = p[1];
-                break;
-            case 'state':
-                oauth.state = decodeURIComponent(p[1]);
-                break;
-            case 'redirect_fragment':
-                oauth.fragment = decodeURIComponent(p[1]);
-                break;
-            case 'prompt':
-                oauth.prompt = p[1];
-                break;
-        }
-    }
-
-    if (oauth.state && oauth.state == sessionStorage.oauthState) {
-        oauth.callback = true;
-        delete sessionStorage.oauthState;
-    } else {
-        oauth.callback = false;
-    }
-
-    if (oauth.callback) {
-        window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (oauth.fragment ? '#' + oauth.fragment : ''));
-    } else if (oauth.fragment) {
-        window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (oauth.fragment ? '#' + oauth.fragment : ''));
-    }
-
-    return oauth;
-}());
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/integration/js/src/main/resources/META-INF/resources/js/test.html b/integration/js/src/main/resources/META-INF/resources/js/test.html
deleted file mode 100644
index fb20e03c79..0000000000
--- a/integration/js/src/main/resources/META-INF/resources/js/test.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-    
-
-
-
-
-
-
-
diff --git a/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java b/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java
index 1da980789d..262bf60268 100755
--- a/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java
+++ b/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java
@@ -37,8 +37,9 @@ public class KeycloakServerApplication extends KeycloakApplication {
         try {
             RealmManager manager = new RealmManager(session);
 
-            if (rep.getId() == null) {
-                throw new RuntimeException("Realm id not specified");
+            if (rep.getId() != null && manager.getRealm(rep.getId()) != null) {
+                log.info("Not importing realm " + rep.getRealm() + " realm already exists");
+                return;
             }
 
             if (manager.getRealmByName(rep.getRealm()) != null) {