package org.apereo.cas.adaptors.u2f; import com.yubico.u2f.U2F; import com.yubico.u2f.data.DeviceRegistration; import com.yubico.u2f.data.messages.AuthenticateRequestData; import com.yubico.u2f.data.messages.AuthenticateResponse; import com.yubico.u2f.exceptions.DeviceCompromisedException; import org.apereo.cas.adaptors.u2f.storage.U2FDeviceRepository; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.authentication.Credential; import org.apereo.cas.authentication.HandlerResult; import org.apereo.cas.authentication.PreventedException; import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.authentication.principal.PrincipalFactory; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.web.support.WebUtils; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; import java.security.GeneralSecurityException; /** * This is {@link U2FAuthenticationHandler}. * * @author Misagh Moayyed * @since 5.1.0 */ public class U2FAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler { private final U2F u2f = new U2F(); private final U2FDeviceRepository u2FDeviceRepository; public U2FAuthenticationHandler(final String name, final ServicesManager servicesManager, final PrincipalFactory principalFactory, final U2FDeviceRepository u2FDeviceRepository) { super(name, servicesManager, principalFactory, null); this.u2FDeviceRepository = u2FDeviceRepository; } @Override protected HandlerResult doAuthentication(final Credential credential) throws GeneralSecurityException, PreventedException { final U2FTokenCredential tokenCredential = (U2FTokenCredential) credential; final RequestContext context = RequestContextHolder.getRequestContext(); if (context == null) { new IllegalArgumentException("No request context could be found to locate an authentication event"); } final Authentication authentication = WebUtils.getAuthentication(context); if (authentication == null) { new IllegalArgumentException("Request context has no reference to an authentication event to locate a principal"); } final Principal p = authentication.getPrincipal(); final AuthenticateResponse authenticateResponse = AuthenticateResponse.fromJson(tokenCredential.getToken()); final String authJson = u2FDeviceRepository.getDeviceAuthenticationRequest(authenticateResponse.getRequestId(), p.getId()); final AuthenticateRequestData authenticateRequest = AuthenticateRequestData.fromJson(authJson); DeviceRegistration registration = null; try { registration = u2f.finishAuthentication(authenticateRequest, authenticateResponse, u2FDeviceRepository.getRegisteredDevices(p.getId())); return createHandlerResult(tokenCredential, p, null); } catch (final DeviceCompromisedException e) { registration = e.getDeviceRegistration(); throw new PreventedException("Device possibly compromised and therefore blocked: " + e.getMessage(), e); } finally { u2FDeviceRepository.authenticateDevice(p.getId(), registration); } } @Override public boolean supports(final Credential credential) { return U2FTokenCredential.class.isAssignableFrom(credential.getClass()); } }