@@ -31,24 +36,28 @@
<#if displayMessage && message?has_content>
- ${message.summary}
+ ${message.summary}
#if>
-
diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
index 5de3439431..f84516d035 100644
--- a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
+++ b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
@@ -1,20 +1,43 @@
+.login-pf .container {
+ padding-top: 40px;
+}
+
#kc-logo {
+ width: 100%;
+}
+
+#kc-logo-wrapper {
background-image: url("../img/keycloak-logo.png");
background-repeat: no-repeat;
- position: absolute;
- top: 50px;
- right: 50px;
+ background-position: top right;
+
height: 37px;
- width: 150px;
+
+ margin: 50px;
+}
+
+#kc-header {
+ overflow: visible;
+ padding-left: 80px;
+ white-space: nowrap;
}
#kc-header-wrapper {
font-size: 26px;
- height: 18px;
text-transform: uppercase;
- display: block;
+/* display: block;
+ position: relative;
+ top: -80px;*/
+}
+
+#kc-container-wrapper {
+ bottom: 13%;
+ position: absolute;
+ width: 100%;
+}
+
+#kc-content {
position: relative;
- top: -80px;
}
#kc-form-options span {
@@ -27,18 +50,18 @@
margin-bottom: 10px;
}
+#kc-feedback-wrapper {
+ display: inline-block;
+ width: auto;
-#kc-feedback {
background-position: left bottom;
background-repeat: no-repeat;
- padding-bottom: 21px;
+ padding-bottom: 10px;
- position: absolute;
- top: -40px;
- white-space: nowrap;
}
#kc-feedback span {
+ display: block;
padding: 0.90909090909091em 3.63636363636364em;
border-style: solid;
border-width: 1px 1px 0px 1px;
@@ -52,7 +75,7 @@
margin-bottom: 0;
}
-.feedback-error {
+.feedback-error #kc-feedback-wrapper {
background-image: url(../img/feedback-error-arrow-down.png);
}
.feedback-error span {
@@ -61,7 +84,7 @@
background-color: #f8e7e7;
}
-.feedback-success {
+.feedback-success #kc-feedback-wrapper {
background-image: url(../img/feedback-success-arrow-down.png);
}
.feedback-success span {
@@ -70,7 +93,7 @@
background-color: #e4f1e1;
}
-.feedback-warning {
+.feedback-warning #kc-feedback-wrapper {
background-image: url(../img/feedback-warning-arrow-down.png);
}
.feedback-warning span {
@@ -79,6 +102,10 @@
background-color: #fef1e9;
}
+#kc-registration {
+ margin-bottom: 15px;
+}
+
/* TOTP */
ol#kc-totp-settings {
@@ -121,6 +148,12 @@ ol#kc-totp-settings li:first-of-type {
width: 50%;
}
+/* Code */
+#kc-code textarea {
+ width: 100%;
+ height: 8em;
+}
+
/* Social */
#kc-social-providers ul {
@@ -130,14 +163,17 @@ ol#kc-totp-settings li:first-of-type {
#kc-social-providers li {
display: block;
- margin-top: 1em;
- width: 130px;
+ margin-top: 5px;
}
#kc-social-providers li:first-of-type {
margin-top: 0;
}
+.zocial {
+ width: 125px;
+}
+
.zocial.facebook,
.zocial.github,
.zocial.google,
@@ -167,26 +203,50 @@ ol#kc-totp-settings li:first-of-type {
}
@media (max-width: 767px) {
- #kc-logo {
- position: inherit;
- display: inline-block;
+ #kc-logo-wrapper {
+ background-image: url("../img/keycloak-logo.png");
+ background-repeat: no-repeat;
+ background-position: top center;
+
+ height: 37px;
+
margin: 20px;
- float: right;
+ }
+
+ #kc-header {
+ padding-left: 40px;
+ padding-right: 40px;
+ white-space: normal;
+ float: none;
}
#kc-feedback {
- position: inherit;
- display: inline-block;
- margin-left: 20px;
+ padding-left: 40px;
+ padding-right: 40px;
+ float: none;
}
- #kc-social-providers {
- margin-top: 30px;
+ #kc-container-wrapper {
+ position: inherit;
+ float: none;
+ }
+
+ #kc-form {
+ padding-left: 40px;
+ padding-right: 40px;
+ float: none;
+ }
+
+ #kc-info-wrapper {
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ margin-top: 20px;
+ padding-top: 20px;
+ padding-left: 20px;
+ padding-right: 40px;
}
#kc-social-providers li {
- float: left;
- margin-right: 10px;
- margin-top: 0;
+ display: inline-block;
+ margin-right: 5px;
}
}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
index 4b46ae1229..aba1e9bf25 100644
--- a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
+++ b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
@@ -1,24 +1,25 @@
parent=base
styles=lib/patternfly/css/patternfly.css lib/zocial/zocial.css css/login.css
+meta=viewport==width=device-width,initial-scale=1
kcHtmlClass=login-pf
-kcContainerClass=container
-kcContainerWrapperClass=row
+kcContentClass=col-sm-12 col-md-12 col-lg-12 container
+kcContentWrapperClass=row
-kcHeaderClass=col-sm-12
+kcHeaderClass=col-xs-12 col-sm-7 col-md-6 col-lg-5
-kcFeedBackClass=col-sm-offset-2 col-md-offset-4
+kcFeedBackClass=col-xs-12 col-sm-5 col-md-6 col-lg-7
-kcFormAreaClass=col-sm-7 col-md-6 col-lg-5 login
+kcFormAreaClass=col-xs-12 col-sm-8 col-md-8 col-lg-6 login
kcFormClass=form-horizontal
kcFormGroupClass=form-group
kcLabelClass=control-label
-kcLabelWrapperClass=col-sm-4 col-md-4 col-lg-3
+kcLabelWrapperClass=col-xs-12 col-sm-12 col-md-4 col-lg-3
kcInputClass=form-control
-kcInputWrapperClass=col-sm-8 col-md-8 col-lg-9
-kcFormOptionsClass=col-sm-offset-4 col-sm-4 col-md-offset-4 col-md-4 col-lg-offset-3 col-lg-5
-kcFormButtonsClass=col-sm-4 col-md-4 col-lg-4 submit
+kcInputWrapperClass=col-xs-12 col-sm-12 col-md-8 col-lg-9
+kcFormOptionsClass=col-xs-5 col-sm-5 col-md-offset-4 col-md-4 col-lg-offset-3 col-lg-5
+kcFormButtonsClass=col-xs-7 col-sm-7 col-md-4 col-lg-4 submit
-kcInfoAreaClass=col-sm-5 col-md-6 col-lg-7 details
\ No newline at end of file
+kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-6 details
\ No newline at end of file
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
index e112755da7..2eb370418e 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
@@ -27,6 +27,8 @@ public interface LoginForms {
public Response createOAuthGrant();
+ public Response createCode();
+
public LoginForms setAccessCode(String accessCodeId, String accessCode);
public LoginForms setAccessRequest(List
realmRolesRequested, MultivaluedMap resourceRolesRequested);
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
index f0d1300c89..2b0cd23597 100644
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsPages.java
@@ -5,6 +5,6 @@ package org.keycloak.login;
*/
public enum LoginFormsPages {
- LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, LOGIN_USERNAME_REMINDER, REGISTER, ERROR, LOGIN_UPDATE_PROFILE;
+ LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, ERROR, LOGIN_UPDATE_PROFILE, CODE;
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
index b139aa5785..69e17fcfd7 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
@@ -8,6 +8,7 @@ import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeLoader;
import org.keycloak.login.LoginForms;
import org.keycloak.login.LoginFormsPages;
+import org.keycloak.login.freemarker.model.CodeBean;
import org.keycloak.login.freemarker.model.LoginBean;
import org.keycloak.login.freemarker.model.MessageBean;
import org.keycloak.login.freemarker.model.OAuthGrantBean;
@@ -178,6 +179,9 @@ public class FreeMarkerLoginForms implements LoginForms {
case OAUTH_GRANT:
attributes.put("oauth", new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested));
break;
+ case CODE:
+ attributes.put("code", new CodeBean(accessCode, messageType == MessageType.ERROR ? message : null));
+ break;
}
try {
@@ -197,10 +201,6 @@ public class FreeMarkerLoginForms implements LoginForms {
return createResponse(LoginFormsPages.LOGIN_RESET_PASSWORD);
}
- public Response createUsernameReminder() {
- return createResponse(LoginFormsPages.LOGIN_USERNAME_REMINDER);
- }
-
public Response createLoginTotp() {
return createResponse(LoginFormsPages.LOGIN_TOTP);
}
@@ -218,6 +218,11 @@ public class FreeMarkerLoginForms implements LoginForms {
return createResponse(LoginFormsPages.OAUTH_GRANT);
}
+ @Override
+ public Response createCode() {
+ return createResponse(LoginFormsPages.CODE);
+ }
+
public FreeMarkerLoginForms setError(String message) {
this.message = message;
this.messageType = MessageType.ERROR;
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
index a57e8a316f..02e20ec3b7 100644
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/Templates.java
@@ -29,6 +29,8 @@ public class Templates {
return "error.ftl";
case LOGIN_UPDATE_PROFILE:
return "login-update-profile.ftl";
+ case CODE:
+ return "code.ftl";
default:
throw new IllegalArgumentException();
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/CodeBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/CodeBean.java
new file mode 100644
index 0000000000..8851f9eade
--- /dev/null
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/CodeBean.java
@@ -0,0 +1,27 @@
+package org.keycloak.login.freemarker.model;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class CodeBean {
+
+ private final String code;
+ private final String error;
+
+ public CodeBean(String code, String error) {
+ this.code = code;
+ this.error = error;
+ }
+
+ public boolean isSuccess() {
+ return code != null && error == null;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getError() {
+ return error;
+ }
+}
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 5d4bf8c7b8..dd49966b8d 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
@@ -5,7 +5,7 @@ var Keycloak = function (options) {
return new Keycloak(options);
}
- var instance = this;
+ var kc = this;
if (!options.url) {
var scripts = document.getElementsByTagName('script');
@@ -33,7 +33,7 @@ var Keycloak = function (options) {
throw 'clientSecret missing';
}
- this.init = function (successCallback, errorCallback) {
+ kc.init = function (successCallback, errorCallback) {
if (window.oauth.callback) {
delete sessionStorage.oauthToken;
processCallback(successCallback, errorCallback);
@@ -44,50 +44,50 @@ var Keycloak = function (options) {
} else if (options.onload) {
switch (options.onload) {
case 'login-required' :
- window.location = createLoginUrl(true);
+ window.location = kc.createLoginUrl(true);
break;
case 'check-sso' :
- window.location = createLoginUrl(false);
+ window.location = kc.createLoginUrl(false);
break;
}
}
}
- this.login = function () {
- window.location.href = createLoginUrl(true);
+ kc.login = function () {
+ window.location.href = kc.createLoginUrl(true);
}
- this.logout = function () {
+ kc.logout = function () {
setToken(undefined);
- window.location.href = createLogoutUrl();
+ window.location.href = kc.createLogoutUrl();
}
- this.hasRealmRole = function (role) {
- var access = this.realmAccess;
+ kc.hasRealmRole = function (role) {
+ var access = kc.realmAccess;
return access && access.roles.indexOf(role) >= 0 || false;
}
- this.hasResourceRole = function (role, resource) {
- if (!this.resourceAccess) {
+ kc.hasResourceRole = function (role, resource) {
+ if (!kc.resourceAccess) {
return false;
}
- var access = this.resourceAccess[resource || options.clientId];
+ var access = kc.resourceAccess[resource || options.clientId];
return access && access.roles.indexOf(role) >= 0 || false;
}
- this.loadUserProfile = function (success, error) {
- var url = getRealmUrl() + '/account';
+ kc.loadUserProfile = function (success, error) {
+ var url = kc.getRealmUrl() + '/account';
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json');
- req.setRequestHeader('Authorization', 'bearer ' + this.token);
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
- instance.profile = JSON.parse(req.responseText);
- success && success(instance.profile)
+ kc.profile = JSON.parse(req.responseText);
+ success && success(kc.profile)
} else {
var response = { status: req.status, statusText: req.status };
if (req.responseText) {
@@ -108,22 +108,22 @@ var Keycloak = function (options) {
* @param successCallback
* @param errorCallback
*/
- this.onValidAccessToken = function(successCallback, errorCallback) {
- if (!this.tokenParsed) {
+ kc.onValidAccessToken = function(successCallback, errorCallback) {
+ if (!kc.tokenParsed) {
console.log('no token');
errorCallback();
return;
}
var currTime = new Date().getTime() / 1000;
- if (currTime > this.tokenParsed['exp']) {
- if (!this.refreshToken) {
+ if (currTime > kc.tokenParsed['exp']) {
+ if (!kc.refreshToken) {
console.log('no refresh token');
errorCallback();
return;
}
console.log('calling refresh');
- var params = 'grant_type=refresh_token&' + 'refresh_token=' + this.refreshToken;
- var url = getRealmUrl() + '/tokens/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);
@@ -134,8 +134,8 @@ var Keycloak = function (options) {
if (req.status == 200) {
console.log('Refresh Success');
var tokenResponse = JSON.parse(req.responseText);
- this.refreshToken = tokenResponse['refresh_token'];
- setToken(tokenResponse['access_token'], successCallback);
+ 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 });
@@ -150,7 +150,7 @@ var Keycloak = function (options) {
}
- function getRealmUrl() {
+ kc.getRealmUrl = function() {
return options.url + '/auth/rest/realms/' + encodeURIComponent(options.realm);
}
@@ -161,7 +161,7 @@ var Keycloak = function (options) {
if (code) {
var params = 'code=' + code;
- var url = getRealmUrl() + '/tokens/access/codes';
+ var url = kc.getRealmUrl() + '/tokens/access/codes';
var req = new XMLHttpRequest();
req.open('POST', url, true, options.clientId, options.clientSecret);
@@ -171,8 +171,8 @@ var Keycloak = function (options) {
if (req.readyState == 4) {
if (req.status == 200) {
var tokenResponse = JSON.parse(req.responseText);
- instance.refreshToken = tokenResponse['refresh_token'];
- setToken(tokenResponse['access_token'], successCallback);
+ kc.refreshToken = tokenResponse['refresh_token'];
+ kc.setToken(tokenResponse['access_token'], successCallback);
} else {
errorCallback && errorCallback({ authenticated: false, status: req.status, statusText: req.statusText });
}
@@ -189,33 +189,33 @@ var Keycloak = function (options) {
}
}
- function setToken(token, successCallback) {
+ kc.setToken = function(token, successCallback) {
if (token) {
sessionStorage.oauthToken = token;
window.oauth.token = token;
- instance.token = token;
+ kc.token = token;
- instance.tokenParsed = JSON.parse(atob(token.split('.')[1]));
- instance.authenticated = true;
- instance.username = instance.tokenParsed.sub;
- instance.realmAccess = instance.tokenParsed.realm_access;
- instance.resourceAccess = instance.tokenParsed.resource_access;
+ kc.tokenParsed = JSON.parse(atob(token.split('.')[1]));
+ kc.authenticated = true;
+ kc.username = kc.tokenParsed.sub;
+ kc.realmAccess = kc.tokenParsed.realm_access;
+ kc.resourceAccess = kc.tokenParsed.resource_access;
setTimeout(function() {
- successCallback && successCallback({ authenticated: instance.authenticated, username: instance.username });
+ successCallback && successCallback({ authenticated: kc.authenticated, username: kc.username });
}, 0);
} else {
delete sessionStorage.oauthToken;
delete window.oauth.token;
- delete instance.token;
+ delete kc.token;
}
}
- function createLoginUrl(prompt) {
+ kc.createLoginUrl = function(prompt) {
var state = createUUID();
sessionStorage.oauthState = state;
- var url = getRealmUrl()
+ var url = kc.getRealmUrl()
+ '/tokens/login'
+ '?client_id=' + encodeURIComponent(options.clientId)
+ '&redirect_uri=' + getEncodedRedirectUri()
@@ -229,17 +229,22 @@ var Keycloak = function (options) {
return url;
}
- function createLogoutUrl() {
- var url = getRealmUrl()
+ kc.createLogoutUrl = function() {
+ var url = kc.getRealmUrl()
+ '/tokens/logout'
+ '?redirect_uri=' + getEncodedRedirectUri();
return url;
}
function getEncodedRedirectUri() {
- var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
- if (location.hash) {
- url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ var url;
+ if (options.redirectUri) {
+ url = options.redirectUri;
+ } else {
+ url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
+ if (location.hash) {
+ url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ }
}
return encodeURI(url);
}
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index d243bd1b42..0630397295 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -11,4 +11,6 @@ public interface Constants {
String INTERNAL_ROLE = "KEYCLOAK_";
String ACCOUNT_MANAGEMENT_APP = "account";
+
+ String INSTALLED_APP_URN = "urn:ietf:wg:oauth:2.0:oob";
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index 38d00e25a4..eeb6674d66 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -38,6 +38,7 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.TokenService;
+import javax.ws.rs.Path;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
@@ -79,24 +80,32 @@ public class OAuthFlows {
public Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect, boolean rememberMe) {
String code = accessCode.getCode();
- UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code);
- log.debug("redirectAccessCode: state: {0}", state);
- if (state != null)
- redirectUri.queryParam("state", state);
- Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
- Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
- rememberMe = rememberMe || remember != null;
- location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo, rememberMe));
- return location.build();
+
+ if (Constants.INSTALLED_APP_URN.equals(redirect)) {
+ return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode.getId(), code).createCode();
+ } else {
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code);
+ log.debug("redirectAccessCode: state: {0}", state);
+ if (state != null)
+ redirectUri.queryParam("state", state);
+ Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+ Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+ rememberMe = rememberMe || remember != null;
+ location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo, rememberMe));
+ return location.build();
+ }
}
public Response redirectError(ClientModel client, String error, String state, String redirect) {
- UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("error", error);
- if (state != null) {
- redirectUri.queryParam("state", state);
+ if (Constants.INSTALLED_APP_URN.equals(redirect)) {
+ return Flows.forms(realm, request, uriInfo).setError(error).createCode();
+ } else {
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("error", error);
+ if (state != null) {
+ redirectUri.queryParam("state", state);
+ }
+ return Response.status(302).location(redirectUri.build()).build();
}
-
- return Response.status(302).location(redirectUri.build()).build();
}
public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 8d9b742320..18fb97d30e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -38,6 +38,8 @@ import org.json.JSONObject;
import org.junit.Assert;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.AccessScope;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.UserRepresentation;
@@ -156,6 +158,12 @@ public class OAuthClient {
}
}
+ public void verifyCode(String code) {
+ if (!RSAProvider.verify(new JWSInput(code), realmPublicKey)) {
+ throw new RuntimeException("Failed to verify code");
+ }
+ }
+
public String getClientId() {
return clientId;
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 620ecadfbc..4e1f043304 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -26,8 +26,8 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AuthorizationCodeResponse;
@@ -36,6 +36,7 @@ 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.By;
import org.openqa.selenium.WebDriver;
import java.io.IOException;
@@ -73,6 +74,21 @@ public class AuthorizationCodeTest {
Assert.assertNotNull(response.getCode());
Assert.assertEquals("mystate", response.getState());
Assert.assertNull(response.getError());
+
+ oauth.verifyCode(response.getCode());
+ }
+
+ @Test
+ public void authorizationRequestInstalledApp() throws IOException {
+ oauth.redirectUri(Constants.INSTALLED_APP_URN);
+
+ oauth.doLogin("test-user@localhost", "password");
+
+ String title = driver.getTitle();
+ Assert.assertTrue(title.startsWith("Success code="));
+
+ String code = driver.findElement(By.id("code")).getText();
+ oauth.verifyCode(code);
}
@Test
@@ -94,6 +110,8 @@ public class AuthorizationCodeTest {
Assert.assertTrue(response.isRedirected());
Assert.assertNotNull(response.getCode());
+
+ oauth.verifyCode(response.getCode());
}
@Test
@@ -104,6 +122,8 @@ public class AuthorizationCodeTest {
Assert.assertNotNull(response.getCode());
Assert.assertNull(response.getState());
Assert.assertNull(response.getError());
+
+ oauth.verifyCode(response.getCode());
}
}