Last active
August 6, 2022 20:22
-
-
Save benbenbenbenbenben/8310a6f31fc660a6a4b7f0357f28922f to your computer and use it in GitHub Desktop.
how to login to microsoftonline.com/your.tenancy.xyz using WebRequest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Configuration; | |
using System.IO; | |
using System.IO.Compression; | |
using System.Linq; | |
using System.Net; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
using System.Web; | |
namespace Dynamics365 | |
{ | |
public class MicrosoftOnlineLogin | |
{ | |
public static CookieContainer Login(string username, string password, string application) | |
{ | |
var wtrealm = HttpUtility.UrlEncode("spn:00000015-0000-0000-c000-000000000000"); | |
var wreply = HttpUtility.UrlEncode(application); | |
var tenant = username.Split('@').Last().ToLower(); | |
var interactiveUrl = $"https://login.microsoftonline.com/{tenant}/wsfed?wa=wsignin1.0&wtrealm={wtrealm}&wreply={wreply}"; | |
var loginUrl = $"https://login.microsoftonline.com/{tenant}/login"; | |
var cookies = new CookieContainer(); | |
// 1. GET ms login page | |
HttpWebRequest getLoginClient = (HttpWebRequest)WebRequest.Create(interactiveUrl); | |
getLoginClient.CookieContainer = cookies; | |
var getLoginResponse = getLoginClient.GetResponse(); | |
var getLoginResponseStream = new StreamReader(getLoginResponse.GetResponseStream()); | |
var getLoginResponseBody = getLoginResponseStream.ReadToEnd(); | |
// 2. POST to ms/tenant/login | |
HttpWebRequest postLoginClient = (HttpWebRequest)WebRequest.Create(loginUrl); | |
postLoginClient.CookieContainer = cookies; | |
postLoginClient.Headers.Clear(); | |
postLoginClient.Headers.Add("accept-Encoding: gzip, deflate, br"); | |
postLoginClient.Headers.Add("Accept-Language: en-US,en;q=0.8;en-GB;q=0.6"); | |
// 2.b. copy cookie in to POST | |
// postLoginClient.Headers.Add("Cookie", getLoginResponse.Headers["Set-Cookie"]); | |
// 2.c. prepare body | |
var postLoginBody = (new Dictionary<string, string>() | |
{ | |
["login"] = username, | |
["passwd"] = password, | |
["ctx"] = Regex.Match(getLoginResponseBody, "hidden.*ctx.*value=\"(.*)\"").Groups[1].Value, | |
["flowToken"] = Regex.Match(getLoginResponseBody, "hidden.*flowToken.*value=\"(.*)\"").Groups[1].Value, | |
["canary"] = Regex.Match(getLoginResponseBody, "hidden.*canary.*value=\"(.*)\"").Groups[1].Value, | |
["type"] = "11", | |
["LoginOptions"] = "3", | |
["NewUser"] = "1", | |
["idsbho"] = "1", | |
["PwdPad"] = "", | |
["sso"] = "", | |
["vv"] = "", | |
["uiver"] = "1" | |
}).Aggregate(HttpUtility.ParseQueryString(""), (seed, current) => | |
{ | |
seed.Add(current.Key, current.Value); | |
return seed; | |
}).ToString(); | |
postLoginClient.Method = "POST"; | |
postLoginClient.ContentType = "application/x-www-form-urlencoded"; | |
postLoginClient.ContentLength = postLoginBody.Length; | |
// 2.d. dispatch request and read response | |
var postLoginRequestStream = postLoginClient.GetRequestStream(); | |
using (var sw = new StreamWriter(postLoginRequestStream)) | |
{ | |
sw.Write(postLoginBody); | |
sw.Flush(); | |
sw.Close(); | |
} | |
var postLoginResponse = postLoginClient.GetResponse(); | |
var postLoginResponseBody = string.Empty; | |
using (var decomp = new GZipStream(postLoginResponse.GetResponseStream(), CompressionMode.Decompress)) | |
{ | |
byte[] buffer = new byte[1024]; | |
using (var ms = new MemoryStream()) | |
{ | |
int nRead; | |
while ((nRead = decomp.Read(buffer, 0, buffer.Length)) > 0) | |
{ | |
ms.Write(buffer, 0, nRead); | |
} | |
ms.Position = 0; | |
var tr = new StreamReader(ms); | |
postLoginResponseBody = tr.ReadToEnd(); | |
} | |
} | |
// 3. POST to app instance | |
HttpWebRequest axPostClient = (HttpWebRequest)WebRequest.Create(application); | |
axPostClient.CookieContainer = cookies; | |
axPostClient.Headers.Add("Cache-Control: max-age=0"); | |
axPostClient.Headers.Add("Origin: https://login.microsoftonline.com"); | |
axPostClient.Headers.Add("Upgrade-Insecure-Requests: 1"); | |
//axPostClient.Headers.Add("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2767.0 Safari/537.36"); | |
axPostClient.ContentType = "application/x-www-form-urlencoded"; | |
axPostClient.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; | |
axPostClient.Referer = loginUrl; | |
axPostClient.Headers.Add("Accept-Encoding: gzip, deflate, br"); | |
axPostClient.Headers.Add("Accept-Language: en-GB,en-US;q=0.8,en;q=0.6"); | |
var axPostBody = (new Dictionary<string, string>() | |
{ | |
["wa"] = "wsignin1.0", | |
["wresult"] = | |
HttpUtility.HtmlDecode(Regex.Match(postLoginResponseBody, "name=\"wresult\" value=\"([^\"]*)\"").Groups[1].Value), | |
["wctx"] = "rm=0&id=passive&ru=/" | |
}).Aggregate(HttpUtility.ParseQueryString(""), (seed, current) => | |
{ | |
seed.Add(current.Key, current.Value); | |
return seed; | |
}).ToString(); | |
axPostClient.Method = "POST"; | |
axPostClient.ContentLength = axPostBody.Length; | |
var axPostRequestStream = axPostClient.GetRequestStream(); | |
using (var sw = new StreamWriter(axPostRequestStream)) | |
{ | |
sw.Write(axPostBody); | |
sw.Flush(); | |
sw.Close(); | |
} | |
var axPostResponse = axPostClient.GetResponse(); | |
var axPostResponseBody = string.Empty; | |
using (var decomp = new GZipStream(axPostResponse.GetResponseStream(), CompressionMode.Decompress)) | |
{ | |
byte[] buffer = new byte[1024]; | |
using (var ms = new MemoryStream()) | |
{ | |
int nRead; | |
while ((nRead = decomp.Read(buffer, 0, buffer.Length)) > 0) | |
{ | |
ms.Write(buffer, 0, nRead); | |
} | |
ms.Position = 0; | |
var tr = new StreamReader(ms); | |
axPostResponseBody = tr.ReadToEnd(); | |
} | |
} | |
return cookies; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you are trying to integrate build tools with Dynamics AX7 / Dynamics 365 and you can't get hold of your Office 365 administrator then you might need something like this!