Created
August 27, 2023 18:20
-
-
Save msfayed/10a7ad2f16a76d29eb09ccd48aa7ea1d to your computer and use it in GitHub Desktop.
Revisions
-
msfayed created this gist
Aug 27, 2023 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,131 @@ class Program { static async Task Main(string[] args) { var _usePKCE = false; // ====================================== var clientId = "clientId"; var clientSecret = "clientSecret"; //_usePKCE = true; // for SPA/Mobile (without backend) // ====================================== var authorizationEndpoint = "http://localhost:8090/authorize"; var tokenEndpoint = "http://localhost:8090/token"; var redirectUri = "http://localhost:8080/"; var scope = "openid profile email"; // Adjust the scope based on your needs var state = Guid.NewGuid().ToString(); var authorizeUrl = $"{authorizationEndpoint}?client_id={clientId}&redirect_uri={HttpUtility.UrlEncode(redirectUri)}&response_type=code&scope={System.Web.HttpUtility.UrlEncode(scope)}&state={state}"; var code_verifier = ""; var code_challenge = ""; if (_usePKCE) { code_verifier = getCodeVerifier(); code_challenge = getCodeChallenge(code_verifier); authorizeUrl += $"&code_challenge={code_challenge}&code_challenge_method=S256 "; } Clipboard.SetText(authorizeUrl); Console.WriteLine("Please open the following URL in your browser to authenticate: [ already in your clipboard ;) ] \n\n"); Console.WriteLine(authorizeUrl + "\n\n"); using (var listener = new HttpListener()) { listener.Prefixes.Add(redirectUri); listener.Start(); Console.WriteLine("Waiting for callback..."); while (true) { var context = await listener.GetContextAsync(); var request = context.Request; if (request.HttpMethod == "GET") { var queryParameters = HttpUtility.ParseQueryString(request.Url.Query); var authorizationCode = queryParameters["code"]; var receivedState = queryParameters["state"]; var tokenContent = ""; if (receivedState == state) { // Exchange authorization code for access token Console.WriteLine($"Received authorization code: {authorizationCode}\n\n"); var tokenClient = new System.Net.Http.HttpClient(); var tokenRequest = new Dictionary<string, string> { { "grant_type", "authorization_code" }, { "code", authorizationCode }, { "redirect_uri", redirectUri }, { "client_id", clientId } }; if (_usePKCE) { tokenRequest.Add("code_verifier", code_verifier); } else { tokenRequest.Add("client_secret", clientSecret); } var tokenResponse = await tokenClient.PostAsync(tokenEndpoint, new FormUrlEncodedContent(tokenRequest)); tokenContent = await tokenResponse.Content.ReadAsStringAsync(); Console.WriteLine($"Token Response: {tokenContent}"); } else { Console.WriteLine("Received state does not match."); } string responseString = "Authorization successful.\nJWT can be decoded at: https://jwt.io/ \nResponse:\n\n" + tokenContent; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); context.Response.AddHeader("Content-Type", "application/json; charset=utf-8"); context.Response.ContentLength64 = buffer.Length; context.Response.OutputStream.Write(buffer, 0, buffer.Length); context.Response.Close(); break; } } listener.Stop(); } } static string getCodeVerifier() { var rng = RandomNumberGenerator.Create(); var bytes = new byte[32]; rng.GetBytes(bytes); // It is recommended to use a URL-safe string as code_verifier. // See section 4 of RFC 7636 for more details. return Convert.ToBase64String(bytes) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); } static string getCodeChallenge(string val) { using (var sha256 = SHA256.Create()) { var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(val)); return Convert.ToBase64String(challengeBytes) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); } } }