From 8cc0dc126ac0efc7c3385cb655bb19a404f601a8 Mon Sep 17 00:00:00 2001 From: keetz Date: Mon, 2 Oct 2017 12:16:03 +0530 Subject: [PATCH] Added more test cases and fixes --- .../authenticator/totp/TOTPAuthenticator.java | 758 +++++++++--------- .../authenticator/totp/util/TOTPUtil.java | 15 +- .../totp/TOTPAuthenticatorTest.java | 144 +++- .../totp/TOTPKeyGeneratorTest.java | 2 +- 4 files changed, 533 insertions(+), 386 deletions(-) diff --git a/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticator.java b/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticator.java index 8de31131f..b88c7daf8 100644 --- a/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticator.java +++ b/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticator.java @@ -57,309 +57,313 @@ * @since 2.0.3 */ public class TOTPAuthenticator extends AbstractApplicationAuthenticator - implements LocalApplicationAuthenticator { + implements LocalApplicationAuthenticator { - private static final long serialVersionUID = 2009231028659744926L; - private static Log log = LogFactory.getLog(TOTPAuthenticator.class); + private static final long serialVersionUID = 2009231028659744926L; + private static Log log = LogFactory.getLog(TOTPAuthenticator.class); - /** - * Check whether token or action are in request. - * - * @param request The http servlet request - * @return true, if token or action are not null - */ - @Override - public boolean canHandle(HttpServletRequest request) { - String token = request.getParameter(TOTPAuthenticatorConstants.TOKEN); - String action = request.getParameter(TOTPAuthenticatorConstants.SEND_TOKEN); - String enableTOTP = request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP); - return (token != null || action != null || enableTOTP != null); - } + /** + * Check whether token or action are in request. + * + * @param request The http servlet request + * @return true, if token or action are not null + */ + @Override + public boolean canHandle(HttpServletRequest request) { + String token = request.getParameter(TOTPAuthenticatorConstants.TOKEN); + String action = request.getParameter(TOTPAuthenticatorConstants.SEND_TOKEN); + String enableTOTP = request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP); + return (token != null || action != null || enableTOTP != null); + } - /** - * This method is overridden to check additional condition like whether request is having - * sendToken, token parameters, generateTOTPToken and authentication name. - * - * @param request Http servlet request - * @param response Http servlet response - * @param context AuthenticationContext - * @return AuthenticatorFlowStatus - * @throws AuthenticationFailedException User tenant domain mismatch - * @throws LogoutFailedException Error while checking logout request - */ - @Override - public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, - AuthenticationContext context) - throws AuthenticationFailedException, LogoutFailedException { - if (context.isLogoutRequest()) { - return AuthenticatorFlowStatus.SUCCESS_COMPLETED; - } else if (request.getParameter(TOTPAuthenticatorConstants.SEND_TOKEN) != null) { - if (generateTOTPToken(context)) { - return AuthenticatorFlowStatus.INCOMPLETE; - } else { - return AuthenticatorFlowStatus.FAIL_COMPLETED; - } - } else if (StringUtils - .isNotEmpty(request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP))) { - // if the request comes with MOBILE_NUMBER, it will go through this flow. - initiateAuthenticationRequest(request, response, context); - if (context.getProperty(TOTPAuthenticatorConstants.AUTHENTICATION) - .equals(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME)) { - return AuthenticatorFlowStatus.INCOMPLETE; - } else { - return AuthenticatorFlowStatus.SUCCESS_COMPLETED; - } - } else if (request.getParameter(TOTPAuthenticatorConstants.TOKEN) == null) { - initiateAuthenticationRequest(request, response, context); - if (context.getProperty(TOTPAuthenticatorConstants.AUTHENTICATION) - .equals(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME)) { - return AuthenticatorFlowStatus.INCOMPLETE; - } else { - return AuthenticatorFlowStatus.SUCCESS_COMPLETED; - } - } else { - return super.process(request, response, context); - } - } + /** + * This method is overridden to check additional condition like whether request is having + * sendToken, token parameters, generateTOTPToken and authentication name. + * + * @param request Http servlet request + * @param response Http servlet response + * @param context AuthenticationContext + * @return AuthenticatorFlowStatus + * @throws AuthenticationFailedException User tenant domain mismatch + * @throws LogoutFailedException Error while checking logout request + */ + @Override + public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, + AuthenticationContext context) + throws AuthenticationFailedException, LogoutFailedException { + if (context.isLogoutRequest()) { + return AuthenticatorFlowStatus.SUCCESS_COMPLETED; + } else if (request.getParameter(TOTPAuthenticatorConstants.SEND_TOKEN) != null) { + if (generateTOTPToken(context)) { + return AuthenticatorFlowStatus.INCOMPLETE; + } else { + return AuthenticatorFlowStatus.FAIL_COMPLETED; + } + } else if (StringUtils + .isNotEmpty(request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP))) { + // if the request comes with MOBILE_NUMBER, it will go through this flow. + initiateAuthenticationRequest(request, response, context); + if (context.getProperty(TOTPAuthenticatorConstants.AUTHENTICATION) + .equals(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME)) { + return AuthenticatorFlowStatus.INCOMPLETE; + } else { + return AuthenticatorFlowStatus.SUCCESS_COMPLETED; + } + } else if (request.getParameter(TOTPAuthenticatorConstants.TOKEN) == null) { + initiateAuthenticationRequest(request, response, context); + if (context.getProperty(TOTPAuthenticatorConstants.AUTHENTICATION) + .equals(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME)) { + return AuthenticatorFlowStatus.INCOMPLETE; + } else { + return AuthenticatorFlowStatus.SUCCESS_COMPLETED; + } + } else { + return super.process(request, response, context); + } + } - /** - * Initiate authentication request. - * - * @param request The request - * @param response The response - * @param context The authentication context - * @throws AuthenticationFailedException If authenticatedUser could not be identified - */ - @Override - protected void initiateAuthenticationRequest(HttpServletRequest request, - HttpServletResponse response, - AuthenticationContext context) - throws AuthenticationFailedException { - String username = null; - AuthenticatedUser authenticatedUser; - String tenantDomain = context.getTenantDomain(); - context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, - TOTPAuthenticatorConstants.AUTHENTICATOR_NAME); - if (!tenantDomain.equals(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN)) { - IdentityHelperUtil - .loadApplicationAuthenticationXMLFromRegistry(context, getName(), tenantDomain); - } - String retryParam = ""; - try { - FederatedAuthenticatorUtil.setUsernameFromFirstStep(context); - username = String.valueOf(context.getProperty("username")); - authenticatedUser = (AuthenticatedUser) context.getProperty("authenticatedUser"); - // find the authenticated user. - if (authenticatedUser == null) { - throw new AuthenticationFailedException( - "Authentication failed!. Cannot proceed further without identifying the user"); - } - if (context.isRetrying()) { - retryParam = "&authFailure=true&authFailureMsg=login.fail.message"; - } - boolean isTOTPEnabled = isTOTPEnabledForLocalUser(username); - if (log.isDebugEnabled()) { - log.debug("TOTP is enabled by user: " + isTOTPEnabled); - } - boolean isTOTPEnabledByAdmin = IdentityHelperUtil.checkSecondStepEnableByAdmin(context); - if (log.isDebugEnabled()) { - log.debug("TOTP is enabled by admin: " + isTOTPEnabledByAdmin); - } - String multiOptionURI = request.getParameter("multiOptionURI"); - multiOptionURI = multiOptionURI != null ? "&multiOptionURI=" + Encode.forUriComponent(multiOptionURI) : ""; - String totpLoginPageUrl = - getLoginPage(context) + ("?sessionDataKey=" + context.getContextIdentifier()) + - "&authenticators=" + getName() + "&type=totp" + retryParam + "&username=" + - username + multiOptionURI; - String totpErrorPageUrl = - getErrorPage(context) + ("?sessionDataKey=" + context.getContextIdentifier()) + - "&authenticators=" + getName() + "&type=totp_error" + retryParam + - "&username=" + username + multiOptionURI; - if (isTOTPEnabled && request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP) == null) { - //if TOTP is enabled for the user. - response.sendRedirect(totpLoginPageUrl); - } else { - if (TOTPUtil.isEnrolUserInAuthenticationFlowEnabled(context) - && request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP) == null) { - //if TOTP is not enabled for the user and he hasn't redirected to the enrolment page yet. - if (log.isDebugEnabled()) { - log.debug("User has not enabled TOTP: " + username); - } - Map claims = TOTPKeyGenerator.generateClaims(username, false, context); - context.setProperty(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, - claims.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL)); - context.setProperty(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL, - claims.get(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL)); - String qrURL = claims.get(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL); - TOTPUtil.redirectToEnableTOTPReqPage(request, response, context, qrURL); - } else if (Boolean.valueOf(request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP))) { - //if TOTP is not enabled for the user and user continued the enrolment. - context.setProperty(TOTPAuthenticatorConstants.ENABLE_TOTP, true); - response.sendRedirect(totpLoginPageUrl); - } else { - if (isTOTPEnabledByAdmin) { - //if TOTP is not enabled for the user and admin enforces TOTP. - response.sendRedirect(totpErrorPageUrl); - } else { - //if admin does not enforces TOTP and TOTP is not enabled for the user. - context.setSubject(authenticatedUser); - StepConfig stepConfig = context.getSequenceConfig().getStepMap() - .get(context.getCurrentStep() - 1); - if (stepConfig.getAuthenticatedAutenticator() - .getApplicationAuthenticator() instanceof LocalApplicationAuthenticator) { - context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, - TOTPAuthenticatorConstants.BASIC); - } else { - context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, - TOTPAuthenticatorConstants.FEDERETOR); - } - } - } - } - } catch (IOException e) { - throw new AuthenticationFailedException( - "Error when redirecting the TOTP login response, user : " + username, e); - } catch (TOTPException e) { - throw new AuthenticationFailedException( - "Error when checking TOTP enabled for the user : " + username, e); - } catch (AuthenticationFailedException e) { - throw new AuthenticationFailedException( - "Authentication failed!. Cannot get the username from first step.", e); - } - } + /** + * Initiate authentication request. + * + * @param request The request + * @param response The response + * @param context The authentication context + * @throws AuthenticationFailedException If authenticatedUser could not be identified + */ + @Override + protected void initiateAuthenticationRequest(HttpServletRequest request, + HttpServletResponse response, + AuthenticationContext context) + throws AuthenticationFailedException { + String username = null; + AuthenticatedUser authenticatedUser; + String tenantDomain = context.getTenantDomain(); + context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, + TOTPAuthenticatorConstants.AUTHENTICATOR_NAME); + if (!tenantDomain.equals(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN)) { + IdentityHelperUtil + .loadApplicationAuthenticationXMLFromRegistry(context, getName(), tenantDomain); + } + String retryParam = ""; + try { + FederatedAuthenticatorUtil.setUsernameFromFirstStep(context); + username = String.valueOf(context.getProperty("username")); + authenticatedUser = (AuthenticatedUser) context.getProperty("authenticatedUser"); + // find the authenticated user. + if (authenticatedUser == null) { + throw new AuthenticationFailedException( + "Authentication failed!. Cannot proceed further without identifying the user"); + } + if (context.isRetrying()) { + retryParam = "&authFailure=true&authFailureMsg=login.fail.message"; + } + boolean isTOTPEnabled = isTOTPEnabledForLocalUser(username); + if (log.isDebugEnabled()) { + log.debug("TOTP is enabled by user: " + isTOTPEnabled); + } + boolean isTOTPEnabledByAdmin = IdentityHelperUtil.checkSecondStepEnableByAdmin(context); + if (log.isDebugEnabled()) { + log.debug("TOTP is enabled by admin: " + isTOTPEnabledByAdmin); + } + String multiOptionURI = request.getParameter("multiOptionURI"); + multiOptionURI = multiOptionURI != null ? "&multiOptionURI=" + Encode.forUriComponent(multiOptionURI) : ""; + String totpLoginPageUrl = + getLoginPage(context) + ("?sessionDataKey=" + context.getContextIdentifier()) + + "&authenticators=" + getName() + "&type=totp" + retryParam + "&username=" + + username + multiOptionURI; + String totpErrorPageUrl = + getErrorPage(context) + ("?sessionDataKey=" + context.getContextIdentifier()) + + "&authenticators=" + getName() + "&type=totp_error" + retryParam + + "&username=" + username + multiOptionURI; + if (isTOTPEnabled && request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP) == null) { + //if TOTP is enabled for the user. + response.sendRedirect(totpLoginPageUrl); + } else { + if (TOTPUtil.isEnrolUserInAuthenticationFlowEnabled(context) + && request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP) == null) { + //if TOTP is not enabled for the user and he hasn't redirected to the enrolment page yet. + if (log.isDebugEnabled()) { + log.debug("User has not enabled TOTP: " + username); + } + Map claims = TOTPKeyGenerator.generateClaims(username, false, context); + context.setProperty(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, + claims.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL)); + context.setProperty(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL, + claims.get(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL)); + String qrURL = claims.get(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL); + TOTPUtil.redirectToEnableTOTPReqPage(request, response, context, qrURL); + } else if (Boolean.valueOf(request.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP))) { + //if TOTP is not enabled for the user and user continued the enrolment. + context.setProperty(TOTPAuthenticatorConstants.ENABLE_TOTP, true); + response.sendRedirect(totpLoginPageUrl); + } else { + if (isTOTPEnabledByAdmin) { + //if TOTP is not enabled for the user and admin enforces TOTP. + response.sendRedirect(totpErrorPageUrl); + } else { + //if admin does not enforces TOTP and TOTP is not enabled for the user. + context.setSubject(authenticatedUser); + StepConfig stepConfig = context.getSequenceConfig().getStepMap() + .get(context.getCurrentStep() - 1); + if (stepConfig.getAuthenticatedAutenticator() + .getApplicationAuthenticator() instanceof LocalApplicationAuthenticator) { + context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, + TOTPAuthenticatorConstants.BASIC); + } else { + context.setProperty(TOTPAuthenticatorConstants.AUTHENTICATION, + TOTPAuthenticatorConstants.FEDERETOR); + } + } + } + } + } catch (IOException e) { + throw new AuthenticationFailedException( + "Error when redirecting the TOTP login response, user : " + username, e); + } catch (TOTPException e) { + throw new AuthenticationFailedException( + "Error when checking TOTP enabled for the user : " + username, e); + } catch (AuthenticationFailedException e) { + throw new AuthenticationFailedException( + "Authentication failed!. Cannot get the username from first step.", e); + } + } - /** - * Get the loginPage from authentication.xml file or use the login page from constant file. - * - * @param context the AuthenticationContext - * @return the loginPage - * @throws AuthenticationFailedException - */ - private String getLoginPage(AuthenticationContext context) throws AuthenticationFailedException { - String loginPage = TOTPUtil.getLoginPageFromXMLFile(context, getName()); - if (StringUtils.isEmpty(loginPage)) { - loginPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL() - .replace(TOTPAuthenticatorConstants.LOGIN_PAGE, TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); - if (log.isDebugEnabled()) { - log.debug("Default endpoint is used"); - } - } - return loginPage; - } + /** + * Get the loginPage from authentication.xml file or use the login page from constant file. + * + * @param context the AuthenticationContext + * @return the loginPage + * @throws AuthenticationFailedException + */ + private String getLoginPage(AuthenticationContext context) throws AuthenticationFailedException { + String loginPage = TOTPUtil.getLoginPageFromXMLFile(context, getName()); + if (StringUtils.isEmpty(loginPage)) { + loginPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL() + .replace(TOTPAuthenticatorConstants.LOGIN_PAGE, TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + if (log.isDebugEnabled()) { + log.debug("Default endpoint is used"); + } + } + return loginPage; + } - /** - * Get the errorPage from authentication.xml file or use the error page from constant file. - * - * @param context the AuthenticationContext - * @return the errorPage - * @throws AuthenticationFailedException - */ - private String getErrorPage(AuthenticationContext context) throws AuthenticationFailedException { - String errorPage = TOTPUtil.getErrorPageFromXMLFile(context, getName()); - if (StringUtils.isEmpty(errorPage)) { - errorPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL() - .replace(TOTPAuthenticatorConstants.LOGIN_PAGE, TOTPAuthenticatorConstants.ERROR_PAGE); - if (log.isDebugEnabled()) { - log.debug("Default endpoint is used"); - } - } - return errorPage; - } + /** + * Get the errorPage from authentication.xml file or use the error page from constant file. + * + * @param context the AuthenticationContext + * @return the errorPage + * @throws AuthenticationFailedException + */ + private String getErrorPage(AuthenticationContext context) throws AuthenticationFailedException { + String errorPage = TOTPUtil.getErrorPageFromXMLFile(context, getName()); + if (StringUtils.isEmpty(errorPage)) { + errorPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL() + .replace(TOTPAuthenticatorConstants.LOGIN_PAGE, TOTPAuthenticatorConstants.ERROR_PAGE); + if (log.isDebugEnabled()) { + log.debug("Default endpoint is used"); + } + } + return errorPage; + } - /** - * This method is overridden to check validation of the given token. - * - * @param request The http servlet request - * @param response The http servlet response - * @param context AuthenticationContext - * @throws AuthenticationFailedException Authentication process failed for user - */ - @Override - protected void processAuthenticationResponse(HttpServletRequest request, - HttpServletResponse response, - AuthenticationContext context) - throws AuthenticationFailedException { - String token = request.getParameter(TOTPAuthenticatorConstants.TOKEN); - String username = context.getProperty("username").toString(); - if (context.getProperty(TOTPAuthenticatorConstants.ENABLE_TOTP) != null && Boolean - .valueOf(context.getProperty(TOTPAuthenticatorConstants.ENABLE_TOTP).toString())) { - //adds the claims to the profile if the user enrol and continued. - Map claims = new HashMap<>(); - claims.put(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, - context.getProperty(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL).toString()); - claims.put(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL, - context.getProperty(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL).toString()); - try { - TOTPKeyGenerator.addTOTPClaimsAndRetrievingQRCodeURL(claims, username, context); - } catch (TOTPException e) { - throw new AuthenticationFailedException("Error while adding TOTP claims to the user : " + username, e); - } - } - if (token != null) { - try { - int tokenValue = Integer.parseInt(token); - if (!isValidTokenLocalUser(tokenValue, username, context)) { - throw new AuthenticationFailedException( - "Authentication failed, user : " + username); - } - context.setSubject(AuthenticatedUser - .createLocalAuthenticatedUserFromSubjectIdentifier( - username)); - } catch (TOTPException | NumberFormatException e) { - throw new AuthenticationFailedException( - "TOTP Authentication process failed for user " + username, e); - } - } - } + /** + * This method is overridden to check validation of the given token. + * + * @param request The http servlet request + * @param response The http servlet response + * @param context AuthenticationContext + * @throws AuthenticationFailedException Authentication process failed for user + */ + @Override + protected void processAuthenticationResponse(HttpServletRequest request, + HttpServletResponse response, + AuthenticationContext context) + throws AuthenticationFailedException { + String token = request.getParameter(TOTPAuthenticatorConstants.TOKEN); + String username = context.getProperty("username").toString(); + if (context.getProperty(TOTPAuthenticatorConstants.ENABLE_TOTP) != null && Boolean + .valueOf(context.getProperty(TOTPAuthenticatorConstants.ENABLE_TOTP).toString())) { + //adds the claims to the profile if the user enrol and continued. + Map claims = new HashMap<>(); + if (context.getProperty(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL) != null) { + claims.put(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, + context.getProperty(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL).toString()); + } + if (context.getProperty(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL) != null) { + claims.put(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL, + context.getProperty(TOTPAuthenticatorConstants.QR_CODE_CLAIM_URL).toString()); + } + try { + TOTPKeyGenerator.addTOTPClaimsAndRetrievingQRCodeURL(claims, username, context); + } catch (TOTPException e) { + throw new AuthenticationFailedException("Error while adding TOTP claims to the user : " + username, e); + } + } + if (token != null) { + try { + int tokenValue = Integer.parseInt(token); + if (!isValidTokenLocalUser(tokenValue, username, context)) { + throw new AuthenticationFailedException( + "Authentication failed, user : " + username); + } + context.setSubject(AuthenticatedUser + .createLocalAuthenticatedUserFromSubjectIdentifier( + username)); + } catch (TOTPException | NumberFormatException e) { + throw new AuthenticationFailedException( + "TOTP Authentication process failed for user " + username, e); + } + } + } - /** - * Check whether status of retrying authentication. - * - * @return true, if retry authentication is enabled - */ - @Override - protected boolean retryAuthenticationEnabled() { - return true; - } + /** + * Check whether status of retrying authentication. + * + * @return true, if retry authentication is enabled + */ + @Override + protected boolean retryAuthenticationEnabled() { + return true; + } - /** - * Get requested session ID. - * - * @param request The http servlet request - * @return Requested session ID - */ - @Override - public String getContextIdentifier(HttpServletRequest request) { - return request.getRequestedSessionId(); - } + /** + * Get requested session ID. + * + * @param request The http servlet request + * @return Requested session ID + */ + @Override + public String getContextIdentifier(HttpServletRequest request) { + return request.getRequestedSessionId(); + } - /** - * Get friendly name. - * - * @return Authenticator friendly name - */ - @Override - public String getFriendlyName() { - return TOTPAuthenticatorConstants.AUTHENTICATOR_FRIENDLY_NAME; - } + /** + * Get friendly name. + * + * @return Authenticator friendly name + */ + @Override + public String getFriendlyName() { + return TOTPAuthenticatorConstants.AUTHENTICATOR_FRIENDLY_NAME; + } - /** - * Get authenticator name. - * - * @return Authenticator name - */ - @Override - public String getName() { - return TOTPAuthenticatorConstants.AUTHENTICATOR_NAME; - } + /** + * Get authenticator name. + * + * @return Authenticator name + */ + @Override + public String getName() { + return TOTPAuthenticatorConstants.AUTHENTICATOR_NAME; + } - /** - * Generate TOTP token. - * - * @param context AuthenticationContext - * @return true, if token is generated successfully - */ - private boolean generateTOTPToken(AuthenticationContext context) { + /** + * Generate TOTP token. + * + * @param context AuthenticationContext + * @return true, if token is generated successfully + */ + private boolean generateTOTPToken(AuthenticationContext context) { String username; if (context.getProperty("username") == null) { log.error("No username found in the authentication context."); @@ -377,92 +381,92 @@ private boolean generateTOTPToken(AuthenticationContext context) { } } return true; - } + } - /** - * Check whether TOTP is enabled for local user or not. - * - * @param username Username of the user - * @return true, if TOTP enable for local user - * @throws TOTPException when user realm is null or could not find user - */ - private boolean isTOTPEnabledForLocalUser(String username) - throws TOTPException, AuthenticationFailedException { - UserRealm userRealm = TOTPUtil.getUserRealm(username); - String tenantAwareUsername = null; - try { - tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username); - if (userRealm != null) { - Map UserClaimValues = - userRealm.getUserStoreManager().getUserClaimValues - (tenantAwareUsername, new String[] - { TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL }, null); - String secretKey = - UserClaimValues.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL); - return StringUtils.isNotBlank(secretKey); - } else { - throw new TOTPException( - "Cannot find the user realm for the given tenant domain : " + - CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); - } - } catch (UserStoreException e) { - throw new TOTPException( - "TOTPAccessController failed while trying to access userRealm of the user : " + - tenantAwareUsername, e); - } - } + /** + * Check whether TOTP is enabled for local user or not. + * + * @param username Username of the user + * @return true, if TOTP enable for local user + * @throws TOTPException when user realm is null or could not find user + */ + private boolean isTOTPEnabledForLocalUser(String username) + throws TOTPException, AuthenticationFailedException { + UserRealm userRealm = TOTPUtil.getUserRealm(username); + String tenantAwareUsername = null; + try { + tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username); + if (userRealm != null) { + Map UserClaimValues = + userRealm.getUserStoreManager().getUserClaimValues + (tenantAwareUsername, new String[] + {TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL}, null); + String secretKey = + UserClaimValues.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL); + return StringUtils.isNotBlank(secretKey); + } else { + throw new TOTPException( + "Cannot find the user realm for the given tenant domain : " + + CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); + } + } catch (UserStoreException e) { + throw new TOTPException( + "TOTPAccessController failed while trying to access userRealm of the user : " + + tenantAwareUsername, e); + } + } - /** - * Verify whether a given token is valid for a stored local user. - * - * @param token TOTP Token which needs to be validated - * @param context Authentication context - * @param username Username of the user - * @return true if token is valid otherwise false - * @throws TOTPException UserRealm for user or tenant domain is null - */ - private boolean isValidTokenLocalUser(int token, String username, AuthenticationContext context) - throws TOTPException { - TOTPKeyRepresentation encoding = TOTPKeyRepresentation.BASE32; - String tenantDomain = MultitenantUtils.getTenantDomain(username); - String tenantAwareUsername = null; - try { - if (TOTPAuthenticatorConstants.BASE64 - .equals(TOTPUtil.getEncodingMethod(tenantDomain, context))) { - encoding = TOTPKeyRepresentation.BASE64; - } - long timeStep = TimeUnit.SECONDS.toMillis(TOTPUtil.getTimeStepSize(context)); - int windowSize = TOTPUtil.getWindowSize(context); - TOTPAuthenticatorConfig.TOTPAuthenticatorConfigBuilder totpAuthenticatorConfigBuilder = - new TOTPAuthenticatorConfig.TOTPAuthenticatorConfigBuilder() - .setKeyRepresentation(encoding).setWindowSize(windowSize) - .setTimeStepSizeInMillis(timeStep); - TOTPAuthenticatorCredentials totpAuthenticator = - new TOTPAuthenticatorCredentials(totpAuthenticatorConfigBuilder.build()); - tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username); - UserRealm userRealm = TOTPUtil.getUserRealm(username); - if (userRealm != null) { - Map userClaimValues = userRealm - .getUserStoreManager().getUserClaimValues - (tenantAwareUsername, new String[] - { TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL }, null); - String secretKey = TOTPUtil.decrypt( - userClaimValues.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL)); - return totpAuthenticator.authorize(secretKey, token); - } else { - throw new TOTPException( - "Cannot find the user realm for the given tenant domain : " + - CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); - } - } catch (UserStoreException e) { - throw new TOTPException( - "TOTPTokenVerifier failed while trying to access userRealm of the user : " + - tenantAwareUsername, e); - } catch (CryptoException e) { - throw new TOTPException("Error while decrypting the key", e); - } catch (AuthenticationFailedException e) { - throw new TOTPException( - "TOTPTokenVerifier cannot find the property value for encodingMethod"); - } - } + /** + * Verify whether a given token is valid for a stored local user. + * + * @param token TOTP Token which needs to be validated + * @param context Authentication context + * @param username Username of the user + * @return true if token is valid otherwise false + * @throws TOTPException UserRealm for user or tenant domain is null + */ + private boolean isValidTokenLocalUser(int token, String username, AuthenticationContext context) + throws TOTPException { + TOTPKeyRepresentation encoding = TOTPKeyRepresentation.BASE32; + String tenantDomain = MultitenantUtils.getTenantDomain(username); + String tenantAwareUsername = null; + try { + if (TOTPAuthenticatorConstants.BASE64 + .equals(TOTPUtil.getEncodingMethod(tenantDomain, context))) { + encoding = TOTPKeyRepresentation.BASE64; + } + long timeStep = TimeUnit.SECONDS.toMillis(TOTPUtil.getTimeStepSize(context)); + int windowSize = TOTPUtil.getWindowSize(context); + TOTPAuthenticatorConfig.TOTPAuthenticatorConfigBuilder totpAuthenticatorConfigBuilder = + new TOTPAuthenticatorConfig.TOTPAuthenticatorConfigBuilder() + .setKeyRepresentation(encoding).setWindowSize(windowSize) + .setTimeStepSizeInMillis(timeStep); + TOTPAuthenticatorCredentials totpAuthenticator = + new TOTPAuthenticatorCredentials(totpAuthenticatorConfigBuilder.build()); + tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username); + UserRealm userRealm = TOTPUtil.getUserRealm(username); + if (userRealm != null) { + Map userClaimValues = userRealm + .getUserStoreManager().getUserClaimValues + (tenantAwareUsername, new String[] + {TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL}, null); + String secretKey = TOTPUtil.decrypt( + userClaimValues.get(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL)); + return totpAuthenticator.authorize(secretKey, token); + } else { + throw new TOTPException( + "Cannot find the user realm for the given tenant domain : " + + CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); + } + } catch (UserStoreException e) { + throw new TOTPException( + "TOTPTokenVerifier failed while trying to access userRealm of the user : " + + tenantAwareUsername, e); + } catch (CryptoException e) { + throw new TOTPException("Error while decrypting the key", e); + } catch (AuthenticationFailedException e) { + throw new TOTPException( + "TOTPTokenVerifier cannot find the property value for encodingMethod"); + } + } } \ No newline at end of file diff --git a/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/util/TOTPUtil.java b/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/util/TOTPUtil.java index a0bc63246..e14cb21d6 100644 --- a/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/util/TOTPUtil.java +++ b/component/authenticator/src/main/java/org/wso2/carbon/identity/application/authenticator/totp/util/TOTPUtil.java @@ -96,18 +96,23 @@ public static String decrypt(String cipherText) throws CryptoException { * @return encoding method */ public static String getEncodingMethod(String tenantDomain, AuthenticationContext context) { - String encodingMethod; + String encodingMethod = null; if (tenantDomain.equals(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN)) { encodingMethod = String.valueOf(getTOTPParameters().get(TOTPAuthenticatorConstants.ENCODING_METHOD)); } else { Object getPropertiesFromIdentityConfig = context .getProperty(TOTPAuthenticatorConstants.GET_PROPERTY_FROM_IDENTITY_CONFIG); if (getPropertiesFromIdentityConfig == null) { - encodingMethod = context.getProperty(TOTPAuthenticatorConstants.ENCODING_METHOD).toString(); + if (context.getProperty(TOTPAuthenticatorConstants.ENCODING_METHOD) != null) { + encodingMethod = context.getProperty(TOTPAuthenticatorConstants.ENCODING_METHOD).toString(); + } } else { - encodingMethod = String.valueOf( - IdentityHelperUtil.getAuthenticatorParameters(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME) - .get(TOTPAuthenticatorConstants.ENCODING_METHOD)); + if (IdentityHelperUtil.getAuthenticatorParameters(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME) + .get(TOTPAuthenticatorConstants.ENCODING_METHOD) != null) { + encodingMethod = String.valueOf( + IdentityHelperUtil.getAuthenticatorParameters(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME) + .get(TOTPAuthenticatorConstants.ENCODING_METHOD)); + } } } if (TOTPAuthenticatorConstants.BASE64.equals(encodingMethod)) { diff --git a/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticatorTest.java b/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticatorTest.java index 3b1dabb9a..c54145e4e 100644 --- a/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticatorTest.java +++ b/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPAuthenticatorTest.java @@ -18,8 +18,10 @@ */ package org.wso2.carbon.identity.application.authenticator.totp; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Spy; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockObjectFactory; import org.powermock.reflect.Whitebox; @@ -31,25 +33,32 @@ import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.extension.identity.helper.FederatedAuthenticatorUtil; import org.wso2.carbon.extension.identity.helper.util.IdentityHelperUtil; +import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus; import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade; import org.wso2.carbon.identity.application.authentication.framework.config.builder.FileBasedConfigurationBuilder; +import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.authenticator.totp.exception.TOTPException; import org.wso2.carbon.identity.application.authenticator.totp.util.TOTPUtil; import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.UserStoreException; import org.wso2.carbon.user.core.UserStoreManager; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.HashMap; import java.util.Map; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import static org.powermock.api.mockito.PowerMockito.*; @@ -57,6 +66,7 @@ @PrepareForTest({TOTPUtil.class, TOTPTokenGenerator.class, ConfigurationFacade.class, TOTPTokenGenerator.class, FileBasedConfigurationBuilder.class, IdentityHelperUtil.class, CarbonContext.class, FederatedAuthenticatorUtil.class}) +@PowerMockIgnore({"javax.crypto.*" }) public class TOTPAuthenticatorTest { @Mock @@ -86,7 +96,28 @@ public class TOTPAuthenticatorTest { private UserStoreManager userStoreManager; @Spy - FederatedAuthenticatorUtil federatedAuthenticatorUtil; + private FederatedAuthenticatorUtil federatedAuthenticatorUtil; + + @Mock + private SequenceConfig sequenceConfig; + + @Mock + private Map mockedMap; + + @Mock + private StepConfig stepConfig; + + @Mock + private AuthenticatorConfig authenticatorConfig; + + @Mock + private ApplicationAuthenticator applicationAuthenticator; + + @Spy + private AuthenticationContext mockedContext; + + @Mock + private FileBasedConfigurationBuilder fileBasedConfigurationBuilder; @BeforeMethod public void setUp() { @@ -279,6 +310,113 @@ public void testIsTOTPEnabledForLocalUser() throws Exception { Whitebox.invokeMethod(totpAuthenticator, "isTOTPEnabledForLocalUser", "admin"); } + @Test(description = "Test case for initiateAuthenticationRequest() method when authenticated user is null", + expectedExceptions = {AuthenticationFailedException.class}) + public void testInitiateAuthenticationRequestWithNullUser() throws AuthenticationFailedException { + context.setTenantDomain(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN); + totpAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, context); + } + + @Test(description = "Test case for initiateAuthenticationRequest() method with totp enabled user.") + public void testInitiateAuthenticationRequest() throws AuthenticationFailedException, UserStoreException { + String username = "admin"; + AuthenticatedUser authenticatedUser = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(username); + AuthenticationContext authenticationContext = new AuthenticationContext(); + authenticationContext.setTenantDomain(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN); + authenticationContext.setProperty("username", username); + authenticationContext.setProperty("authenticatedUser", authenticatedUser); + Map claims = new HashMap<>(); + claims.put(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, "AnySecretKey"); + when(TOTPUtil.getUserRealm(anyString())).thenReturn(userRealm); + when(userRealm.getUserStoreManager()).thenReturn(userStoreManager); + when(userStoreManager.getUserClaimValues(username, new String[] + { TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL }, null)).thenReturn(claims); + when(httpServletRequest.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP)).thenReturn(null); + when(TOTPUtil.getLoginPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(TOTPUtil.getErrorPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + + totpAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test(description = "Test case for initiateAuthenticationRequest() method when admin does not enforces TOTP and " + + "TOTP is not enabled for the user.") + public void testInitiateAuthenticationRequestWithEnrollment() throws AuthenticationFailedException, + UserStoreException { + String username = "admin"; + AuthenticatedUser authenticatedUser = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(username); + mockedContext.setTenantDomain(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN); + mockedContext.setProperty("username", username); + mockedContext.setProperty("authenticatedUser", authenticatedUser); + Map claims = new HashMap<>(); + claims.put(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, "AnySecretKey"); + when(TOTPUtil.getUserRealm(anyString())).thenReturn(userRealm); + when(userRealm.getUserStoreManager()).thenReturn(userStoreManager); + when(httpServletRequest.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP)).thenReturn(null); + when(TOTPUtil.getLoginPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(TOTPUtil.getErrorPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(mockedContext.getSequenceConfig()).thenReturn(sequenceConfig); + when(sequenceConfig.getStepMap()).thenReturn(mockedMap); + when(mockedMap.get(anyObject())).thenReturn(stepConfig); + when(stepConfig.getAuthenticatedAutenticator()).thenReturn(authenticatorConfig); + when(authenticatorConfig.getApplicationAuthenticator()).thenReturn(applicationAuthenticator); + totpAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, mockedContext); + Assert.assertEquals(mockedContext.getProperty(TOTPAuthenticatorConstants.AUTHENTICATION), + TOTPAuthenticatorConstants.FEDERETOR); + } + + @Test(description = "Test case for initiateAuthenticationRequest() method when admin enforces TOTP and " + + "TOTP is not enabled for the user.", priority=2) + public void testInitiateAuthenticationRequestAdminEnforces() throws AuthenticationFailedException, UserStoreException, IOException { + String username = "admin"; + AuthenticatedUser authenticatedUser = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(username); + context.setTenantDomain(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN); + context.setProperty("username", username); + context.setProperty("authenticatedUser", authenticatedUser); + Map claims = new HashMap<>(); + claims.put(TOTPAuthenticatorConstants.SECRET_KEY_CLAIM_URL, "AnySecretKey"); + when(TOTPUtil.getUserRealm(anyString())).thenReturn(userRealm); + when(userRealm.getUserStoreManager()).thenReturn(userStoreManager); + when(httpServletRequest.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP)).thenReturn(null); + when(IdentityHelperUtil.checkSecondStepEnableByAdmin(context)).thenReturn(true); + when(TOTPUtil.getLoginPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(TOTPUtil.getErrorPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(context.getSequenceConfig()).thenReturn(sequenceConfig); + when(sequenceConfig.getStepMap()).thenReturn(mockedMap); + when(mockedMap.get(anyObject())).thenReturn(stepConfig); + when(stepConfig.getAuthenticatedAutenticator()).thenReturn(authenticatorConfig); + when(authenticatorConfig.getApplicationAuthenticator()).thenReturn(applicationAuthenticator); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + totpAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, context); + verify(httpServletResponse).sendRedirect(captor.capture()); + Assert.assertTrue(captor.getValue().contains(TOTPAuthenticatorConstants.AUTHENTICATOR_NAME)); + } + + @Test(description = "Test case for initiateAuthenticationRequest() method when admin enforces TOTP and " + + "TOTP is not enabled for the user.", priority=2) + public void testInitiateAuthenticationWithEnableTOTP() throws AuthenticationFailedException, UserStoreException, IOException { + String username = "admin"; + AuthenticatedUser authenticatedUser = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(username); + context.setTenantDomain(TOTPAuthenticatorConstants.SUPER_TENANT_DOMAIN); + context.setProperty("username", username); + context.setProperty("authenticatedUser", authenticatedUser); + when(TOTPUtil.getUserRealm(anyString())).thenReturn(userRealm); + when(userRealm.getUserStoreManager()).thenReturn(userStoreManager); + when(httpServletRequest.getParameter(TOTPAuthenticatorConstants.ENABLE_TOTP)).thenReturn(null); + when(IdentityHelperUtil.checkSecondStepEnableByAdmin(context)).thenReturn(true); + when(TOTPUtil.getLoginPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(TOTPUtil.getErrorPageFromXMLFile(any(AuthenticationContext.class), anyString())). + thenReturn(TOTPAuthenticatorConstants.TOTP_LOGIN_PAGE); + when(TOTPUtil.isEnrolUserInAuthenticationFlowEnabled(context)).thenReturn(true); + totpAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, context); + } + @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); diff --git a/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPKeyGeneratorTest.java b/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPKeyGeneratorTest.java index 2b2aa4153..8d11455a1 100644 --- a/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPKeyGeneratorTest.java +++ b/component/authenticator/src/test/java/org/wso2/carbon/identity/application/authenticator/totp/TOTPKeyGeneratorTest.java @@ -78,7 +78,7 @@ public void testResetLocalWithException() throws AuthenticationFailedException, } @Test - public void testaddTOTPClaimsAndRetrievingQRCodeURL() throws AuthenticationFailedException, + public void testAddTOTPClaimsAndRetrievingQRCodeURL() throws AuthenticationFailedException, UserStoreException, TOTPException { Map claims = new HashMap<>(); String qrCodeUrl = "http://wso2.org/claims/identity/" +