8000 fix: PAR authorization request supports registration via `prompt=create` by vilmosnagy · Pull Request #50183 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ private Response process(final MultivaluedMap<String, String> params) {

// Add support for Initiating User Registration via OpenID Connect 1.0 via prompt=create
// see: https://openid.net/specs/openid-connect-prompt-create-1_0.html#section-4.1
if (OIDCLoginProtocol.PROMPT_VALUE_CREATE.equals(params.getFirst(OAuth2Constants.PROMPT))) {
String promptValue = Optional
.ofNullable(params.getFirst(OAuth2Constants.PROMPT))
.orElseGet(() -> session.getContext().getAuthenticationSession().getClientNote(OAuth2Constants.PROMPT));

if (OIDCLoginProtocol.PROMPT_VALUE_CREATE.equals(promptValue)) {
if (!Organizations.isRegistrationAllowed(session, realm)) {
throw new ErrorPageException(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.REGISTRATION_NOT_ALLOWED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.keycloak.protocol.oid4vc.model.Proofs;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.page.RegisterPage;
import org.keycloak.tests.oid4vc.OID4VCIssuerTestBase;
import org.keycloak.tests.oid4vc.OID4VCProofTestUtils;
import org.keycloak.tests.oid4vc.issuance.OID4VCAuthorizationCodeFlowTestBase;
Expand Down Expand Up @@ -58,6 +60,8 @@ protected String getFirstNameProtocolMapperName() {
return "givenName";
}

@InjectPage
RegisterPage registerPage;
@Override
protected void verifyCredentialStructure(Object credentialObj) {
assertNotNull(credentialObj, "JWT credential object should not be null");
Expand Down Expand Up @@ -197,6 +201,55 @@ public void testAuthorizationCodeFlowWithPARAndAuthorizationDetailsFailure() thr
assertTrue(errorDescription != null && errorDescription.contains("Invalid authorization_details"), "Error message should indicate authorization_details processing failure");
}

@Test
public void testAuthorizationCodeFlowWithPARPromptCreateOpensTheRegistrationPage() throws Exception {
testRealm.updateWithCleanup(r -> r.registrationAllowed(true));
CredentialIssuer issuer = wallet.getIssuerMetadata(ctx);
OIDCConfigurationRepresentation openidConfig = wallet.getAuthorizationServerMetadata(ctx);

// Step 1: Create PAR request with authorization_details
String credentialConfigurationId = ctx.getCredentialConfigurationId();

// Create authorization details with claims
ClaimsDescription claim = new ClaimsDescription();
List<Object> claimPath = Arrays.asList("credentialSubject", getExpectedClaimPath());
claim.setPath(claimPath);
claim.setMandatory(true);

OID4VCAuthorizationDetail authDetail = new OID4VCAuthorizationDetail();
authDetail.setType(OPENID_CREDENTIAL);
authDetail.setCredentialConfigurationId(credentialConfigurationId);
authDetail.setClaims(List.of(claim));
authDetail.setLocations(Collections.singletonList(issuer.getCredentialIssuer()));

List<OID4VCAuthorizationDetail> authDetails = List.of(authDetail);

// Create PAR request
ParResponse parResponse = oauth.pushedAuthorizationRequest()
.endpoint(openidConfig.getPushedAuthorizationRequestEndpoint())
.client(client.getClientId(), client.getSecret())
.scopeParam(ctx.getScope())
.authorizationDetails(authDetails)
.state("test-state")
.nonce("test-nonce")
.prompt("create")
.send();
assertEquals(HttpStatus.SC_CREATED, parResponse.getStatusCode());
String requestUri = parResponse.getRequestUri();
assertNotNull(requestUri, "Request URI should not be null");

// Step 2: Perform authorization with PAR
oauth.client(client.getClientId(), client.getSecret());
oauth.scope(ctx.getScope());
oauth.loginForm().requestUri(requestUri).open();

registerPage.assertCurrent();
registerPage.register("Vilmos", "Szabó-Nagy", "vilmos@email", "vnagy", "AppleTree123");
testRealm.cleanup().add(r -> r.users().search("vnagy").stream().findFirst().ifPresent(u -> r.users().delete(u.getId())));
AuthorizationEndpointResponse authResponse = oauth.parseLoginResponse();
assertTrue(authResponse.isSuccess());
}

@Test
public void testAuthorizationCodeFlowWithPARButNoAuthorizationDetailsInTokenRequest() throws Exception {
wallet.getIssuerMetadata(ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public ParRequest requestUri(String requestUri) {
return this;
}

public ParRequest prompt(String prompt) {
parameter(OIDCLoginProtocol.PROMPT_PARAM, prompt);
return this;
}

public ParRequest scopeParam(String scope) {
scopeExplicitlySet = true;
parameter(OAuth2Constants.SCOPE, scope);
Expand Down
Loading
0