/// <summary> /// Called on error and represents the authform with a error message /// </summary> /// <param name="request">the http request object</param> /// <param name="ex">exception message</param> /// <returns>new instance of IAdapterPresentationForm derived class</returns> public IAdapterPresentation OnError(HttpListenerRequest request, ExternalAuthenticationException ex) { Log("OnError, ExternalAuthenticationException: " + ex.Message); var form = new AdapterPresentationForm(); form.ErrorMessage = ex.Message; return(form); }
/// <summary> /// Initiates a new authentication process and returns our form to the AD FS system. /// </summary> /// <param name="identityClaim">Claim information from the ADFS</param> /// <param name="request">The http request</param> /// <param name="authContext">The context for the authentication</param> /// <returns>new instance of IAdapterPresentationForm</returns> public IAdapterPresentation BeginAuthentication(Claim identityClaim, HttpListenerRequest request, IAuthenticationContext authContext) { Log("BeginAuthentication: identityClaim: " + identityClaim.Value); string username, domain, upn = ""; // separates the username from the domain string[] tmp = identityClaim.Value.Split('\\'); if (tmp.Length > 1) { username = tmp[1]; domain = tmp[0]; if (use_upn) { // get UPN from sAMAccountName Log("Getting UPN for user:"******" and domain: " + domain + "..."); PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain); UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username); upn = user.UserPrincipalName; Log("Found UPN: " + upn); } else { upn = "not used"; } } else { username = tmp[0]; upn = tmp[0]; domain = ""; } Log("UPN value: " + upn + ", Domain value: " + domain); // use upn or sam as loginname attribute if (use_upn) { username = upn; } // Prepare the form var form = new AdapterPresentationForm(); // trigger challenges with service account or empty pass if configured PIResponse response = null; if (privacyIDEA != null) { if (this.triggerChallenge) { response = privacyIDEA.TriggerChallenges(username, domain); } else if (this.sendEmptyPassword) { response = privacyIDEA.ValidateCheck(username, "", domain: domain); } } else { Error("privacyIDEA not initialized!"); } // Evaluate the response for triggered token and prepare the form accordingly if (response != null) { if (response.Challenges.Count > 0) { form = ExtractChallengeDataToForm(response, form, authContext); } else if (response.Value) { // Success in step 1, carry this over to the second step so that step will be skipped authContext.Data.Add("authSuccess", "1"); form.AutoSubmit = "1"; } else { if (!string.IsNullOrEmpty(response.ErrorMessage)) { Error("Error in first step: " + response.ErrorMessage); form.ErrorMessage = response.ErrorMessage; } else { Error("Sent something in first step and got failure without message"); } } } form.Mode = "otp"; authContext.Data.Add("userid", username); authContext.Data.Add("domain", domain); // Perform optional user enrollment // If a challenge was triggered previously, checking if the user has a token is skipped if (enrollmentEnabled && (response != null && string.IsNullOrEmpty(response.TransactionID) || (response == null)) && !privacyIDEA.UserHasToken(username, domain)) { PIEnrollResponse res = privacyIDEA.TokenInit(username, domain); if (enrollmentApps.Any()) { form.EnrollmentApps = enrollmentApps; } form.EnrollmentUrl = res.TotpUrl; form.EnrollmentImg = res.Base64TotpImage; } return(form); }
private AdapterPresentationForm ExtractChallengeDataToForm(PIResponse response, AdapterPresentationForm form, IAuthenticationContext authContext) { // explicitly overwrite here. If another challenge was triggered, it will have a different transaction_id. authContext.Data["transactionid"] = response.TransactionID; form.Message = response.Message; if (response.TriggeredTokenTypes().Contains("push")) { form.PushAvailable = "1"; form.PushMessage = response.PushMessage(); } if (response.TriggeredTokenTypes().Contains("webauthn")) { string webAuthnSignRequest = response.WebAuthnSignRequest(); form.WebAuthnSignRequest = webAuthnSignRequest; } return(form); }
/// <summary> /// Called when our form is submitted. /// </summary> /// <param name="authContext"></param> /// <param name="proofData"></param> /// <param name="request"></param> /// <param name="outgoingClaims"></param> /// <returns></returns> public IAdapterPresentation TryEndAuthentication(IAuthenticationContext authContext, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { Log("TryEndAuthentication"); // Early exit if step 2 can be skipped if (authContext != null) { if ((string)GetFromDict(authContext.Data, "authSuccess", "") == "1") { outgoingClaims = Claims(); return(null); } } outgoingClaims = new Claim[0]; if (proofData == null || proofData.Properties == null) { throw new ExternalAuthenticationException("Error - ProofData is empty", authContext); } if (this.privacyIDEA == null) { Error("PrivacyIDEA is not initialized!"); throw new ExternalAuthenticationException("PrivacyIDEA is not initialized!", authContext); } Dictionary <string, object> contextDict = authContext.Data; Dictionary <string, object> proofDict = proofData.Properties; Log("ProofData: " + string.Join(", ", proofData.Properties)); Log("AuthContext: " + string.Join(", ", authContext.Data)); // Prepare form to return, fill with values from proofData var form = new AdapterPresentationForm(); string otp = (string)GetFromDict(proofDict, "otp"); string mode = (string)GetFromDict(proofDict, "mode"); string modeChanged = (string)GetFromDict(proofDict, "modeChanged"); string pushAvailable = (string)GetFromDict(proofDict, "pushAvailable"); string message = (string)GetFromDict(proofDict, "message"); string webAuthnSignRequest = (string)GetFromDict(proofDict, "webAuthnSignRequest"); string domain = (string)GetFromDict(proofDict, "domain"); string strAuthCounter = (string)GetFromDict(proofDict, "authCounter", "0"); if (!string.IsNullOrEmpty(strAuthCounter)) { form.AuthCounter = (int.Parse(strAuthCounter) + 1).ToString(); } form.Message = message; form.Mode = mode; form.PushAvailable = pushAvailable; if (!string.IsNullOrEmpty(webAuthnSignRequest)) { form.WebAuthnSignRequest = webAuthnSignRequest; } string transactionid = (string)GetFromDict(contextDict, "transactionid"); string user = (string)GetFromDict(contextDict, "userid"); if (modeChanged == "1") { return(form); } // Do the authentication according to the mode we are in PIResponse response = null; if (mode == "push") { if (privacyIDEA.PollTransaction(transactionid)) { // Push confirmed, finish the authentication via /validate/check using an empty otp // https://privacyidea.readthedocs.io/en/latest/tokens/authentication_modes.html#outofband-mode response = privacyIDEA.ValidateCheck(user, "", transactionid, domain); } else { // Else push not confirmed yet form.ErrorMessage = "Authenication not confirmed yet!"; } } else if (mode == "webauthn") { string origin = (string)GetFromDict(proofDict, "origin"); string webauthnresponse = (string)GetFromDict(proofDict, "webAuthnSignResponse"); if (string.IsNullOrEmpty(origin) || string.IsNullOrEmpty(webauthnresponse)) { Error("Incomplete data for WebAuthn authentication: WebAuthnSignResponse or Origin is missing!"); form.ErrorMessage = "Could not complete WebAuthn authentication. Try again or use another token type."; } else { response = privacyIDEA.ValidateCheckWebAuthn(user, transactionid, webauthnresponse, origin, domain); } } else { // Mode == OTP response = privacyIDEA.ValidateCheck(user, otp, transactionid, domain); } // If we get this far, the login data provided was wrong, an error occured or another challenge was triggered. bool newChallenge = false; if (response != null) { if (response.Challenges.Count > 0) { newChallenge = true; form = ExtractChallengeDataToForm(response, form, authContext); } else if (response.Value) { outgoingClaims = Claims(); return(null); } else { Log("Response value was false!"); // Set the error message from the response or a default form.ErrorMessage = (!string.IsNullOrEmpty(response.ErrorMessage)) ? response.ErrorMessage + " (" + response.ErrorCode + ")" : "Wrong OTP value!"; } } else { // In case of unconfirmed push response will be null too. Therefore, set the message only if there is none yet. if (string.IsNullOrEmpty(form.ErrorMessage)) { form.ErrorMessage = "The authentication server could not be reached."; Error("Reponse from server was null!"); } } // Set a generic error if none was set yet and no new challenge was triggered if (string.IsNullOrEmpty(form.ErrorMessage) && !newChallenge) { form.ErrorMessage = "An error occured."; } // Return form with error or new challenge return(form); }