I'm implementing a REST service that requires authentication. I cannot store any per-user state (such as a randomly-generated token) because my service does not have direct access to a database, only to another backend service.

The solution I came up with is creating a JSON Web Token (JWT) when the user authenticates. The JWT claims set contains the user ID in the Subject ("sub") field. The server then encrypts the claims set directly ("alg":"dir") using AES GCM with 256 bit key ("enc":"A256GCM") creating a JWE. The key is generated once when the service starts and stored in memory.

To authenticate, the client submits the username/password, and the server responds with the token described above. The client then sends that token with each subsequent request.

When the client submits the token with subsequent requests, the server decrypts it using the key, and assumes the user ID in the "sub" field to be the ID of the current user, without any further authentication checks. Token expiration is handled by the "exp" field in the JWT claims set.

The connection between the client and the server will use SSL/TLS, so the token will not leak.

I'm using this library to create and read JWTs as I don't trust myself to write correct cryptography code.

My questions:


Is the above approach secure? Can an attacker impersonate another user by manipulating the token?
Is the approach over-complicated? Would using MAC (in other words: JWS) instead of encryption have the same security? (or possibly more, since it's simpler and there's less chance of making a mistake). There's nothing particularly secret in the JWT claims set, and the user knowing their own ID doesn't matter.
Is my choice of JWE algorithm and encryption appropriate?

For the JWE "alg", the library I'm using supports direct encryption (using the key directly to encrypt the claims set) and RSA (generating a new key to encrypt the claims set for each token, and encrypting the generated key with an RSA public key). I chose the former because it's easier to generate a symmetric key than an RSA key.
For the JWE "enc", the library supports AES GCM and AES CBC HMAC SHA2 (with various bit lengths). I chose GCM arbitrarily.



Your basic approach is valid: generate the JWT when the user logs in, expect subsequent messages to carry the JWT, trust the subject field in the JWT in those subsequent messages if the JWT is valid. But there are several things you should be aware of:


As Daisetsu say, you could use a MAC ("alg":"HS256") as MACs are specifically designed to prevent alteration of the payload, while encryption algorithms typically (counter-intuitively) are not. However since you're specifically using AES in GCM mode, you already get tamper-resistant encryption ("authenticated encryption"), so that's not really a concern.
When validating an incoming JWT, be careful what you consider valid. For example, I could call your service with {"sub":"me","alg":"none"} and while that JWT is valid in some sense, it isn't something you want to accept.
Since JWT is a draft, not a standard yet, it might change. If it changes enough, the library you're using might have to change in ways that break compatibility with your code.
If you can't store any server-side state, you have no way to invalidate the JWT when the user logs out. In effect your service has no logout function, which may be a security problem especially if you set the expiration time too far in the future.
If you set the expiration time too soon, you may have a problem with users still being logged in but not having a valid JWT. This may lead to awkward error-handling and user workflow issues.


Since you said your server has no access to a database, I assume the actual login is handled somewhere else, perhaps the backend server you mentioned. You didn't say how your server knows that the user just logged in. Depending on user perception of the relationship between your service and the thing they know they logged into, the last two points above might be moot.
