1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! `BearerToken` construction, `Authorization` header parsing, and scope validation.
//!
//! Demonstrates:
//! - `AuthScheme` variant matching
//! - `BearerToken` construction and debug redaction
//! - `BasicCredentials` from username/password
//! - `OAuth2Token` with token type
//! - `AuthorizationHeader` parse + round-trip
//! - `Scope` and `Permission` — compose, subset check, intersection
//!
//! Requires the `auth` feature (default):
//! Run: `cargo run --example auth_flow`
use api_bones::auth::{
AuthScheme, AuthorizationHeader, BasicCredentials, BearerToken, OAuth2Token, Permission, Scope,
};
fn main() {
// -------------------------------------------------------------------------
// 1. AuthScheme — parse and display all variants
// -------------------------------------------------------------------------
println!("AuthScheme variants:");
let schemes = [
AuthScheme::Bearer,
AuthScheme::Basic,
AuthScheme::ApiKey,
AuthScheme::OAuth2,
AuthScheme::Digest,
AuthScheme::Custom("NTLM".to_owned()),
];
for scheme in &schemes {
let parsed: AuthScheme = scheme.to_string().parse().unwrap();
println!(" {scheme} round-trips={}", parsed == *scheme);
}
// -------------------------------------------------------------------------
// 2. BearerToken — construction and debug redaction
// -------------------------------------------------------------------------
println!("\nBearerToken:");
let tok = BearerToken::new("eyJhbGciOiJIUzI1NiJ9.payload.sig");
println!(" as_str = {:?}", tok.as_str());
println!(" debug = {tok:?}"); // must not leak the value
assert!(!format!("{tok:?}").contains("eyJhbGciOiJIUzI1NiJ9"));
// -------------------------------------------------------------------------
// 3. BasicCredentials — construction + password redaction
// -------------------------------------------------------------------------
println!("\nBasicCredentials:");
let creds = BasicCredentials::new("alice", "s3cr3t");
println!(" username = {}", creds.username());
println!(" debug = {creds:?}");
assert!(!format!("{creds:?}").contains("s3cr3t"));
// -------------------------------------------------------------------------
// 4. OAuth2Token — token with type
// -------------------------------------------------------------------------
println!("\nOAuth2Token:");
let oauth = OAuth2Token::new("access-token-value", Some("Bearer"));
println!(" token_type = {:?}", oauth.token_type());
println!(" debug = {oauth:?}");
assert!(!format!("{oauth:?}").contains("access-token-value"));
// -------------------------------------------------------------------------
// 5. AuthorizationHeader — parse Bearer
// -------------------------------------------------------------------------
println!("\nAuthorizationHeader (Bearer):");
let auth: AuthorizationHeader = "Bearer my-secret-token".parse().unwrap();
println!(" scheme = {:?}", auth.scheme());
println!(" to_string = {auth}");
if let AuthorizationHeader::Bearer(t) = &auth {
println!(" token = {}", t.as_str());
}
assert_eq!(auth.to_string(), "Bearer my-secret-token");
// AuthorizationHeader — parse Basic (base64-decoded automatically)
println!("\nAuthorizationHeader (Basic):");
let basic: AuthorizationHeader = "Basic dXNlcjpwYXNz".parse().unwrap();
if let AuthorizationHeader::Basic(c) = &basic {
println!(" username = {}", c.username());
println!(" password = [REDACTED in debug]");
}
// Round-trip back to the same base64-encoded header value
println!(" to_string = {basic}");
assert_eq!(basic.to_string(), "Basic dXNlcjpwYXNz");
// -------------------------------------------------------------------------
// 6. Permission — construct and validate
// -------------------------------------------------------------------------
println!("\nPermission:");
let p = Permission::new("orders:read").unwrap();
println!(" as_str = {}", p.as_str());
let empty_err = Permission::new("").unwrap_err();
println!(" empty error = {empty_err}");
let ws_err = Permission::new("bad token").unwrap_err();
println!(" space error = {ws_err}");
// -------------------------------------------------------------------------
// 7. Scope — compose, check, subset, intersection
// -------------------------------------------------------------------------
println!("\nScope:");
let full: Scope = "read write admin openid".parse().unwrap();
println!(" full = {full} (len={})", full.len());
let required: Scope = "read write".parse().unwrap();
println!(" required = {required}");
println!(
" required is_subset_of full = {}",
required.is_subset_of(&full)
);
println!(
" full is_subset_of required = {}",
full.is_subset_of(&required)
);
let a: Scope = "read write".parse().unwrap();
let b: Scope = "write admin".parse().unwrap();
let union = a.union(&b);
let inter = a.intersection(&b);
println!(" union({a}, {b}) = {union}");
println!(" intersection({a}, {b}) = {inter}");
println!(" full.contains(\"admin\") = {}", full.contains("admin"));
println!(" full.contains(\"delete\") = {}", full.contains("delete"));
// -------------------------------------------------------------------------
// 8. Scope from_permissions
// -------------------------------------------------------------------------
let perms = ["read", "write", "openid"]
.iter()
.map(|s| Permission::new(s).unwrap())
.collect::<Vec<_>>();
let scope = Scope::from_permissions(perms);
println!("\nScope::from_permissions = {scope} (len={})", scope.len());
}