package net.unicon.cas.addons.authentication.strong.oath.totp; import org.jasig.cas.authentication.handler.AuthenticationException; import org.jasig.cas.authentication.handler.AuthenticationHandler; import org.jasig.cas.authentication.principal.Credentials; import org.jasig.cas.authentication.principal.UsernamePasswordCredentials; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Map; /** * AuthenticationHandler to authenticate provided credentials in a form of time-based one-tme passwords (TOTP) * * Re-uses standard CAS' <code>UsernamePasswordCredentials</code> and assumes that one time password tokens * are appropriately un-marshaled into UsernamePasswordCredentials.password field by layers above it. * * @author Dmitriy Kopylenko * @author Unicon, inc. * * @since 0.5 */ public class TotpAuthenticationHandler implements AuthenticationHandler { private TotpOathDetailsSource totpOathDetailsSource; private static Logger logger = LoggerFactory.getLogger(TotpAuthenticationHandler.class); public TotpAuthenticationHandler(TotpOathDetailsSource totpOathDetailsSource) { this.totpOathDetailsSource = totpOathDetailsSource; } @Override public boolean authenticate(Credentials credentials) throws AuthenticationException { UsernamePasswordCredentials totpCredentials = (UsernamePasswordCredentials) credentials; logger.info("Authenticating one time password for {}", totpCredentials); //PrincipalNotFoundException may result in this call. It should be caught by higher layer and dealt with accordingly. Map<TotpOathDetailsSource.OTP, Object> attrs = this.totpOathDetailsSource.getOtpAttributesForPrincipal(totpCredentials.getUsername()); String seceretKey = (String) attrs.get(TotpOathDetailsSource.OTP.SECRET_KEY); Integer interval = (Integer) attrs.get(TotpOathDetailsSource.OTP.INTERVAL); Integer intervalWindow = (Integer) attrs.get(TotpOathDetailsSource.OTP.INTERVAL_WINDOW); try { return TOTPUtils.checkCode(seceretKey, Long.valueOf(totpCredentials.getPassword()), interval, intervalWindow); } catch (NoSuchAlgorithmException ex) { logger.error(ex.getMessage()); return false; } catch (InvalidKeyException ex) { logger.error(ex.getMessage()); return false; } } @Override public boolean supports(Credentials credentials) { return credentials != null && (UsernamePasswordCredentials.class.equals(credentials.getClass())); } }