示例#1
0
        /// <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);
        }
示例#2
0
        /// <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);
        }
示例#3
0
        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);
        }
示例#4
0
        /// <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);
        }