Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Client Initiated Backchannel Authentication #2725

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
54 changes: 54 additions & 0 deletions components/org.wso2.carbon.identity.oauth.ciba/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,54 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.wso2</groupId>
<artifactId>httpcore</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
<artifactId>org.wso2.carbon.identity.oauth</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.event</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.governance</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.notification.push</groupId>
<artifactId>org.wso2.carbon.identity.notification.push.device.handler</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Test Dependencies-->
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
Expand Down Expand Up @@ -115,6 +159,7 @@
javax.servlet.*;version ="${imp.pkg.version.javax.servlet}",

org.apache.commons.logging.* ; version="${commons-logging.osgi.version.range}",
org.apache.http.client.utils; version="${httpcomponents-httpclient.wso2.version}",
org.apache.felix.scr; version = "${apache.felix.scr.ds.annotations.version}",
org.apache.oltu.oauth2.* ; version = "${oltu.package.import.version.range}",

Expand All @@ -139,6 +184,15 @@
="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.core.*; version =
"${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.event.*;
version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.utils.multitenancy; version="${carbon.kernel.imp.pkg.version.range}",
org.wso2.carbon.identity.governance.*;
version="${identity.governance.imp.pkg.version.range}",
org.wso2.carbon.identity.notification.push.device.handler.*;
version="${identity.notification.push.import.version.range}",
org.wso2.carbon.identity.application.authentication.framework.*;
version="${carbon.identity.framework.imp.pkg.version.range}",
</Import-Package>
<Export-Package>
!org.wso2.carbon.identity.oauth.ciba.internal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.wso2.carbon.identity.oauth.ciba.exceptions.CibaCoreException;
import org.wso2.carbon.identity.oauth.ciba.model.CibaAuthCodeRequest;
import org.wso2.carbon.identity.oauth.ciba.model.CibaAuthCodeResponse;
import org.wso2.carbon.identity.oauth.ciba.model.CibaUserNotificationContext;

/**
* Provides authentication services.
Expand All @@ -33,9 +34,23 @@ public interface CibaAuthService {
*
* @param cibaAuthCodeRequest CIBA Authentication Request Data Transfer Object.
* @return CibaAuthCodeResponse CIBA Authentication Response Data Transfer Object.
* @throws CibaCoreException Core exception from CIBA module.
* @throws CibaCoreException Core exception from CIBA module.
* @throws CibaClientException Client exception from CIBA core component.
*/
CibaAuthCodeResponse generateAuthCodeResponse(CibaAuthCodeRequest cibaAuthCodeRequest) throws CibaCoreException,
CibaClientException;

default String resolveUser(CibaAuthCodeRequest cibaAuthCodeRequest) throws CibaCoreException, CibaClientException {

return null;
}

default void triggerNotification(CibaUserNotificationContext cibaUserNotificationContext)
throws CibaCoreException {

}

default void updateStatus(String authCodeKey, Enum authenticationStatus) throws CibaCoreException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
import org.wso2.carbon.identity.oauth.ciba.dao.CibaDAOFactory;
import org.wso2.carbon.identity.oauth.ciba.exceptions.CibaClientException;
import org.wso2.carbon.identity.oauth.ciba.exceptions.CibaCoreException;
import org.wso2.carbon.identity.oauth.ciba.handlers.CibaUserNotificationHandler;
import org.wso2.carbon.identity.oauth.ciba.internal.CibaServiceComponentHolder;
import org.wso2.carbon.identity.oauth.ciba.model.CibaAuthCodeDO;
import org.wso2.carbon.identity.oauth.ciba.model.CibaAuthCodeRequest;
import org.wso2.carbon.identity.oauth.ciba.model.CibaAuthCodeResponse;
import org.wso2.carbon.identity.oauth.ciba.model.CibaUserNotificationContext;
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
Expand All @@ -45,6 +48,7 @@
public class CibaAuthServiceImpl implements CibaAuthService {

private static Log log = LogFactory.getLog(CibaAuthServiceImpl.class);
private static CibaUserNotificationHandler cibaUserNotificationHandler = new CibaUserNotificationHandler();

@Override
public CibaAuthCodeResponse generateAuthCodeResponse(CibaAuthCodeRequest cibaAuthCodeRequest)
Expand All @@ -55,6 +59,25 @@ public CibaAuthCodeResponse generateAuthCodeResponse(CibaAuthCodeRequest cibaAut
return buildAuthCodeResponse(cibaAuthCodeRequest, cibaAuthCodeDO);
}

@Override
public String resolveUser(CibaAuthCodeRequest cibaAuthCodeRequest) throws CibaCoreException, CibaClientException {

return CibaServiceComponentHolder.getCibaUserResolver().resolveUser(cibaAuthCodeRequest);
}

@Override
public void triggerNotification(CibaUserNotificationContext cibaUserNotificationContext) throws CibaCoreException {

cibaUserNotificationHandler.sendNotification(cibaUserNotificationContext);
}

@Override
public void updateStatus(String authCodeKey, Enum authenticationStatus) throws CibaCoreException {

CibaDAOFactory.getInstance().getCibaAuthMgtDAO()
.updateStatus(authCodeKey, authenticationStatus);
}

/**
* Returns a unique AuthCodeKey.
*
Expand Down Expand Up @@ -146,6 +169,7 @@ private CibaAuthCodeResponse buildAuthCodeResponse(CibaAuthCodeRequest cibaAuthC
cibaAuthCodeResponse.setClientId(clientID);
cibaAuthCodeResponse.setScopes(cibaAuthCodeRequest.getScopes());
cibaAuthCodeResponse.setExpiresIn(cibaAuthCodeDO.getExpiresIn());
cibaAuthCodeResponse.setAuthCodeKey(cibaAuthCodeDO.getCibaAuthCodeKey());

if (StringUtils.isNotBlank(cibaAuthCodeRequest.getBindingMessage())) {
cibaAuthCodeResponse.setBindingMessage(cibaAuthCodeRequest.getBindingMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,29 @@ public class CibaConstants {
public static final String RESPONSE_TYPE_VALUE = "cibaAuthCode";
public static final String USER_IDENTITY = "user";
public static final String BINDING_MESSAGE = "binding_message";
public static final String LOGIN_HINT = "login_hint";
public static final String TRANSACTION_CONTEXT = "transaction_context";
public static final String UTC = "UTC";
public static final String EXPIRES_IN = "expires_in";
public static final String CIBA_USER_AUTH_ENDPOINT = "/oauth2/ciba_auth";
public static final String CIBA_AUTH_CODE_KEY = "authCodeKey";
public static final String DEFAULT_CIBA_USER_LOGIN_TEMPLATE_NAME = "CIBAUserLoginRequest";
public static final String CIBA_USER_LOGIN_LINK_PLACEHOLDER_NAME = "user-login-link";
public static final String CIBA_BINDING_MESSAGE_PLACEHOLDER_NAME = "binding-message";
public static final String CIBA_APP_PLACEHOLDER_NAME = "application-name";
public static final String SMS_EVENT_TRIGGER_NAME = "TRIGGER_SMS_NOTIFICATION_LOCAL";
public static final String TEMPLATE_TYPE = "TEMPLATE_TYPE";
public static final String ARBITRARY_SEND_TO = "send-to";
public static final String CIBA_SUCCESS_ENDPOINT_PATH = "/authenticationendpoint/device_success.do";
public static final String PUSH_NOTIFICATION_CHANNEL = "PUSH_NOTIFICATION";
public static final String NOTIFICATION_SCENARIO = "NOTIFICATION_SCENARIO";
public static final String CIBA_NOTIFICATION_SCENARIO = "AUTHENTICATION";
public static final String PUSH_ID = "pushId";
public static final String DEVICE_TOKEN = "deviceToken";
public static final String NOTIFICATION_PROVIDER = "notificationProvider";
public static final String CHALLENGE = "challenge";
public static final String DEVICE_ID = "deviceId";
public static final String PUSH_NOTIFICATION_EVENT_NAME = "TRIGGER_PUSH_NOTIFICATION";

private CibaConstants() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.utils.URIBuilder;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.core.ServiceURLBuilder;
import org.wso2.carbon.identity.core.URLBuilderException;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.oauth.ciba.common.AuthReqStatus;
import org.wso2.carbon.identity.oauth.ciba.common.CibaConstants;
import org.wso2.carbon.identity.oauth.ciba.dao.CibaDAOFactory;
Expand All @@ -34,6 +39,11 @@
import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO;
import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeRespDTO;
import org.wso2.carbon.identity.oauth2.model.OAuth2Parameters;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;

import java.net.URISyntaxException;

import static org.wso2.carbon.identity.oauth.ciba.common.CibaConstants.CIBA_SUCCESS_ENDPOINT_PATH;

/**
* Handles authorize requests with CibaAuthCode as response type.
Expand All @@ -51,9 +61,6 @@ public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx
// Assigning authenticated user for the request that to be persisted.
AuthenticatedUser cibaAuthenticatedUser = authorizationReqDTO.getUser();

// Assigning the authentication status that to be persisted.
Enum authenticationStatus = AuthReqStatus.AUTHENTICATED;

String authCodeKey =
CibaDAOFactory.getInstance().getCibaAuthMgtDAO().getCibaAuthCodeKey(authorizationReqDTO.getNonce());

Expand All @@ -62,8 +69,10 @@ public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx
.persistAuthenticationSuccess(authCodeKey, cibaAuthenticatedUser);

// Building custom CallBack URL.
String callbackURL = authorizationReqDTO.getCallbackUrl() + "?authenticationStatus=" + authenticationStatus;
respDTO.setCallbackURI(callbackURL);
OAuthAppDO oAuthAppDO = (OAuthAppDO) oauthAuthzMsgCtx.getProperty("OAuthAppDO");
String redirectionURI = getCibaFlowCompletionPageURI(oAuthAppDO.getApplicationName(),
oauthAuthzMsgCtx.getAuthorizationReqDTO().getTenantDomain());
respDTO.setCallbackURI(redirectionURI);
return respDTO;
} catch (CibaCoreException e) {
throw new IdentityOAuth2Exception("Error occurred in persisting authenticated user and authentication " +
Expand Down Expand Up @@ -149,4 +158,36 @@ public boolean isAuthorizedClient(OAuthAuthzReqMessageContext authzReqMsgCtx) th
}
return true;
}

/**
* This method is used to generate the ciba flow authentication completed page URI.
*
* @param appName Service provider name.
* @param tenantDomain Tenant domain.
* @return Redirection URI
*/
private static String getCibaFlowCompletionPageURI(String appName, String tenantDomain)
throws IdentityOAuth2Exception {

try {
String pageURI = ServiceURLBuilder.create().addPath(CIBA_SUCCESS_ENDPOINT_PATH).build()
.getAbsolutePublicURL();
URIBuilder uriBuilder = new URIBuilder(pageURI);
uriBuilder.addParameter(org.wso2.carbon.identity.oauth2.device.constants.Constants.APP_NAME, appName);
if (!IdentityTenantUtil.isTenantQualifiedUrlsEnabled() && isNotSuperTenant(tenantDomain)) {
// Append tenant domain to path when the tenant-qualified url mode is disabled.
uriBuilder.addParameter(FrameworkUtils.TENANT_DOMAIN, tenantDomain);
}
return uriBuilder.build().toString();
} catch (URISyntaxException | URLBuilderException e) {
throw new IdentityOAuth2Exception("Error occurred when getting the ciba flow authentication completed" +
" page URI.", e);
}
}

private static boolean isNotSuperTenant(String tenantDomain) {

return (StringUtils.isNotBlank(tenantDomain) &&
!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025, 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
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.oauth.ciba.handlers;

import org.wso2.carbon.identity.oauth2.authz.validators.AbstractResponseTypeRequestValidator;

import static org.wso2.carbon.identity.oauth.ciba.common.CibaConstants.RESPONSE_TYPE_VALUE;

/**
* Ciba response type request validator.
*/
public class CibaResponseTypeRequestValidator extends AbstractResponseTypeRequestValidator {

public CibaResponseTypeRequestValidator() {

}

@Override
public String getResponseType() {

return RESPONSE_TYPE_VALUE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ public void validateMethod(HttpServletRequest request) throws OAuthProblemExcept
}
}

@Override
public void validateContentType(HttpServletRequest request) throws OAuthProblemException {

}

}
Loading