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 boolean isSecure;
|
||||
protected boolean publicClient;
|
||||
protected boolean pkceEnabled;
|
||||
protected String getStateCode() {
|
||||
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
||||
}
|
||||
|
@ -127,14 +126,6 @@ public class AbstractOAuthClient {
|
|||
this.relativeUrlsUsed = relativeUrlsUsed;
|
||||
}
|
||||
|
||||
public boolean isPkceEnabled() {
|
||||
return pkceEnabled;
|
||||
}
|
||||
|
||||
public void setPkceEnabled(boolean pkceEnabled) {
|
||||
this.pkceEnabled = pkceEnabled;
|
||||
}
|
||||
|
||||
protected String stripOauthParametersFromRedirect(String uri) {
|
||||
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
|
||||
.replaceQueryParam(OAuth2Constants.CODE, null)
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.util.TokenUtil;
|
||||
|
@ -45,7 +44,6 @@ import jakarta.ws.rs.core.UriBuilder;
|
|||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
public static class OAuthRedirect extends AbstractOAuthClient {
|
||||
static class OAuthRedirect extends AbstractOAuthClient {
|
||||
|
||||
/**
|
||||
* closes client
|
||||
|
@ -182,20 +180,6 @@ public abstract class AbstractSecuredLocalService {
|
|||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||
.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();
|
||||
|
||||
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.UriBuilder;
|
||||
import org.jboss.resteasy.reactive.NoCache;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.authentication.requiredactions.DeleteAccount;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.common.util.Environment;
|
||||
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
||||
import org.keycloak.utils.SecureContextResolver;
|
||||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -48,6 +50,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Scanner;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -213,12 +216,28 @@ public class AccountConsole implements AccountResourceProvider {
|
|||
}
|
||||
URI targetUri = consoleUriBuilder.build(realm.getName());
|
||||
|
||||
var oauthRedirect = new AbstractSecuredLocalService.OAuthRedirect();
|
||||
oauthRedirect.setAuthUrl(OIDCLoginProtocolService.authUrl(session.getContext().getUri()).build(realm.getName()).toString());
|
||||
oauthRedirect.setClientId(Constants.ACCOUNT_CONSOLE_CLIENT_ID);
|
||||
oauthRedirect.setPkceEnabled(true);
|
||||
oauthRedirect.setSecure(realm.getSslRequired().isRequired(session.getContext().getConnection()));
|
||||
return oauthRedirect.redirect(session.getContext().getUri(), targetUri.toString());
|
||||
String pkceChallenge;
|
||||
try {
|
||||
// Add PKCE parameters as it is required for the account-console client.
|
||||
// Because the account console configuration requires PKCE, we need to send this with the redirect in order to not fail validations.
|
||||
// The real PKCE challenge will be sent by the account-console OIDC client JavaScript integration.
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue