6

I have a url that authenticates my credentials on a server. Is there a way to make it invisible? The simple code looks exactly like this:

public void DoAuth()
    {
        String uri = GetUri();
        ProcessStartInfo startInfo = new ProcessStartInfo(uri);
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;
        Process.Start(startInfo);

    }

however ProcessWindowStyle.Hidden doesn't seem to do the trick. If I set UseShellExecute to false then I get Win32Exception with the message The system cannot find the file specified The url is a authentication on Spotify server to get the playlists and it looks something like this https://accounts.spotify.com/authorize/client_id=26d287105as12315e12ds56e31491889f3cd293..

Is there an other way to make this process invisible?

Edit: http sample

public void DoAuth()
    {
        String uri = GetUri();

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        HttpWebResponse webResponse;
        try
        {
            webResponse = (HttpWebResponse)request.GetResponse();
            Console.WriteLine("Error code: {0}", webResponse.StatusCode);
            using (Stream data = webResponse.GetResponseStream())
            using (var reader = new StreamReader(data))
            {
                //do what here?
            }
        }
        catch (Exception e)
        {
            throw;
        }
    }

The entire .cs file containing the sample above:

using SpotifyAPI.Web.Enums;
using SpotifyAPI.Web.Models;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace SpotifyAPI.Web.Auth
{
    public class ImplicitGrantAuth
    {
        public delegate void OnResponseReceived(Token token, String state);

        private SimpleHttpServer _httpServer;
        private Thread _httpThread;
        public String ClientId { get; set; }
        public String RedirectUri { get; set; }
        public String State { get; set; }
        public Scope Scope { get; set; }
        public Boolean ShowDialog { get; set; }

        public event OnResponseReceived OnResponseReceivedEvent;

        /// <summary>
        ///     Start the auth process (Make sure the internal HTTP-Server ist started)
        /// </summary>
        public void DoAuth()
        {
            String uri = GetUri();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            HttpWebResponse webResponse;
            try
            {
                webResponse = (HttpWebResponse)request.GetResponse();
                Console.WriteLine("Error code: {0}", webResponse.StatusCode);
                using (Stream data = webResponse.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    //nothing
                }
            }
            catch (Exception e)
            {
                throw;
            }


            /*ProcessStartInfo startInfo = new ProcessStartInfo(uri);
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            Process.Start(startInfo);
            */
        }

        private String GetUri()
        {
            StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?");
            builder.Append("client_id=" + ClientId);
            builder.Append("&response_type=token");
            builder.Append("&redirect_uri=" + RedirectUri);
            builder.Append("&state=" + State);
            builder.Append("&scope=" + Scope.GetStringAttribute(" "));
            builder.Append("&show_dialog=" + ShowDialog);
            return builder.ToString();
        }

        /// <summary>
        ///     Start the internal HTTP-Server
        /// </summary>
        public void StartHttpServer(int port = 80)
        {
            _httpServer = new SimpleHttpServer(port, AuthType.Implicit);
            _httpServer.OnAuth += HttpServerOnOnAuth;

            _httpThread = new Thread(_httpServer.Listen);
            _httpThread.Start();
        }

        private void HttpServerOnOnAuth(AuthEventArgs e)
        {
            OnResponseReceivedEvent?.Invoke(new Token
            {
                AccessToken = e.Code,
                TokenType = e.TokenType,
                ExpiresIn = e.ExpiresIn,
                Error = e.Error
            }, e.State);
        }

        /// <summary>
        ///     This will stop the internal HTTP-Server (Should be called after you got the Token)
        /// </summary>
        public void StopHttpServer()
        {
            _httpServer.Dispose();
            _httpServer = null;
        }
    }
}

And they are called like this:

_auth.OnResponseReceivedEvent += _auth_OnResponseReceivedEvent;
_auth.StartHttpServer(8000);
_auth.DoAuth();

The github url with a full runnable sampe is here: https://github.com/JohnnyCrazy/SpotifyAPI-NET Download it and run the Spotify Test to connect to Spotify's web api to reproduce the sample I have.

John P.
  • 1,199
  • 2
  • 10
  • 33
  • 2
    That's not how broswers work; you can't do that. You may want to send a direct HTTP request (but beware of cookies) – SLaks May 01 '16 at 01:59
  • @SLaks I thought about that but what kind of request? And what should I do with the response? Don't I have to set the cookies on that request? I will update a small sample of how I understand what you say – John P. May 01 '16 at 02:13
  • What do _you_ want to do with the response? That's entirely up to you. – SLaks May 01 '16 at 03:12
  • @SLaks that's the problem here. Normally I do nothing with the response. As far as the connection is done then there is an open socket on the connection and I can get the playlists from Spotify. I will attach the entire .cs file so you can check the entire code if you have the time and will – John P. May 01 '16 at 16:20
  • What kind of project are you using ? for a .net process you don't have to have any UI – Micah Armantrout May 06 '16 at 19:02
  • @MicahArmantrout You can find a full sample here: https://github.com/JohnnyCrazy/SpotifyAPI-NET. Spotify.dll provides a sample with the web api. The authentication done in the dll is the .cs file on the first post. – John P. May 06 '16 at 19:33
  • @MicahArmantrout you can download and run the sample provided there. – John P. May 06 '16 at 19:34
  • So 1) *is there a way to make a process invisible?* http://stackoverflow.com/a/30414483/495455 and 2) *The Edit?*, Its not clear what the problem is? I ran the ExampleForm and used the HttpWebRequest as per your edit and it ran fine, what were you expecting? – Jeremy Thompson May 09 '16 at 07:29
  • @ I am expecting to make it invisible. I am not sure I understand how the process for the authentication is done. I just want to make it invisible. A http request would make it invisible but I don't know how I can combine it with the app so the tcp keeps listening as long as it takes. As I said, the main goal is to make it invisible, one way or another. – John P. May 09 '16 at 19:28
  • Library author speaking. I **think** you are asking the wrong question for your problem. Do you want to hide the window or do you want authorization without user-action? Hiding the window will just open the login-page, but how would you interact with the local browser and click its buttons? `ImplicitGrantAuth` **can't** be used with simple HTTP-Requests, it uses multiple redirects and also Client-JS Code – Jonas Dellinger May 13 '16 at 18:18
  • @JohnnyDellinger I want to authorize it without user-action... Got me. Hope I find a solution here – John P. May 13 '16 at 18:48
  • @JohnP. Posted an answer, simulating a browser is one solution – Jonas Dellinger May 13 '16 at 19:43

4 Answers4

3

The entrypoint of a GUI program on Windows is the famous WinMain() function. It looks like this:

  int CALLBACK WinMain(
    _In_ HINSTANCE hInstance,
    _In_ HINSTANCE hPrevInstance,
    _In_ LPSTR     lpCmdLine,
    _In_ int       nCmdShow
  );

The hInstance and hPrevInstance arguments are legacy and date back to Windows versions <= 3, versions that did not have support processes yet and required an app to take care of multiple instances of a task itself. The lpCmdLine argument are the command line arguments.

nCmdShow is the important one and the subject of your question. Expected values are SW_HIDE, SW_SHOWNORMAL, SW_SHOWMAXIMIZE or SW_SHOWMINIMIZE. You can easily map them yourself to the possible ProcessWindowStyle enum values.

It is also exposed in the properties of a shortcut on your desktop. For example:

enter image description here

I expanded the Run combobox, note the match with the ProcessWindowStyle enum values. Except Hidden, hint of trouble there.


A typical C program passes the nCmdShow argument directly to the ShowWindow() function to display the main window (error checking omitted):

   HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   ShowWindow(hWnd, nCmdShow);

You'd be happy with such a program. It is however not the way many programs actually work. They check the value of nCmdShow and explicitly filter out SW_HIDDEN. Or they restore the window state that the user last used. In other words, they do this:

   HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   if (nCmdShow == SW_HIDDEN) nCmdShow = SW_SHOWNORMAL;
   if (HasPreviousWindowState) nCmdShow = PreviousWindowState;   // optional
   ShowWindow(hWnd, nCmdShow);

This is for a done for good reasons. Restoring the previous state is an obvious usability preference, many users will prefer it works that way for a browser for example. I do.

More to the point for the missing Hidden option in the shortcut configuration dialog, both the OS and well-designed GUI programs intentionally avoid creating a usability nightmare. Where the program gets started but there is no way for the user to activate the program. There is no taskbar button for a hidden window. Alt+Tab does not work. The only way the user can regain control over the program is to terminate it with Task Manager. It is also exploitable, malware could start a program and commandeer it and the user never notices.

All good reasons why such a program prevents this from happening. Nothing you can do about it, the program overrides your choice and has the last say about it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • It took me some time reading and re-reading this to understand. What about http connection instead of a browser that is built. It could be possible couldn't it? It is just luck of knowledge I believe. I am null in tcp connections and http processes but I do believe there must be a way to do what is being done here with http instead of browser. – John P. May 11 '16 at 18:58
2

Go into the project properties in Visual Studio and change your project type to Class Library. Save it. Then change it to Windows Application and save.

That should get rid of any UI you are not explicitly creating yourself.

hoodaticus
  • 3,772
  • 1
  • 18
  • 28
2

I don't think you need to start a new process to get authorization from Spoitfy. You should make a Get/Post call and get authorization token and then include it in your future API calls.

GET https://accounts.spotify.com/authorize

I am referring to Spoitfy's online help page.

https://developer.spotify.com/web-api/authorization-guide/
Ye Peng
  • 183
  • 6
1

Classic XY-Problem, you are not searching for a way to hide the window, you want to authenticate a user without user-actions involved.

Since ImplicitGrantAuth won't work with simple HTTP-Requests, you need a workaround:

Authenticate without user-action: Headless Browser

While ImplicitGrantAuth was not made for this kind of task, it's still possible but not cleanly. You will need a headless browser which you can control in your C# App, like Selenium.

Consider following code using Selenium (related SO Question):

void DoAuth()
{
    IWebDriver driver = new FirefoxDriver();

    driver.Navigate().GoToUrl(GetUri());
    IWebElement query = driver.FindElement(By.Name("username")); //You need to search for the correct element
    query.SendKeys("username");
    query = driver.FineElement(By.Name("password"));
    query.SendKeys("password");

    //Somehow submit the form and wait till you get the Token via URL
    //Thread.Sleep(5000);
    String urlWithToken = driver.Url;

    //Analyze it and get the token out of it

    driver.Quit();
}

This is just pseudo-correct code, but you should get the idea. Use a headless browser which you can control, fill in all form inputs, submit and analyze the URL. Facebook-Login would be the same principle.

But note: OAuth was not designed for this type of use and you propably should not use such hacky workarounds in production

Community
  • 1
  • 1
Jonas Dellinger
  • 1,294
  • 9
  • 19
  • Isn't it possible to be done with any http request? As far as I am seeing in the Spotify developer site, I can see it is... If not can you explain why in case you understand? – John P. May 13 '16 at 20:16
  • @JohnP. `ImplicitGrantAuth` can't be issued just with HTTP-Requests. `ClientCredentialsAuth` can, because it won't have access to any user-information and needs your `Client-Secret key`. The whole sense behind OAuth is, that the user has to specifically grant your app access to his data before you can access it. If it would be possible with simple HTTP-Requests, the user's password has to be transmitted which spotify wouldn't like for sure. – Jonas Dellinger May 14 '16 at 13:11