### Generate QR Code for User with multiOTP Source: https://github.com/multiotp/multiotpcredentialprovider/wiki/Home Generates a QR code for a specified user, typically used for initial multiOTP setup or enrollment. This command is part of the local-only strong authentication setup. ```bash multiotp -qrcode my_user my_qrcode.png ``` -------------------------------- ### Create New Local User with multiOTP Source: https://github.com/multiotp/multiotpcredentialprovider/wiki/Home Use this command to create a new local user and associate it with multiOTP authentication. This is part of the local-only strong authentication setup. ```bash multiotp -fastcreatenopin my_user ``` -------------------------------- ### Silent MSI Installation of MultiOTP Credential Provider Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Use these commands for silent deployment via Group Policy or SCCM. Ensure the Visual C++ Redistributable is installed first. Properties can be passed inline or via a transform file. ```bat REM ── Group Policy / SCCM silent deployment example ── REM Deploy the Visual C++ Redistributable first (required) msiexec /i vc_redist.x64.exe /quiet /norestart REM Install multiOTP Credential Provider with a transform file msiexec /i "multiOTPCredentialProvider.msi" /qn ^ TRANSFORMS="multiOTPCredentialProvider.mst" REM ── Or pass all properties inline without a transform ── msiexec /i "multiOTPCredentialProvider.msi" /qn ^ MULTIOTP_URL="https://auth.contoso.com" ^ MULTIOTP_SECRET="MySharedSecret2024" ^ MULTIOTP_TIMEOUT=5 ^ MULTIOTP_TIMEOUTCP=60 ^ MULTIOTP_CACHE=1 ^ MULTIOTP_CPUSLOGON="0e" ^ MULTIOTP_CPUSUNLOCK="0e" ^ MULTIOTP_CPUSCREDUI="0d" ^ MULTIOTP_TWO_STEP_HIDE_OTP=1 ^ MULTIOTP_TWO_STEP_SEND_PASSWORD=0 ^ MULTIOTP_UPNFORMAT=0 ^ MULTIOTP_EXCLUDED_ACCOUNT="CONTOSO\svc_backup" ^ MULTIOTP_TIMEOUTUNLOCK=30 ^ MULTIOTP_WITHOUT2FA=1 ^ MULTIOTP_DISPLAYSMSLINK=0 ^ MULTIOTP_DISPLAYEMAILLINK=0 ^ MULTIOTP_DISPLAYLASTUSER=1 ^ MULTIOTP_DISPLAYUSERLOCKED=1 ^ MULTIOTP_LOGINTEXT="Please enter your OTP" ^ MULTIOTP_OTP_TEXT="One-Time Password" ^ MULTIOTP_OTP_FAIL_TEXT="Authentication failed: %s" ^ MULTIOTP_BITMAP_PATH="C:\\Corp\login_logo.bmp" ``` ```bat REM Force immediate GPO application gpupdate /force /boot ``` -------------------------------- ### QR Code for User Token Provisioning Source: https://github.com/multiotp/multiotpcredentialprovider/blob/master/stable/templates/template.html Scan this QR code to provision your personal token generator into the multiOTP application on your smartphone. This is the final step for token setup. ```html {MultiotpQrCodeUserToken account=MultiotpUserDescription width=250 height=250} ``` ```html {MultiotpQrCodeUserToken account=MultiotpUserDescription width=500 height=500} ``` -------------------------------- ### HTTP(S) Communication with multiOTP Server Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Use the Endpoint class to send POST or GET requests to the multiOTP server. It handles URL encoding, JSON parsing for authentication, challenges, and errors. TLS peer verification is configurable. ```cpp #include "Endpoint.h" #include "PIConf.h" #include "SecureString.h" PICONFIG conf; conf.hostname = L"auth.example.com"; conf.customPort = 443; conf.ignoreInvalidCN = false; conf.ignoreUnknownCA = false; Endpoint ep(conf); // Build a /validate/check POST body SecureString body; body += ep.encodePair("user", "jsmith"); body += "&"; body += ep.encodePair("pass", SecureString("Password1")); body += "&"; body += ep.encodePair("transaction_id", ""); // Send the request and retrieve raw JSON std::string jsonResponse = ep.connect("/validate/check", body, RequestMethod::POST); // Parse for authentication success/failure HRESULT authResult = ep.parseAuthenticationRequest(jsonResponse); // Returns PI_AUTH_SUCCESS, PI_AUTH_FAILURE, PI_TRIGGERED_CHALLENGE, etc. // Parse for a triggered challenge (e.g., push token) Challenge c; HRESULT challengeResult = ep.parseTriggerRequest(jsonResponse, c); if (challengeResult == EP_TRIGGERED_CHALLENGE) { wprintf(L"Challenge: %s | TxID: %s\n", c.message.c_str(), c.transaction_id.c_str()); // c.tta == TTA::PUSH or TTA::OTP or TTA::BOTH } // Check for application-level error in the JSON std::string errMsg; int errCode = 0; HRESULT parseErr = ep.parseForError(jsonResponse, errMsg, errCode); if (parseErr == PI_JSON_ERROR_CONTAINED) { wprintf("Server error %d: %s\n", errCode, errMsg.c_str()); } ``` -------------------------------- ### QR Code for multiOTP Token App Source: https://github.com/multiotp/multiotpcredentialprovider/blob/master/stable/templates/template.html Scan this QR code to install the multiOTP token app or other compatible authenticator applications like FreeOTP, Authenticator, or 2 Steps Authenticator. ```html {MultiotpQrCodeUrl url=http://go.6co.ch/motp width=200 height=200} ``` -------------------------------- ### Start Push Approval Wait with PrivacyIDEA::asyncPollTransaction Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Initiates a background thread to poll for push authentication approval. The provided callback is invoked on a worker thread upon completion. Ensure `PrivacyIDEA` and necessary headers are included. ```cpp #include "PrivacyIDEA.h" #include "Codes.h" #include // After validateCheck returns PI_TRIGGERED_CHALLENGE: void StartPushApprovalWait(PrivacyIDEA& pi, const std::string& username, const std::string& transactionId, ICredentialProviderCredentialEvents* pEvents) { // Callback is invoked on a worker thread when polling completes. pi.asyncPollTransaction( username, transactionId, [pEvents](bool approved) { if (approved) { // Notify the credential tile to auto-submit // pEvents->SetFieldString(self, STATUS_FIELD, L"Approved!"); wprintf(L"Push approved — proceeding with logon\n"); } else { wprintf(L"Push denied or timed out\n"); } } ); // To cancel (e.g. user clicks Cancel on the logon screen): // pi.stopPoll(); } ``` -------------------------------- ### QR Code for OTP Authentication App Source: https://github.com/multiotp/multiotpcredentialprovider/blob/master/stable/templates/template.html Scan this QR code to install a compatible OTP generator application on your smartphone. It is valid for Android, iOS, Windows Phone, and BlackBerry. ```html {MultiotpQrCodeUrl url=http://go.6co.ch/otpauth width=200 height=200} ``` -------------------------------- ### Manual Token Provisioning Details Source: https://github.com/multiotp/multiotpcredentialprovider/blob/master/stable/templates/template.html Provides the necessary details to manually configure your token in an OTP application if QR code provisioning is not supported. Includes algorithm, secret seed, number of digits, and time interval. ```text algorithm: {MultiotpUserAlgorithm} secret seed: {MultiotpUserTokenSeed} (or {MultiotpUserTokenSeedBase32} in Base32, like for Google Authenticator) number of digits: {MultiotpUserTokenNumberOfDigits} time interval: {MultiotpUserTokenTimeInterval} next event: {MultiotpUserTokenNextEvent} ``` -------------------------------- ### Configure multiOTP Server Connection Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Build a PICONFIG struct to target a multiOTP server over HTTPS. Ensure correct TLS validation and configure realm mapping for Windows domains to multiOTP realms. This configuration is used to construct the MultiOTP authentication engine. ```cpp #include "PIConf.h" #include "MultiOTP.h" // Build a configuration targeting a multiOTP server over HTTPS PICONFIG conf; conf.hostname = L"192.168.1.88"; conf.path = L""; // empty = use server root conf.customPort = 443; conf.ignoreInvalidCN = false; // enforce valid TLS common name conf.ignoreUnknownCA = false; // enforce trusted CA conf.defaultRealm = L""; conf.logPasswords = false; // never log credentials in production conf.offlineFilePath = L"C:\\multiotp\\offlineFile.json"; conf.offlineTryWindow = 10; // check ±10 HOTP steps for offline OTP // Optional WinHTTP timeouts (milliseconds, 0 = system default) conf.resolveTimeoutMS = 0; conf.connectTimeoutMS = 5000; conf.sendTimeoutMS = 5000; conf.receiveTimeoutMS = 5000; // Realm mapping: Windows domain → multiOTP realm conf.realmMap[L"CONTOSO"] = L"contoso_realm"; conf.realmMap[L"CORP"] = L"corp_realm"; // Construct the MultiOTP authentication engine MultiOTP mfa(conf); ``` -------------------------------- ### Enable and Use Logger for Debugging Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Configure the Logger singleton to write diagnostic output. Use DebugPrint for messages that should only appear in debug builds or when release logging is enabled. Use ReleaseDebugPrint for messages always written in release builds when release logging is enabled. Log typed values and sensitive strings using the overloaded log method. ```cpp #include "Logger.h" // Enable release-mode logging (set from MultiOTPConfiguration::releaseLog registry flag) Logger::Get().releaseLog = true; // Debug-only log (compiled out or suppressed in release unless releaseLog is set) DebugPrint("Starting OTP validation"); // char* DebugPrint(L"Username: "); // wchar_t* // Always written in release builds when releaseLog == true ReleaseDebugPrint("Authentication result received"); // Log typed values directly int errorCode = 24; Logger::Get().log(errorCode, __FILENAME__, __LINE__, true); // int Logger::Get().log(std::wstring(L"CONTOSO\\jsmith"), __FILENAME__, __LINE__, false); // Sensitive strings — use SecureString to avoid plaintext in memory SecureString otpValue("123456"); Logger::Get().log(otpValue, __FILENAME__, __LINE__, false); // SecureString overload // Log files: // Debug → C:\PICredentialProviderDebugLog.txt // Release → C:\PICredentialProviderLog.txt ``` -------------------------------- ### Writing multiOTP Registry Settings Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Use MultiotpRegistry helpers to write string and integer values to the multiOTP registry. This includes persisting domain names, caching user authentication data, and storing UPN mappings. ```cpp #include "MultiotpRegistry.h" // --- WRITE EXAMPLES --- // Persist the flat domain name for offline fallback (called after DsGetDcNameW succeeds) writeRegistryValueString(CONF_FLAT_DOMAIN, L"CONTOSO"); // Cache the last authenticated username and timestamp writeRegistryValueString(LAST_USER_AUTHENTICATED, L"jsmith"); writeRegistryValueInteger(LAST_USER_TIMESTAMP, minutesSinceEpoch()); // Store a UPN↔Legacy name mapping for offline domain resolution writeKeyValueInMultiOTPRegistry( HKEY_CLASSES_ROOT, L"UPNcache", // sub-key of the CLSID key L"jsmith@contoso.com", // value name = UPN L"CONTOSO\\jsmith" // data = legacy SAM name ); ``` -------------------------------- ### Initialize and Use OfflineHandler for Offline OTP Verification Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Manages offline OTP verification using a local JSON file storing PBKDF2-SHA512 hashed OTP chains. Requires `OfflineHandler.h` and `SecureString.h`. The `tryWindow` parameter determines the range of hashes to check. ```cpp #include "OfflineHandler.h" #include "SecureString.h" // 1. Construct handler pointing at the offline data file OfflineHandler offlineHandler(L"C:\\multiotp\\offlineFile.json", /*tryWindow=*/10); // 2. Check whether cached data exists for a user before attempting network auth HRESULT avail = offlineHandler.isDataVailable("jsmith"); if (avail == S_OK) { wprintf(L"Offline data available for jsmith\n"); } // 3. Verify an OTP entirely offline (no network) SecureWString otp(L"123456"); HRESULT result = offlineHandler.verifyOfflineOTP(otp, "jsmith"); if (result == PI_OFFLINE_OTP_SUCCESS) { wprintf(L"Offline OTP verified!\n"); } else if (result == PI_OFFLINE_WRONG_OTP) { wprintf(L"Wrong OTP\n"); } else if (result == PI_OFFLINE_DATA_NO_OTPS_LEFT) { wprintf(L"OTP cache exhausted — must go online to refill\n"); } // 4. After a successful online auth the server response may contain new offline data std::string serverJsonResponse = R"({ "result": {"status": true, "value": true}, "auth_items": { "offline": [{ "username": "jsmith", "refilltoken": "abc123", "response": {"serial": "TOTP1234", "otplen": 6, "hashes": {"0":"","1":""}} }] } })"; offlineHandler.parseForOfflineData(serverJsonResponse); // 5. Refill the cache when running low, using the refilltoken returned above std::string refillToken, serial; offlineHandler.getRefillTokenAndSerial("jsmith", refillToken, serial); // → send refillToken + serial to /validate/offlinerefill, then: offlineHandler.parseRefillResponse(serverRefillJson, "jsmith"); ``` -------------------------------- ### Reading multiOTP Registry Settings Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Use MultiotpRegistry helpers to read string and integer values from the multiOTP registry configuration. Default fallbacks are provided for string reads. ```cpp #include "MultiotpRegistry.h" // --- READ EXAMPLES --- // Read the path to multiotp.exe (string with default fallback) PWSTR multiotpPath = nullptr; DWORD pathLen = readRegistryValueString(CONF_PATH, &multiotpPath, L"C:\\multiotp\\"); // multiotpPath → e.g. L"C:\\Program Files\\multiOTP\\" // Read the server URL list PWSTR servers = nullptr; readRegistryValueString(CONF_SERVERS, &servers, L"https://192.168.1.88"); // Read the shared secret PWSTR secret = nullptr; readRegistryValueString(CONF_SHARED_SECRET, &secret, L"ClientServerSecret"); // Read integer values DWORD timeout = readRegistryValueInteger(CONF_TIMEOUT, 60); // seconds DWORD serverTimeout = readRegistryValueInteger(CONF_SERVER_TIMEOUT, 5); // seconds DWORD cacheEnabled = readRegistryValueInteger(CONF_CACHE_ENABLED, 1); // 0|1 DWORD upnFormat = readRegistryValueInteger(CONF_UPN_FORMAT, 0); // 0|1 ``` -------------------------------- ### Authenticate User with MultiOTP::validateCheck Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Handles user authentication using OTP. It attempts offline verification first, then invokes `multiotp.exe` for online verification. Supports Windows SID identification and detailed error propagation. Use when a user submits username, password, and OTP. ```cpp #include "MultiOTP.h" #include "Codes.h" #include "SecureString.h" // Called inside ICredentialProviderCredential::GetSerialization after // the user has submitted username + password + OTP fields. HRESULT AuthenticateUser( MultiOTP& mfa, const std::wstring& username, // e.g. L"jsmith" const std::wstring& domain, // e.g. L"CONTOSO" const SecureWString& otp, // e.g. L"123456" const std::wstring& userSid // e.g. L"S-1-5-21-..." ) { HRESULT errorCode = S_OK; std::string transactionId = ""; // empty for standard OTP; set for challenge-response HRESULT hr = mfa.validateCheck(username, domain, otp, transactionId, errorCode, userSid); switch (hr) { case PI_AUTH_SUCCESS: // OTP verified — proceed to pack Kerberos credential buffer wprintf(L"Authentication SUCCESS for %s\%s\n", domain.c_str(), username.c_str()); return S_OK; case PI_AUTH_FAILURE: // Wrong OTP or wrong password wprintf(L"Authentication FAILED (code %d)\n", errorCode); return E_ACCESSDENIED; case PI_TRIGGERED_CHALLENGE: // Server triggered a challenge (e.g. push notification sent) wprintf(L"Challenge triggered — waiting for out-of-band approval\n"); return S_FALSE; case PI_ENDPOINT_SETUP_ERROR: case PI_ENDPOINT_SERVER_UNAVAILABLE: // Network error — offline fallback may have been attempted wprintf(L"Server unreachable, offline result: %d\n", errorCode); return E_FAIL; default: return hr; } } ``` -------------------------------- ### Adapt UI Based on User Token Type with MultiOTP::userTokenType Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Determines a user's token type by invoking `multiotp.exe -iswithout2fa`. The result dictates UI behavior, such as skipping OTP fields for push tokens or bypassing 2FA entirely. Use this to customize the user experience based on their authentication method. ```cpp #include "MultiOTP.h" #include "MultiotpHelpers.h" // MULTIOTP_IS_PUSH_TOKEN, MULTIOTP_IS_WITHOUT2FA, etc. void AdaptUIForUser(MultiOTP& mfa, const std::wstring& username, const std::wstring& domain, const std::wstring& userSid) { HRESULT tokenType = mfa.userTokenType(username, domain, userSid); // Exit codes returned by multiotp.exe -iswithout2fa: // 6 = MULTIOTP_IS_PUSH_TOKEN → show "Approve on phone" UI // 7 = MULTIOTP_IS_WITH_TOKEN → show OTP input field // 8 = MULTIOTP_IS_WITHOUT2FA → skip OTP entirely // 24 = MULTIOTP_IS_LOCKED → show "account locked" message // 25 = MULTIOTP_IS_DELAYED → show "too many tries, wait" message // 21 = user not found → treat as normal OTP flow // 99 = unknown error if (tokenType == MULTIOTP_IS_PUSH_TOKEN) { wprintf(L"Push token detected — hiding OTP field, polling for approval\n"); // hideCPField(self, pCPCE, OTP_FIELD_ID); } else if (tokenType == MULTIOTP_IS_WITHOUT2FA) { wprintf(L"User %s is exempt from 2FA\n", username.c_str()); // Skip second-factor screen entirely } else if (tokenType == MULTIOTP_IS_LOCKED) { wprintf(L"User %s is LOCKED\n", username.c_str()); } else if (tokenType == MULTIOTP_IS_DELAYED) { wprintf(L"User %s is DELAYED — too many failed attempts\n", username.c_str()); } else { wprintf(L"Standard OTP token — showing OTP field\n"); } } ``` -------------------------------- ### Serialize and Submit Credential for LSA Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt This function serializes a user's Windows password into a KERB_INTERACTIVE_UNLOCK_LOGON structure and submits it to LSA after successful OTP validation. It involves initializing the structure, packing it into a byte buffer, and retrieving the Negotiate auth package. ```cpp #include "MultiotpHelpers.h" #include #include // Called from CCredential::GetSerialization after OTP is verified: HRESULT SerializeAndSubmitCredential( PWSTR domain, PWSTR username, PWSTR password, CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, // CPUS_LOGON, CPUS_UNLOCK_WORKSTATION, CPUS_CREDUI CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs ) { KERB_INTERACTIVE_UNLOCK_LOGON kiul; // Step 1: initialize structure with weak string references HRESULT hr = MultiOTPKerbInteractiveUnlockLogonInit( domain, username, password, cpus, &kiul); if (FAILED(hr)) return hr; // Step 2: pack into flat buffer (offsets replace pointers) BYTE* prgb = nullptr; DWORD cb = 0; hr = MultiOTPKerbInteractiveUnlockLogonPack(kiul, &prgb, &cb); if (FAILED(hr)) return hr; // Step 3: retrieve the Negotiate auth package from LSA ULONG authPackage = 0; hr = MultiOTPRetrieveNegotiateAuthPackage(&authPackage); if (FAILED(hr)) { CoTaskMemFree(prgb); return hr; } // Step 4: fill the serialization output pcpcs->clsidCredentialProvider = CLSID_CMultiOTPCredentialProvider; pcpcs->rgbSerialization = prgb; pcpcs->cbSerialization = cb; pcpcs->ulAuthenticationPackage = authPackage; *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED; return S_OK; } ``` -------------------------------- ### Username Normalization with getCleanUsername Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt This C++ function normalizes various username formats for use with multiotp.exe. It queries Active Directory, caches results, and falls back to cached values when the domain controller is unreachable. ```cpp #include "MultiotpHelpers.h" // Case 1: local account on a workgroup machine // username = L"alice", domain = L"WORKSTATION01" std::wstring clean1 = getCleanUsername(L"alice", L"WORKSTATION01"); // → L"alice" (no domain in registry, no UPN mode) // Case 2: domain account, legacy mode (default) // CONF_DOMAIN_NAME registry = L"contoso.com", CONF_UPN_FORMAT = 0 // DsGetDcNameW resolves flat domain = "CONTOSO" std::wstring clean2 = getCleanUsername(L"jsmith", L"CONTOSO"); // → L"jsmith" (legacy SAM account name sent to multiotp) // Case 3: domain account, UPN mode (CONF_UPN_FORMAT = 1) // TranslateNameW(NameSamCompatible → NameUserPrincipal) → jsmith@contoso.com std::wstring clean3 = getCleanUsername(L"jsmith", L"CONTOSO"); // → L"jsmith@contoso.com" // Case 4: user typed UPN on login screen, legacy mode // TranslateNameW(NameUserPrincipal → NameSamCompatible) → CONTOSO\jsmith → strip domain std::wstring clean4 = getCleanUsername(L"jsmith@contoso.com", L""); // → L"jsmith" // Case 5: offline, DC unreachable — falls back to UPNcache / LEGACYcache registry entries // writeKeyValueInMultiOTPRegistry(HKRC, L"LEGACYcache", L"jsmith@contoso.com", L"CONTOSO\jsmith") std::wstring clean5 = getCleanUsername(L"jsmith@contoso.com", L""); // → L"jsmith" (read from LEGACYcache when DC is down) ``` -------------------------------- ### Translate Exit Code to Error Message Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Demonstrates how to use the `getErrorMessage` helper function to convert a multiOTP exit code into a localized display string. This function reads the `otp_fail_text` registry value and substitutes the error description into the template. ```cpp #include "Codes.h" #include "MultiotpHelpers.h" // Core authentication return codes (from PrivacyIDEA / MultiOTP layer) // PI_AUTH_SUCCESS 0x88809010 — OTP verified // PI_AUTH_FAILURE 0x88809011 — Wrong OTP / bad credentials // PI_AUTH_ERROR 0x88809012 — Server-side error (see getLastError()) // PI_TRIGGERED_CHALLENGE 0x88809013 — Challenge sent (push / SMS) // PI_WRONG_OFFLINE_SERVER_UNAVAILABLE 0x88809014 — Offline OTP wrong & server down // PI_ENDPOINT_SETUP_ERROR 0x88809015 — WinHTTP initialization failed // Offline-specific codes // PI_OFFLINE_OTP_SUCCESS 0x78809006 // PI_OFFLINE_OTP_FAILURE 0x78809007 // PI_OFFLINE_DATA_NO_OTPS_LEFT 0x88809020 // PI_OFFLINE_DATA_USER_NOT_FOUND 0x88809021 // PI_OFFLINE_NO_OFFLINE_DATA 0x88809022 // PI_OFFLINE_WRONG_OTP 0x88809025 // multiOTP exit code → HRESULT (returned from multiotp.exe via GetExitCodeProcess) // MULTIOTP_SUCCESS 0 — Authenticated // MULTIOTP_IS_PUSH_TOKEN 6 — User has push token // MULTIOTP_IS_WITH_TOKEN 7 — User has standard OTP token // MULTIOTP_IS_WITHOUT2FA 8 — User exempt from 2FA // MULTIOTP_IS_LOCKED 24 — Account locked // MULTIOTP_IS_DELAYED 25 — Temporarily locked (back-off) // MULTIOTP_UNKNOWN_ERROR 99 — Catch-all error // Translate an exit code to a localized display string // (reads the otp_fail_text registry value and substitutes %s) HRESULT exitCode = 24; std::wstring msg = getErrorMessage(exitCode); // → e.g. "Authentication failed: User locked (too many tries)" // (if otp_fail_text = L"Authentication failed: %s") ``` -------------------------------- ### MultiOTP::userTokenType Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Queries the type of token associated with a user. This is achieved by executing `multiotp.exe -iswithout2fa "" -cp` and interpreting the exit code. The result determines how the logon UI behaves, such as skipping OTP fields for push token users or bypassing 2FA entirely for users flagged `without2FA`. ```APIDOC ## MultiOTP::userTokenType ### Description Queries the type of token associated with a user. This is achieved by executing `multiotp.exe -iswithout2fa "" -cp` and interpreting the exit code. The result determines how the logon UI behaves, such as skipping OTP fields for push token users or bypassing 2FA entirely for users flagged `without2FA`. ### Method `MultiOTP::userTokenType(const std::wstring& username, const std::wstring& domain, const std::wstring& userSid)` ### Parameters - **username** (std::wstring) - The username to query. - **domain** (std::wstring) - The domain of the user. - **userSid** (std::wstring) - The Windows Security Identifier (SID) of the user. ### Return Values - **MULTIOTP_IS_PUSH_TOKEN (6)**: User has a push token. - **MULTIOTP_IS_WITH_TOKEN (7)**: User has a standard OTP token. - **MULTIOTP_IS_WITHOUT2FA (8)**: User is exempt from 2FA. - **MULTIOTP_IS_LOCKED (24)**: User account is locked. - **MULTIOTP_IS_DELAYED (25)**: User account has too many failed attempts and is delayed. - **User not found (21)**: User was not found, treated as normal OTP flow. - **Unknown error (99)**: An unknown error occurred. ``` -------------------------------- ### MultiOTP::validateCheck Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Authenticates a user by verifying their One-Time Password (OTP). It supports both offline verification using a local cache and online verification by invoking `multiotp.exe`. This function extends the base `PrivacyIDEA::validateCheck` to include Windows SID identification and detailed error propagation. ```APIDOC ## MultiOTP::validateCheck ### Description Authenticates a user by verifying their One-Time Password (OTP). It supports both offline verification using a local cache and online verification by invoking `multiotp.exe`. This function extends the base `PrivacyIDEA::validateCheck` to include Windows SID identification and detailed error propagation. ### Method `MultiOTP::validateCheck(const std::wstring& username, const std::wstring& domain, const SecureWString& otp, std::string& transactionId, HRESULT& errorCode, const std::wstring& userSid)` ### Parameters - **username** (std::wstring) - The username for authentication. - **domain** (std::wstring) - The domain of the user. - **otp** (SecureWString) - The One-Time Password provided by the user. - **transactionId** (std::string&) - Output parameter for transaction ID, used for challenge-response. - **errorCode** (HRESULT&) - Output parameter for detailed error codes. - **userSid** (std::wstring) - The Windows Security Identifier (SID) of the user. ### Return Values - **PI_AUTH_SUCCESS**: OTP verified successfully. - **PI_AUTH_FAILURE**: OTP or password verification failed. - **PI_TRIGGERED_CHALLENGE**: A challenge was triggered (e.g., push notification). - **PI_ENDPOINT_SETUP_ERROR**: Error during endpoint setup. - **PI_ENDPOINT_SERVER_UNAVAILABLE**: Server is unavailable. - Other `HRESULT` values for different error conditions. ``` -------------------------------- ### Poll Once with PrivacyIDEA::pollTransaction Source: https://context7.com/multiotp/multiotpcredentialprovider/llms.txt Performs a single synchronous poll for a transaction. Useful when the caller manages the polling loop. Returns `PI_TRANSACTION_SUCCESS` or `PI_TRANSACTION_FAILURE`. ```cpp // Single synchronous poll (useful if the caller manages the loop): HRESULT PollOnce(PrivacyIDEA& pi, const std::string& txId) { HRESULT hr = pi.pollTransaction(txId); // Returns PI_TRANSACTION_SUCCESS or PI_TRANSACTION_FAILURE return hr; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.