diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java index ebc84f65..ed787940 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java @@ -81,6 +81,9 @@ private OIDCAuthenticatorConstants() { public static final String SCOPE_PARAM_SUFFIX = "_scope_param"; public static final String REDIRECTION_PROMPT = "REDIRECTION_PROMPT"; public static final String SCOPE = "scope"; + public static final String QUESTION_SIGN = "\\?"; + public static final String AMPERSAND_SIGN = "&"; + public static final String EQUAL_SIGN = "="; /** * This class holds the constants related to authenticator configuration parameters. diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java index f266d1a4..45912358 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2013-2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -93,6 +93,7 @@ import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; +import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.ParseException; @@ -449,9 +450,48 @@ protected Map getSubjectAttributes(OAuthClientResponse tok protected void initiateAuthenticationRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException { + try { + DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = null; + if (LoggerUtils.isDiagnosticLogsEnabled() && context.getAuthenticatorProperties() != null) { + diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder( + getComponentId(), INITIATE_OUTBOUND_AUTH_REQUEST); + diagnosticLogBuilder.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION) + .resultStatus(DiagnosticLog.ResultStatus.SUCCESS) + .inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep()) + .inputParam("authenticator properties", context.getAuthenticatorProperties().keySet()) + .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName()) + .inputParams(getApplicationDetails(context)); + } + + String loginPage = prepareLoginPage(request, context); + response.sendRedirect(loginPage); + if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { + String scopes = extractScopesFromURL(loginPage); + if (StringUtils.isNotEmpty(scopes)) { + diagnosticLogBuilder.inputParam("scopes", scopes); + } + diagnosticLogBuilder.resultMessage("Redirecting to the federated IDP login page."); + LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + } + } catch (IOException e) { + throw new AuthenticationFailedException(ErrorMessages.IO_ERROR.getCode(), e.getMessage(), e); + } + } + + /** + * Prepare the login page needed for initiating authentication request. + * + * @param request Http Servlet Request. + * @param context Authentication Context of the flow. + * @return Login page needed for initiating authentication request. + */ + protected String prepareLoginPage(HttpServletRequest request, AuthenticationContext context) + throws AuthenticationFailedException { + try { if (LoggerUtils.isDiagnosticLogsEnabled()) { - DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder( + DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = + new DiagnosticLog.DiagnosticLogBuilder( getComponentId(), INITIATE_OUTBOUND_AUTH_REQUEST); diagnosticLogBuilder.resultMessage("Initiate outbound OIDC authentication request.") .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION) @@ -463,17 +503,6 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer } Map authenticatorProperties = context.getAuthenticatorProperties(); if (authenticatorProperties != null) { - DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = null; - if (LoggerUtils.isDiagnosticLogsEnabled()) { - diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(getComponentId(), - INITIATE_OUTBOUND_AUTH_REQUEST); - diagnosticLogBuilder.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION) - .resultStatus(DiagnosticLog.ResultStatus.SUCCESS) - .inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep()) - .inputParam("authenticator properties", authenticatorProperties.keySet()) - .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName()) - .inputParams(getApplicationDetails(context)); - } String clientId = authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID); String authorizationEP = getOIDCAuthzEndpoint(authenticatorProperties); String callbackurl = getCallbackUrl(authenticatorProperties); @@ -502,9 +531,6 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer String queryString = getQueryString(authenticatorProperties); if (StringUtils.isNotBlank(scopes)) { - if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { - diagnosticLogBuilder.inputParam("scopes", scopes); - } queryString += "&scope=" + scopes; } queryString = interpretQueryString(context, queryString, request.getParameterMap()); @@ -567,11 +593,7 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer } } context.setProperty(OIDCAuthenticatorConstants.AUTHENTICATOR_NAME + REDIRECT_URL_SUFFIX, loginPage); - response.sendRedirect(loginPage); - if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { - diagnosticLogBuilder.resultMessage("Redirecting to the federated IDP login page."); - LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); - } + return loginPage; } else { if (LOG.isDebugEnabled()) { LOG.debug(ErrorMessages.RETRIEVING_AUTHENTICATOR_PROPERTIES_FAILED.getMessage()); @@ -590,13 +612,10 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer throw new AuthenticationFailedException(ErrorMessages.BUILDING_AUTHORIZATION_CODE_REQUEST_FAILED.getCode(), e.getMessage(), e); - } catch (IOException e) { - throw new AuthenticationFailedException(ErrorMessages.IO_ERROR.getCode(), e.getMessage(), e); } catch (OAuthSystemException e) { throw new AuthenticationFailedException(ErrorMessages.BUILDING_AUTHORIZATION_CODE_REQUEST_FAILED.getCode(), e.getMessage(), e); } - return; } /** @@ -2038,7 +2057,7 @@ private AuthenticatorFlowStatus processLogout(HttpServletRequest request, HttpSe * @param context Authentication context. * @return Map of application details. */ - private Map getApplicationDetails(AuthenticationContext context) { + protected Map getApplicationDetails(AuthenticationContext context) { Map applicationDetailsMap = new HashMap<>(); FrameworkUtils.getApplicationResourceId(context).ifPresent(applicationId -> @@ -2049,6 +2068,29 @@ private Map getApplicationDetails(AuthenticationContext context) return applicationDetailsMap; } + /** + * Extract query param scopes from a given url. + * + * @param url Given url. + * @return Extracted scopes as a String. + */ + protected String extractScopesFromURL(String url) throws UnsupportedEncodingException { + + if (StringUtils.isNotBlank(url)) { + String[] splitUrl = url.split(OIDCAuthenticatorConstants.QUESTION_SIGN, 2); + if (splitUrl.length == 2) { + String[] params = splitUrl[1].split(OIDCAuthenticatorConstants.AMPERSAND_SIGN); + for (String param : params) { + String[] keyValue = param.split(OIDCAuthenticatorConstants.EQUAL_SIGN, 2); + if (keyValue.length == 2 && OAuthConstants.OAuth20Params.SCOPE.equals(keyValue[0])) { + return URLDecoder.decode(keyValue[1], FrameworkUtils.UTF_8); + } + } + } + } + return StringUtils.EMPTY; + } + private static List getUserAttributeClaimMappingList(AuthenticatedUser authenticatedUser) { return authenticatedUser.getUserAttributes().keySet().stream()