First things first: you are not using the correct terminology. This may be problematic if you are looking for documentation.
What you call "authorization" is what the rest of the world calls "authentication". What you call "authentication" is what the rest of the world calls "session management".
Authentication is about making sure that whoever is at the other end of the line really is who he claims to be. Session management is about extending authentication over several successive requests, which collectively constitute a session. Authorization is about deciding what a specific, duly authenticated user should be allowed to see and do.
When using passwords, authentication is defined as follows: a user U will be deemed "authenticated" if he can demonstrate knowledge of a password that only user U knows. This calls for two important comments:
Use SSL. The "show the password" authentication method implies, really, showing the password. The client should show it only to the proper server (yours), not to a fake server, and certainly not to the world at large. SSL will provide the necessary server authentication (that's what the server's certificate is about) and encryption. Also, SSL will bind the authentication with whatever is afterwards sent over the link, in particular your token. You won't get any decent security without something similar to SSL, and that's hard to implement and run correctly (especially in a Web context), so use SSL.
Don't store passwords themselves. The server does not need to store the password, but only enough to be able to verify a given password. This is where bcrypt is used: you store a password hash on the server. The idea is that if an attacker succeeds in having a look at your database, he won't get all the passwords in one go. That's a second line of defence.
Password hashing has a lot of theory and involves some configurable slowness and salts. The good news is that bcrypt is a good solution, and most bcrypt implementations already manage the salt. That is, when a salt must be generated, the bcrypt implementation generates one internally and properly; and the salt is encoded in the output. This means that the bcrypt output is a string that you just have to store and read back. See this answer for some details. You should not have to muck with salts directly.
For session management, you want to give to the client, after successful authentication, some fast way to tell to the server that it's him again. Sending back a session token is the normal way of ding that.
Other characteristics are merely extra safety tricks:
You may want to restrict the validity of the token in time, so as to force a new authentication regularly or after a given inactivity period. For this, the server must somehow remember the time at which the token was issued or last used.
You may want to restrict the token to a given IP address, on the basis that... what exactly ? If you do things properly, then if the token is stolen, then the attacker has enough control on the victim's machine that he may launch the fake commands from the victim's machine itself. On the other hand, normal users may sometimes switch IP addresses (there are ISP who provide dynamic IP addresses and change them occasionally; a user may have a laptop and use the WiFi from whatever restaurant he currently sits in). In any case, if you want to restrict a given session token to a specific IP address, then the server must somehow remember the said IP address.
In both case, the server must remember. On the server side, whenever a session token is created, the server must be able to map back that token value to the proper user identity. So there should be on the server a table, indexed by session token, which points back to the "session data", in particular the user name. An issuance timestamp is handy here to allow an obsolescence policy, in order to prevent that table from growing indefinitely (entries for old, expired tokens can then be removed automatically).
If you have millions of clients then you may want to offload such storage on the client itself. This is a size optimization. Now (and only now) would you envision a special "token format" which encodes some data that your server would like to remember about the session, but cannot or will not actually store in its database. The generic scheme is the following: for a given session, there is some data D which contains everything that the server wants to remember about the session. This includes the user name, the time of issuance or last use, possibly the client's IP address if you really wish to put restrictions at that level... then the token value is an encoding of D || M (concatenation of D and M) where M is a Message Authentication Code (in practice, HMAC) computed over D with a secret key K that the server stores.
The server uses K to generate the token (i.e. compute M). The server also uses K to validate an incoming token. Such a token can be seen like a sort of ID card: after successful authentication, the server grants to the client a temporary ID card, which can be verified by whoever knows the MAC key, i.e. the server itself, without having to maintain some knowledge of all issued cards. From the point of view of the server, things go the following way: if a client connects and sends a token T, then the server validates the MAC part of T, and if that matches, then server knows that T was issued by itself, and its contents are therefore trustworthy.
This scheme has some consequences:
Since the client does the storage, the server can manage millions of simultaneous sessions without having to pay the storage cost for it.
There is no easy way to enforce an early session close. As long as a client remembers a given token T, and the expiry date (encoded in the D-part of T) is still in the future, then the client can connect back and will be considered authenticated. If you want to be able to force-evict sessions (e.g. as an administrative action) then you will have to maintain some sort of revocation list (a list of seemingly-valid tokens that you decide not to accept nonetheless). Alternatively, by changing the key K on the server, you cancel all existing tokens simultaneously.
The client will see D. If you just want to store the user name, his IP address and his date of last authentication, this is not a problem: the user already knows all that. However, generically, if you want to store user-dependent data in the user token D that the user should not be able to see, then you will have to use encryption (which complements but does not replace the MAC).
You cannot prevent a user from doing a rollback. If a client connects and sends T1 with data D1, and the server sends back a token T2 with data D2, to be used for subsequent requests, the server has no real way to prevent the client from sending back T1 again. This is unavoidable if the server has a "no user-specific memory" stance.
You have a secret key K to manage on the server. Secret keys for HMAC are easy enough to generate (just get 16 bytes from your CSPRNG) but you have to store that key (so that the server does not forget it when it reboots); if the server has several front-ends then all of them must know the same key K; that key K is very valuable to attackers since knowing K allows impersonating every user, so K would have to be well protected (simply storing it in the database would make it an easy prey to basic SQL injection attacks).
Summary: unless you have clear performance issues which require client-side storage of session data, I recommend that you refrain from using a MAC-based kind of scheme. Don't encode user data in the token. Do this:
- When a user logs in (the client shows the user name and password, and the password verification with the stored bcrypt value succeeds), generate a new token T as a sequence of 16 bytes (128 bits) obtained from a CSPRNG (
System.Security.RNGCryptoServiceProvider... depending on the OS and language). The server creates a new row in the "session table" which maps T to the user data (name, date of last use, IP address...). T is sent back to the client.
- When a user connects and sends a token T, look it up in the session table to recover the user data. It is sufficient that the client knows T to consider the user as duly authenticated.
- Prune expired rows from the session table at regular intervals.
- And do everything above within SSL.