Skip to content

Instantly share code, notes, and snippets.

@msfayed
Created August 27, 2023 18:20
Show Gist options
  • Save msfayed/10a7ad2f16a76d29eb09ccd48aa7ea1d to your computer and use it in GitHub Desktop.
Save msfayed/10a7ad2f16a76d29eb09ccd48aa7ea1d to your computer and use it in GitHub Desktop.
LIQPad oauth2-oidc-client to test https://github.com/msfayed/oauth2-oidc-mock or any OICD server
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('/', '_');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment