// Performs an MFA login interactively using Console public static RestClient Authenticate(string podEndpoint) { RestClient client = new RestClient(podEndpoint); Console.Write("Username: "******"User"] = Console.ReadLine(); args["Version"] = "1.0"; Dictionary<string, dynamic> startResult = client.CallApi("/security/startauthentication", args); // First thing to check for is whether we should repeat the call against a more specific pod name (tenant specific url): if(startResult["success"] && startResult["Result"].ContainsKey("PodFqdn")) { Console.WriteLine("Auth redirected to {0}", startResult["Result"]["PodFqdn"]); client.Endpoint = string.Format("https://{0}", startResult["Result"]["PodFqdn"]); startResult = client.CallApi("/security/startauthentication", args); } // Get the session id to use in handshaking for MFA string authSessionId = startResult["Result"]["SessionId"]; string tenantId = startResult["Result"]["TenantId"]; // Also get the collection of challenges we need to satisfy var challengeCollection = startResult["Result"]["Challenges"]; // We need to satisfy one of each challenge collection: for(int x = 0; x < challengeCollection.Count; x++) { // Present the option(s) to the user for(int mechIdx = 0; mechIdx < challengeCollection[x]["Mechanisms"].Count; mechIdx++) { Console.WriteLine("Option {0}: {1}", mechIdx, MechToDescription(challengeCollection[x]["Mechanisms"][mechIdx])); } int optionSelect = -1; if (challengeCollection[x]["Mechanisms"].Count == 1) { optionSelect = 0; } else { while (optionSelect < 0 || optionSelect > challengeCollection[x]["Mechanisms"].Count) { Console.Write("Select option and press enter: "); string optEntered = Console.ReadLine(); int.TryParse(optEntered, out optionSelect); } } AdvanceForMech(client, tenantId, authSessionId, challengeCollection[x]["Mechanisms"][optionSelect]); } return client; }
private static void AdvanceForMech(RestClient client, string tenantId, string sessionId, dynamic mech) { Dictionary<string, dynamic> advanceArgs = new Dictionary<string, dynamic>(); advanceArgs["TenantId"] = tenantId; advanceArgs["SessionId"] = sessionId; advanceArgs["MechanismId"] = mech["MechanismId"]; advanceArgs["PersistentLogin"] = false; // Write prompt Console.Write(MechToPrompt(mech)); // Read or poll for response. For StartTextOob we simplify and require user to enter the response, rather // than simultaenously prompting and polling, though this can be done as well. string answerType = mech["AnswerType"]; switch (answerType) { case "Text": case "StartTextOob": { if (answerType == "StartTextOob") { // First we start oob, to get the mech activated advanceArgs["Action"] = "StartOOB"; client.CallApi("/security/advanceauthentication", advanceArgs); } // Now prompt for the value to submit and do so string promptResponse = ReadMaskedPassword(); advanceArgs["Answer"] = promptResponse; advanceArgs["Action"] = "Answer"; var result = client.CallApi("/security/advanceauthentication", advanceArgs); if (!result["success"] || !(result["Result"]["Summary"] == "StartNextChallenge" || result["Result"]["Summary"] == "LoginSuccess")) { throw new ApplicationException(result["Message"]); } } break; case "StartOob": // Pure out of band mech, simply poll until complete or fail advanceArgs["Action"] = "StartOOB"; client.CallApi("/security/advanceauthentication", advanceArgs); // Poll advanceArgs["Action"] = "Poll"; Dictionary<string, dynamic> pollResult = new Dictionary<string, dynamic>(); do { Console.Write("."); pollResult = client.CallApi("/security/advanceauthentication", advanceArgs); System.Threading.Thread.Sleep(1000); } while (pollResult["success"] && pollResult["Result"]["Summary"] == "OobPending"); // We are done polling, did it work? if (!pollResult["success"] || !(pollResult["Result"]["Summary"] == "StartNextChallenge" || pollResult["Result"]["Summary"] == "LoginSuccess")) { throw new ApplicationException(pollResult["Message"]); } break; } }