0.2 to 0.3
Upgrade from the latest 0.2 release to 0.3. This release does not change
feature flags or the minimum supported Rust version. The
changelog lists every other change.
Do these three updates wherever they appear in your crate:
- Rename the response-signature policy types.
- Replace public
timecrate types withstd::time. - Decide whether typed IdP responses must carry an embedded signature.
Response signatures
Section titled “Response signatures”MessageSignaturePolicy and SpValidationPolicy::messages are removed. There
is no deprecated alias.
Before:
use saml_rs::{MessageSignaturePolicy, SpValidationPolicy};
let mut validation = SpValidationPolicy::strict();validation.messages = MessageSignaturePolicy::RequireSigned;After:
use saml_rs::{ResponseSignaturePolicy, SpValidationPolicy};
let mut validation = SpValidationPolicy::strict();validation.responses = ResponseSignaturePolicy::RequireSigned;The replacement variants are:
RequireSigned, which authenticates every Response;RequireForEncryptedCbc, which signs a Response that contains a CBC-encrypted Assertion;AllowUnsignedEncryptedCbcForCompatibility, which relaxes that CBC case. This variant is the default of the standalone enum. From 0.6, useAllowUnsignedEncryptedCbcandRespondSso::allow_unsigned_encrypted_cbc(0.5 to 0.6).
SpValidationPolicy::strict() still requires a trusted signature directly
over the consumed Assertion, and it uses
ResponseSignaturePolicy::RequireForEncryptedCbc. Plaintext responses do not
need Response-root coverage unless you opt in:
use saml_rs::{ResponseSignaturePolicy, SpValidationPolicy};
let validation = SpValidationPolicy { responses: ResponseSignaturePolicy::RequireSigned, ..SpValidationPolicy::strict()};RequireSigned is satisfied only by trusted signature evidence over the
Response root. An Assertion-only signature does not satisfy it. A verified
HTTP-Redirect detached signature, or an HTTP-POST-SimpleSign CD04 signature,
still satisfies the response-message requirement on those bindings.
When one of those binding-level messages is authenticated, the typed receiver
requires Response@Destination and rejects both a missing value and a
mismatch. An unsigned HTTP-POST Response whose Assertion is signed directly
does not pick up that Destination requirement.
A signature directly over the Assertion is saml-rs policy. It matches
WantAssertionsSigned in SAML metadata. Why a signed Response without a
signed Assertion can still be valid is explained in
standards conformance.
Time types
Section titled “Time types”Public validation and replay APIs now take std::time::SystemTime and
std::time::Duration. Parsing still uses the time crate internally. The
affected APIs are:
SamlValidationContext::newandSamlValidationContext::now;SamlValidationContext::with_replay_retentionandSamlValidationContext::replay_retention;ReplayCache::check_and_store;FlowOptions::nowon the raw compatibility API.
An unsupported validation instant now returns SamlError instead of
panicking. An invalid or unrepresentable replay expiration, including zero
retention and a retention that overflows SystemTime, returns
SamlError::TimeWindowInvalid with TimeWindowField::ReplayExpiration.
Clocks and replay
Section titled “Clocks and replay”Replace OffsetDateTime::now_utc() with SystemTime::now(), and replace
time::Duration constructors with std::time::Duration.
Before:
use saml_rs::{ReplayPolicy, SamlValidationContext};use time::{Duration, OffsetDateTime};
let validation = SamlValidationContext::new( OffsetDateTime::now_utc(), ReplayPolicy::DisabledForCompatibility,).with_replay_retention(Duration::minutes(5));After:
use std::time::{Duration, SystemTime};
use saml_rs::{ReplayPolicy, SamlValidationContext};
let validation = SamlValidationContext::new( SystemTime::now(), ReplayPolicy::DisabledForCompatibility,).with_replay_retention(Duration::from_secs(5 * 60));Build a fixed test clock from SystemTime::UNIX_EPOCH with checked_add or
checked_sub.
ReplayCache
Section titled “ReplayCache”check_and_store now takes a SystemTime expiration.
Before:
use saml_rs::{ReplayCache, ReplayKey, SamlError};use time::OffsetDateTime;
struct Cache;
impl ReplayCache for Cache { fn check_and_store( &mut self, key: ReplayKey, expires_at: OffsetDateTime, ) -> Result<(), SamlError> { let _ = (key, expires_at); Ok(()) }}After:
use std::time::SystemTime;
use saml_rs::{ReplayCache, ReplayKey, SamlError};
struct Cache;
impl ReplayCache for Cache { fn check_and_store( &mut self, key: ReplayKey, expires_at: SystemTime, ) -> Result<(), SamlError> { let _ = (key, expires_at); Ok(()) }}Store the key and expiration atomically. If the cache uses another timestamp type, convert at the storage boundary. Keep subsecond precision, and keep instants before the Unix epoch when the backend can store them.
Flow options
Section titled “Flow options”Assign a SystemTime when you pin the validation instant:
use std::time::SystemTime;
use saml_rs::raw::FlowOptions;
let mut options = FlowOptions::default();options.now = Some(SystemTime::now());Drop a direct time dependency after every call site has moved, if the
application used time only for these APIs. Keep it when the rest of the
application still needs it.
Encrypted responses
Section titled “Encrypted responses”A typed IdP signs the top-level Response when it generates an
EncryptedAssertion with a known CBC-mode algorithm. To force an embedded
XML signature on any HTTP-POST response:
let options = RespondSso::post().sign_response();let response = idp.respond_sso(sp, &request, subject, options)?;sign_response() does not replace HTTP-POST-SimpleSign. That binding still
uses its detached signature.
To keep an unsigned CBC-encrypted response for a legacy peer:
let options = RespondSso::post().allow_unsigned_encrypted_cbc_for_compatibility();let response = idp.respond_sso(sp, &request, subject, options)?;Checklist
Section titled “Checklist”- Replace
MessageSignaturePolicyandSpValidationPolicy::messages. - Replace public
OffsetDateTimevalidation values withSystemTime. - Replace public
time::Durationreplay values withstd::time::Duration. - Update every
ReplayCache::check_and_storeimplementation. - Update raw
FlowOptions::nowassignments. - Remove a direct
timedependency when no application code still uses it. - Choose embedded Response signing for typed IdP HTTP-POST output, or opt out for a legacy CBC peer.
- Run validation and replay tests, including fixed-clock and expiration cases.