Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Network game user accounts (for a.cc members)

This thread is locked; no one can reply to it. rss feed Print
Network game user accounts (for a.cc members)
Mark Oates
Member #1,146
March 2001
avatar

I'm wondering if it's possible to use existing allegro.cc member accounts for an allegro multiplayer online game network. Similar to how Nintendo accounts allow for playing games on the Nintendo network, Steam accounts allow for all the Valve multiplayer games, Apple ID for iOS games, etc. I want to investigate something similar for Allegro.

I want to avoid going through the whole process of new registration + validation with all that new infrastructure, and I would like the accounts to be the same familiar allegro.cc usernames / avatars that we know so well. I know that a similar process is used to facilitate enrolment in SantaHack and SpeedHack, what about enrolling for actual multiplayer games? What would that entail?

I've made a rudimentary TCP multiplayer game service (with boost::asio, compiles on Linux & Windows) that's volleying messages to connected clients from a persistent dedicated server. I've been thinking about how I could extend it into something we could all play around with (perhaps provided as an addon and a dedicated service similar to Unity3D's "Photon"). My idea for a minimum viable proof-of-concept is to create an "Allegro Land" game (think Miiverse or Nintendo Land's courtyard), where we could walk around with custom avatars (in 3D) and chat. The prototype I have now is already syncing 2D bounding boxes and talk bubbles between clients. :)

Matthew Leverton
Supreme Loser
January 1999
avatar

There would be no way to know if such an authentication method were secure. The client could easily be stealing the passwords.

The game would have to launch a browser to use for authentication, which would then need to pass data back to the game. Technically feasible if the game is configured to launch via a custom scheme, but cumbersome.

Alternatively, The website could generate a one time pass code for use with the game, but that wouldn't be seamless either.

Chris Katko
Member #1,881
January 2002
avatar

The other issue would be whether Matthew is okay with a possibly popular game flooding his website with authentication requests and possible influx of non-programmers here.

Just something to consider.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Felix-The-Ghost
Member #9,729
April 2008
avatar

Isn't that what the Minecraft launcher does?

==========================
<--- The ghost with the most!
---------------------------
[Website] [Youtube]

Mark Oates
Member #1,146
March 2001
avatar

The game would have to launch a browser to use for authentication, which would then need to pass data back to the game. Technically feasible if the game is configured to launch via a custom scheme, but cumbersome.

So you're saying, for example, the game might have a login button, and upon clicking:

  1. The game would post to allegro.cc that a login is being staged (with a key g1s8AaS9g9E0ahGa)

  2. The game launches the browser and sends it to https://www.allegro.cc/auth_login/?key=g1s8AaS9g9E0ahGa

  3. The user logs in on allegro.cc

  4. allegro.cc signals back to the game that the user is valid and member number X.

Is this what you mean? That's cool. :o I'm guessing a socket would need to be kept open between allegro.cc and the game while it's waiting.

Isn't that what the Minecraft launcher does?

Interesting. So instead of launching a browser, you would have a small separate standalone program for the sole purpose of authenticating members. The authentication that the standalone program provides would be available to any games that are running (maybe with shared memory or something).

So the process might work something like this:

  1. The user opens the trusted standalone program and types in their a.cc username/password. Clicks "login".

  2. Allegro.cc responds to a successful login by (1) creating a sesion_token in its database and (2) returning a copy of the session_token to the standalone program.

  3. The auth program signals (or whatever) to any running games that the user X has a valid session token for member X. The game could validate this with the username/session_token and allegro.cc.

At first I thought opening a browser would be convoluted, but now I'm thinking it seems much more liquid than a standalone login program.

Matthew Leverton
Supreme Loser
January 1999
avatar

allegro.cc signals back to the game that the user is valid and member number X.

It could do that, yeah. I was saying that it could redirect the browser to foobar://login?token=foo. Assuming your application has registered that scheme, then the application would take over from there (validating the token in secret).

But if you already have a server up and running for the game, your interpretation would be slightly more simple.

Quote:

the authentication that the standalone program provides would be available to any games that are running (maybe with shared memory or something).

That sounds like it would be insecure. The client would have to directly authenticate via the server at some point, or else the whole thing could be spoofed.

Although I suppose if this standalone program simply relayed some content that was signed by the server using a secret key (that no client has), then you could create some sort of trust.

Mark Oates
Member #1,146
March 2001
avatar

That sounds like it would be insecure. The client would have to directly authenticate via the server at some point, or else the whole thing could be spoofed.

For the standalone concept, the standalone would generate a token that any game would then use to directly authenticate with a.cc. Rather than the game validating an a.cc user with username/password, it would validate with the username/token that was created by the standalone<->a.cc.

I seem to like the browser idea better, though. After logging in on a desktop environment, a landing page of "You're logged in! Return to your game. (click here to close)" would probably be good enough. For small online allegro games, it's probably not reasonable to expect users to add a new URI scheme to their registry (unless it's more trivial that I think).

I wonder how this concept would work in a mobile environment. In that case, I can see the foobar://login?token=foo would be more necessary - it would seamlessly "escape" out of the browser and back to the game.

I'm not sure the best way to authenticate the client identity with the network server, however. Perhaps a.cc could send a key to both the server and the user when the login occurs, and as long as they both match when the client sends data to the server it could be considered valid.

It seems reasonable. A client, a network service, and a.cc communicating over TCP.

beoran
Member #12,636
March 2011

It's not necessary to use a browser, just use an HTTP client library like libcurl. (http://curl.haxx.se/libcurl/). The allegro.cc server could then be extended with an API that produce, e.g JSON. to check the logins. Just throwing a few ideas in here. :)

Peter Hull
Member #1,136
March 2001

This stuff makes my head spin - but - are you effectively asking a.cc to be an OpenID provider?

Matthew Leverton
Supreme Loser
January 1999
avatar

beoran said:

It's not necessary to use a browser, just use an HTTP client library like libcurl.

Right, but then you have to trust that the client isn't stealing your credentials.

This stuff makes my head spin - but - are you effectively asking a.cc to be an OpenID provider?

It already is (in concept). The wiki and Speedhack share authentication with a.cc.

beoran
Member #12,636
March 2011

Well if it's an open source game then you can rely the client. If it's binaries you have to sign them and trust the author. Same goes for anything anyway. If either the client or the server is unreliable, then you can get into problems.

Still, one thing that could help would be be to have a separate password per application. Every app can register itself and a user-chosen password. You'd have to log in once in a.cc to approve those logins. Well, just some brainstorming.

Mark Oates
Member #1,146
March 2001
avatar

I have a naive implementation of the browser-launching solution that works pretty well. The trick for this technique is that the server does not complete the HTTP response to the game until the user has successfully authorized. Here are some pretty pictures:

Run the command line 'game':
{"name":"609495","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/c\/bc0acb0f7efbf9d0d02d5720f9edb4c1.png","w":1677,"h":975,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/c\/bc0acb0f7efbf9d0d02d5720f9edb4c1"}609495

it launches the browser to the URL:
{"name":"609492","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/5\/05f9f764ac1d9692b7dcf2d54af3125e.png","w":1919,"h":946,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/5\/05f9f764ac1d9692b7dcf2d54af3125e"}609492

enter credentials (clock is counting down from the server)
{"name":"609493","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/2\/52e411a3249a3e1d8f01a0f10c479394.png","w":1919,"h":936,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/2\/52e411a3249a3e1d8f01a0f10c479394"}609493

credentials validated on the browser and the 'game' is notified.
{"name":"609494","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/3\/f35c9d9a3f4a0e8c8f4e3242ae5a8b42.png","w":1918,"h":937,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/3\/f35c9d9a3f4a0e8c8f4e3242ae5a8b42"}609494

A more complete description of the implementation is as follows:

1. The game launches.
2. The game says "press ENTER to login" (user presses ENTER)
3. The game sends an HTTP request (that remains open) to https://authserver.cc/stage_auth_request.php?auth_key=[32 chars of A-Za-z0-9]
4. The server creates a 'pending' record in its auth_request table. (The table has the following schema)
5. The server responds (but still not completing the response) with the exact magical phrase "Authorization request staged. Awaiting user login..." and flushes the output.
6. The game, while watching the input buffer from the HTTP request, reads the magical phrase.
7. The game knows that the request is ready and launches the browser to https://authserver.cc/validate_auth.php?auth_key=[32 chars of A-Za-z0-9]
8. The user enters their credentials at the trusted website.
9. With a successful username/password, the server updates the 'pending' record to show that it has been used.
10. And finally, the HTTP request that has stayed open is completed when the server observes that the auth_request record has been updated (in my case the server just polls the database every 2 seconds).
11. The sever concludes and completes the HTTP request with the phrase "The authentication was successful! The shared_id_key is [[$shared_id_key]] and the allegro_member_id is (($validated_by_user_id))"
12. If a return_url was provided in the initial game's request (step 3), then the server POSTS the allegro_member_id and shared_id_key to the return_url

Here are the files

Keep in mind, I did this in a day so there's some redundancy and needs refactoring. I did this all on Windows in localhost - I'll check it on a remote Linux server tomorrow and probably put it up on GitHub.

Chris Katko
Member #1,881
January 2002
avatar

Wow, keep up the good work!

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

LennyLen
Member #5,313
December 2004
avatar

So how are you going to assure people that you're not stealing their passwords?

Mark Oates
Member #1,146
March 2001
avatar

LennyLen said:

So how are you going to assure people that you're not stealing their passwords?

You would only ever enter your credentials at https://www.allegro.cc. 8-)

[edit]

Updates

Mkay, here are the files on GitHub.

Here is the game example program working on Ubuntu while validating the credentials "alex/password" on a remote demo server http://auth.zeoxdesign.com.
{"name":"609498","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/9\/d929b93061992e7feacfe9f29a516aaa.png","w":1070,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/9\/d929b93061992e7feacfe9f29a516aaa"}609498

If anybody wants to try compiling and running this demo, you can use http://auth.zeoxdesign.com as the first argument and use "alex/password" as the username/password. :)

To facilitate discussion, here is the example game program:

#SelectExpand
1/* 2 This example program requires CURL 3*/ 4 5 6#include <iostream> 7#include <string> 8#include <sstream> 9#include <algorithm> 10 11#include <curl/curl.h> 12 13 14struct RequestData 15{ 16 std::string auth_domain_name; 17 std::string auth_key; 18 std::string full_response; 19 bool request_remotely_initialized; 20}; 21 22 23std::string generate_auth_key(unsigned length = 32) 24{ 25 srand(time(0)); 26 std::string selection_set; 27 for (unsigned i=0; i<length; i++) selection_set += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 28 std::random_shuffle(selection_set.begin(), selection_set.end()); 29 return selection_set.substr(0, length); 30} 31 32 33void launch_browser_to_login(RequestData *request_data) 34{ 35 std::string validate_auth_url = request_data->auth_domain_name + "/validate_auth.php?auth_key=" + request_data->auth_key; 36 37#ifdef _WIN32 38 #include <windows.h> 39 // this one is non-blocking 40 ShellExecute(NULL, "open", validate_auth_url.c_str(), 41 NULL, NULL, SW_SHOWNORMAL); 42#elif defined __APPLE__ 43 std::stringstream command; 44 command << "open " << validate_auth_url; 45 // this one is blocking 46 system(command.str().c_str()); 47#else 48 // assume a Linux environment 49 std::stringstream command; 50 command << "xdg-open " << validate_auth_url; 51 // this one is blocking 52 system(command.str().c_str()); 53#endif 54} 55 56 57 58size_t CURL_buffer_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) 59{ 60 std::string new_data; 61 RequestData *request_data = static_cast<RequestData *>(userdata); 62 for (unsigned i = 0; i < size*nmemb; i++) 63 new_data.push_back(ptr[i]); 64 65 std::cout << new_data; // comment this out for a "quiet" mode 66 67 request_data->full_response += new_data; 68 69 // check that the expected magical phrase has been provided. 70 if (!request_data->request_remotely_initialized 71 && (request_data->full_response.substr(0, 52) == "Authorization request staged. Awaiting user login...")) 72 { 73 request_data->request_remotely_initialized = true; 74 std::cout << std::endl << "Authorization request recieved by the server. Launching browser..." << std::endl; 75 launch_browser_to_login(request_data); 76 } 77 78 return size*nmemb; 79} 80 81 82int send_stage_auth_request_to_authority(std::string auth_domain_name) 83{ 84 RequestData *request_data = new RequestData; 85 request_data->auth_key = generate_auth_key(); 86 request_data->auth_domain_name = auth_domain_name; 87 request_data->request_remotely_initialized = false; 88 89 std::string auth_url = request_data->auth_domain_name + "/stage_auth_request.php?auth_key=" + request_data->auth_key; 90 91 std::cout << "sending request to " << auth_url << std::endl; 92 93 // start the curl operation 94 CURL *curl = curl_easy_init(); 95 if (curl) 96 { 97 CURLcode res; 98 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CURL_buffer_write_callback); 99 curl_easy_setopt(curl, CURLOPT_WRITEDATA, request_data); 100 curl_easy_setopt(curl, CURLOPT_URL, auth_url.c_str()); 101 res = curl_easy_perform(curl); 102 // curl_easy_perform is blocking. If this implementation were to be used in an a game, 103 // then this send_stage_auth_request_to_authority() function should be executed as a thread. 104 // that way the game could provide status feedback and/or play a 'waiting' animation. 105 // Any status info be extracted during the CURL_buffer_write_callback and accessible 106 // through a RequestData object (and should probably be sealed in a mutex) 107 if (res == CURLE_OK) 108 { 109 std::cout << "curl request successfully completed." << std::endl; 110 if (!request_data->request_remotely_initialized) std::cout << "ERROR: The authorization server did not respond as expected." << std::endl; 111 } 112 else 113 { 114 std::cout << "curl Error " << res << ": " << curl_easy_strerror(res) << std::endl; 115 } 116 curl_easy_cleanup(curl); 117 } 118 else 119 { 120 std::cerr << "Error: Could not initialize curl" << std::endl; 121 return 1; 122 } 123 return 0; 124} 125 126 127 128int main(int argc, char* argv[]) 129{ 130 if (argc == 1) 131 { 132 std::cerr << "usage: acc_auth_ex http://www.someauthdomain.cc" << std::endl; 133 return 1; 134 } 135 136 std::cout << "Are you ready? Ready to start the authorization process? :D Press ENTER and you will be wisked away to the authorization page for " << argv[1] << " on your browser." << std::endl; 137 std::cin.ignore(); 138 139 send_stage_auth_request_to_authority(argv[1]); 140}

jmasterx
Member #11,410
October 2009

How does the browser portion work? As in, once the user has authenticated through the browser, what happens? How does the c++ app get notified?

Mark Oates
Member #1,146
March 2001
avatar

The request that is initiated by the c++ app to the server is left "hanging". A user then logs in through a browser with the auth_key. When that happens, then the "hanging" request is completed and closed with the confirmation response.

[edit] There are two connections... one by the c++ app to the server (left hanging)... and then a completely different, typical login through a browser.

jmasterx
Member #11,410
October 2009

That's interesting. I did not think a request could hang for so long. What's the timeout?

Mark Oates
Member #1,146
March 2001
avatar

In this case it's set for 3 minutes.

On the server, the script is kept from timing out with set_time_limit(). Assuming there isn't a global override somewhere, it will work. The server is also sending the current countdown integer every 2 seconds, so data is still being transferred while it's hanging. Also, apparently, sleep() does not count towards the execution time of the script.

It's set here: https://github.com/MarkOates/acc_auth_server_PHP/blob/master/stage_auth_request.php#L26-L32

On the c++ app, CURL never times out. But it should probably be set with CURLOPT_TIMEOUT.

jmasterx
Member #11,410
October 2009

Cool

A while back I wanted to integrate Facebook login into my game but decided against it because I could not figure out how I would get the token from the browser, but I guess something like this might work.

bamccaig
Member #7,536
July 2006
avatar

I think there are a few issues with this design. For one thing, I wouldn't trust that the browser will block. I would expect at least on some platforms that the system call to launch the browser would communicate with an existing instance of the browser, pass the URL request on to that, and exit immediately.

For this reason I think that communicating with any browser from an external process will likely be problematic. Of course, an alternative solution might be asking the user to transfer a token from the browser to the application (i.e., steam does something like this via E-mail to verify that you are who you say you are, but that's in addition to authenticating, not part of the authentication itself). This would obviously not be very secure, and an onlooker could again potentially steal it. At least, if the game ties the request token with the response token then it should prevent abuse unless the "game" chooses the same token for both users. Which leads me to the next point: there should probably be a proxy server between the game and A.cc. The server should be responsible for generating the request token and initiating the background connection with A.cc, not the client application. It would be too easy to hack the game executable to play nicely with the weaknesses of this technique.

In addition, command line arguments are often exposed to all users of a system meaning that if your token was passed to the browser via a querystring via a command line argument then a malicious user on the system could theoretically load up that same URL and beat you to it (unless the server is going to quickly invalidate it upon first use, but that could lead to user friendliness issues if they screw up their credentials the first time). In theory, all a malicious user could do with it is cause you to log in as themselves, which does seem odd, but that could still be abused (theoretically).

Lastly you're unnecessarily allocating your data on the heap and appear to be leaking the memory (which doesn't matter for this example, but is sloppy). >:(

I'm really not sure that there is a trusted and secure way to do this...

Mark Oates
Member #1,146
March 2001
avatar

bamccaig said:

I think there are a few issues with this design. For one thing, I wouldn't trust that the browser will block. I would expect at least on some platforms that the system call to launch the browser would communicate with an existing instance of the browser, pass the URL request on to that, and exit immediately.

No part of the design expects or requires a blocking browser process. Non-blocking is the preferred behavior. When developing on windows, throwing a system("open browser") in there for Linux/Mac is just easier. Dropping a fork() in there or ensuring a background (&) process would produce nicer behavior.

Quote:

For this reason I think that communicating with any browser from an external process will likely be problematic.

No process is communicating with the browser, so there's no problem. ;) The app only communicates with the authenticating server. It also launches the browser to a URL, but has no other interactivity with the browser than that.

Quote:

In theory, all a malicious user could do with it is cause you to log in as themselves, which does seem odd, but that could still be abused (theoretically).

Your attempt at the web login would fail, and you would identified as "fake_usr_8739". Well, nothing seems out of the ordinary here! *keeps playing*

The cool thing about this design is that no identifiable information is asked of you. The only thing that is provided to the network game server is 1) your IP address and 2) that you are allegro user #7536, that's it. Your login authorization is on http://www.allegro.cc and nothing changes with that. If you're logged in already, it's possible that the design could be improved to only require an "approve" button. Any additional information about your identity on a.cc (avatar, username) is public anyway.

Ponder on it a bit more?

It's cool, it works. :)

Quote:

I'm really not sure that there is a trusted and secure way to do this...

Then stop relying on the security of your browser for day-to-day stuff, because it could be compromised and stealing all ur loginz.

Quote:

appear to be leaking the memory (which doesn't matter for this example, but is sloppy). >:(

Yea, I forgot to delete that RequestData didn't I? >:(

Bam, I really appreciate your feedback. I think that if you ponder on it a bit more you'll see that some of the points you brought up are already addressed and/or are not a concern. If you spot more stuff, or see something, please bring it up! It might just need some clarification.

Go to: