Is this safe enough or am I making a horrible mistake?
Safe enough. Overkill, honestly. The most notable improvement I'd recommend here in particular is hashing the session token before storing it in the DB (or checking it against the DB-stored list of sessions). That way, even if an attacker gets a look at the DB, they couldn't steal somebody else's session. You should also enforce a server-side timeout for sessions, if you're going to have them time out at all.
Of course, there's a ton of other considerations that go into an authentication system (much less authorization). What credentials to use and how to verify them, how to handle loss of credentials (e.g. forgotten password), multi-factor auth, and so much more.
Also, specifically with cookies, you need to make sure your service is secure against CSRF (Cross-Site Request Forgery) attacks. There's a bunch of ways to do that but you do need to do at least one of them.
seems bad to me that it only uses 0-9 characters, it'd probably be much more secure if I used ASCII letters as well
Nope, that's actually pretty irrelevant. 16x UInt32 is 512 bits (64 bytes), which is excessively high entropy for a session token (most are in the 128-256 bits range, and every extra bit doubles the difficulty to brute-force the token). Representing it as decimal digits [0-9] isn't any less secure than representing it as hex [0-9A-F] or base64 [0-9A-Za-z./], and also isn't any more secure than representing it as octal [0-7] or even binary [0-1]. 64 bytes can be 512 binary digits, or 171 octal digits (including two bits of padding), or 155 decimal digits (again including some padding), or 128 hex digits, or 86 base64 digits (including some bits of padding, plus usually it would be further padded with two =
for a total length of 88).
You see how the string gets shorter the wider the character set used? Yet they all have the exact same amount of unguessability (entropy).
Now, string concatenation of decimal values (without delimiters or padding) in particular is a bad way to handle this, because it does introduce ambiguities (simple example: the string "101234" could be formed by the numbers "101","234", or "10","1234", or "10","12","34", or... you get the idea), but it still doesn't matter. Even if we assume that the string randomly ended up only 100 digits long - less likely than correctly picking a randomly selected atom of the planet Earth - it would still ludicrously unguessable (279-332 bits of entropy, far stronger than the encryption keys used for financial communications).