nosauth 0.1.5
Library that lets you obtain auth token so you can login to NosTale official servers
License: MIT

# NosTale-Auth
Simple library that lets you generate "magic" value for the NoS0577 login packet

# Python installation
`pip install nosauth`

Please refer to the example.py for working example

# The packet
New login packet `NoS0577` is used when you login with Gameforge launcher

That's how it looks like:
`"NoS0577 " + SESSION_TOKEN + "  " + INSTALLATION_GUID + " 003662BF" + " " + REGION_CODE + char(0xB) + "0.9.3.3126" + " 0 " + MD5_STR(MD5_FILE("NostaleClientX.exe") + MD5_FILE("NostaleClient.exe"))`

* `NoS0577` - The header of the packet, const value
* `SESSION_TOKEN` - Value generated by this library, after the value there are two spaces in the login packet
* `INSTALLATION_GUID` - Id that is generated during installation, for login purposes it probably may be random, stored in the windows registry under key name `InstallationId` in `SOFTWARE\\WOW6432Node\\Gameforge4d\\TNTClient\\MainApp`
* `003662BF` - Random value converted to HEX
* `REGION_CODE` - GF code of your region - `4` for PL
* `char(0xB)` - Single character with ASCII code `0xB`
* `0.9.3.3114` - Current version of client, may be obtained from the NostaleClientX.exe file version
* `0` - const value
* `MD5_STR(MD5_FILE("NostaleClientX.exe") + MD5_FILE("NostaleClient.exe"))` - MD5 generated from concatenation of MD5 uppercase strings of NostaleClientX.exe and NostaleClient.exe 

# The useless stuff

The client makes some useless stuff (at least - for us) like

1. When you press "Start" The launcher generates mostly like pseudo-random GUID and saves it to the environment variable called `_TNT_SESSION_ID`
2. Launcher launches the client with `gf` parameter
3. Client reads the `_TNT_SESSION_ID` value from the system environment variables, the value is further used to identify the client in the launcher (in case you run multiple NosTale clients)
4. Now the client and the launcher talk over newly created [pipe](https://docs.microsoft.com/en-us/windows/desktop/ipc/pipes) using JSON-RPC protocol.
5. The client queries the launcher using the `_TNT_SESSION_ID` value, the client requests info such as `USERNAME` and `code`, then it translates the `code` into `SESSION_TOKEN` using simple algorithm and sends it along with login packet

# Core part

## User-Agent

There are three types of User-Agents. For the majority of requests you will use the Chromium one (for example this specified in `Accounts` section below).
For the second and third type the User-Agent looks like: `Chrome/Cversion (MAGIC) GameforgeClient/2.1.22`, for example `Chrome/C2.1.22.784 (6a28914b) GameforgeClient/2.1.22`

Where the `MAGIC` is:

### For the second type

The second type of User-Agent is used for example during request to `/api/v1/patching/download/nostale/default?branchToken`. There are instructions how to generate this user-agent:

NOTE: The ouput of all hashing algorithms is hexlified string, not raw bytes!
Firsly, you need to grab first number from your `TNT-Installation-Id`.

In case the first number is even `(number % 2 == 0)` or there are only letters in your `TNT-Installation-Id`:
* `MAGIC` = first 8 characters from left of `SHA256(SHA256(Cert) + SHA1(version) + SHA256(TNT-Installation-Id))`

Otherwise (when the first number is odd):
* `MAGIC` = the last 8 characters of `SHA256(SHA1(Cert) + SHA256(version) + SHA1(TNT-Installation-Id))`

### For the third type

The third type of User-Agent is used for example while getting the auth code, so it's probably the most important one for you.

NOTE: The ouput of all hashing algorithms is hexlified string, not raw bytes!
Firsly, you need to grab first number from your `TNT-Installation-Id`.

In case the first number is even `(number % 2 == 0)` or there are only letters in your `TNT-Installation-Id`:
* `MAGIC` = 2 first chars of your account id + first 8 characters from left of `SHA256(SHA256(Cert) + SHA1(version) + SHA256(TNT-Installation-Id) + SHA1(account-id))`

Otherwise (when the first number is odd):
* `MAGIC` = 2 first chars of your account id + the last 8 characters of `SHA256(SHA1(Cert) + SHA256(version) + SHA1(TNT-Installation-Id) + SHA256(account-id))`


Where:
* `Cert` - Gameforge PEM cert embedded into launcher
* `version` - The current version of the launcher, for example "C2.1.22.784"
* `account-id` - Id of the account you are trying to log-in 

### Obtaining token example:
This is an example of obtaining the third type of token with following data:
* `version` = C2.1.22.784
* `Cert` = cert.pem file from this repository
* `account-id` = fb50ca7a-6ba2-11eb-9439-0242ac130002
* `TNT-Installation-Id` = a777c5e7-c9ac-407b-99b4-1a5934137f43

The first number of `TNT-Installation-Id` is 7, it is odd number so:
* `SHA1(Cert)` = 6a62b8e71fac63afc5abcb927a63f83aaa2ccb5b
* `SHA256(version) = SHA256("C2.1.22.784")` = bb3dc2ed5d66d85d099d97513c52fbe699e61e5e8c71f91b9137566514c04e51
* `SHA1(TNT-Installation-Id) = SHA1("a777c5e7-c9ac-407b-99b4-1a5934137f43")` = 8b3c8dbe01fbb1d18ec288b74f072915f8d268b4
* `SHA256(Account-Id) = SHA256("fb50ca7a-6ba2-11eb-9439-0242ac130002")` = bcabe70d5883ceead32fe116322824be320b18a98a241a5370a5de5e34763697
* `2 first chars of account id` = fb
* `SHA256(SHA1(Cert) + SHA256(version) + SHA1(TNT-Installation-Id) + SHA256(account-id)) = SHA256(6a62b8e71fac63afc5abcb927a63f83aaa2ccb5b + bb3dc2ed5d66d85d099d97513c52fbe699e61e5e8c71f91b9137566514c04e51 + 8b3c8dbe01fbb1d18ec288b74f072915f8d268b4 + bcabe70d5883ceead32fe116322824be320b18a98a241a5370a5de5e34763697)` = 825e786ede7e1421cda70988d9e493a4adeb9ce2986a48848763bd5cb506b95a
* `Last 8 characters of the previous one` = b506b95a
* Result = `2 first chars of account id` + `Last 8 characters of the previous one` = fbb506b95a


## Auth

To obtain the token first you need to auth yourself. To do so you need to send `POST` request to `https://spark.gameforge.com/api/v1/auth/sessions`, you send it `only once`.

In the request header you need to specify `TNT-Installation-Id` from the windows registry.
In the body of the request you need to specify `JSON` content:
* `email` - your email
* `locale` - example: `pl_PL`
* `password` - your password

In the response you will get `JSON` content:
* `token` - value that is used later in API requests, it is NOT that one to use in login packet

## Accounts

Since some time you may bind multiple game accounts to your GF account. To handle it you need to make `GET` request to `https://spark.gameforge.com/api/v1/user/accounts`

In the request header you need to specify:
* `TNT-Installation-Id` - value from windows registry
* `User-Agent` - Eg. `Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36`
* `Authorization` - `Bearer ` + `TOKEN_FROM_AUTH_REQUEST`

In the response you get `JSON` array of all the accounts. Top level keys in the list are `account ids`.

## Almost done

To obtain the right token you need to make `POST` request to `https://spark.gameforge.com/api/v1/auth/thin/codes`
In the request header you need to specify:
* `TNT-Installation-Id` - value from windows registry
* `User-Agent` - This is the third type token, for the details look at the `User-Agent` paragraph at the beginning of the document, eg. `Chrome/C2.1.22.784 (fbb506b95a) GameforgeClient/2.1.22`
* `Authorization` - `Bearer ` + `TOKEN_FROM_AUTH_REQUEST`

In the request `JSON` body you need to specify:
* `platformGameAccountId` - the id of selected account from previous section

In the response you get `JSON` content with:
* `code` - The value you are looking for 

You may call the `api/v1/auth/thin/codes` multiple times with the auth token obtained from `api/v1/auth/thin/sessions`

## Finally, the SESSION_TOKEN

To use the `code` in login packet you need to convert it to `SESSION_TOKEN`. The conversion is very simple. It changes the `code` into hex string.

Lets say you got `code` equal to `a857263a-3fc1-4c60-ad78-9b6d9a2a0691`, after the conversion it will look like `61383537323633612D336663312D346336302D616437382D396236643961326130363931` because you convert characters from `code` element by element into hexstring, so:
* `a` -> 97 -> 0x61 -> `61`
* `8` -> 56 -> 0x38 -> `38`

and so on, so the string will look like `6138...`