Component Logging Configuration Options
nevisProxy Logging
Logging Configuration
With Kubernetes, 3 type of log entries are combined into a single log file:
- access.log - transaction log containing a line for each HTTP request.
- apache.log - Apache error and debug messages.
- navajo.log - error and tracing info generated by Navajo servlet container, servlets and filters.
nevisProxy log levels (impacting navajo.log) are based on a numerical system where a default threshold is specified (5, which equates to NOTICE). Trace levels can then be set for the individual filters/servlets which increase/decrease the default number. However, Admin4 hides that complexity such that you simply explicitly state the name of the log level rather than any number for the trace groups you are interested in. The default logging is set to:
- NavajoOp = INFO (traces the constructed filter chain and the invoked servlet)
- NProxyOp = INFO (provides information about frontend and backend calls on one single line)
- All other trace groups = NOTICE
In nevisAdmin4, if you add ANY log level override, then, although not displayed, the default NavajoOp/NProxyOp=INFO log levels will remain in force (unless you explicitly specify an override level for those trace groups).
See Log files and Debugging in the Nevis documentation for more information, and in particular the names and purposes of the different trace groups that can be logged to navajo.log.
Understanding Log Entries
The proxy documentation includes examples and explanations of the log entries for 4 key trace groups - NavajoOp, NProxyOp, IsiwebOp and Apache. See Tracing examples.
From that document, some of the key parameters to understand are:
InvS - invoked sequences - the filter chain with the invoked servlet. For example:
invS='/* (Redirect_Default) /* (ErrorHandler_Default) /* (ResponseHeader_Default) /nevisidm/* (SessionHandler_Password_Realm) /* (Observability_ResponseTraceId_Default) /nevisidm/admin/* (ModSecurity_nevisIDM_Administration_GUI) /nevisidm/* (Authentication_Password_Realm) >Connector_Password_Realm>LoginRenderer_nevisLogrend_Instance>NevisLogrendConnector_nevisLogrend_Instance'sC - the returned status code (to the client). The NProxyOp traces actually show both frontend (sCF) and backend (sCB) responses.
Event - understanding the event that triggered a given entry is, of course, very useful - a list of nevisProxy events can be found here. You will note that the majority of log entries will have a value of
<NULL>clID - the client ID (aka HttpSession ID) will be populated if it was accessed. Note; the browser cookie will effectively point to the user’s HttpSessionID, but for a given session the clID will remain fixed whilst the cookie that points to it may change.
cookie - this is a SHA256 hash of the cookie that can be observed at the browser. This only appears with NProxyOp tracing and at time of writing is only non-NULL when the session is first created. This was a bug and will be addressed in the next release but it will still only be populated if the given request has invoked a SessionManagementFilter.
trID - transaction ID. This consists of 2 parts separated by a full stop. The first (longer) part is the “trace ID” whilst the latter (shorter) part is the spanID. Those values can also be seen at the start of the log entries as part of the larger traceparent header. It is the “traceID” part that is of most interest as it passed ot other Nevis components and so permits the tracing of a request as it passes to the various components.
If there are actual errors reported within log entries, a code will be appended to the error message. The error codes are detailed here.
Example Troubleshooting Approaches
nevisProxy includes some pointers for troubleshooting in two sections: Troubleshooting and FAQ.
Check for ModSecurity Violations
nevisProxy implements its WAF functionality with the ModSecurity filter and the OWASP Core Rule Set (CRS). To check for policy viloations that ultimately typically block requests:
With default logging in place, NavajoOps=INFO and NProxyOp=INFO, grep for “ModSecurity”.
kubectl logs <proxy-pod-name> -n <your-namespace> --follow | grep "ModSecurity"Identify ModSecurity-specific errors.
Note “Trace ID” e.g. in the example above it is
744e756fa29a6041e1b4cf44086157a5.Search for that specific Trace ID to obtain all hits for the given request(s):
kubectl logs <proxy-pod-name> -n <your-namespace> --follow | grep "744e756fa29a6041e1b4cf44086157a5"
The following extract from a nevisProxy log shows 6 relevant entries related to the proxy denying a request for “/auth/fidouaf/authenticationresponse/” due to triggering of two ModSecurity violations.
[navajo.log] 2025 08 20 16:36:14.464 isi3web IW4ModsecF 00026.140457365964352.744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15 5-NOTICE(MS05): Matched rule with non-disruptive action: ModSecurity: Warning. Matched "Operator `Within' with parameter `|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudev (16 characters omitted)' against variable `TX:content_type' (Value: `|application/fido+uaf|' ) [file "/var/opt/nevisproxy/default/host-martin.uksouth.cloudapp.azure.com/WEB-INF/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "938"] [id "920420"] [rev ""] [msg "Request content type is not allowed by policy"] [data "|application/fido+uaf|"] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/255/153"] [tag "PCI/12.1"] [hostname "martin.uksouth.cloudapp.azure.com"] [uri "/auth/fidouaf/authenticationresponse/"] [unique_id "175570777479.546619"] [ref "o0,20v66,34t:lowercase"]
[navajo.log] 2025 08 20 16:36:14.466 isi3web IW4ModsecF 00026.140457365964352.744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15 3-ERROR (MS02): (ModSecurity_To_Fix_Mobile_Authentication) Modsecurity: Block request-body in phase:2, send status 403 to client ([client 10.244.0.208] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/var/opt/nevisproxy/default/host-martin.uksouth.cloudapp.azure.com/WEB-INF/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "81"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "martin.uksouth.cloudapp.azure.com"] [uri "/auth/fidouaf/authenticationresponse/"] [unique_id "175570777479.546619"] [ref ""]) [MODS-0003]
[navajo.log] 2025 08 20 16:36:14.466 isi3web IW4ErrorFl 00026.140457365964352.744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15 5-NOTICE: ErrorMapping_1_0::handleError: (ErrorHandler_Default) calling Servlet Hosting_Default because of ErrorCode 403
[navajo.log] 2025 08 20 16:36:14.467 isi3web NavajoOp 00026.140457365964352.744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15 6-INFO : <<<<< 'POST /auth/fidouaf/authenticationresponse/' invS='/* (ErrorHandler_Default) /* (ResponseHeader_Default) /* (SessionHandler_FIDO_UAF_Authentication_Realm) /* (Observability_ResponseTraceId_Default) /auth/fidouaf/authenticationresponse/* (ModSecurity_To_Fix_Mobile_Authentication) >Hosting_Default' sC='403' bS='2126' dTF='7' dTFrs='0' rmIP='10.244.0.208' Event='MS05,MS02' clID='<NULL>' trID='744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15' (cR=1)
[navajo.log] 2025 08 20 16:36:14.467 isi3web NProxyOp 00026.140457365964352.744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15 6-INFO : reqF="POST /auth/fidouaf/authenticationresponse/ HTTP/1.1" reqDecF=<NULL> ipF=10.244.0.208 sCF=403 bSF=2126 dTF=7 reqB=<NULL> adrB=<NULL> ipB=<NULL> sCB=<NULL> dTB=<NULL> dTcB=<NULL> dTsB=<NULL> dTr1B=<NULL> dTr2B=<NULL> dTFrs=0 invS=/* (ErrorHandler_Default) /* (ResponseHeader_Default) /* (SessionHandler_FIDO_UAF_Authentication_Realm) /* (Observability_ResponseTraceId_Default) /auth/fidouaf/authenticationresponse/* (ModSecurity_To_Fix_Mobile_Authentication) >Hosting_Default cR=1 usrID=<NULL> Event=MS05,MS02 clID=<NULL> cookie=<NULL> sslID=<NULL> trID=744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15
[access.log] 10.244.0.208 - - [20/Aug/2025:16:36:14 +0000] "POST /auth/fidouaf/authenticationresponse/ HTTP/1.1" 403 2126 1304 0 martin.uksouth.cloudapp.azure.com "-" "NMASDK/3.9.0.926 (iPhone14,7; iPhoneOS 18.5) ch.nevis.accessapp.presales.k8s/2.11.1551.1551" trID=744e756fa29a6041e1b4cf44086157a5.a6a0ede7aa9acb15
- The 1st entry shows that a rule was triggered from REQUEST-920-PROTOCOL-ENFORCEMENT.conf concerned with the allowed Content-Types. The precise line and rule ID are referenced although this entry reflects a non-disruptive action.
- The 2nd entry however, shows the final score assessment based upon a specific rule in REQUEST-949-BLOCKING-EVALUATION.conf. Again the specific line and rule ID are referenced. Crucially, this entry indicates a blocking decision with error code “MS02” and isntruction to issue a “403” response to the client.
- The final entry is the access log entry confirming the 403 response that was returned for the given request.
Using “log only“ mode may be useful to begin to evaluate issues without actually preventing access. Grepping the log for a string of “MODS” is a quick way to identify blocked requests.
The error and event codes referenced above are very useful here.
Check for Broken Links - 404 Errors
Broken links for pages, images, stylesheets etc. may well be easy to spot by simply browsing, but not always. Different log entries can be used to spot these errors. For example, the access.log file will display a relatively concise entry, however, it only shows the error that is returned to the client and does NOT indicate whether the error was returned by a protected web application (where the broken links are) or whether nevisProxy made the decision to return the error due to not recognising the requested URL.
For example, a request for a “junk.html” file that returned a 404 error may or may not have been proxied to a backend application:
[access.log] 10.244.0.163 - - [26/Aug/2025:10:26:03 +0000] "GET /nevisdemo/public/junk.html HTTP/1.1" 404 1976 - 0 demo.presales.azure.nevis.dev "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0" trID=3f6e3ad1a238400b393a327c84d0c0f8.b5f811238c3043b8`
It would be better to use the corresponding entry from navajo.log and its NProxyOp filter:
[navajo.log] 2025 08 26 10:26:03.352 isi3web NProxyOp 00024.140379150984768.3f6e3ad1a238400b393a327c84d0c0f8.b5f811238c3043b8 6-INFO : reqF="GET /nevisdemo/public/junk.html HTTP/1.1" reqDecF=<NULL> ipF=10.244.0.163 sCF=404 bSF=1976 dTF=11 reqB="GET /nevisdemo/public/junk.html HTTP/1.1" adrB=demo-bank:8080 ipB=10.0.138.232 sCB=404 dTB=4 dTcB=1 dTsB=0 dTr1B=3 dTr2B=0 dTFrs=0 invS=/* (Redirect_Default) /* (ErrorHandler_Default) /* (ResponseHeader_Default) /* (Observability_ResponseTraceId_Default) /nevisdemo/public/* (CSRF_Default) /nevisdemo/public/* (ModSecurity_DemoBank_-_Public_Pages) >Connector_DemoBank_-_Public_Pages>Hosting_Default cR=1 usrID=<NULL> Event=<NULL> clID=<NULL> cookie=<NULL> sslID=<NULL> trID=3f6e3ad1a238400b393a327c84d0c0f8.b5f811238c3043b8
In the above example, both the frontend and backend response codes are displayed (sCF=404 and sCB=404) indicating that the request WAS proxied to a backend webapp which made the deicsion to return a 404 that nevisProxy simply forwarded to the client. If nevisProxy did not recognse the requested URL then it would have made the decision to return a 404 itself and would not have contacted the backend webapp, with the result that sCB would have a value of <NULL>, as in the following example:
[navajo.log] 2025 08 26 10:37:07.268 isi3web NProxyOp 00024.140379482576448.c43f63df7685dc9813d60b269eee805d.753789db7ceb1f1c 6-INFO : reqF="GET /junk.html HTTP/1.1" reqDecF=<NULL> ipF=10.244.0.163 sCF=404 bSF=1976 dTF=2 reqB=<NULL> adrB=<NULL> ipB=<NULL> sCB=<NULL> dTB=<NULL> dTcB=<NULL> dTsB=<NULL> dTr1B=<NULL> dTr2B=<NULL> dTFrs=0 invS=/* (Redirect_Default) /* (ErrorHandler_Default) /* (ResponseHeader_Default) /* (Observability_ResponseTraceId_Default) >Hosting_Hosting_-_Demo_Landing_Page>Hosting_Default cR=1 usrID=<NULL> Event=<NULL> clID=<NULL> cookie=<NULL> sslID=<NULL> trID=c43f63df7685dc9813d60b269eee805d.753789db7ceb1f1c
It is also possible for nevisProxy to return a 500 error rather than a 404 if it does not recognise the path. Notice that the NProxyOp log entry precedes the error code with an = symbol unlike the other entries. Hence, to simply search for the specific entries we want and to avoid random hits where the “404” string appears in a transactionID, use:
kubectl logs <proxy-pod-name> -n <your-namespace> --follow | grep "=404"
Of course, a similar approach can be used to search for other error codes e.g. “500” or “403”. Finally, it can be useful to search for certain error codes returned to the client that have been initiated by nevisProxy and it’s processing e.g.
kubectl logs <proxy-pod-name> -n <your-namespace> --follow | grep "=403" | grep "sCB=<NULL>"
Filter log file for specific log type
You may want to strip out all additional log entries and simply focus on a single entry for every client request e.g. filter on “access.log” to briefly see each client request.
kubectl logs <proxy-pod-name> -n <your-namespace> --follow | grep "=access.log"
Track user session based on browser cookie
When investigating a specific problem observed in a test browser it can be useful to investigate behaviour by first obtaining the browser session cookie using standard (F12) browser inspection tools. Take an example cookie sent to the user’s browser:
set-cookie
Session_Self_Registration_Realm=0018f40af002WXYw670Zp7VKtNKgKQwPubQE4r6DG9iPgUahMFFRi0AHIE; Path=/; Secure; HttpOnly; samesite=none
The cookie value of 0018f40af002WXYw670Zp7VKtNKgKQwPubQE4r6DG9iPgUahMFFRi0AHIE is not typically written to the nevisProxy logs (unless a DEBUG_HIGH level is set for the NPSessionFlttrace group). However, this session can be pinpointed with default logs by taking a sha256 hash/base64 conversion of the cookie value.
A good tool to do this is SHA256 - Online Tools. Just make sure to set the output to Base64.
For example, the corresponding hashed/b64 version is jE8B73aKYqElH7ITud+uMeUN3lxbspkANz2H+TaBgVI=. Searching for that entry in the log would return a hit like the following where the “cookie” value is populated:
[Navajo.log] 2025 08 19 15:52:36.963 isi3web NProxyOp 00024.139882518021696.a071a7cfba52e5ffe7ef827d0700a19a.b0e1dd65f1219a3c 6-INFO : reqF="GET /nevisdemo/protected/transactions HTTP/1.1" reqDecF=<NULL> ipF=10.244.0.163 sCF=302 bSF=312 dTF=47 reqB=<NULL> adrB=<NULL> ipB=<NULL> sCB=<NULL> dTB=<NULL> dTcB=<NULL> dTsB=<NULL> dTr1B=<NULL> dTr2B=<NULL> dTFrs=0 invS=/* (Redirect_Default) /* (ErrorHandler_Default) /* (ResponseHeader_Default) /nevisdemo/protected/* (SessionHandler_Self_Registration_Realm) /* (Observability_ResponseTraceId_Default) /nevisdemo/protected/* (CSRF_Default) /nevisdemo/protected/* (Authentication_Self_Registration_Realm) cR=1 usrID=<NULL> Event=SC01 clID=0009f40af002GcpSfNJQKslMDcjpsgEdtggVmy3HMWfRAwBaQOJiEjADON cookie=jE8B73aKYqElH7ITud+uMeUN3lxbspkANz2H+TaBgVI= sslID=<NULL> trID=a071a7cfba52e5ffe7ef827d0700a19a.b0e1dd65f1219a3c
From here you can obtain the traceID and continue troubleshooting as needed.
nevisAuth Logging
Logging Configuration
Recommended base logging:
- AuthEngine = INFO (to see AuthState processing)
- Vars = INFO (to work with variables referenced whilst processing)
Some specific additional log levels:
- OAuth2 = DEBUG (to investigate OAuth2 or OIDC issues)
- Saml = DEBUG (to investigate SAML issues)
Furthermore, if you are using nevisAdapt and it’s authentication connector pattern, you can log Adaptive authentication behavour as described here using the following trace levels:
AdaptAuthState-Core = INFO
AdaptAuthState-RememberMe = INFO
AdaptAuthState-Termination = INFO
Understanding Log Entries
Consider the following example log entry:
[esauth4sv.log] 2025-08-01T08:43:43,212 p776011197-1551 365a2bdfc7338515a2020d409d392697 eec1e8280b444f97 AuthEngine INFO ENGINE PROCESS: Self_Registration_Realm_Classic_or_Usernameless- AuthEngine - This is the module that generates the entry.
- INFO - The log level for this entry. This entry was logged due to a log level of “AuthEngine = INFO”. As a result any entries generated by AutheEngine of levels INFO, ERROR or WARN will be output.
- PROCESS - the following string indicates the pattern names of the invoked authentication realm and the authentication step. Note underscores are used to separate pattern names as well as to replace spaces, and other special characters will be removed.
- p776011197-1551 - the name of the thread. It can useful to see which log lines belong to the same request if nothing else is available.
- 365a2bdfc7338515a2020d409d392697 - the “TraceId” part of the “traceparent” HTTP header that is added to requests as they pass between Nevis components. See Trace Context. It is useful to search for all hits of a given Traceid across all Nevis components to understand the end-to-end processing across the solution
- eec1e8280b444f97 - the “SpanId” is just the id for the specific point in hte trace and isn’t particularly useful for troubleshooting.
Consider the following 2 snippets from example log entries:
SessionId 'demobank.presales.azure.nevis.dev0009f40ade02PjFWLGeWzIWdntEJ8BuWkaydWmQpMpJiQu4CGz0GMjAKtjSelf_Registration_Realm
SessionId 'xmyk5IR_2V4hd8_uQIXIbL-AhrTIEyB5VVetrLZ8HxM', trying to get session- The first entry with the longer value is the initial / unauthenticated sessionId (consisting of entryId, clientId (nevisProxy session), domain).
- The second entry with the shorter value is the final random generated sessionId which is created after the auth_done is reached so the authentication was completed.
- However, these “SessionId”s are specific to nevisAuth only and do not directly correlate to any client-side browser cookie. As a result they are of limited interest for meaningful troubleshooting.
Consider the following example log entry:
[esauth4sv.log] 2025-08-01T08:48:28,440 tp776011197-400 6e9eeebf1fbb64c73757451fcf4503ea b12860b8049e798e AuthEngine INFO ENGINE TRANSITION: Self_Registration_Realm_Nevis_SecToken---default-->Self_Registration_Realm_Prepare_Done (Startover: false, Conditions: stepup- The “Conditions” element is referring to the operation that was invoked within nevisAuth. Typical values are
authenticate/setup/kill/logout. - The specific “Conditions: stepup” could either relate to specific administrator step-up authentication configuration or it could relate to functionality within a pattern without explicit administrator configuration. A “stepup” can be triggered by nevisProxy through either an IdentityCreationFilter with RecheckAuthentication=on (e.g. because the request goes to a path that is always handled by nevisAuth), or a SecurityRoleFilter with DynamicRoleAcquire=true (e.g. to issue a token).
- The “Conditions” element is referring to the operation that was invoked within nevisAuth. Typical values are
Example Troubleshooting Approaches
Summarise a User Authentication Flow
This will allow you to understand how a user login is handled and you can confirm the logical flow through the AuthStates is as expected.
With AuthEngine=INFO, grep the logfile for the “TRANSITION” keyword which will show how control is passed between AuthStates:
kubectl logs <auth-pod-name> -n <your-namespace> --follow | grep "TRANSITION"
On a live system with lots of users concurrently logging in, it can be challenging to identify any given user journey, so the following suggestion may be more helpful.
Debug One Specific User’s Session Amongst Many User Sessions
From nevisIDM, determine the target user’s “ExtID” (e.g. “1005”).
Grep the log file for that “ExtId” and note down all associated Trace IDs (as a user's login will likely result in multiple requests which will have distinct Trace IDs).
kubectl logs <auth-pod-name> -n <your-namespace> --follow | grep "1005"As an example, say you discover 4 Trace IDs, then you can extract all associated entries using egrep:
kubectl logs <auth-pod-name> -n <your-namespace> --follow |egrep "e1e7566588d9b6c69159d62319414a38|9b6767d6907e481514042291fcbe6838|a6874eed014b6791835abae18b809c4f|53f169fa14f5642c7b844055f0d22594"You can further refine the query to look for specific strings to investigate further e.g. to understand the TRANSITIONS that the use progressed through:
kubectl logs <auth-pod-name> -n <your-namespace> --follow |egrep "e1e7566588d9b6c69159d62319414a38|9b6767d6907e481514042291fcbe6838|a6874eed014b6791835abae18b809c4f|53f169fa14f5642c7b844055f0d22594" |grep "TRANSITION"
AuthState Debugging
- With Vars andAuthEngine set to INFO, search for errors.
- Having identified the AuthState that has the problem, set the corresponding log level to TRACE to dig deeper !(The relevant log level can be found in the above yaml file, or public docs)!
Working with Variables
nevisAdmin4 patterns do not have selector-type functionality when specifying variables to extract or to inject into tokens/assertions, for example. As a result, it can be challenging to know what names to use, especially because variables can have different formats. Use the following approach:
- Set AuthLog "Vars" to INFO
- Preform desired action (typically a login)
- Search for “Vars” entries and identify the desired value either by spotting a relevant name or an expected value. e.g. you may be interested in a variable called “profileId” or knowing the value is “1000” you could look for that value.
- Determine the full variable name from the log entry e.g. you would find that “profileId” has a correct name of
ch.adnovum.nevisidm.profileId.
Debugging Risk Scoring (nevisAdapt)
There is a section later in this document that describes logging within the nevisAdapt component, but it is probably of more use to enable and analise Adapt-related log entries from within nevisAuth.
Consider the following log extract for a user login that invokes the nevisAdapt Connector in the presence of a trace group log level of AdaptAuthState-Core = INFO. It consists of 6 entries that are easily correlated via the single common Trace ID of 6f2514a9ddfb3084445113acde7c9f7c.
[esauth4sv.log] 2025-08-27T14:32:01,123 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO The calculated weighted risk score is: -0.375
[esauth4sv.log] 2025-08-27T14:32:01,124 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO User: 1005
[esauth4sv.log] 2025-08-27T14:32:01,124 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO Message: Low risk, no further action required.
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO Result 0:NevisAdaptModuleResult{moduleName='NevisAdaptDeviceRecognition', riskScore=0, confidence=1, results=[established-device,private-device], status=PROCESSED, trained=true, notes='{"status":"established-device,private-device"}', analyzerResults='[AnalyzerResult{analyzerName='DeviceSharingAnalyzer', result='private-device', riskScore=0}, AnalyzerResult{analyzerName='DeviceCookieAnalyzer', result='established-device', riskScore=-0.2}]'}
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO Result 1:NevisAdaptModuleResult{moduleName='NevisAdaptDeviceFingerprint', riskScore=0, confidence=1, results=[established-browser-fingerprint,established-fingerprint,private-fingerprint], status=PROCESSED, trained=true, notes='{"browser":"Firefox","device":"Other","operatingSystem":"Windows","status":"established-browser-fingerprint,established-fingerprint,private-fingerprint"}', analyzerResults='[AnalyzerResult{analyzerName='FingerprintAnalyzer', result='established-fingerprint', riskScore=0}, AnalyzerResult{analyzerName='BrowserFingerprintAnalyzer', result='established-browser-fingerprint', riskScore=-0.2}, AnalyzerResult{analyzerName='FingerprintSharingAnalyzer', result='private-fingerprint', riskScore=0}]'}
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO Result 2:NevisAdaptModuleResult{moduleName='NevisAdaptGeolocation', riskScore=0, confidence=1, results=[established-ip,trusted-country,established-country,low-ip-velocity,ip-reputation-ok], status=PROCESSED, trained=true, notes='{"country":"United Kingdom of Great Britain and Northern Ireland","city":"London","countryCode":"GB","latitude":51.508530,"status":"established-country,established-ip,ip-reputation-ok,low-ip-velocity,trusted-country","longitude":-0.125740}', analyzerResults='[AnalyzerResult{analyzerName='SuspiciousCountryAnalyzer', result='trusted-country', riskScore=0}, AnalyzerResult{analyzerName='IpVelocityAnalyzer', result='low-ip-velocity', riskScore=0}, AnalyzerResult{analyzerName='IpReputationAnalyzer', result='ip-reputation-ok', riskScore=0}, AnalyzerResult{analyzerName='IpAddressAnalyzer', result='established-ip', riskScore=-0.15}, AnalyzerResult{analyzerName='GeoLocationAnalyzer', result='established-country', riskScore=0}]'}
Entry 1 - indicates the overall weighted risk score from all 10 analysers used by the 3 nevisAdapt modules. Risk = - 0.375, in this example
Entry 2 - indicates the extId of the authenticating user. In this case, 1005.
Entry 3 - indicates the risk level of this event. The actual weighted risk score doesn’t directly dictate the action - it is based upon the configured risk thresholds which determine the categorization of the event into low, medium or high “buckets” (or if the event is considered to be in a training/learning mode). nevisAdapt would be unaware of this categorization and so why it is recommended to debug/understand risk scoring and subsequent actions in the context of a nevisAuth log rather than a nevisAdapt log. Typical entries you would see here include:
Risk score level: untrained - authentication step-up recommended
Low risk, no further action required.
Risk score level: medium - user notification recommended
Risk score level: high - authentication step-up recommended
noteThe recommendations are fixed and do NOT reflect any patterns that are actually configured.
For entries 4-6, the following view has “prettified” them so as to ease understanding:
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO
Result 0:NevisAdaptModuleResult
{moduleName='NevisAdaptDeviceRecognition',
riskScore=0,
confidence=1,
results=[established-device,private-device],
status=PROCESSED,
trained=true,
notes='{
"status":"established-device,private-device"}',
analyzerResults='[
AnalyzerResult{analyzerName='DeviceSharingAnalyzer', result='private-device', riskScore=0},
AnalyzerResult{analyzerName='DeviceCookieAnalyzer', result='established-device', riskScore=-0.2}
]'
}
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO
Result 1:NevisAdaptModuleResult
{moduleName='NevisAdaptDeviceFingerprint',
riskScore=0,
confidence=1,
results=[established-browser-fingerprint,established-fingerprint,private-fingerprint],
status=PROCESSED,
trained=true,
notes='{"browser":"Firefox","device":"Other","operatingSystem":"Windows",
"status":"established-browser-fingerprint,established-fingerprint,private-fingerprint"}',
analyzerResults='[
AnalyzerResult{analyzerName='FingerprintAnalyzer', result='established-fingerprint', riskScore=0},
AnalyzerResult{analyzerName='BrowserFingerprintAnalyzer', result='established-browser-fingerprint', riskScore=-0.2},
AnalyzerResult{analyzerName='FingerprintSharingAnalyzer', result='private-fingerprint', riskScore=0}]'}
[esauth4sv.log] 2025-08-27T14:32:01,127 tp1772686209-39 6f2514a9ddfb3084445113acde7c9f7c f559dec2a60f40f4 AdaptAuthState-Core INFO
Result 2:NevisAdaptModuleResult
{moduleName='NevisAdaptGeolocation',
riskScore=0,
confidence=1,
results=[established-ip,trusted-country,established-country,low-ip-velocity,ip-reputation-ok],
status=PROCESSED,
trained=true,
notes='{"country":"United Kingdom of Great Britain and Northern Ireland","city":"London","countryCode":"GB","latitude":51.508530,
"status":"established-country,established-ip,ip-reputation-ok,low-ip-velocity,trusted-country","longitude":-0.125740}',
analyzerResults='[
AnalyzerResult{analyzerName='SuspiciousCountryAnalyzer', result='trusted-country', riskScore=0},
AnalyzerResult{analyzerName='IpVelocityAnalyzer', result='low-ip-velocity', riskScore=0},
AnalyzerResult{analyzerName='IpReputationAnalyzer', result='ip-reputation-ok', riskScore=0},
AnalyzerResult{analyzerName='IpAddressAnalyzer', result='established-ip', riskScore=-0.15},
AnalyzerResult{analyzerName='GeoLocationAnalyzer', result='established-country', riskScore=0}]'}
- Entry 4 - shows the risk scoring of the “NevisAdaptDeviceRecognition” module. It consists of 2 analyzers which independently have risk scores in this example of 0 and -0.2.
- Entry 5 - shows the risk scoring of the “NevisAdaptDeviceFingerprint” module. It consists of 3 analyzers which independently have risk scores in this example of 0, -0.2 and 0.
- Entry 6 - shows the risk scoring of the “NevisAdaptGeolocation” module. It consists of 5 analyzers which independently have risk scores in this example of 0, 0, 0, -0.15 and 0.
All 10 of these scores are combined/normalized depending upon the weighting profile and result in the overall risk score of -0.375 reported in the 1st entry.
As a reference, some further associated events with examples when using AuthState logging of nevisAdapt-related events:
- Session related exceptions: Unable to set session ID:
{SessionException message} - Connection/Timeout exceptions: Timeout error occurred:
{root cause} - AuthState initialization: Device cookie configuration was found. Cookie name:
{name} - Token generation: Sending {new/found} cookie
'{name}'='{value}' - Incorrect configuration: Failing configuration:
[{key}:{value}] - nevisAdapt result validation: Invalid nevisAdapt module result:
{DTO} - Authentication step messages: NevisAdaptCalculatorAuthState.process
- API DTO details:
NevisAdaptModuleResult{moduleName='{str}', riskScore={num}, confidence={num}, results=[results], status={enum}, trained={boolean}, notes='{str}', analyzerResults='{str}'}
nevisIDM Logging
Logging Configuration
With Kubernetes, 2 type of log entries are combined into a single log file:
- audit.log - audit entries relating to actions to create/modify/delete objects such as users and credentials.
- application.log - remaining entries to cover general component activities
The most important log level settings are described here.
Understanding Log Entries
The audit log format is explained here. In general, the format is:
{
headerFields,
"client":{...},
"actor" :{...},
"subject" :{...},
"eventData":{...}
}
Of particular note are:
traceID - the Trace ID which can be correlated to corresponding entries in other components.
eventType - fundamentally there are 4 categories:
- Create -
*_CREATEwhere the _eventData consists of the updated or new values (in the field newValues) and all values of the entity after the update (in the field updatedState). There will be no oldValues. - Modify -
*_MODIFYwhere the _eventData consists of the updated or new values (in the field newValues), the old values before the update (in the field oldValues) as well as all values of the entity after the update (in the field updatedState). - Delete -
*_DELETEwhere the _eventData consists of the old values only (in the field oldValues). - Authorization Denied -
AUTHORIZATION_DENIEDwhere the eventData contains the required role (RequiredRole) in both the newValues field and the updatedState field. - The
*character represents one of many possible events e.g.CREDENTIAL_LOGIN_INFO.
- Create -
client - this is a little confusing as it appears multiple times and has different meanings. As per the summary format, above, the “client” section refers to the calling entity e.g. a nevisAuth pod. However, all objects within the nevisIDM database are stored within a “client” (that has both “extID” and “name” attributes) where the “client” is a fundamental logical entity that represents a customer, organization, or specific business unit within the system.
Consider the following example entry that relates to a user login where some user attributes are modified. It has been prettified to make it esier to understand, but appears as a single entry in the log file:
{
"logVersion": 1,
"timestamp": "2025-08-26T14:07:42.044+0000",
"source": "nevisidm@idm-7697c55fcc-9fbmf",
"eventType": "CREDENTIAL_LOGIN_INFO_MODIFY",
"trID": "",
"spanID": "a8d7fbc487a7fa36",
"traceID": "d9a116bd684033ee3b95f00fcec2a1de",
"sessionID": "1756217067317",
"client": {
"sessionId": "",
"entryPoint": "nevisauth@auth-579f7f57cc-fndt5"
},
"actor": {
"firstName": "nevisAuth",
"lastName": "User",
"email": "[email protected]",
"loginId": "nevisauth",
"extId": "99",
"isTechnicalUser": true,
"client": {
"extId": "100",
"name": "Default"
},
"unit": {
"profileExtId": "99",
"extId": "100",
"name": "Default",
"hierarchyName": "/100"
}
},
"subject": {
"firstName": "Martin",
"lastName": "Day",
"email": "[email protected]",
"loginId": "[email protected]",
"extId": "1005",
"isTechnicalUser": false,
"client": {
"extId": "100",
"name": "Default"
}
},
"eventData": {
"newValues": {
"lastLogin": "2025-08-26T14:07:42.000+0000",
"loginSuccessCount": "31"
},
"oldValues": {
"lastLogin": "2025-08-26T14:04:27.000+0000",
"loginSuccessCount": "30"
},
"updatedState": {
"userExtId": "1005",
"lastLogin": "2025-08-26T14:07:42.000+0000",
"credentialExtId": "1025",
"loginSuccessCount": "31",
"clientExtId": "100",
"typeId": "1",
"lastFailure": "",
"lastFailureCount": "0"
}
}
}- The first “client” reference indicates that the entryPoint is "nevisauth@auth-579f7f57cc-fndt5" which is a nevisAuth pod.
- The second “client” entry is part of the “actor” section and indicates that the identity used to perform the action (“nevisAuth”) exists in the Default/100 “client”.
- Similarly, the third “client” entry is part of the “subject” section and indicates that the identity that the action was performed upon (“martin.day@nevis,net”) also exists in the Default/100 “client”.
If trying to use the “kubectl logs” approach to chain multiple greps to search for entries with multiple values then it appears to be problematic when using the “-f” switch. Perhaps it is related to grep not being the best tool for working with JSON. Regardless, multiple chained greps can be used if the -f switch is NOT used.
Example Troubleshooting Approaches
Bear in mind that audit.log entries may take a few seconds before appearing in the log file.
Reduce Background Noise in Log File
With default log settings, there are multiple INFO-level entries that appear every 5 seconds relating to health checks. To remove such noise, you could consider reducing the level back to the overall WARN level for the “standalone” logger i.e.:
ch.nevis.idm.standalone = WARN
Debug Outbound Email Sending
An example of email sending by nevisIDM is when a nevisAdapt notification is sent on elevated risk.
nevisIDM isn’t the only component that sends email outbound, as nevisAuth also sends eTAN messages.
Setting a default log level to DEBUG would allow observation of all key entries related to sending the email, but that would generate an extreme amount of logging. Instead use the following 3 specific log levels:
org.springframework.web.filter.CommonsRequestLoggingFilter = DEBUG: To observe requests sent TO nevisIDM by nevisAdapt (amongst other components).ch.nevis.idm.events.queue.EstivateEventQueue = DEBUG: To see details of the email to be sent.org.quartz.core = ERROR: To see any errors with email sending from the scheduler engine - note; it may take a little while before these errors will appear.
Confirm that nevisAdapt successfully sends the email notification request to nevisIDM.
kubectl logs <idm-pod-name> -n <your-namespace> --follow | grep "DEBUG Before request"You should a request like the following to “/nevisidm/api/notification/v1/”:
[application.log] 2025-09-23T13:50:47,934 qtp571662993-56 a742c171b83c6d8018961ee777835850 97c15c3de9f39d7a k.web.filter.CommonsRequestLoggingFilter DEBUG Before request [POST /nevisidm/api/notification/v1/, client=10.244.0.224, user=99]Confirm that the email template is correctly invoked and the desired parameters populated. Note; due to formatting, you will have to add the “-A” switch to return both the matching line entry and a specified number of lines after the entry to capture all relevant info for the entry. The following example searches for the entry + 20 lines after.
kubectl logs <idm-pod-name> -n <your-namespace> --follow | grep "EmailJob" -A 20You should see an entry like the following that indicates the email contents as well as the email connection details so that you can confirm the endpoint and credentials are valid:
[application.log] 2025-09-23T13:27:01,662 tp1111968536-57 2a7cecb68bf756154ae7c735a92363ca b32e3a15c3fe3923 evis.idm.events.queue.EstivateEventQueue DEBUG Persisting PersistentQueueEntry: ch.adnovum.nevisidm.service.dto.PersistentQueueEntry@4f7666dd[itemId=1607,userId=nevisauth,eventId=email,jobName=EmailJob,state=0,type=1,data1=<html>
Dear Client,<br>
We noticed an access with your user account with new login details.<br>
Details of the login request:
<table>
<tr><td>Time<td><td>2025-09-23T13:27:00Z</td></tr>
<tr><td>IP<td><td>192.42.116.24</td></tr>
<tr><td>Location<td><td>NL - Amsterdam, Netherlands (Kingdom of the) (52.378502, 4.89998)</td></tr>
<tr><td>Device information<td><td>Other Windows Firefox</td></tr>
</table>
<br>
<div>
If you could not recognize this login request please visit the following link: <a href="https://multi.presales.azure.nevis.dev/feedback/applyFeedback?feedbackToken=eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..Ktbs9MCKoTXyjmX1KFGr2w.Gt2Wgq5J8Z34JELOjVHl9Val8ELR0BLFFmCU6i70vfu2RQAE9NZ5y4MqhgmbcOToghwuIiFa_b7CWWEbO9P7R5Y6SvDJ6t6T9wydzaM1uLz2KO13u-QDMf6mLjxT9d_lxz9ZEA4tOwqHsbxDqiDqHUGFD2UJzzR9w_8aTFN6izWbwN7uBSAjX1Eu7aJO15KIenU7wYyEO-nTBy6HBKhCb0SI7WDG9wy3VnkhQFJ0-PI.ZkEuWPGnO6pQagBcYC9olg">distrust</a>.
</div>
</html>,data2={[email protected], applicationjakarta.mail.debug.enabled=false, subject=Important: New login attempt detected, applicationjakarta.mail.smtp.host=smtp.sendgrid.net, applicationjakarta.mail.smtp.username=apikey, userId=1005, applicationjakarta.mail.smtp.password=SG.xxxxxx.-xxxxxxxx, applicationjakarta.mail.smtp.timeout=60, mailStarttlsEnabled=false, [email protected], htmlEmail=true, htmlContentEncoding=ISO-8859-1, applicationjakarta.mail.smtp.port=2525, applicationjakarta.mail.smtp.connection.timeout=60, contentType=text/html, [email protected], skipEmailValidation=false},data3=<null>,timeStamp=1758634021659,clientId=100,scheduleDate=<null>,targetClientId=100]Optionally, you can also check for another log entry like with step 1 except it relates to AFTER the request is processed by nevisIDM:
kubectl logs <idm-pod-name> -n <your-namespace> --follow | grep "DEBUG After request"You should see an entry like the following that indicates the user (userExtId) to whom the email is to be sent along with all the parameters that are avaialble to be populated by the invoked email template:
[application.log] 2025-09-23T12:28:53,060 qtp571662993-58 110a0c8397464eba193d881fdf5c63ae dadc8e35af178c80 k.web.filter.CommonsRequestLoggingFilter DEBUG After request [POST /nevisidm/api/notification/v1/, client=10.244.0.220, user=99, payload={"clientExtId":"100","userExtId":"1005","notificationType":"nevisAdaptNotification","sendingMethod":["HTMLemail"],"async":"true","placeholders":{"en":{},"de":{},"languageIndependent":{"EXTERNAL_DATE":"2025-09-23T12:28:52Z","EXTERNAL_CITY":"Staten Island","EXTERNAL_COUNTRY_CODE":"US","EXTERNAL_COUNTRY":"United States of America","EXTERNAL_LATITUDE":"40.576283","EXTERNAL_LONGITUDE":"-74.144836","EXTERNAL_IP_ADDRESS":"199.195.251.119","EXTERNAL_DEVICE":"Other","EXTERNAL_BROWSER":"Firefox","EXTERNAL_OS":"Windows","EXTERNAL_DISTRUST_URL":"https://multi.presales.azure.nevis.dev/feedback/applyFeedback?feedbackToken=eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..dnMf1DaQdNMNen7hCPFcPg.qmcYL3gOTiFqb9uCBrdJZKppZL1g4VnHqeh7jNlrS2uCMqjKmCn3crwjWFrmHswCJUuHH1dVZWYMD7f48qLUbHpIIUyk4iS7xwRX-2IVBr_bR44dwu2bAVQIv1W8IiTsSUzlC5INZLc9TFAa7WwQxhIZGBuYox8zBIvMiD_FmoutBIBFqGrkCy7AwXlFiIJSNYnLGw4y_jwHByN0UBaCtM3cOAX_dqGXiUC0OQI7ErM.QjsH2bK5lFrArUXpgu54fg"}}}]noteAt this point it is not proven that the email was sent successfully as the email sending is processed asynchronously.
Check for error messages received by the scheduler. Again, you would need the “-A” switch to see additional lines after the error string to observe the useful detail. Note; the scheduler may take a little time to receive and display any errors:
kubectl logs <idm-pod-name> -n <your-namespace> --follow | grep "EmailJob" -A 20Although different search strings COULD be used, this is actually the same search used for step 2. Look for the specific error cause. In the example below, the credentials are invalid:
[application.log] 2025-09-23T14:14:41,398 EVENT_Worker-2 9d7049b99900cb6b8a0c9cb990ebd390 2689af695b442a4f org.quartz.core.JobRunShell ERROR Job JOBS.EmailJob threw an unhandled Exception:
ch.adnovum.nevisidm.service.exception.NevisidmBusinessException: ch.nevis.idm.tan.TANChannelException: Error sending mail message, unable to compose email.
at ch.nevis.idm.events.EmailJob.executeInternal(EmailJob.java:96) ~[nevisidm-business-8.2505.3.10.jar:8.2505.3.10]
at ch.nevis.idm.events.IdmJob.execute(IdmJob.java:85) ~[nevisidm-business-8.2505.3.10.jar:8.2505.3.10]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [quartz-2.3.2.jar:?]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.2.jar:?]
Caused by: ch.nevis.idm.tan.TANChannelException: Error sending mail message, unable to compose email.
at ch.nevis.idm.tan.EmailChannel.send(EmailChannel.java:184) ~[nevisidm-business-8.2505.3.10.jar:8.2505.3.10]
at ch.nevis.idm.events.EmailJob.executeInternal(EmailJob.java:92) ~[nevisidm-business-8.2505.3.10.jar:8.2505.3.10]
... 3 more
Caused by: jakarta.mail.AuthenticationFailedException: 535 Authentication failed: The provided authorization grant is invalid, expired, or revoked
The above steps described how to understand the log entries that relate to email sending via nevisIDM, but with the org.quartz.core = DEBUG log setting you can simply search for the “ERROR” string to quickly identify issues with sending emails.
General Search for “ERROR” Entries
This a quick way to discover issues. Of course you will only see ERROR entries related to specific log levels enabled (e.g. see org.quartz.core example above):
kubectl logs <idm-pod-name> -n <your-namespace> --follow | grep "ERROR"
A couple of examples:
CSRF issue
[application.log] 2025-08-27T10:24:11,366 qtp834212917-59 baf7ae6389652c9a83d80161f41c9c2d fa2928a158489457 org.owasp.csrfguard.action.Log ERROR potential cross-site request forgery (CSRF) attack thwarted (user:1005, ip:10.244.2.250, method:POST, uri:/nevisidm/selfadmin/UserModify.do, error:The token should exist in the storage at this point)Note the presence of the Trace ID (
baf7ae6389652c9a83d80161f41c9c2d) which can be used to correlate with other component log entries. This is relatively common to see and can occur if the user has accessed an object from within the administration GUI (which will return a CSRF token) but delays making a configuration change until after the CSRF token has expired.Missing Attribute from SecToken
[application.log] 2025-08-27T10:44:27,143 qtp834212917-59 8b3ea5450787804074e3ddc7a75b8c57 b9da9aee56a6629a filters.JspAppUserRequestInjectingFilter ERROR Caught exception while determining principal, invalidating session and denying access (Did not find profile id in secToken, expected field='profileId' (HINT: check your TokenAssembler configuration))The reason for this error is quite clear. Whilst requesting access to nevisIDM (via nevisIDM GUI), the user principal must be established in order to determine permissions. The information is obtained from the SecToken that is forwarded by nevisProxy. In this case, the profileId information is not present and so permissions cannot be determined - hence the error and the user is denied access. This may happen for example, if the user authenticated via a Social or SAML provider such that the profileId attribute has not been established. The solution would be to lookup the missing attribute and add it to the SecToken.
nevisAdapt Logging
Unfortunately, nevisAdapt does not use Trace IDs and as a result it is challenging to correlate Adapt log entries to entries in other components in anything other than test systems.
Logging Configuration
The logging trace groups (class filters) that can be used to delve deeper into nevisAdapt processing are described here.
The default logging is set to:
- ch.nevis.nevisadapt = INFO
- ch.nevis.nevisadapt.util.logging.OpTracer = DEBUG (this allows you to observe the operational requests sent by nevisAuth udring login and logout events)
Understanding Log Entries
With default logging in place as described above, a user login that invokes the nevisAdapt Connector for risk scoring will trigger creation of 9 log entries as shown in the screenshot, below:

Of these:
- Entries 1, 4 and 7 indicate the use of an internal X509 authentication from nevisAuth to nevisAapt that then permits processing in 3 stages:
- saveObservationsAndCalculateRisk
- approveSession (assuming the authenticated session is established correctly within nevisAuth)
- registerRiskEvents (assuming session was approved, the
- Entries 2,5,8 are DEBUG-level entries reflecting the 3 calls made for the above 3 stages
- Entries 3,6,9 are INFO-level entries reflecting the responses to the above 3 calls that indicate the response time.
Taken together, they indicate standard successful processing of a risk score but other than that, the only other info of note is that the overall weighted risk score of the associated login event is -0.375. In other words, not particularly helpful to understand the risk scoring for any given event. The (current) lack of Trace ID further limits its usefulness.
Although log levels could be increased to gather more information about risk scoring (in particular, AdaptModules-Generic=INFO), it is probbly better to use the specific Adapt-related trace groups within nevisAuth as described earlier, or to use the Adaptive Insights web application if you wish to understand a risk scoring event.
Example Troubleshooting Approaches
Determining Problematic Sub-component
This is a general catch-all approach when investigating nevisAdapt processing issues rather than using logs to understand risk scoring.
If watching an error log, the group name after the string “nevisadapt” usually gives a hint as to which subcomponent had the issue, e.g. ch.nevis.nevisadapt +
.controller-> REST controller.entity,.repository-> DB.module,.analyzer-> Risk analysis
Observing Session Removal Behaviours
nevisAdapt (rather than nevisProxy or nevisAuth) may attempt to terminate a user session as a result of the distrust/feedback feature or administrative action via the Insights Dashboard. To observe such events, enable AdaptAuthMngmt=INFO logging.
This will give entries such as:
[nevisadapt.log] 2025-08-28 13:56:13,570 [https-jsse-nio-0.0.0.0-8888-exec-4] INFO AdaptAuthMngmt - Attempting session removal at https://auth:8991/nevisauth/management/session/QBSdM6ZQbmH2Plfb9j9ia0pkj7Sl0Jr6LuQ2YakJL6M
nevisFIDO UAF Logging (including SDK usecase)
Logging Configuration
Default logging uses the category ch.nevis.auth.fido.application.Application=INFO
This setting provides:
- log messages during startup and when the startup is done
- 1 line per incoming request
- 1 line for each API call towards nevisIDM
Understanding Log Entries
The nevisFIDO UAF documentation includes various resources for understanding responses that are surfaced in logs. In particular:
- FIDO UAF:
- UAF status codes - In the case of a “1498” error, the server is indicating that the received content from the client is unacceptable. The client error codes, below, can then be consulted to understand WHY the client was unable to respond normally.
- Client error codes
- ASM (Authenticator Specific Module) status codes
As with other components, the Trace ID can be seen in the log entries and can be used to correlate requests with other components.
Be aware that the log files will occasionally show entries like the following. These are background calls initiated by registered mobile authenticators “checking in” with the neivsFIDO backend.
In the example, 035592af-bb99-4a95-a638-0aad43e1ea37 is the identifier that points to a specific generic credential within nevisIDM that holds dispatch information for an authenticator. These calls are used to refresh/update the stored information. For example, if a Firebase push token is updated or if the user enables/disables push notifications on the device.
[nevisfido.log] 2025-08-28T14:57:54,843 tp2059287891-52 a9f5e1ae450401e7073994c22a298ebc 76f4c0e38099ab05 k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: PATCH /nevisfido/token/dispatch/targets/035592af-bb99-4a95-a638-0aad43e1ea37, headers=[Content-Type:"application/jose;charset=UTF-8", X-Request-ID:"e7e908b94b62943125b6cfe37a5504ba", X-Forwarded-For:"92.40.168.159", X-Forwarded-Port:"443", Content-Length:"439", X-Real-IP:"92.40.168.159",
User-Agent:"NMASDK/3.9.0.926 (iPhone14,7; iPhoneOS 18.5) ch.nevis.accessapp.presales.k8s/2.11.1551.1551", X-Forwarded-Proto:"https", X-Forwarded-Scheme:"https", Accept-Language:"en-GB,en;q=0.9", X-Scheme:"https", X-Forwarded-Host:"demo.presales.azure.nevis.dev", Connection:"keep-alive", traceparent:"00-a9f5e1ae450401e7073994c22a298ebc-321d74532ff41ba0-01", Accept-Encoding:"gzip, deflate, br", Host:"demo.presales.azure.nevis.dev:443", Accept:"application/json"], payload=eyJhbGciOiJFUzI1NiJ9.eyJ0YXJnZXQiOiJkQmVPdTRCazhreHptTWhwcFhzMkl2OkFQQTkxYkVJQmdsQmRQS0tCN05PVElxSkpVcl9DWGo1YnlvSmNwN1ZTRE1NeG5NNV9OdFdoaW9pN28tUVpjWG9iV2pVNzM5VnRfWUlQOEZDQ0N0TkRrWDFfQWVYVGlDa0J1RmE4N3J6UTRYLVdkRG5GNmdtR3pNIiwicmVxdWVzdElkIjoiNTFlYzc2NWQtYTg3MC00ZmYwLWFjOGQtOWRlZGVjMjZhNzQ5IiwiY3JlYXRpb25UaW1lSW5FcG9jaE1pbGxpcyI6MTc1NjM5MzA3MzYzNn0.7R2xdAg4DWxA0VRHHbS_KtSz5UHN6r9etWE5o2mrp3blAE0WrO9XQhwpX8Fxszyc-O6K24SJKujAZDXWS-umlA]
Example Troubleshooting Approaches
Debug a QR Code-based (Usernameless) Login
Set a log level of “org.springframework.web.filter.CommonsRequestLoggingFilter = DEBUG" - to debug incoming requests.
As this flow involves requests from both a browser as well as a mobile app together with the fact that the user (extId) is initially unknown means that correlating all requests to a single user login process is challenging in anything other than test systems where traffic is low. So this section highlights the key log entries that should be seen when debugging your own usernameless login issues:
A QR code is requested when the user invokes a usernameless flow:
[nevisfido.log] 2025-08-28T14:57:42,145 tp2059287891-54 5f49c523753e71e30e6066cc1a151c28 89468f7515501a9e k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: POST /nevisfido/token/dispatch/authentication, headers=[Accept:"application/json", traceparent:"00-5f49c523753e71e30e6066cc1a151c28-227337aa66c55e37-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"nevisfido:9443", Content-Length:"51", Content-Type:"application/json; charset=UTF-8", Connection:"keep-alive", User-Agent:"Apache-HttpClient/5.4.3 (Java/21.0.7)"], payload={"dispatcher":"link","getUafRequest":{"op":"Auth"}}]infoThere will be 2 similar-looking entries for before and after the request is processed. The latter is displayed above as it is most useful as it includes the payload - in this case an authentication event using a “link” dispatcher. This before/after approach to recording events is repeated on subsequent steps as well, so in all cases the “after” request entry is the one that is referred to.
Instead of using an explict “png-qr-code” dispatcher, as the docs recommend, a “link” dispatcher is used which is more flexible as it allows both a link for mobile-only use cases to be presented as well as the QR code which can be scanned from a separate mobile device.
Browser checks status of authentication event that has been triggered.
This is as the result of javscript repeatedly sending the request every 2 secs with a standard Suite/Platform deployment until the authentication journey completes or the page displaying the QR code is closed. As a result, the log file can quickly fill with these entries.
[nevisfido.log] 2025-08-28T14:57:42,341 tp2059287891-53 5cbae7683a395a784426cea33563de71 6b1875832e115711 k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: POST /nevisfido/status, headers=[Accept:"application/json", traceparent:"00-5cbae7683a395a784426cea33563de71-3e8236cf72cd3633-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"nevisfido:9443", Content-Length:"52", Content-Type:"application/json; charset=UTF-8", Connection:"keep-alive", User-Agent:"Apache-HttpClient/5.4.3 (Java/21.0.7)"], payload={"sessionId":"7e64c9b0-8f63-42d5-889e-81f9a0cb784f"}]Authentication Request is Retrieved
[nevisfido.log] 2025-08-28T14:57:58,659 tp2059287891-53 816bf6f12d8f3840382768bc1cbd2c54 eb915b353c7b52bc k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: POST /nevisfido/token/redeem/authentication, headers=[Content-Type:"application/json;charset=UTF-8", X-Request-ID:"78d3f88e3595fd1de9fa521d0524c367", X-Forwarded-For:"92.40.168.159", X-Forwarded-Port:"443", Content-Length:"48", X-Real-IP:"92.40.168.159", User-Agent:"NMASDK/3.9.0.926 (iPhone14,7; iPhoneOS 18.5) ch.nevis.accessapp.presales.k8s/2.11.1551.1551", X-Forwarded-Proto:"https", X-Forwarded-Scheme:"https", Accept-Language:"en-GB,en;q=0.9", X-Scheme:"https", X-Forwarded-Host:"demo.presales.azure.nevis.dev", Connection:"keep-alive", traceparent:"00-816bf6f12d8f3840382768bc1cbd2c54-2c59aefb1952ead0-01", Accept-Encoding:"gzip, deflate, br", Host:"demo.presales.azure.nevis.dev:443", Accept:"application/fido+uaf"], payload={"token":"64c05e7c-9aaf-40a2-9ba2-2f121065d9f8"}]In this request, the token that was obtained from the JWT encoded into the QR code that was scanned previously is now used to obtain the actual authentication request
Facets are checked by the mobile app:
[nevisfido.log] 2025-08-28T14:57:58,924 tp2059287891-52 ae8142df838b8f4bcc8a5ce1b25376b0 a8be3830b6b20c5a k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: GET /nevisfido/uaf/1.1/facets, headers=[Content-Type:"application/json;charset=UTF-8", X-Request-ID:"3cf0d722cdb01cca6f8daa098d32c56b", X-Forwarded-For:"92.40.168.159", X-Forwarded-Port:"443", X-Real-IP:"92.40.168.159", User-Agent:"NMASDK/3.9.0.926 (iPhone14,7; iPhoneOS 18.5) ch.nevis.accessapp.presales.k8s/2.11.1551.1551", X-Forwarded-Proto:"https", X-Forwarded-Scheme:"https", Accept-Language:"en-GB,en;q=0.9", X-Scheme:"https", X-Forwarded-Host:"demo.presales.azure.nevis.dev", Connection:"keep-alive", traceparent:"00-ae8142df838b8f4bcc8a5ce1b25376b0-856c0e59efa52c9a-01", Accept-Encoding:"gzip, deflate, br", Host:"demo.presales.azure.nevis.dev:443", Accept:"*/*"]]Basically, the mobile app is confirming that it is talking to the correct backend.
Mobile device returns the authentication response:
[nevisfido.log] 2025-08-28T14:58:01,726 tp2059287891-53 941a8b5e762c9061e6a70cb73fbb168a c276992b144e2d6d k.web.filter.CommonsRequestLoggingFilter DEBUG After Request: POST /nevisfido/uaf/1.1/authentication/, headers=[Content-Type:"application/fido+uaf;charset=UTF-8", X-Scheme:"https", traceparent:"00-941a8b5e762c9061e6a70cb73fbb168a-256be6a6084e739d-01", Connection:"keep-alive", User-Agent:"NMASDK/3.9.0.926 (iPhone14,7; iPhoneOS 18.5) ch.nevis.accessapp.presales.k8s/2.11.1551.1551", X-Real-IP:"92.40.168.159", Accept-Language:"en-GB,en;q=0.9", Accept:"application/fido+uaf", X-Forwarded-Proto:"https", Accept-Encoding:"gzip, deflate, br", X-Forwarded-Host:"demo.presales.azure.nevis.dev", X-Request-ID:"a166c3dba4c8cdd041273f4883f5711b", Host:"demo.presales.azure.nevis.dev:443", X-Forwarded-Port:"443", Content-Length:"1291", Cookie:"p_route=1756393079.432.27.166803|1d058a4cb5cc0c654d6e4e6a44266177", X-Forwarded-Scheme:"https", X-Forwarded-For:"92.40.168.159"], payload={"uafResponse":"[{\"assertions\":[{\"assertion\":\"Aj7GAAQ-dgALLgkARjFEMCMxMDAzDi4FAAEAAQIADy4IAEzA2eNR_jZwCi4gAImyp3PAh2DKOiGimlfx60jSI-iRWnDZOXMfjYWZ7noREC4AAAkuIAAcUvK2D7SK6z3ot6XDe4gS5HFzjQQeq2B-BIMNXZe-bw0uBAAAAAAABi5IADBGAiEAyGGlFlmciPm9bVtJeAOPnQOyvmu5PxyAcMbvvIauzHcCIQCt6n92lOlbA1QbwzsAHLuXfkFkJi6qZkhOH59GAIWjkA\",\"assertionScheme\":\"UAFV1TLV\"}],\"fcParams\":\"eyJjaGFubmVsQmluZGluZyI6e30sImZhY2V0SUQiOiJpb3M6YnVuZGxlLWlkOmNoLm5ldmlzLmFjY2Vzc2FwcC5wcmVzYWxlcy5rOHMiLCJjaGFsbGVuZ2UiOiJxYzREZEJfaEVUSWY1Y2FtV2lhYWdzQmFFdUFSWC1ZNHBmME9BS3JpOFN4Ni04eGtoZmZIVEFfdnVWQk5OdE54UE1zc1B5V0c1UUh1c0JMZjR4SXM0QSIsImFwcElEIjoiaHR0cHM6XC9cL2RlbW8ucHJlc2FsZXMuYXp1cmUubmV2aXMuZGV2XC9uZXZpc2ZpZG9cL3VhZlwvMS4xXC9mYWNldHMifQ\",\"header\":{\"appID\":\"https:\\\/\\\/demo.presales.azure.nevis.dev\\\/nevisfido\\\/uaf\\\/1.1\\\/facets\",\"exts\":[{\"data\":\"7e64c9b0-8f63-42d5-889e-81f9a0cb784f\",\"fail_if_unknown\":false,\"id\":\"ch.nevis.auth.fido.uaf.sessionid\"},{\"data\":\"https:\\\/\\\/demo.presales.azure.nevis.dev\",\"fail_if_unknown\":true,\"id\":\"ch.nevis.auth.fido.uaf.serverBaseURL\"}],\"op\":\"Auth\",\"serverData\":\"N19xwFvT3fLUCdEe2JarQnl-PVYAzOObB62ezkJ-wWeDui7fNVXo7eWfvr6efd8njj7oECv_R26uvYuu1i95cA\",\"upv\":{\"major\":1,\"minor\":1}}}]","context":"{\"clientErrorCode\":0}"}]
The bulk of the payload in the challenge isn’t practically of use although the “clientErrorCode” is very useful to search for. In this case, it is “0” meaning no error, but this string can be grepped for generally such that problem (non-zero) authentication events can be easily identified. The codes can then be checked using the online docs referenced earlier.
Debug Entire FIDO UAF Component
Thist would typically be a last resort due to the amount of entries generated. Set a log level of ch.nevis.auth.fido = DEBUG.
nevisFIDO FIDO2/Passkey Logging
As with nevisFIDO UAF logging, default logging uses the category ch.nevis.auth.fido.application.Application=INFO.
This setting provides: log messages during startup and when the startup is done.
The online help suggests two further loggers:
- org.springframework.web.filter.CommonsRequestLoggingFilter = DEBUG - to debug incoming requests.
- ch.nevis.auth.fido = DEBUG - to debug the entire component.
Furthermore, although not publicly documented, there are also:
- ch.nevis.auth.fido.api.webauthn = DEBUG – FIDO2 specific logs at the API level.
- com.webauthn4j = DEBUG – deeper insights (if any) into FIDO2 protocol processing (third-party library).
- ch.nevis.auth.fido.fido2.authstate = DEBUG – FIDO2 related AuthStates (part of nevisAuth, not nevisFIDO).
Understanding Log Entries
There is no specific public documentation regarding interpreting nevisFIDO FIDO2 log files.
As with other components, the Trace ID can be seen in the log entries and can be used to correlate requests with other components.
Example Troubleshooting Approaches
Debug a Passkey Autofill UI Authentication Flow.
Before analysing log files it is useful to understand the actual flow and components that are involved.
- Firstly, unlike FIDO UAF, all requests from the client are proxied to nevisAuth and from there, requests are submitted to nevisFIDO as required.
- Secondly, nevisFIDO is basically responsible for generating registration/authentication challenges (to be sent to the client), and for processing registration/authneitcation responses.
The following explains how a passkey Autofill UI authentication works (from the client perspective):
- Initial Page Load: The user navigates to the login page presented by nevisProxy/nevisLogRend.
- Server Sends Challenge: The server responds with an HTML page containing a login form. Crucially, a hidden input field namedfido2_attestation_options is included in the form. This field contains a JSON string with the necessary information for FIDO2 authentication, including a uniquechallenge, the rpId (relying party ID, which is the domain name), and a timeout.
- Client-Side Scripting: The page loads several JavaScript files, including passkey_autofill.js. This script contains an authenticate() function that is designed to run automatically. It finds the hiddenfido2_attestation_options field, parses its value, and uses the data to call SimpleWebAuthnBrowser.startAuthentication(). It also setsuseBrowserAutofill to true. SimpleWebAuthnBrowser.startAuthentication() is a higher-level, library-specific function that takes a single object as an argument, typically called optionsJSON, which is provided by the server. The library's main job is to prepare this data in the correct format for the browser's native API (navigator.credentials.get()). It first checks if the browser supports WebAuthn, then it processes the challenge and other options received from the server. navigator.credentials.get()is the underlying, native browser API that performs the actual authentication request.
- Browser Prompts for Passkey: By calling startAuthentication with the useBrowserAutofill: true option, the browser is instructed to prompt the user with a passkey autofill UI. Depending upon the browser, this occurs as the user clicks into the username field.
- User Authentication: If the user selects a passkey, the browser uses the authenticator (e.g., a biometric sensor or security key) to sign the challenge provided by the server.
- Assertion Response: After successful user authentication, the SimpleWebAuthnBrowser library's promise resolves with an assertionResponse object. This object contains payloads such asid, type, response.clientDataJSON, response.authenticatorData, and response.signature.
- Submission to Server: The passkey_autofill.js script then creates a new form, populates it with the data from the assertionResponse object, and submits it to the server at the path /nevisfido/fido2/assertion/result. This final step completes the authentication process with the server.
To see the authentication interactions within nevisFIDO logs use: org.springframework.web.filter.CommonsRequestLoggingFilter = DEBUG.
With this setting you will see 4 entries for a single authentication. They refer to just 2 calls from nevisAuth to nevisFIDO (a request to obtain a challenge and a request to validate a response), but there are 2 entries relating to each request - “Before Request” and “After Request”. The “After Request” entries are of most interest as they show the actual payloads to be processed.
This a normal passkey autofill UI authentication trace:
Request from nevisAuth to generate a challenge:
[nevisfido.log] 2025-09-24T14:44:37.121Z level=DEBUG thread=qtp480605403-36 logger=k.web.filter.CommonsRequestLoggingFilter trace_id=7d31ab909ac86963951d08505899fcbd span_id=ca7f26debce84976 :: After Request: POST /nevisfido/fido2/attestation/options, headers=[Accept:"application/json", Content-Type:"application/json;charset=UTF-8", traceparent:"00-7d31ab909ac86963951d08505899fcbd-093240bed755ab22-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"fido:9443", Content-Length:"45", Connection:"keep-alive", User-Agent:"Apache-HttpClient/5.5 (Java/21.0.8)"], payload={"username":"","userVerification":"required"}]Note, that the userVerification is required and that the username is unknown at this point.
(Following user verification via passkey prompt) request from nevisAuth to validate the response (assertion result):
[nevisfido.log] 2025-09-24T14:44:46.329Z level=DEBUG thread=qtp480605403-39 logger=k.web.filter.CommonsRequestLoggingFilter trace_id=cb56662e3998e1b5a99de15e91dad899 span_id=747725adafa394bd :: After Request: POST /nevisfido/fido2/assertion/result, headers=[Accept:"application/json", Content-Type:"application/json;charset=UTF-8", traceparent:"00-cb56662e3998e1b5a99de15e91dad899-296b7bd079c188c9-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"fido:9443", Content-Length:"733", Connection:"keep-alive", User-Agent:"Apache-HttpClient/5.5 (Java/21.0.8)"], payload={"id":"4AGfH957Rj2Ec3b2g2-RBMa8CxWoavw4-vzewWieLgE","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiMHVYckg2N2dSUU9JTlRHZnd1OFJ3ZyIsIm9yaWdpbiI6Imh0dHBzOi8vbG9naW4tdGVzdC5zd2lzc2xvcy5nZXRuZXZpcy5uZXQiLCJjcm9zc09yaWdpbiI6ZmFsc2V9","authenticatorData":"TNxgEi4aybxSxZ_G2iyuIaRSSJMjeVo0CmECWV7USNgFAAAAJw","signature":"GUj_i0jU7pGIG-r0mG4AnAkSub26woPHRkxKpNWo1hWuOQdA0oJ7qK39Qfqiv35JOeVo5w7NR7mXanS7tJKzA3Y3qg8Lr0FvFhM8pnM8YPtyt5AFv8eMqM_53QoHz-lOyyodLlN6hhq2JvchMPcww1limcsTp6P0xjkQjtmXjXZc44DRYFGjCTWEQVAz5Y_dnyCMUzxNPDs6X0ETFjlWmpTio3BnS-H6pJTXVDIQPDoH_ePgV30D6GjvkgPRDqRAq63daesIGvBa0SKvdugkO6GnjOx8cuFbizFPfb5NRUhduopsizUkQ6QLUUJVTsNqqoLXFxosj-wu_uDqnS6oVg","userHandle":"MTAwMg"}}]
If you also use ch.nevis.auth.fido = DEBUG, then there are a few more entries indicating connection to nevisIDM where the last login information status is updated for the user and passkey credential:
[nevisfido.log] 2025-09-24T14:44:46.181Z level=DEBUG thread=qtp480605403-39 logger=fo.impl.IdmLoginInformationUpdateService trace_id=cb56662e3998e1b5a99de15e91dad899 span_id=747725adafa394bd :: Updating last login information status as 'successful' in nevisIDM for user with extId '1002' with credential extId '1002'
If the user cancels the passkey prompt, then there is no further traffic to nevisFIDO and its logs - so there would be no error to search for, just the ommission of the 2nd message: POST /nevisfido/fido2/assertion/result”
Debug a Passkey Registration Flow
As for authenticaiton, use the option: org.springframework.web.filter.CommonsRequestLoggingFilter = DEBUG.
Again, this will show 2 requests of interest, although the associated information is a little different:
Request from nevisAuth to generate a challenge:
[nevisfido.log] 2025-09-24T15:25:36.725Z level=DEBUG thread=qtp480605403-42 logger=k.web.filter.CommonsRequestLoggingFilter trace_id=7c0fefe79ce3d47c7211a3a52b45b3cd span_id=ae7e21ec1e73408c :: After Request: POST /nevisfido/fido2/attestation/options, headers=[Accept:"application/json", Content-Type:"application/json;charset=UTF-8", User-Agent:"nevisAuth-TokenAuthHttpClient", Authorization:"Basic ZmFsc2U6PHNlY1Rva2VuIHZlcnNpb249IkNTU08tMS4wIiBzaWduVGltZT0iMjAyNTA5MjQxNTI1MzZaIiB0dGw9IjE4MDAiPjxhdHRyPjx1c2VyaWQ+ZmFsc2U8L3VzZXJpZD48ZmllbGQgbmFtZT0icHJvZmlsZUlkIj4xMDA5PC9maWVsZD48ZW50cnlpZD5uZXZpc2F1dGhAYXV0aC02OWRkYmZkN2Y2LXZrYzZqPC9lbnRyeWlkPjxlc2F1dGhpZD5uZXZpc2F1dGhAYXV0aC02OWRkYmZkN2Y2LXZrYzZqPC9lc2F1dGhpZD48c2Vzc2lkPjE3NTg3Mjc1MzY0MDc8L3Nlc3NpZD48ZmllbGQgbmFtZT0icm9sZXMiPnVudXNlZDwvZmllbGQ+PC9hdHRyPjxzaWduYXR1cmUgYWxnPSJTSEEyNTZ3aXRoUlNBIiBmaW5nZXJQcmludD0iRUI6NDU6RjM6QkY6MEQ6ODY6NDI6MDU6QUM6MkY6RTI6MTk6RDU6MDg6MkI6MEYiPmNzYmZvSGc5bERLdDJhRXlod0FFUVFnUW4wNFR2QnlXUWUyVHhlQUVUNFpaVk42Yk1GQXdmdDJaU3d5ZU1pN090UTZVcy9JUFdEbTYydUowWDY1WTRYSVR4UXdsb1BKVEcrM2lLSm9mcTVUUDBhdVQzTUtLQkpxcjJVellpaVY1OGR6NnNNalZUODkrUGQ2ZHFDeGpZbUtaaGFiMDZkclpLamtRc01SaFdhWUVzVkc4dUx3ZTZEVHJKT2p6TDZuR21FdTRxTFJYbWRySDJXenpURlZ5aUVmd0xnY253VElpVVR1OVZvUjVHNEt3NXE3S29zR3VLdFFVd3ZrVWhsUkhIbFNkVmM5RW0vVExheitmRTJYaEFSWkxaeXEvQWdxc3BOVVpMTzZ4OTkvbTcxL1dnWnUxRFF6Q2ozT01ZQkxIaVk5NG1hcjRiZjhYTjFWemlXODh4UT09PC9zaWduYXR1cmU+PC9zZWNUb2tlbj4=", traceparent:"00-7c0fefe79ce3d47c7211a3a52b45b3cd-a541d674d39becb6-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"fido:9443", Content-Length:"215", Connection:"keep-alive"], payload={"username":"[email protected]","displayName":"[email protected]","authenticatorSelection":{"requireResidentKey":"true","residentKey":"required","userVerification":"required"},"attestation":"none"}]Note, that this request to obtain a challenge is a little different as in order to perform a passkey registration, the user must already be known. In particular:
- Like the authentication use case, the endpoint is “/nevisfido/fido2/attestation/options”
- The Authorization header includes a secToken (Base64-encoded)
- The payload indicates the username
- The payload indicates the displayName which will appear within the passkey prompt presented to the user.
(Following user verification via passkey prompt) request from nevisAuth to validate the response (attestation result):
[nevisfido.log] 2025-09-24T15:25:43.288Z level=DEBUG thread=qtp480605403-42 logger=k.web.filter.CommonsRequestLoggingFilter trace_id=61bdb7fa5a18efde46c139b68698478b span_id=0f8aefcbd6b7d79b :: After Request: POST /nevisfido/fido2/attestation/result, headers=[Accept:"application/json", Content-Type:"application/json;charset=UTF-8", User-Agent:"nevisAuth-TokenAuthHttpClient", Authorization:"Basic
ZmFsc2U6PHNlY1Rva2VuIHZlcnNpb249IkNTU08tMS4wIiBzaWduVGltZT0iMjAyNTA5MjQxNTI1MzZaIiB0dGw9IjE4MDAiPjxhdHRyPjx1c2VyaWQ+ZmFsc2U8L3VzZXJpZD48ZmllbGQgbmFtZT0icHJvZmlsZUlkIj4xMDA5PC9maWVsZD48ZW50cnlpZD5uZXZpc2F1dGhAYXV0aC02OWRkYmZkN2Y2LXZrYzZqPC9lbnRyeWlkPjxlc2F1dGhpZD5uZXZpc2F1dGhAYXV0aC02OWRkYmZkN2Y2LXZrYzZqPC9lc2F1dGhpZD48c2Vzc2lkPjE3NTg3Mjc1MzY0MDc8L3Nlc3NpZD48ZmllbGQgbmFtZT0icm9sZXMiPnVudXNlZDwvZmllbGQ+PC9hdHRyPjxzaWduYXR1cmUgYWxnPSJTSEEyNTZ3aXRoUlNBIiBmaW5nZXJQcmludD0iRUI6NDU6RjM6QkY6MEQ6ODY6NDI6MDU6QUM6MkY6RTI6MTk6RDU6MDg6MkI6MEYiPmNzYmZvSGc5bERLdDJhRXlod0FFUVFnUW4wNFR2QnlXUWUyVHhlQUVUNFpaVk42Yk1GQXdmdDJaU3d5ZU1pN090UTZVcy9JUFdEbTYydUowWDY1WTRYSVR4UXdsb1BKVEcrM2lLSm9mcTVUUDBhdVQzTUtLQkpxcjJVellpaVY1OGR6NnNNalZUODkrUGQ2ZHFDeGpZbUtaaGFiMDZkclpLamtRc01SaFdhWUVzVkc4dUx3ZTZEVHJKT2p6TDZuR21FdTRxTFJYbWRySDJXenpURlZ5aUVmd0xnY253VElpVVR1OVZvUjVHNEt3NXE3S29zR3VLdFFVd3ZrVWhsUkhIbFNkVmM5RW0vVExheitmRTJYaEFSWkxaeXEvQWdxc3BOVVpMTzZ4OTkvbTcxL1dnWnUxRFF6Q2ozT01ZQkxIaVk5NG1hcjRiZjhYTjFWemlXODh4UT09PC9zaWduYXR1cmU+PC9zZWNUb2tlbj4=", traceparent:"00-61bdb7fa5a18efde46c139b68698478b-c2c16eae5703201a-01", Accept-Encoding:"gzip, x-gzip, deflate", Host:"fido:9443", Content-Length:"963", Connection:"keep-alive"], payload={"id":"ugUrus1WzPLaEzRpRzLX1VTXCpB7qJq322wskXr7hFQ","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiandpY1F2RDRTUnF4a0VWNHVGNUhwUSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9naW4tdGVzdC5zd2lzc2xvcy5nZXRuZXZpcy5uZXQiLCJjcm9zc09yaWdpbiI6ZmFsc2V9","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVkBZ0zcYBIuGsm8UsWfxtosriGkUkiTI3laNAphAlle1EjYRQAAAAAAAAAAAAAAAAAAAAAAAAAAACC6BSu6zVbM8toTNGlHMtfVVNcKkHuomrfbbCyRevuEVKQBAwM5AQAgWQEA3NPW8JXJ5k1KY59TkZRpdsVDQU1r1bU5XqdJqkp-vzf5ERj3dtYHccSYhz1Tz_ySnDcpkkdAZdWmXFPxPz0aTMu7OuBij2g7bHXNAhNDFyurQYq7XQ9upXPwMOORKn6Gi_q5gFEY4cj5k8DUkPB3rATsvEplrSo_moIj4smQ5PGStSbK-N3YHaGyLuQ4gwOZ3x-anz3BnEUAyhqRsoigGXiZ4bSn_WfJV0PrceI8D-KKJlgoyFL4wW27DbxZhQnoI_adbWIpFFVc3hYv2G4_07NQ0NpJDjSSpAjG72ReFr7z5XJrdVOrCG5wTqVBGU31NmrHrQVQkG9DSV2lIrB2JSFDAQAB"},"userFriendlyName":"Firefox on Windows","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0"}]
- Unlike the authentication use case, the endpoint is “/nevisfido/fido2/attestation/result” instead of “/nevisfido/fido2/assertion/result”.
- There is an attestationObject which contains the public key that the client just generated and needs to be ultimately stored in nevisIDM - against the user object within a fido2 credential.
If you also use ch.nevis.auth.fido = DEBUG, then there are a few more entries indicating connections to nevisIDM where the user is looked up and the fido2 credential is stored.