Use account-console client for server-side auth check
Also generate PKCE verifier and use challenge parameters Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
This commit is contained in:
parent
729417b20a
commit
40bdc902f0
3 changed files with 26 additions and 32 deletions
|
@ -42,7 +42,6 @@ public class AbstractOAuthClient {
|
||||||
protected String stateCookiePath;
|
protected String stateCookiePath;
|
||||||
protected boolean isSecure;
|
protected boolean isSecure;
|
||||||
protected boolean publicClient;
|
protected boolean publicClient;
|
||||||
protected boolean pkceEnabled;
|
|
||||||
protected String getStateCode() {
|
protected String getStateCode() {
|
||||||
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
|
@ -127,14 +126,6 @@ public class AbstractOAuthClient {
|
||||||
this.relativeUrlsUsed = relativeUrlsUsed;
|
this.relativeUrlsUsed = relativeUrlsUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPkceEnabled() {
|
|
||||||
return pkceEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPkceEnabled(boolean pkceEnabled) {
|
|
||||||
this.pkceEnabled = pkceEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String stripOauthParametersFromRedirect(String uri) {
|
protected String stripOauthParametersFromRedirect(String uri) {
|
||||||
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
|
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
|
||||||
.replaceQueryParam(OAuth2Constants.CODE, null)
|
.replaceQueryParam(OAuth2Constants.CODE, null)
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
|
||||||
import org.keycloak.services.managers.Auth;
|
import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
@ -45,7 +44,6 @@ import jakarta.ws.rs.core.UriBuilder;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for securing local services. Provides login basics as well as CSRF check basics
|
* Helper class for securing local services. Provides login basics as well as CSRF check basics
|
||||||
|
@ -163,7 +161,7 @@ public abstract class AbstractSecuredLocalService {
|
||||||
return oauth.redirect(session.getContext().getUri(), accountUri.toString());
|
return oauth.redirect(session.getContext().getUri(), accountUri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OAuthRedirect extends AbstractOAuthClient {
|
static class OAuthRedirect extends AbstractOAuthClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* closes client
|
* closes client
|
||||||
|
@ -182,20 +180,6 @@ public abstract class AbstractSecuredLocalService {
|
||||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||||
.queryParam(OAuth2Constants.SCOPE, scopeParam);
|
.queryParam(OAuth2Constants.SCOPE, scopeParam);
|
||||||
|
|
||||||
if (isPkceEnabled()) {
|
|
||||||
String pkceChallenge;
|
|
||||||
try {
|
|
||||||
// TODO generate PKCE challenge based on server value
|
|
||||||
String codeVerifier = UUID.randomUUID().toString();
|
|
||||||
pkceChallenge = PkceUtils.generateS256CodeChallenge(codeVerifier);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
uriBuilder
|
|
||||||
.queryParam(OAuth2Constants.CODE_CHALLENGE, pkceChallenge)
|
|
||||||
.queryParam(OAuth2Constants.CODE_CHALLENGE_METHOD, OAuth2Constants.PKCE_METHOD_S256);
|
|
||||||
}
|
|
||||||
|
|
||||||
URI url = uriBuilder.build();
|
URI url = uriBuilder.build();
|
||||||
|
|
||||||
NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true);
|
NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true);
|
||||||
|
|
|
@ -6,10 +6,12 @@ import jakarta.ws.rs.PathParam;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.UriBuilder;
|
import jakarta.ws.rs.core.UriBuilder;
|
||||||
import org.jboss.resteasy.reactive.NoCache;
|
import org.jboss.resteasy.reactive.NoCache;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.authentication.requiredactions.DeleteAccount;
|
import org.keycloak.authentication.requiredactions.DeleteAccount;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
import org.keycloak.common.util.Environment;
|
import org.keycloak.common.util.Environment;
|
||||||
|
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
||||||
import org.keycloak.utils.SecureContextResolver;
|
import org.keycloak.utils.SecureContextResolver;
|
||||||
import org.keycloak.models.AccountRoles;
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -48,6 +50,7 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -213,12 +216,28 @@ public class AccountConsole implements AccountResourceProvider {
|
||||||
}
|
}
|
||||||
URI targetUri = consoleUriBuilder.build(realm.getName());
|
URI targetUri = consoleUriBuilder.build(realm.getName());
|
||||||
|
|
||||||
var oauthRedirect = new AbstractSecuredLocalService.OAuthRedirect();
|
String pkceChallenge;
|
||||||
oauthRedirect.setAuthUrl(OIDCLoginProtocolService.authUrl(session.getContext().getUri()).build(realm.getName()).toString());
|
try {
|
||||||
oauthRedirect.setClientId(Constants.ACCOUNT_CONSOLE_CLIENT_ID);
|
// Add PKCE parameters as it is required for the account-console client.
|
||||||
oauthRedirect.setPkceEnabled(true);
|
// Because the account console configuration requires PKCE, we need to send this with the redirect in order to not fail validations.
|
||||||
oauthRedirect.setSecure(realm.getSslRequired().isRequired(session.getContext().getConnection()));
|
// The real PKCE challenge will be sent by the account-console OIDC client JavaScript integration.
|
||||||
return oauthRedirect.redirect(session.getContext().getUri(), targetUri.toString());
|
String codeVerifier = UUID.randomUUID().toString();
|
||||||
|
pkceChallenge = PkceUtils.generateS256CodeChallenge(codeVerifier);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// this should never happen
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
UriBuilder uriBuilder = UriBuilder.fromUri(OIDCLoginProtocolService.authUrl(session.getContext().getUri()).build(realm.getName()).toString())
|
||||||
|
.queryParam(OAuth2Constants.CLIENT_ID, Constants.ACCOUNT_CONSOLE_CLIENT_ID)
|
||||||
|
.queryParam(OAuth2Constants.REDIRECT_URI, targetUri)
|
||||||
|
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||||
|
.queryParam(OAuth2Constants.CODE_CHALLENGE, pkceChallenge)
|
||||||
|
.queryParam(OAuth2Constants.CODE_CHALLENGE_METHOD, OAuth2Constants.PKCE_METHOD_S256);
|
||||||
|
|
||||||
|
URI url = uriBuilder.build();
|
||||||
|
|
||||||
|
return Response.status(302).location(url).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> supportedLocales(Properties messages) {
|
private Map<String, String> supportedLocales(Properties messages) {
|
||||||
|
|
Loading…
Reference in a new issue