import groovy.json.JsonBuilder import ch.nevis.esauth.auth.engine.AuthResponse def getHeader(String name) { def inctx = request.getLoginContext() // case-insensitive lookup of HTTP headers def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER) map.putAll(inctx) return map['connection.HttpHeader.' + name] } def clearFidoUAFSession() { LOG.debug("start new FIDO UAF session (skipping ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']}") def s = request.getAuthSession(true) s.removeAttribute('ch.nevis.auth.fido.uaf.fidouafsessionid') inargs.remove('fallback') } def clearIdmSessionAttributes() { def s = request.getAuthSession(true) def sessionKeySet = new HashSet(session.keySet()) sessionKeySet.each { key -> if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ ) { s.removeAttribute(key) } } } // check, whether we are still processing the correct AuthnRequest if (inargs.containsKey('authRequestId') && (inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) { // wrong request, "force" a timeout LOG.debug('authentication timeout enforced, due to concurrent requests -> return a 408') response.setIsDirectResponse(true) response.setContentType('text/html; charset=UTF-8') response.setContent('Timeout') response.setHttpStatusCode(205) response.setHeader('IDP-AUTH', 'Timeout') // CONTINUE to keep the other request beeing processed response.setStatus(AuthResponse.AUTH_CONTINUE) return } // dispatch AJAX calls and form POST when operation is done if (inargs['fidoUafDone'] == 'true' || inargs.containsKey('o.fidoUafSessionId.v') || getHeader('Content-Type') == 'application/json') { if (inargs.containsKey('o.fidoUafSessionId.v') && (inargs['o.fidoUafSessionId.v'] != session['ch.nevis.auth.fido.uaf.fidouafsessionid'])) { // received polling for wrong fido session; make sure, that stops LOG.debug("received polling for wrong fido session ${inargs['o.fidoUafSessionId.v']} (correct: ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']})") def json = new JsonBuilder() json { "status" "unknown" "timestamp" org.joda.time.DateTime.now().toString() } String body = json.toString() response.setContent(body) response.setContentType('application/json') response.setHttpStatusCode(200) response.setIsDirectResponse(true) response.setStatus(AuthResponse.AUTH_CONTINUE) return } if (inargs['fidoUafDone'] == 'true') { // get clean state, before validating user in IDM LOG.debug("clear IDM session attributes") clearIdmSessionAttributes() } // continue with OutOfBandFidoUafAuthState response.setResult('ok') } // dispatch form post with fallback input field : transition to FIDO Token authentication if (inargs['fallback'] == 'fallback') { response.setResult('fido2') } // dispatch to recovery if (inargs['fallback'] == 'recovery') { response.addOutArg('nevis.transfer.destination', parameters.get('recoveryurl')) response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE) response.setIsRedirectTransfer(true) // Remove existing cookies before redirecting to RECOVERY def agovRecoveryCookie = "agovRecovery=deleted; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict; Secure; HttpOnly" response.setHeader('Set-Cookie', agovRecoveryCookie) return } // dispatch form post with onReload input field : refresh QR-code FIDO UAF if (inargs.containsKey('onReload')) { clearFidoUAFSession() response.setResult('default') } // dispatch form post with fallback input field : go to registration with right loa if (inargs['fallback'] == 'register') { response.setResult('registration') }