public async Task PerformAuthenticationAsync(Action activateCallback) { // Clear any previous tokens this.AccessToken = null; this.RefreshToken = null; // Generates state and PKCE values. string state = OAuthHelpers.RandomDataBase64url(32); string codeVerifier = OAuthHelpers.RandomDataBase64url(32); string codeChallenge = OAuthHelpers.Base64urlencodeNoPadding(OAuthHelpers.Sha256(codeVerifier)); // Creates a redirect URI using an available port on the loopback address. string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, OAuthHelpers.GetRandomUnusedPort()); Output("redirect URI: " + redirectURI); // Creates an HttpListener to listen for requests on that redirect URI. var http = new HttpListener(); http.Prefixes.Add(redirectURI); Output("Listening.."); http.Start(); // Creates the OAuth 2.0 authorization request. string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}", AuthorizationEndpoint, System.Uri.EscapeDataString(redirectURI), ClientID, state, codeChallenge, CodeChallengeMethod); // Opens request in the browser. System.Diagnostics.Process.Start(authorizationRequest); // Waits for the OAuth authorization response. var context = await http.GetContextAsync(); // Brings this app back to the foreground. activateCallback?.Invoke(); // Sends an HTTP response to the browser. var response = context.Response; var buffer = Encoding.UTF8.GetBytes(ResponseString); response.ContentLength64 = buffer.Length; var responseOutput = response.OutputStream; Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) => { responseOutput.Close(); http.Stop(); Console.WriteLine("HTTP server stopped."); }); // Checks for errors. if (context.Request.QueryString.Get("error") != null) { throw new OAuthException(string.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error"))); } if (context.Request.QueryString.Get("code") == null || context.Request.QueryString.Get("state") == null) { throw new OAuthException("Malformed authorization response. " + context.Request.QueryString); } // extracts the code var code = context.Request.QueryString.Get("code"); var incoming_state = context.Request.QueryString.Get("state"); // Compares the receieved state to the expected value, to ensure that // this app made the request which resulted in authorization. if (incoming_state != state) { throw new OAuthException(string.Format("Received request with invalid state ({0})", incoming_state)); } Output("Authorization code: " + code); // Starts the code exchange at the Token Endpoint. await PerformCodeExchange(code, codeVerifier, redirectURI); }