Skip to content

Commit

Permalink
feat(jans-auth-server): introduced new 'prepareAuthzRequest' method i…
Browse files Browse the repository at this point in the history
…n authorization challenge script (#10598)

#10588

Signed-off-by: YuriyZ <[email protected]>
  • Loading branch information
yuriyz authored Jan 13, 2025
1 parent 5fb1903 commit 02c240e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,19 @@ public int getApiVersion() {
public Map<String, String> getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}

@Override
public void prepareAuthzRequest(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
if (sessionObject != null) {
final Map<String, String> sessionAttributes = sessionObject.getAttributes().getAttributes();

// set scope from session into request object
final String scopeFromSession = sessionAttributes.get("scope");
if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
context.getAuthzRequest().setScope(scopeFromSession);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ The Authorization Challenage script implements the [AuthorizationChallenageType]
|:-----|:------|
|`def authorize(self, context)`| Called when the request is received. |
|`def getAuthenticationMethodClaims(self, context)`| Called to get authn method claims. It is injected into `id_token`. Returns key-value map. |
|`def prepareAuthzRequest(self, context)`| Prepared authorization request before `authorize` method. It's good place to restore data from session if needed. |

`authorize` method returns true/false which indicates to server whether to issue `authorization_code` in response or not.

If parameters is not present then error has to be created and `false` returned.
If all is good script has to return `true` and it's strongly recommended to set user `context.getExecutionContext().setUser(user);` so AS can keep tracking what exactly user is authenticated.

`prepareAuthzRequest` should typically be used for authorization request manipulation before `authorize` method is invoked.
Also if there is multi-step flow where some data are stored in `session` object, it is good place to restore data from session into request (please find example in sample below).

### Objects
| Object name | Object description |
Expand Down Expand Up @@ -458,6 +461,19 @@ public class AuthorizationChallenge implements AuthorizationChallengeType {
@Override
public Map<String, String> getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}
@Override
public void prepareAuthzRequest(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
if (sessionObject != null) {
final Map<String, String> sessionAttributes = sessionObject.getAttributes().getAttributes();
final String scopeFromSession = sessionAttributes.get("scope");
if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
context.getAuthzRequest().setScope(scopeFromSession);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,19 @@ public int getApiVersion() {
public Map<String, String> getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}

@Override
public void prepareAuthzRequest(Object scriptContext) {
ExternalScriptContext context = (ExternalScriptContext) scriptContext;
final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
if (sessionObject != null) {
final Map<String, String> sessionAttributes = sessionObject.getAttributes().getAttributes();

// set scope from session into request object
final String scopeFromSession = sessionAttributes.get("scope");
if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
context.getAuthzRequest().setScope(scopeFromSession);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ public Response requestAuthorization(AuthzRequest authzRequest) {
public void prepareAuthzRequest(AuthzRequest authzRequest) {
authzRequest.setScope(ServerUtil.urlDecode(authzRequest.getScope()));

externalAuthorizationChallengeService.externalPrepareAuthzRequest(authzRequest);

if (StringUtils.isNotBlank(authzRequest.getAuthorizationChallengeSession())) {
final AuthorizationChallengeSession session = authorizationChallengeSessionService.getAuthorizationChallengeSession(authzRequest.getAuthorizationChallengeSession());

Expand Down Expand Up @@ -158,7 +160,7 @@ public Response authorize(AuthzRequest authzRequest) throws IOException, TokenBi
executionContext.setSessionId(sessionUser);

if (user == null) {
log.trace("Executing external authentication challenge");
log.trace("Executing external authentication challenge ... (requestedScopes: {})", scopes);

final boolean ok = externalAuthorizationChallengeService.externalAuthorize(executionContext);
if (!ok) {
Expand All @@ -179,6 +181,8 @@ public Response authorize(AuthzRequest authzRequest) throws IOException, TokenBi

String grantAcr = executionContext.getScript() != null ? executionContext.getScript().getName() : authzRequest.getAcrValues();

log.trace("Creating authorization code grant with: scope {}, acr {}", scopes, grantAcr);

AuthorizationCodeGrant authorizationGrant = authorizationGrantList.createAuthorizationCodeGrant(user, client, new Date());
authorizationGrant.setNonce(authzRequest.getNonce());
authorizationGrant.setJwtAuthorizationRequest(authzRequest.getJwtRequest());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.jans.as.model.authorize.AuthorizeErrorResponseType;
import io.jans.as.model.configuration.AppConfiguration;
import io.jans.as.model.error.ErrorResponseFactory;
import io.jans.as.server.authorize.ws.rs.AuthzRequest;
import io.jans.as.server.model.common.ExecutionContext;
import io.jans.as.server.service.external.context.ExternalScriptContext;
import io.jans.model.custom.script.CustomScriptType;
Expand Down Expand Up @@ -134,4 +135,45 @@ public CustomScriptConfiguration identifyScript(List<String> acrValues) {
log.trace("Unable to find script by acr_values {}", acrValues);
return getCustomScriptConfigurationByName(appConfiguration.getAuthorizationChallengeDefaultAcr());
}

public void externalPrepareAuthzRequest(AuthzRequest authzRequest) {
final List<String> acrValues = authzRequest.getAcrValuesList();
final CustomScriptConfiguration script = identifyScript(acrValues);
if (script == null) {
String msg = String.format("Unable to identify script by acr_values %s.", acrValues);
log.debug(msg);
throw new WebApplicationException(errorResponseFactory
.newErrorResponse(Response.Status.BAD_REQUEST)
.entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_REQUEST, authzRequest.getState(), msg))
.build());
}

log.trace("Executing python 'prepareAuthzRequest' method, script name: {}, clientId: {}, scope: {}, authorizationChallengeSession: {}",
script.getName(), authzRequest.getClientId(), authzRequest.getScope(), authzRequest.getAuthorizationChallengeSession());

ExecutionContext executionContext = ExecutionContext.of(authzRequest);
executionContext.setScript(script);

try {
AuthorizationChallengeType authorizationChallengeType = (AuthorizationChallengeType) script.getExternalType();
final ExternalScriptContext scriptContext = new ExternalScriptContext(executionContext);
authorizationChallengeType.prepareAuthzRequest(scriptContext);

scriptContext.throwWebApplicationExceptionIfSet();
} catch (WebApplicationException e) {
if (log.isTraceEnabled()) {
log.trace("WebApplicationException from script", e);
}
throw e;
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
saveScriptError(script.getCustomScript(), ex);
throw new WebApplicationException(errorResponseFactory
.newErrorResponse(Response.Status.INTERNAL_SERVER_ERROR)
.entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.ACCESS_DENIED, executionContext.getAuthzRequest().getState(), "Unable to run 'prepareAuthzRequest' method authorization challenge script."))
.build());
}

log.trace("Finished 'prepareAuthzRequest' method, script name: {}, clientId: {}", script.getName(), executionContext.getAuthzRequest().getClientId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ public interface AuthorizationChallengeType extends BaseExternalType {
boolean authorize(Object context);

Map<String, String> getAuthenticationMethodClaims(Object context);

// prepare authzRequest - AuthzRequest class
void prepareAuthzRequest(Object context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public Map<String, String> getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}

@Override
public void prepareAuthzRequest(Object context) {
}

@Override
public boolean init(Map<String, SimpleCustomProperty> configurationAttributes) {
return false;
Expand Down

0 comments on commit 02c240e

Please sign in to comment.