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
137
138
use crate::{Subject, error::Result, introspect::IntrospectionResult};
use core::{future::Future, marker::Sync};
pub(crate) use jsonwebtoken::Header;
use std::collections::HashSet;
use std::fmt::Debug;
use typed_builder::TypedBuilder;
pub trait Authenticator
where
Self: Send + Sync + Clone,
{
/// Authenticate a token. This must validate the tokens signature and claims.
/// For opaque tokens, handlers may connect to the `IdP` to validate the token.
///
/// # Errors
/// - Token is not valid.
fn authenticate(&self, token: &str) -> impl Future<Output = Result<Authentication>> + Send;
/// Check if the authenticator can handle the token.
/// This is used in the [`AuthenticatorChain`](`crate::AuthenticatorChain`) to determine which authenticator to use.
/// This should be a quick check that doesn't involve cryptographic operations.
fn can_handle_token(&self, token: &str, introspection_result: &IntrospectionResult) -> bool;
/// Returns an id that uniquely identifies the `IdP` this authenticator is for.
fn idp_id(&self) -> Option<&String>;
/// Collects the IdP identifier(s) associated with this authenticator.
///
/// By default this yields a single-element vector containing the result of `self.idp_id()`
/// (converted to a `&str`) for a standalone authenticator. Implementations that represent
/// a chain of authenticators should return one element per child authenticator in chain order.
///
/// # Returns
///
/// A `Vec<Option<&str>>` where each element is the IdP identifier for one authenticator in the chain,
/// or `None` when an authenticator does not have an IdP identifier.
fn idp_ids(&self) -> Vec<Option<&str>> {
vec![self.idp_id().map(String::as_str)]
}
}
#[derive(Debug, PartialEq, Eq, Clone, TypedBuilder)]
/// Information about a successful authentication.
/// Use [`Authentication::subject()`] for a unique identifier of the user.
pub struct Authentication {
// --------- Raw token data ---------
/// Header of the provided token if any.
/// Not all tokens have a header. JWTs do, but opaque tokens don't.
token_header: Option<Header>,
/// Claims of the provided token provided as a json Value.
/// This struct also contains some popular claims as strongly typed fields,
/// which should be preferred over accessing the claims directly.
claims: serde_json::Value,
/// Subject of the token - consists of a unique identifier of the idp
/// and the id of the subject in the idp.
subject: Subject,
/// Full name of the user intended for human use.
name: Option<String>,
/// Email of the user.
email: Option<String>,
/// The type of the principal making the request.
principal_type: Option<PrincipalType>,
/// Roles of the user extracted from the token if any.
#[builder(default)]
roles: Option<Vec<String>>,
/// Audiences of the token.
#[builder(default)]
audiences: HashSet<String>,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
/// Type of the principal making the request.
pub enum PrincipalType {
Human,
Application,
}
impl Authentication {
#[must_use]
/// Get the token header if it exists.
pub fn token_header(&self) -> Option<&Header> {
self.token_header.as_ref()
}
#[must_use]
/// Get the content of a claim from the token.
/// If the claim does not exist, this will return None.
pub fn claims(&self, key: &str) -> Option<&serde_json::Value> {
self.claims.get(key)
}
#[must_use]
/// Get the subject of the user.
/// Use this to uniquely identify the user.
pub fn subject(&self) -> &Subject {
&self.subject
}
#[must_use]
/// Get the full name of the user.
/// This is intended for human use. It is not guaranteed to be unique and may change.
pub fn full_name(&self) -> Option<&str> {
self.name.as_deref()
}
#[must_use]
/// Get the type of the principal making the request.
/// This is estimated by the [`Authenticator`] implementation and may not be accurate in all cases.
pub fn principal_type(&self) -> Option<PrincipalType> {
self.principal_type
}
#[must_use]
/// Get the email of the user.
pub fn email(&self) -> Option<&str> {
self.email.as_deref()
}
/// Get the roles of the user that were extracted from the token if any.
#[must_use]
pub fn roles(&self) -> Option<&[String]> {
self.roles.as_deref()
}
#[must_use]
/// Get the audiences of the token.
pub fn audiences(&self) -> &HashSet<String> {
&self.audiences
}
#[must_use]
/// Get the IdP identifier of the subject, if present.
/// This is a convenience accessor that maps the `Option<&String>` returned by
/// [`Subject::idp_id`] to `Option<&str>`.
pub fn idp_id(&self) -> Option<&str> {
self.subject().idp_id().map(std::string::String::as_str)
}
}