/// <summary> /// Check the OTP an does the real authentication /// </summary> /// <param name="proofData">the date from the HTML fild</param> /// <param name="authContext">The autch context which contains secrued parametes.</param> /// <returns>True if auth is done and user can be validated</returns> bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("otpvalue")) { throw new ExternalAuthenticationException("Error - no answer found", authContext); } if (!ssl) { ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; } try { string otpvalue = (string)proofData.Properties["otpvalue"]; // fix for #14 and #15 string session_user = (string)authContext.Data["userid"]; string session_realm = (string)authContext.Data["realm"]; string transaction_id = (string)authContext.Data["transaction_id"]; // end fix #if DEBUG Debug.WriteLine(debugPrefix + "OTP Code: " + otpvalue + " User: "******" Server: " + session_realm + " Transaction_id: " + transaction_id); #endif return(otp_prov.getAuthOTP(session_user, otpvalue, session_realm, transaction_id)); } catch { throw new ExternalAuthenticationException("Error - can't validate the otp value", authContext); } }
private string ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("captcha")) { throw new ExternalAuthenticationException("请输入短信验证码!", authContext); } var code = (string)proofData.Properties["captcha"]; return(CommonHelper.Request("get", apiinfo.API_CheckVerifyCode + "?phone=" + phone + "&code=" + code)); }
/// <summary> /// Deserializes the SAML response data. /// </summary> /// <param name="proofData">The AD FS proof data.</param> /// <param name="context">The context.</param> /// <returns>A Second Factor Authentication Response.</returns> public static SecondFactorAuthResponse Deserialize(IProofData proofData, IAuthenticationContext context) { if (!proofData.Properties.ContainsKey("_SAMLResponse")) { throw new ArgumentException("Missing '_SAMLResponse' POST parameter"); } var response = new SecondFactorAuthResponse(new Saml2Id($"_{context.ContextId}"), proofData.Properties["_SAMLResponse"].ToString()); return(response); }
public DummyAdapterPresentation(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] claims) { this.context = context; DummyLogger.Log(string.Format("ActivityId: {0}, ContextId: {1}, Lcid: {2}", context.ActivityId, context.ContextId, context.Lcid)); this.proofData = proofData; DummyLogger.Log(string.Format("ProofData: {0}", proofData.Properties.Count)); this.request = request; DummyLogger.Log(string.Format("ServiceName: {0}, Uri: {1}, UserHostName: {2}, UserHostAddress: {3}", request.ServiceName, request.Url.AbsoluteUri, request.UserHostName, request.UserHostAddress)); this.claims = new Claim[1]; this.claims[0] = new Claim("dummyType", "dummyValue"); claims = this.claims; DummyLogger.Log(string.Format("Claims: {0}", claims.Length)); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = null; IAdapterPresentation result = null; string pin = proofData.Properties["pin"].ToString(); if (pin == "98765") { System.Security.Claims.Claim claim = new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new System.Security.Claims.Claim[] { claim }; } else { result = new SimplePINAdapterPresentation("Authentication failed.", false); } return result; }
private string _str(IProofData o) { if (o == null) { return("null"); } StringBuilder sb = new StringBuilder(32); // TODO: update on change sb.Append("{"); foreach (KeyValuePair <string, object> pair in o.Properties) { sb.Append(pair.Key).Append(":\"").Append(pair.Value).Append("\"; "); } sb.Append("}"); return(sb.ToString()); }
static bool ValidateProofOTPCode(IProofData proofData, IAuthenticationContext authContext) { if (proofData.Properties.ContainsKey("OTPCode")) { OTPCodeAnswer = (string)proofData.Properties["OTPCode"]; Debug.OTPCodeAnswer = OTPCodeAnswer; } if (!String.IsNullOrEmpty(OTPCodeAnswer) && OTPCodeAnswer == OTPCode) { return(true); } else { return(false); } }
static bool ValidateProofPIN(IProofData proofData, IAuthenticationContext authContext) { if (proofData.Properties.ContainsKey("PIN")) { PinAnswer = (string)proofData.Properties["PIN"]; Debug.PinAnswer = PinAnswer; } if (!String.IsNullOrEmpty(PinAnswer) && PinAnswer == PIN) { return(true); } else { return(false); } }
static bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("OTP")) { throw new ExternalAuthenticationException("Error - please input an answer", authContext); } var otp = (string)proofData.Properties["OTP"]; if (otp == answer) { return(true); } else { return(false); } }
private bool ValidateProofDataAsync(IProofData proofData, IAuthenticationContext context, out string response) { string userPrincipalName = (string)context.Data[USERUPN]; string registeredTokenIDs = (string)context.Data[REGISTEREDTOKENIDS]; if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("yubikeyotp")) { throw new ExternalAuthenticationException("Invalid submission - no proof data provided", context); } string otp = proofData.Properties["yubikeyotp"] as string; log.Debug("[{0}] Extracted otp {1} from proof data", context.ActivityId, otp ?? "(null)"); if (string.IsNullOrEmpty(otp) || otp.Length < 13) { response = "Invalid One-Time Password received"; return(false); } // extract the first 12 characters of the token id and convert to all lowercase string tokenID = otp.Substring(0, 12).ToLower(); if (Array.IndexOf(registeredTokenIDs.Split(','), tokenID) == -1) { response = string.Format("Token ID ({0}) not associated with {1}", tokenID, userPrincipalName); return(false); } var client = new YubicoClient(yubico_api_client_id, yubico_api_secret_key); var yubicoAnswer = client.VerifyAsync(otp).GetAwaiter().GetResult(); if (yubicoAnswer == null || yubicoAnswer.Status != YubicoResponseStatus.Ok) { response = yubicoAnswer.Status.ToString(); return(false); } response = "Authenticated completed successfully"; return(true); }
/// <summary> /// Check the OTP and do the real authentication /// </summary> /// <param name="proofData">the data from the HTML field</param> /// <param name="authContext">The auth context which contains secured parametes</param> /// <returns>True if auth is done and user can be validated</returns> private bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (authContext is null) { authContext = new AuthenticationContext(); } if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("otpvalue")) { throw new ExternalAuthenticationException($"ValidateProofData() OTP not found for {authContext.Data["userid"]}", authContext); } if (!ssl) { #pragma warning disable CA5359 // Do Not Disable Certificate Validation ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; #pragma warning restore CA5359 // Do Not Disable Certificate Validation } try { string otpvalue = (string)proofData.Properties["otpvalue"]; string session_user = (string)authContext.Data["userid"]; string session_realm = (string)authContext.Data["realm"]; string transaction_id = authContext.Data.ContainsKey("transaction_id") ? (string)authContext.Data["transaction_id"] : ""; #if DEBUG Debug.WriteLine($"{Helper.debugPrefix} ValidateProofData() user {session_user}, OTP {otpvalue}, realm {session_realm}, transaction {transaction_id}"); #endif // if we're running a server farm and BeginAuthentication was called on a different server if (otp_prov is null) { otp_prov = new OTPprovider(privacyIDEAurl); } return(otp_prov.ValidateOTP(session_user, otpvalue, session_realm, transaction_id)); } catch (Exception ex) { #if DEBUG Debug.WriteLine($"{Helper.debugPrefix} ValidateProofData() exception: {ex.Message}"); #endif throw new ExternalAuthenticationException($"ValidateProofData() exception: {ex.Message}", authContext); } }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext authContext, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { outgoingClaims = new Claim[0]; if (ValidateProofData(proofData, authContext)) { //authn complete - return authn method outgoingClaims = new[] { // Return the required authentication method claim, indicating the particular authentication method used. new Claim( "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp" ) }; return null; } else { //authn not complete - return new instance of IAdapterPresentationForm derived class return new AdapterPresentation(Resources.TokenValidationFailedMessage); } }
private bool ValidateProofDataAsync(IProofData proofData, IAuthenticationContext context, out string response) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("yubikeyotp")) { throw new ExternalAuthenticationException("Invalid submission - no proof data provided", context); } string otp = proofData.Properties["yubikeyotp"] as string; log.Debug("Extracted otp {0} from proof data", otp ?? "(null)"); if (string.IsNullOrEmpty(otp) || otp.Length < 13) { response = "Authentication failed: Invalid One-Time Password received"; return(false); } string tokenID = otp.Substring(0, 12); if (!registeredTokenIDs.Contains(tokenID)) { response = string.Format("Authentication failed: Token ID ({0}) not associated with {1}", tokenID, upn); return(false); } var client = new YubicoClient(yubico_api_client_id, yubico_api_secret_key); var yubicoAnswer = client.VerifyAsync(otp).GetAwaiter().GetResult(); if (yubicoAnswer == null || yubicoAnswer.Status != YubicoResponseStatus.Ok) { response = string.Format("Authentication failed: {0}", yubicoAnswer.Status.ToString()); return(false); } response = "Authenticated completed successfully"; return(true); }
private bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey(TOKEN_FORM_FIELD_NAME)) { throw new ExternalAuthenticationException(Resources.TokenNotFoundMessage, authContext); } string key = GetEncodedSecretKey(this.upn); Totp otp = new Totp(Base32.Base32Encoder.Decode(key)); long step; bool isVerified = otp.VerifyTotp((string)proofData.Properties[TOKEN_FORM_FIELD_NAME], out step, new VerificationWindow(previous: 1, future: 1)); if (!isVerified) { return(false); } string cacheKey = this.upn + "_" + step.ToString(); if (this.cache.Get(cacheKey) != null) { throw new ExternalAuthenticationException(String.Format(Resources.TokenAlreadyUsedMessage, "[" + this.upn + "] [" + step.ToString() + "]"), authContext); } else { var policy = new CacheItemPolicy() { AbsoluteExpiration = DateTime.Now.Add(new TimeSpan(0, 1, 0)) }; this.cache.AddOrGetExisting(new CacheItem(cacheKey, "used"), policy); } return(true); }
private bool ValidateProofData(IProofData proofData, IAuthenticationContext context, out string response) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("yubikeyotp")) { throw new ExternalAuthenticationException("Invalid submission - no proof data provided", context); } string token = proofData.Properties["yubikeyotp"] as string; if (string.IsNullOrEmpty(token) || token.Length < 13) { response = "Authentication failed: Bad One-Time Password"; return(false); } string tokenID = token.Substring(0, 12); if (!registeredTokenIDs.Contains(tokenID)) { response = string.Format("Authentication failed: Unknown YubiKey ID ({0})", tokenID); return(false); } YubicoAnswer yubicoAnswer = new YubicoRequest().Validate(token); response = yubicoAnswer.Status.ToString(); if (!yubicoAnswer.IsValid) { response = string.Format("Authentication failed: {0}", response); } return(yubicoAnswer.IsValid); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext authContext, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { Debug.Caller = "TryEndAuthentication"; outgoingClaims = new Claim[0]; if (proofData == null || proofData.Properties == null || (!proofData.Properties.ContainsKey("PIN") && !proofData.Properties.ContainsKey("OTPCode"))) { throw new ExternalAuthenticationException("Error - no answer found", authContext); } if (!ValidateProofPIN(proofData, authContext)) { //authentication not complete - return new instance of IAdapterPresentationForm derived class return(new SmsPresentationFormPin(Debug)); } if (!ValidateProofOTPCode(proofData, authContext)) { //authentication not complete - return new instance of IAdapterPresentationForm derived class return(new SmsPresentationFormOtp(Debug)); } outgoingClaims = new[] { // Return the required authentication method claim, indicating the particulate authentication method used. new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://example.com/myauthenticationmethod1") }; return(null); }
private bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey(TOKEN_FORM_FIELD_NAME)) { throw new ExternalAuthenticationException(Resources.TokenNotFoundMessage, authContext); } string key = GetEncodedSecretKey(this.upn); Totp otp = new Totp(Base32.Base32Encoder.Decode(key)); long step; bool isVerified = otp.VerifyTotp((string)proofData.Properties[TOKEN_FORM_FIELD_NAME], out step, new VerificationWindow(previous: 1, future: 1)); if (!isVerified) { return false; } string cacheKey = this.upn + "_" + step.ToString(); if (this.cache.Get(cacheKey) != null) { throw new ExternalAuthenticationException(String.Format(Resources.TokenAlreadyUsedMessage, "[" + this.upn + "] [" + step.ToString() + "]"), authContext); } else { var policy = new CacheItemPolicy() { AbsoluteExpiration = DateTime.Now.Add(new TimeSpan(0, 1, 0)) }; this.cache.AddOrGetExisting(new CacheItem(cacheKey, "used"), policy); } return true; }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { if (proofData?.Properties == null || !proofData.Properties.ContainsKey("ChallengeQuestionAnswer") || context?.Data == null || !context.Data.ContainsKey("upn") || string.IsNullOrEmpty((string)context.Data["upn"])) { throw new ExternalAuthenticationException("No answer found or corrupted context.", context); } claims = null; IAdapterPresentation result = null; var upn = (string)context.Data["upn"]; var code = (string)proofData.Properties["ChallengeQuestionAnswer"]; var secretKey = _secretStorageProvider.GetSecretKey(upn); if (TotpAuthenticator.CheckCode(upn, secretKey.Key, code, _usedCodeProvider)) { var claim = new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new[] { claim }; if (!secretKey.Activated) { secretKey.Activated = true; _secretStorageProvider.SetSecretKey(upn, secretKey); } } else { result = new AdapterPresentation(); } return(result); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { AdapterPresentation authResponse = null; string responseMessage = null; outgoingClaims = new Claim[0]; log.Debug("Authentication beginning for {0}", this.upn); bool isValidated = ValidateProofDataAsync(proofData, context, out responseMessage); log.Debug(responseMessage); if (!isValidated) { authResponse = new AdapterPresentation(responseMessage, false); } else { outgoingClaims = new[] { new Claim( "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp" ) }; } return(authResponse); }
/// <summary> /// This method is called by ADFS when this Authentication Adapter should perform the actual authentication. /// </summary> /// <param name="context"></param> /// <param name="proofData"> /// Dictionary of strings to objects, that represents whatever you have asked the client for during /// the BeginAuthentication method /// </param> /// <param name="request"></param> /// <param name="claims"> /// If the Authentication Adapter has successfully performed the authentication, this variable should contain /// at least one claim with type http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod. /// The value of this claim should contain the method of authentication used. It must be one of the values /// listed in the AuthenticationMethods parameter of <see cref="DemoMetadata"/>. /// </param> /// <returns></returns> public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = new Claim[0]; return(new DemoPresentationForm()); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims ) { claims = null; IAdapterPresentation result = null; string userName = proofData.Properties["upn"].ToString(); string pin = proofData.Properties["pin"].ToString(); string pollingEndpoint = proofData.Properties["pollingEndpoint"].ToString(); string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap(); fileMap.ExeConfigFilename = windir + "\\ADFS\\OktaMFA-ADFS.dll.config"; System.Configuration.Configuration cfg = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None); string oktaTenant = cfg.AppSettings.Settings["Tenant"].Value; string authToken = cfg.AppSettings.Settings["apiKey"].Value; string baseUrl = oktaTenant + "/api/v1/"; string pinSuccess = "no"; string verifyResult = "false"; HttpWebRequest upnRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userName); upnRequest.Headers.Add("Authorization", authToken); upnRequest.Method = "GET"; upnRequest.ContentType = "application/json"; var upnResponse = (HttpWebResponse)upnRequest.GetResponse(); var idReader = new StreamReader(upnResponse.GetResponseStream()); var id = idReader.ReadToEnd(); RootObject userProfile = JsonConvert.DeserializeObject<RootObject>(id); string userID = userProfile.id.ToString(); HttpWebRequest factorRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors"); factorRequest.Headers.Add("Authorization", authToken); factorRequest.Method = "GET"; factorRequest.ContentType = "application/json"; factorRequest.Accept = "application/json"; var factorResponse = (HttpWebResponse)factorRequest.GetResponse(); var factorReader = new StreamReader(factorResponse.GetResponseStream()); var factorList = factorReader.ReadToEnd(); RootObject[] factors = JsonConvert.DeserializeObject<RootObject[]>(factorList); string factorID = ""; foreach (RootObject factor in factors) { if (factor.provider == "OKTA" && factor.factorType == "push") { // string pushfactorID = factor.id; // HttpWebRequest pushRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + pushfactorID + "/verify"); // pushRequest.Headers.Add("Authorization", authToken); // pushRequest.Method = "POST"; // pushRequest.ContentType = "application/json"; // pushRequest.Accept = "application/json"; // pushRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"; // var pushResponse = (HttpWebResponse)pushRequest.GetResponse(); // var pushReader = new StreamReader(pushResponse.GetResponseStream()); // var pushStatus = pushReader.ReadToEnd(); // RootObject pushResult = JsonConvert.DeserializeObject<RootObject>(pushStatus); // string pollingEndpoint = pushResult._links.poll.href.ToString(); int attemptPoll = 1; while (verifyResult == "false" && attemptPoll <= 20 && pinSuccess == "no") { HttpWebRequest verifyRequest = (HttpWebRequest)WebRequest.Create(pollingEndpoint); verifyRequest.Headers.Add("Authorization", authToken); verifyRequest.Method = "GET"; verifyRequest.ContentType = "application/json"; verifyRequest.Accept = "application/json"; verifyRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"; var pushAnswer = (HttpWebResponse)verifyRequest.GetResponse(); var pushStatus2 = new StreamReader(pushAnswer.GetResponseStream()); var pushStatus3 = pushStatus2.ReadToEnd(); RootObject pushWait = JsonConvert.DeserializeObject<RootObject>(pushStatus3); if (pushWait.factorResult == "SUCCESS") { verifyResult = "true"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return result; } else { attemptPoll++; } } return result; } if (factor.provider == "OKTA" && factor.factorType == "token:software:totp" && verifyResult == "false" && pin != "") { factorID = factor.id; HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); httprequest.Headers.Add("Authorization", authToken); httprequest.Method = "POST"; httprequest.ContentType = "application/json"; otpCode otpCode = new otpCode { passCode = pin }; string otpString = JsonConvert.SerializeObject(otpCode); using (var streamWriter = new StreamWriter(httprequest.GetRequestStream())) { streamWriter.Write(otpString); } try { var httpResponse = (HttpWebResponse)httprequest.GetResponse(); if (httpResponse.StatusCode.ToString() == "OK" && pin != "" ) { pinSuccess = "yes"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return result; } // using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) // { // var factorResult = streamReader.ReadToEnd(); // } } catch (WebException we) { var failResponse = we.Response as HttpWebResponse; if (failResponse == null) throw; result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); } } } //HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); //httprequest.Headers.Add("Authorization", authToken); //httprequest.Method = "POST"; //httprequest.ContentType = "application/json"; //otpCode otpCode = new otpCode //{ passCode = pin }; //string otpString = JsonConvert.SerializeObject(otpCode); //using (var streamWriter = new StreamWriter(httprequest.GetRequestStream())) //{ // streamWriter.Write(otpString); //} //try //{ // var httpResponse = (HttpWebResponse)httprequest.GetResponse(); // if (httpResponse.StatusCode.ToString() == "OK") // { // System.Security.Claims.Claim claim = new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); // claims = new System.Security.Claims.Claim[] { claim }; // } // using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) // { // var factorResult = streamReader.ReadToEnd(); // } //} //catch (WebException we) //{ // var failResponse = we.Response as HttpWebResponse; // if (failResponse == null) // throw; // result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); //} if (pinSuccess == "yes" || verifyResult == "true") { Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return result; } else { result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); } return result; }
/// <summary> /// Validates the SAML response and set the necessary claims. /// </summary> /// <param name="context">The context.</param> /// <param name="proofData">The post back data.</param> /// <param name="request">The request.</param> /// <param name="claims">The claims.</param> /// <returns>A form if the the validation fails or claims if the validation succeeds.</returns> public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] claims) { this.log.Debug("Enter TryEndAuthentication"); this.log.DebugFormat("context.ActivityId='{0}'; context.ContextId='{1}'; conext.Lcid={2}", context.ActivityId, context.ContextId, context.Lcid); foreach (var d in context.Data) { this.log.DebugFormat("conext.Data: '{0}'='{1}'", d.Key, d.Value); } foreach (var p in proofData.Properties) { this.log.DebugFormat("proofData.Properties: '{0}'='{1}'", p.Key, p.Value); } claims = null; try { var response = SecondFactorAuthResponse.Deserialize(proofData, context); var authnRequestId = $"_{ context.ContextId}"; this.log.InfoFormat("Received response for request with id '{0}'", authnRequestId); var samlResponse = new Saml2Response(response.SamlResponse, new Saml2Id(authnRequestId)); if (samlResponse.Status != Saml2StatusCode.Success) { return(new AuthFailedForm(samlResponse.StatusMessage)); } claims = SamlService.VerifyResponseAndGetAuthenticationClaim(samlResponse); foreach (var claim in claims) { this.log.DebugFormat( "claim.Issuer='{0}'; claim.OriginalIssuer='{1}; claim.Type='{2}'; claim.Value='{3}'", claim.Issuer, claim.OriginalIssuer, claim.Type, claim.Value); foreach (var p in claim.Properties) { this.log.DebugFormat("claim.Properties: '{0}'='{1}'", p.Key, p.Value); } } this.log.InfoFormat("Successfully processed response for request with id '{0}'", authnRequestId); return(null); } catch (Exception ex) { this.log.ErrorFormat("Error while processing the saml response. Details: {0}", ex); return(new AuthFailedForm()); } }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { AdapterPresentation authResponse = null; string responseMessage = null; outgoingClaims = new Claim[0]; // retrieve context data stored earlier string userPrincipalName = (string)context.Data[USERUPN]; log.Debug("[{0}] Authentication beginning for {1}", context.ActivityId, userPrincipalName); bool isValidated = ValidateProofDataAsync(proofData, context, out responseMessage); if (!isValidated) { log.Info("Authentication failed for {0} - {1}", userPrincipalName, responseMessage); authResponse = new AdapterPresentation(responseMessage, false); } else { log.Info("Authentication successful for {0}", userPrincipalName); outgoingClaims = new[] { new Claim( "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp" ) }; } return(authResponse); }
/// <summary> /// Function call after the user hits submit - it proofs the values (OTP pin) /// </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) { outgoingClaims = new Claim[0]; if (ValidateProofData(proofData, authContext)) { //authn complete - return authn method outgoingClaims = new[] { // Return the required authentication method claim, indicating the particulate authentication method used. new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp") }; return(null); } else { //authentication not complete - return new instance of IAdapterPresentationForm derived class and the generic error message return(new AdapterPresentationForm(true, message, uidefinition)); } }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = null; IAdapterPresentation result = null; string userName = proofData.Properties["upn"].ToString(); string userID = proofData.Properties["userID"].ToString(); string pin = proofData.Properties["pin"].ToString(); string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap(); fileMap.ExeConfigFilename = windir + "\\ADFS\\OktaMFA-ADFS.dll.config"; System.Configuration.Configuration cfg = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None); string oktaTenant = cfg.AppSettings.Settings["Tenant"].Value; string authToken = cfg.AppSettings.Settings["apiKey"].Value; string baseUrl = oktaTenant + "/api/v1/"; string pinSuccess = "no"; string verifyResult = "false"; HttpWebRequest factorRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors"); factorRequest.Headers.Add("Authorization", authToken); factorRequest.Method = "GET"; factorRequest.ContentType = "application/json"; factorRequest.Accept = "application/json"; var factorResponse = (HttpWebResponse)factorRequest.GetResponse(); var factorReader = new StreamReader(factorResponse.GetResponseStream()); var factorList = factorReader.ReadToEnd(); RootObject[] factors = JsonConvert.DeserializeObject<RootObject[]>(factorList); string factorID = ""; foreach (RootObject factor in factors) { if (factor.factorType == "sms") { factorID = factor.id; HttpWebRequest verifyRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); verifyRequest.Headers.Add("Authorization", authToken); verifyRequest.Method = "POST"; verifyRequest.ContentType = "application/json"; otpCode otpCode = new otpCode { passCode = pin }; string otpString = JsonConvert.SerializeObject(otpCode); using (var streamWriter = new StreamWriter(verifyRequest.GetRequestStream())) { streamWriter.Write(otpString); } try { var verifyResponse = (HttpWebResponse)verifyRequest.GetResponse(); if (verifyResponse.StatusCode.ToString() == "OK" && pin != "") { pinSuccess = "yes"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return result; } } catch (WebException we) { var failResponse = we.Response as HttpWebResponse; if (failResponse == null) throw; result = new AdapterPresentation("Authentication was unsuccessful, did you enter the sms code correctly?", proofData.Properties["upn"].ToString(), false, proofData.Properties["userID"].ToString()); } } } if (pinSuccess == "yes") { Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return result; } else { result = new AdapterPresentation("Authentication was unsuccessful, did you enter the sms code correctly?", proofData.Properties["upn"].ToString(), false, proofData.Properties["userID"].ToString()); } return result; }
/// <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); }
static bool ValidateProofData(IProofData proofData, IAuthenticationContext authContext) { if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("OTP")) { throw new ExternalAuthenticationException("Error - please input the OTP", authContext); } var otp = (string)proofData.Properties["OTP"]; //Retrieve the YubiKey id stored in the user's AD object //Can be set with ADSI Edit or ADUC (Advanced view) DirectoryEntry entry = new DirectoryEntry(); DirectorySearcher mySearcher = new DirectorySearcher(entry, "(&(objectClass=user)(objectCategory=person)(userPrincipalName=" + upn + "))"); SearchResult dirResult = mySearcher.FindOne(); string yubikeyId = (string)dirResult.Properties["extensionAttribute10"][0]; var nonce = GenerateNonce(20); //First 12 digits/letters of OTP is the id for the YubiKey var userId = otp.Substring(0, 12); EventLog.WriteEntry("Application", "UPN: " + upn + " YubiId: " + yubikeyId, EventLogEntryType.Information); //Verify the user id portion of the YubiKey matches what's stored in AD if (userId != yubikeyId.Substring(8)) { EventLog.WriteEntry("Application", "YubiKey lookup in AD failed for UPN: " + upn, EventLogEntryType.Information); return(false); } byte[] hmacKey = Convert.FromBase64String(apiKey); //Parameters need to be in alphabetical order to generate a valid signature var hmac = GenerateSignature($"id={authId}&nonce={nonce}&otp={otp}", hmacKey); var queryString = ($"?id={authId}&otp={otp}&nonce={nonce}&h={hmac}"); var url = $"{server}{queryString}"; HttpClient client = new HttpClient(); var response = client.GetAsync(new Uri(url)).Result; string content = response.Content.ReadAsStringAsync().Result; string[] separators = new string[] { "\r\n" }; string[] result = content.Split(separators, StringSplitOptions.RemoveEmptyEntries); string outNonce = string.Empty; string outHmac = string.Empty; string outStatus = string.Empty; string outTimeStamp = string.Empty; string outSl = string.Empty; string outOtp = string.Empty; foreach (string x in result) { if (x.StartsWith("h=")) { outHmac = x.Substring(2); } if (x.StartsWith("t=")) { outTimeStamp = x.Substring(2); } if (x.StartsWith("status=")) { outStatus = x.Substring(7); } if (x.StartsWith("nonce=")) { outNonce = x.Substring(6); } if (x.StartsWith("sl=")) { outSl = x.Substring(3); } if (x.StartsWith("otp=")) { outOtp = x.Substring(4); } } //Need the response parameters in alphabetical order for verifying signature var responseList = new SortedDictionary <string, string> { { "t", outTimeStamp }, { "nonce", outNonce }, { "sl", outSl }, { "status", outStatus }, { "otp", outOtp } }; StringBuilder queryBuilder = null; foreach (var pair in responseList) { if (queryBuilder == null) { queryBuilder = new StringBuilder(); } else { queryBuilder.Append("&"); } queryBuilder.AppendFormat("{0}={1}", pair.Key, pair.Value); } var serverSignature = queryBuilder.ToString(); var signatureCheck = GenerateSignature(serverSignature, hmacKey); if (outNonce != nonce) { //Nonce mismatch EventLog.WriteEntry("Application", "YubiKey nonce mismatch for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outHmac != signatureCheck) { //Signature mismatch (server sent a different signature than expected) EventLog.WriteEntry("Application", "YubiKey signature mismatch for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "OK") { //All is good EventLog.WriteEntry("Application", "YubiKey OK for UPN: " + upn, EventLogEntryType.Information); return(true); } if (outStatus == "BAD_OTP") { //OTP not valid EventLog.WriteEntry("Application", "YubiKey BAD OTP for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "REPLAYED_OTP") { //OTP has already been used EventLog.WriteEntry("Application", "YubiKey REPLAYED OTP for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "BAD_SIGNATURE") { //Signature was incorrect EventLog.WriteEntry("Application", "YubiKey BAD SIGNATURE for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "MISSING_PARAMETER") { //Something missing EventLog.WriteEntry("Application", "YubiKey MISSING PARAMETER for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "NO_SUCH_CLIENT") { //Incorrect client EventLog.WriteEntry("Application", "YubiKey NO SUCH CLIENT for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "OPERATION_NOT_ALLOWED") { EventLog.WriteEntry("Application", "YubiKey OPERATION NOT ALLOWED for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "BACKEND_ERROR") { //Something wrong server side (Yubico) EventLog.WriteEntry("Application", "YubiKey BACKEND ERROR for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "NOT_ENOUGH_ANSWERS") { EventLog.WriteEntry("Application", "YubiKey NOT ENOUGH ANSWERS for UPN: " + upn, EventLogEntryType.Information); return(false); } if (outStatus == "REPLAYED_REQUEST") { EventLog.WriteEntry("Application", "YubiKey REPLAYED REQUEST for UPN: " + upn, EventLogEntryType.Information); return(false); } return(false); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = null; IAdapterPresentation result = null; string userName = proofData.Properties["upn"].ToString(); string userID = proofData.Properties["userID"].ToString(); string pin = proofData.Properties["pin"].ToString(); string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap(); fileMap.ExeConfigFilename = windir + "\\ADFS\\OktaMFA-ADFS.dll.config"; System.Configuration.Configuration cfg = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None); string oktaTenant = cfg.AppSettings.Settings["Tenant"].Value; string authToken = cfg.AppSettings.Settings["apiKey"].Value; string baseUrl = oktaTenant + "/api/v1/"; string pinSuccess = "no"; string verifyResult = "false"; HttpWebRequest factorRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors"); factorRequest.Headers.Add("Authorization", authToken); factorRequest.Method = "GET"; factorRequest.ContentType = "application/json"; factorRequest.Accept = "application/json"; var factorResponse = (HttpWebResponse)factorRequest.GetResponse(); var factorReader = new StreamReader(factorResponse.GetResponseStream()); var factorList = factorReader.ReadToEnd(); RootObject[] factors = JsonConvert.DeserializeObject <RootObject[]>(factorList); string factorID = ""; foreach (RootObject factor in factors) { if (factor.factorType == "sms") { factorID = factor.id; HttpWebRequest verifyRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); verifyRequest.Headers.Add("Authorization", authToken); verifyRequest.Method = "POST"; verifyRequest.ContentType = "application/json"; otpCode otpCode = new otpCode { passCode = pin }; string otpString = JsonConvert.SerializeObject(otpCode); using (var streamWriter = new StreamWriter(verifyRequest.GetRequestStream())) { streamWriter.Write(otpString); } try { var verifyResponse = (HttpWebResponse)verifyRequest.GetResponse(); if (verifyResponse.StatusCode.ToString() == "OK" && pin != "") { pinSuccess = "yes"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return(result); } } catch (WebException we) { var failResponse = we.Response as HttpWebResponse; if (failResponse == null) { throw; } result = new AdapterPresentation("Authentication was unsuccessful, did you enter the sms code correctly?", proofData.Properties["upn"].ToString(), false, proofData.Properties["userID"].ToString()); } } } if (pinSuccess == "yes") { Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return(result); } else { result = new AdapterPresentation("Authentication was unsuccessful, did you enter the sms code correctly?", proofData.Properties["upn"].ToString(), false, proofData.Properties["userID"].ToString()); } return(result); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = null; IAdapterPresentation result = null; string userName = proofData.Properties["upn"].ToString(); string pin = proofData.Properties["pin"].ToString(); string pollingEndpoint = proofData.Properties["pollingEndpoint"].ToString(); string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap(); fileMap.ExeConfigFilename = windir + "\\ADFS\\OktaMFA-ADFS.dll.config"; System.Configuration.Configuration cfg = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None); string oktaTenant = cfg.AppSettings.Settings["Tenant"].Value; string authToken = cfg.AppSettings.Settings["apiKey"].Value; string baseUrl = oktaTenant + "/api/v1/"; string pinSuccess = "no"; string verifyResult = "false"; HttpWebRequest upnRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userName); upnRequest.Headers.Add("Authorization", authToken); upnRequest.Method = "GET"; upnRequest.ContentType = "application/json"; var upnResponse = (HttpWebResponse)upnRequest.GetResponse(); var idReader = new StreamReader(upnResponse.GetResponseStream()); var id = idReader.ReadToEnd(); RootObject userProfile = JsonConvert.DeserializeObject <RootObject>(id); string userID = userProfile.id.ToString(); HttpWebRequest factorRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors"); factorRequest.Headers.Add("Authorization", authToken); factorRequest.Method = "GET"; factorRequest.ContentType = "application/json"; factorRequest.Accept = "application/json"; var factorResponse = (HttpWebResponse)factorRequest.GetResponse(); var factorReader = new StreamReader(factorResponse.GetResponseStream()); var factorList = factorReader.ReadToEnd(); RootObject[] factors = JsonConvert.DeserializeObject <RootObject[]>(factorList); string factorID = ""; foreach (RootObject factor in factors) { if (factor.provider == "OKTA" && factor.factorType == "push") { // string pushfactorID = factor.id; // HttpWebRequest pushRequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + pushfactorID + "/verify"); // pushRequest.Headers.Add("Authorization", authToken); // pushRequest.Method = "POST"; // pushRequest.ContentType = "application/json"; // pushRequest.Accept = "application/json"; // pushRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"; // var pushResponse = (HttpWebResponse)pushRequest.GetResponse(); // var pushReader = new StreamReader(pushResponse.GetResponseStream()); // var pushStatus = pushReader.ReadToEnd(); // RootObject pushResult = JsonConvert.DeserializeObject<RootObject>(pushStatus); // string pollingEndpoint = pushResult._links.poll.href.ToString(); int attemptPoll = 1; while (verifyResult == "false" && attemptPoll <= 20 && pinSuccess == "no") { HttpWebRequest verifyRequest = (HttpWebRequest)WebRequest.Create(pollingEndpoint); verifyRequest.Headers.Add("Authorization", authToken); verifyRequest.Method = "GET"; verifyRequest.ContentType = "application/json"; verifyRequest.Accept = "application/json"; verifyRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"; var pushAnswer = (HttpWebResponse)verifyRequest.GetResponse(); var pushStatus2 = new StreamReader(pushAnswer.GetResponseStream()); var pushStatus3 = pushStatus2.ReadToEnd(); RootObject pushWait = JsonConvert.DeserializeObject <RootObject>(pushStatus3); if (pushWait.factorResult == "SUCCESS") { verifyResult = "true"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return(result); } else { attemptPoll++; } } return(result); } if (factor.provider == "OKTA" && factor.factorType == "token:software:totp" && verifyResult == "false" && pin != "") { factorID = factor.id; HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); httprequest.Headers.Add("Authorization", authToken); httprequest.Method = "POST"; httprequest.ContentType = "application/json"; otpCode otpCode = new otpCode { passCode = pin }; string otpString = JsonConvert.SerializeObject(otpCode); using (var streamWriter = new StreamWriter(httprequest.GetRequestStream())) { streamWriter.Write(otpString); } try { var httpResponse = (HttpWebResponse)httprequest.GetResponse(); if (httpResponse.StatusCode.ToString() == "OK" && pin != "") { pinSuccess = "yes"; Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return(result); } // using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) // { // var factorResult = streamReader.ReadToEnd(); // } } catch (WebException we) { var failResponse = we.Response as HttpWebResponse; if (failResponse == null) { throw; } result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); } } } //HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(baseUrl + "users/" + userID + "/factors/" + factorID + "/verify"); //httprequest.Headers.Add("Authorization", authToken); //httprequest.Method = "POST"; //httprequest.ContentType = "application/json"; //otpCode otpCode = new otpCode //{ passCode = pin }; //string otpString = JsonConvert.SerializeObject(otpCode); //using (var streamWriter = new StreamWriter(httprequest.GetRequestStream())) //{ // streamWriter.Write(otpString); //} //try //{ // var httpResponse = (HttpWebResponse)httprequest.GetResponse(); // if (httpResponse.StatusCode.ToString() == "OK") // { // System.Security.Claims.Claim claim = new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); // claims = new System.Security.Claims.Claim[] { claim }; // } // using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) // { // var factorResult = streamReader.ReadToEnd(); // } //} //catch (WebException we) //{ // var failResponse = we.Response as HttpWebResponse; // if (failResponse == null) // throw; // result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); //} if (pinSuccess == "yes" || verifyResult == "true") { Claim claim = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new Claim[] { claim }; return(result); } else { result = new AdapterPresentation("Authentication failed.", proofData.Properties["upn"].ToString(), false); } return(result); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] claims) { claims = new Claim[0]; if (ValidateProofData(proofData, context)) { //authn complete - return authn method claims = new[] { new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/hardwaretoken") }; return(null); } else { return(new YubiKeyMFAPresentationForm()); } }
// Authentication should perform the actual authentication and return at least one Claim on success. // proofData contains a dictionnary of strings to objects that have been asked in the BeginAuthentication public IAdapterPresentation TryEndAuthentication(IAuthenticationContext ctx, IProofData proofData, System.Net.HttpListenerRequest request, out Claim[] claims) { string formAction, upn, msspTransId, logInfo; int state; if (proofData.Properties.ContainsKey("Retry")) { formAction = "Retry"; } else { try { formAction = (string)proofData.Properties["Action"]; } catch (KeyNotFoundException) { formAction = null; } }; //if (formAction == null && proofData.Properties.ContainsKey("SignOut")) { // // if user modifies URL manually during a session, the Cancel action is not captured by ADFS but leaks to this method // formAction = "SignOut"; //}; logger.TraceEvent(TraceEventType.Verbose, 0, "TryEndAuthentication(act:" + formAction + ", ctx:" + _str(ctx) + ", prf:" + _str(proofData) + ", req:" + _str(request)); Logging.Log.TryEndAuthenticationStart(formAction, _str(ctx), _str(proofData), _str(request)); CultureInfo culture = new CultureInfo(ctx.Lcid); upn = (string)ctx.Data[USERUPN]; state = (int)ctx.Data[STATE]; try { // msspTransId is expected to be absent in some error cases, e.g. error 107 msspTransId = (string)ctx.Data[MSSPTRXID]; } catch (KeyNotFoundException) { msspTransId = null; }; logInfo = "upn:\"" + upn + "\", msspTransId:\"" + msspTransId + "\""; claims = null; if (formAction == "Continue") { switch (state) { case 3: logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_OK: " + logInfo + ", state:" + state); Logging.Log.AuthenticationSuccess(state, (int)ctx.Data[STATE], upn, msspTransId); claims = ClaimsHwToken; return(null); case 1: case 31: // fall through for looping below break; default: logger.TraceEvent(TraceEventType.Error, 0, "BAD_STATE: " + logInfo + ", state:" + state); Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, "BAD_STATE"); return(new AdapterPresentation(AuthView.AuthError, cfgAdfs, "action:\"Conitnue\"; state:" + state)); } // check session age, i.e. timespan(Now, authBegin) int ageSeconds = (int)((DateTime.UtcNow.Ticks / 10000 - (long)ctx.Data[AUTHBEGIN]) / 1000); if (ageSeconds >= cfgMid.RequestTimeOutSeconds) { ctx.Data[STATE] = 13; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_TIMEOUT_CONT: " + logInfo + ", state:" + ctx.Data[STATE] + ", age:" + ageSeconds); Logging.Log.AuthenticationTimeout(state, (int)ctx.Data[STATE], ageSeconds, upn, msspTransId); return (((int)ctx.Data[SESSTRIES] < cfgAdfs.SessionMaxTries) ? new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, "Timeout.") : // TODO: construct new ErrorCode for easier I18N new AdapterPresentation(AuthView.AuthError, cfgAdfs, "Timeout.")); } AuthRequestDto req = new AuthRequestDto(); req.PhoneNumber = (string)ctx.Data[MSISDN]; req.DataToBeSigned = (string)ctx.Data[DTBS]; bool needCheckUserSerialNumber = !cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.allowAbsence) || !cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.allowMismatch) || cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.warnMismatch); if (needCheckUserSerialNumber /* cfgMid.UserSerialNumberPolicy != UserSerialNumberPolicy.ignore */ && ctx.Data.ContainsKey(UKEYSN)) { req.UserSerialNumber = (string)ctx.Data[UKEYSN]; } AuthResponseDto rsp; for (int i = ageSeconds; i <= cfgMid.RequestTimeOutSeconds; i += cfgMid.PollResponseIntervalSeconds) { rsp = getWebClient().PollSignature(req, msspTransId); switch (rsp.Status.Code) { case ServiceStatusCode.SIGNATURE: case ServiceStatusCode.VALID_SIGNATURE: ctx.Data[STATE] = 10; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_OK: " + logInfo + ", state:" + ctx.Data[STATE] + ", i:" + i); Logging.Log.AuthenticationSuccess(state, (int)ctx.Data[STATE], upn, msspTransId); // EventLog.WriteEntry(EVENTLOGSource, "Authentication success for " + upn, EventLogEntryType.SuccessAudit, 100); claims = ClaimsHwToken; return(null); case ServiceStatusCode.OUSTANDING_TRANSACTION: ctx.Data[STATE] = 11; logger.TraceEvent(TraceEventType.Verbose, 0, "AUTHN_PENDING: " + logInfo + ", state:" + ctx.Data[STATE] + ", i:" + i); Logging.Log.AuthenticationPending(state, (int)ctx.Data[STATE], upn, msspTransId); System.Threading.Thread.Sleep(1000); break; case ServiceStatusCode.EXPIRED_TRANSACTION: ctx.Data[STATE] = 13; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_TIMEOUT_MID: " + logInfo + ", state:" + ctx.Data[STATE] + ", i:" + i); Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code)); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, rsp)); case ServiceStatusCode.PB_SIGNATURE_PROCESS: ctx.Data[STATE] = 13; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_SIGN_PROCESS: " + logInfo + ", state:" + ctx.Data[STATE] + ", i:" + i); Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code)); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, rsp)); case ServiceStatusCode.USER_CANCEL: ctx.Data[STATE] = 14; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_CANCEL: " + logInfo + ", state:" + ctx.Data[STATE] + ", i:" + i); Logging.Log.AuthenticationCancel(state, (int)ctx.Data[STATE], upn, msspTransId); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, rsp)); default: ctx.Data[STATE] = 12; logger.TraceEvent(TraceEventType.Error, 0, "TECH_ERROR: " + logInfo + ", state:" + ctx.Data[STATE] + ", srvStatusCode:" + (int)rsp.Status.Code + ", srvStatusMsg:\"" + rsp.Status.Message + "\", srvStatusDetail:\"" + (string)rsp.Detail + "\""); if (rsp.Status.Color == ServiceStatusColor.Yellow || rsp.Status.Color == ServiceStatusColor.Green) { Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code)); } else { Logging.Log.AuthenticationTechnicalError(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code), (string)rsp.Detail); }; return(new AdapterPresentation(AuthView.AuthError, cfgAdfs, rsp)); } } ; // for-loop ctx.Data[STATE] = 13; logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_TIMEOUT_ADFS: " + logInfo + ", state:" + ctx.Data[STATE]); Logging.Log.AuthenticationTimeout(state, (int)ctx.Data[STATE], cfgMid.RequestTimeOutSeconds, upn, msspTransId); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, "Timeout.")); } else if (formAction == "Retry") { switch (state) { case 13: case 5: case 35: case 4: case 14: case 34: { // check session age and number of retries int ageSeconds = (int)((DateTime.UtcNow.Ticks / 10000 - (long)ctx.Data[SESSBEGIN]) / 1000); if (ageSeconds >= cfgAdfs.SessionTimeoutSeconds) { logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_SESSION_TIMEOUT: " + logInfo + ", state:" + ctx.Data[STATE] + ", age:" + ageSeconds); Logging.Log.SessionTimeout(state, (int)ctx.Data[STATE], ageSeconds, upn, msspTransId); ctx.Data[STATE] = 22; } else if ((int)ctx.Data[SESSTRIES] >= cfgAdfs.SessionMaxTries) { logger.TraceEvent(TraceEventType.Information, 0, "AUTHN_SESSION_OVERTRIES: " + logInfo + ", state:" + ctx.Data[STATE]); Logging.Log.SessionTooMuchRetries(state, (int)ctx.Data[STATE], (int)ctx.Data[SESSTRIES], upn, msspTransId); ctx.Data[STATE] = 22; } ; if ((int)ctx.Data[STATE] == 22) { return(new AdapterPresentation(AuthView.AutoLogout, cfgAdfs)); } } // start a new asynchronous RequestSignature AuthRequestDto req = new AuthRequestDto(); req.PhoneNumber = (string)ctx.Data[MSISDN]; req.UserLanguage = (UserLanguage)Enum.Parse(typeof(UserLanguage), resMgr.GetString(RES_LANG, culture)); string uiTrxId = Util.BuildRandomBase64Chars(cfgAdfs.LoginNonceLength); req.DataToBeSigned = _buildMobileIdLoginPrompt(req.UserLanguage, culture, uiTrxId); req.TimeOut = cfgMid.RequestTimeOutSeconds; bool needCheckUserSerialNumber = !cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.allowAbsence) || !cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.allowMismatch) || cfgMid.UserSerialNumberPolicy.HasFlag(UserSerialNumberPolicy.warnMismatch); if (needCheckUserSerialNumber /* cfgMid.UserSerialNumberPolicy != UserSerialNumberPolicy.ignore */ && ctx.Data.ContainsKey(UKEYSN)) { req.UserSerialNumber = (string)ctx.Data[UKEYSN]; } ctx.Data[AUTHBEGIN] = DateTime.UtcNow.Ticks / 10000; AuthResponseDto rsp = getWebClient().RequestSignature(req, true /* async */); ctx.Data[SESSTRIES] = (int)ctx.Data[SESSTRIES] + 1; string logMsg = "svcStatus:" + (int)rsp.Status.Code + ", mssTransId:\"" + rsp.MsspTransId + "\", state:"; switch (rsp.Status.Code) { case ServiceStatusCode.VALID_SIGNATURE: case ServiceStatusCode.SIGNATURE: ctx.Data[STATE] = 33; ctx.Data[MSSPTRXID] = rsp.MsspTransId; logger.TraceEvent(TraceEventType.Verbose, 0, logMsg + ctx.Data[STATE]); Logging.Log.AuthenticationSuccess(state, (int)ctx.Data[STATE], upn, msspTransId); return(new AdapterPresentation(AuthView.TransferCtx, cfgAdfs)); case ServiceStatusCode.REQUEST_OK: ctx.Data[STATE] = 31; ctx.Data[MSSPTRXID] = rsp.MsspTransId; ctx.Data[DTBS] = req.DataToBeSigned; logger.TraceEvent(TraceEventType.Verbose, 0, logMsg + ctx.Data[STATE]); Logging.Log.AuthenticationContinue(state, (int)ctx.Data[STATE], upn, msspTransId); return(new AdapterPresentation(AuthView.SignRequestSent, cfgAdfs, req.PhoneNumber, uiTrxId, cfgMid.PollResponseDelaySeconds * 1000)); case ServiceStatusCode.USER_CANCEL: ctx.Data[STATE] = 34; logger.TraceEvent(TraceEventType.Verbose, 0, logMsg + ctx.Data[STATE]); Logging.Log.AuthenticationCancel(state, (int)ctx.Data[STATE], upn, msspTransId); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, rsp)); case ServiceStatusCode.EXPIRED_TRANSACTION: case ServiceStatusCode.PB_SIGNATURE_PROCESS: ctx.Data[STATE] = 35; logger.TraceEvent(TraceEventType.Verbose, 0, logMsg + ctx.Data[STATE]); Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code)); return(new AdapterPresentation(AuthView.RetryOrCancel, cfgAdfs, rsp)); default: ctx.Data[STATE] = 32; logger.TraceEvent((rsp.Status.Color == ServiceStatusColor.Yellow ? TraceEventType.Warning : TraceEventType.Error), 0, logMsg + ctx.Data[STATE] + ", errMsg:\"" + rsp.Status.Message + "\", errDetail:\"" + rsp.Detail + "\""); Logging.Log.AuthenticationTechnicalError(state, (int)ctx.Data[STATE], upn, msspTransId, Enum.GetName(typeof(ServiceStatusCode), rsp.Status.Code), rsp.Detail.ToString()); return(new AdapterPresentation(AuthView.AuthError, cfgAdfs, rsp)); } ; default: logger.TraceEvent(TraceEventType.Error, 0, "BAD_STATE: " + logInfo + ", state:" + state); Logging.Log.AuthenticationFail(state, (int)ctx.Data[STATE], upn, msspTransId, "BAD_STATE"); return(new AdapterPresentation(AuthView.AuthError, cfgAdfs, "action:\"Retry\"; state:" + state)); } } //else if (formAction == "SignOut") //{ // logger.TraceEvent(TraceEventType.Verbose, 0, "SIGNOUT: " + logInfo + "; state:" + state); // return new AdapterPresentation(AuthView.AutoLogout, cfgAdfs); // could lead to endless-loop //} else { logger.TraceEvent(TraceEventType.Error, 0, "Unsupported formAction: " + formAction); Logging.Log.AuthenticationBadForm(state, (int)ctx.Data[STATE], upn, msspTransId, formAction); return(new AdapterPresentation(AuthView.AuthError, cfgAdfs, new AuthResponseDto(ServiceStatusCode.GeneralClientError))); } }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] claims) { DummyLogger.Log("IAuthenticationAdapter.TryEndAuthentication"); return(new DummyAdapterPresentation(context, proofData, request, out claims)); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext authContext, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { outgoingClaims = new Claim[0]; string errorMssg = ValidateProofData(proofData, authContext); if (string.IsNullOrWhiteSpace(errorMssg)) { outgoingClaims = new[] { new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/hardwaretoken") }; return(null); } else { return(new CustomPresentationForm(phoneMssg, errorMssg)); } }
/// <summary> /// Called by AD FS to perform the actual authentication. /// </summary> /// <param name="context"></param> /// <param name="proofData"></param> /// <param name="request"></param> /// <param name="claims"></param> /// <returns> If the Authentication Adapter has successfully performed /// the authentication a claim of type /// http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod /// is returned /// </returns> public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, System.Net.HttpListenerRequest request, out System.Security.Claims.Claim[] claims) { claims = null; IAdapterPresentation result = null; // Ensure the submitted form isn't empty. if (proofData == null || proofData.Properties == null || !proofData.Properties.ContainsKey("pin")) { if (this.debugLogging) { Logging.LogMessage("Either proofData is null or does not contain required property"); } throw new ExternalAuthenticationException(resMgr.GetString("Error_InvalidPIN", new System.Globalization.CultureInfo(context.Lcid)), context); } string pin = proofData.Properties["pin"].ToString(); string userName = this.identityClaim.Split('\\')[1]; // Construct RADIUS auth request. var authPacket = radiusClient.Authenticate(userName, pin); byte[] bIP = IPAddress.Parse(appConfig.NasAddress).GetAddressBytes(); authPacket.SetAttribute(new RadiusAttribute(RadiusAttributeType.NAS_IP_ADDRESS, bIP)); var receivedPacket = radiusClient.SendAndReceivePacket(authPacket).Result; // Handle no response from RADIUS server. if (receivedPacket == null) { if (this.debugLogging) { Logging.LogMessage("No response received from RADIUS server."); } throw new ExternalAuthenticationException(resMgr.GetString("Error_RADIUS_NULL", new System.Globalization.CultureInfo(context.Lcid)), context); } // Examine the different RADIUS responses switch (receivedPacket.PacketType) { case RadiusCode.ACCESS_ACCEPT: System.Security.Claims.Claim claim = new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp"); claims = new System.Security.Claims.Claim[] { claim }; break; case RadiusCode.ACCESS_CHALLENGE: // No way to cater for this. Fail. result = new AdapterPresentation(resMgr.GetString("Error_RADIUS_ACCESS_CHALLENGE", new System.Globalization.CultureInfo(context.Lcid)), false); break; case RadiusCode.ACCESS_REJECT: result = new AdapterPresentation(resMgr.GetString("Error_InvalidPIN", new System.Globalization.CultureInfo(context.Lcid)), false); break; default: result = new AdapterPresentation(resMgr.GetString("Error_RADIUS_OTHER", new System.Globalization.CultureInfo(context.Lcid)), false); break; } if (this.debugLogging) { Logging.LogMessage( "Processed authentication response." + Environment.NewLine + "Packet Type: " + receivedPacket.PacketType.ToString() + Environment.NewLine + "User: " + this.identityClaim); } return(result); }
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext context, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims) { string response = string.Empty; outgoingClaims = new Claim[0]; if (ValidateProofData(proofData, context, out response)) { outgoingClaims = new[] { new Claim( "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", "http://schemas.microsoft.com/ws/2012/12/authmethod/otp" ) }; return(null); } return(new AdapterPresentation(response, false)); }