Crates.io | signed_tokens |
lib.rs | signed_tokens |
version | 0.2.0 |
source | src |
created_at | 2024-07-10 23:56:02.28997 |
updated_at | 2024-07-10 23:56:02.28997 |
description | Creates and verifies digitally-signed tokens suitable for authenticated session IDs. |
homepage | |
repository | https://github.com/davestearns/signed-tokens |
max_upload_size | |
id | 1298913 |
size | 21,849 |
A simple Rust crate for creating and verifying HMAC-signed tokens, with multiple rotating keys.
The canonical use-case for this is authenticated session tokens. After a user successfully signs in, your system should:
During subsequent requests, use this crate to verify the signed token and retrieve the session ID so you can look up the session data in your cache.
After the user successfully signs in, generate a random value for the session ID--a UUID works well for this, but you can use any unique value:
use uuid::Uuid;
let session_id = Uuid::new_v4().to_string();
To digitally sign the session ID, you'll also need some secret keys. Your server is the only one that needs to know about these keys, but they should remain the same after a server restart so that existing sessions will still be valid. You can read them from environment variables, or a file, or a secrets manager service, or whatever. But they should remain secret to your server and never be added directly to your source code.
use signed_tokens::SigningKey;
let signing_keys = vec![
SigningKey::new(env::var("SESSION_SIGNING_KEY_1").unwrap()),
SigningKey::new(env::var("SESSION_SIGNING_KEY_2").unwrap()),
SigningKey::new(env::var("SESSION_SIGNING_KEY_3").unwrap()),
];
You can have up to 255 signing keys. Each key has a status
, which defaults to SigningKeyStatus::SignAndVerify
. This can be changed to VerifyOnly
when you want to stop signing new tokens with the key, but allow existing tokens to be verified with it.
When you sign a session ID, this crate will randomly choose one of the sign-and-verify keys. The chosen key's slice index will be added to the signed and encoded token so that the library knows which key to use when later verifying the token.
To sign your session ID, pass it and your slice of signing keys to the sign()
function:
let token = signed_tokens::sign(&session_id, &signing_keys)?;
let url_safe_base64_token = token.to_string();
The sign()
method returns a SignedToken
struct, which wraps a binary buffer containing the chosen signing key index, the payload (your session ID in this case) and an HMAC signature. This can be turned into a base64-encoded String using the to_string()
method.
When you respond to the client, include the base64-encoded string as a secure HttpOnly cookie. When this cookie comes back in subsequent requests, verify it using the verify()
function.
// Use the same set of signing keys as you did when signing
let verified_token = signed_tokens::verify(&token_from_request_cookie, &signing_keys)?;
let session_id = verified_token.payload();
// look up account info in your cache using `session_id`...
The VerifiedToken
returned from verify()
contains not only the payload you passed when signing the token, but also the SigningKeyStatus
of the key used to verify the token. This is useful for rotating keys over time (see next section for more details).
The verify()
function will return an error Result
if any of the following occur:
It's a good idea to rotate your signing keys over time, even if they are never compromised. To do so without interruption to your clients, follow this set of steps:
status
of the key you want to replace to VerifyOnly
. This will deprecate the key so that existing tokens will still verify, but no new tokens will be signed using that key.VerifiedToken.key_status()
method returns SigningKeyStatus::VerifyOnly
, call the sign()
method again passing the payload from the VerifiedToken
to generate a new SignedToken
using an active signing key. You can then include this new signed token in your response as the new value for your session cookie. The client will then send this new token back to your server during subsequent requests.SigningKey
with a new active one. If a client previously got a token signed with the deprecated key and never returned to your site after it was deprecated, that client's token will no longer verify. The client can simply sign in again to get a refreshed session token.