@@ -7,6 +7,8 @@ the MAuth protocol, and verify the responses. Usage example:
7
7
release any code to Production or deploy in a Client-accessible environment without getting
8
8
approval for the full stack used through the Architecture and Security groups.
9
9
10
+ ## Outgoing Requests
11
+
10
12
``` no_run
11
13
use mauth_client::MAuthInfo;
12
14
use reqwest::Client;
@@ -49,9 +51,136 @@ match client.get("https://www.example.com/").send().await {
49
51
# }
50
52
` ` `
51
53
54
+ # # Incoming Requests
55
+
52
56
The optional `axum-service` feature provides for a Tower Layer and Service that will
53
57
authenticate incoming requests via MAuth V2 or V1 and provide to the lower layers a
54
- validated app_uuid from the request via the ValidatedRequestDetails struct.
58
+ validated app_uuid from the request via the `ValidatedRequestDetails` struct. Note that
59
+ this feature now includes a `RequiredMAuthValidationLayer`, which will reject any
60
+ requests without a valid signature before they reach lower layers, and also a
61
+ ` OptionalMAuthValidationLayer` , which lets all requests through, but only attaches a
62
+ ` ValidatedRequestDetails` extension struct if there is a valid signature. When using this
63
+ layer, it is the responsiblity of the request handler to check for the extension and
64
+ reject requests that are not properly authorized.
65
+
66
+ Note that `ValidatedRequestDetails` implements Axum's `FromRequestParts`, so you can
67
+ specify it bare in a request handler. This implementation includes returning a 401
68
+ Unauthorized status code if the extension is not present. If you would like to return
69
+ a different response, or respond to the lack of the extension in another way, you can
70
+ use a more manual mechanism to check for the extension and decide how to proceed if it
71
+ is not present.
72
+
73
+ # ## Examples for `RequiredMAuthValidationLayer`
74
+
75
+ ` ` ` no_run
76
+ # async fn run_server() {
77
+ use mauth_client::{
78
+ axum_service::RequiredMAuthValidationLayer,
79
+ validate_incoming::ValidatedRequestDetails,
80
+ };
81
+ use axum::{http::StatusCode, Router, routing::get, serve};
82
+ use tokio::net::TcpListener;
83
+
84
+ // If there is not a valid mauth signature, this function will never run at all, and
85
+ // the request will return an empty 401 Unauthorized
86
+ async fn foo() -> StatusCode {
87
+ StatusCode::OK
88
+ }
89
+
90
+ // In addition to returning a 401 Unauthorized without running if there is not a valid
91
+ // MAuth signature, this also makes the validated requesting app UUID available to
92
+ // the function
93
+ async fn bar(details: ValidatedRequestDetails) -> StatusCode {
94
+ println!("Got a request from app with UUID: {}", details.app_uuid);
95
+ StatusCode::OK
96
+ }
97
+
98
+ // This function will run regardless of whether or not there is a mauth signature
99
+ async fn baz() -> StatusCode {
100
+ StatusCode::OK
101
+ }
102
+
103
+ // Attaching the baz route handler after the layer means the layer is not run for
104
+ // requests to that path, so no mauth checking will be performed for that route and
105
+ // any other routes attached after the layer
106
+ let router = Router::new()
107
+ .route("/foo", get(foo))
108
+ .route("/bar", get(bar))
109
+ .layer(RequiredMAuthValidationLayer::from_default_file().unwrap())
110
+ .route("/baz", get(baz));
111
+ let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
112
+ serve(listener, router).await.unwrap();
113
+ # }
114
+ ` ` `
115
+
116
+ # ## Examples for `OptionalMAuthValidationLayer`
117
+
118
+ ` ` ` no_run
119
+ # async fn run_server() {
120
+ use mauth_client::{
121
+ axum_service::OptionalMAuthValidationLayer,
122
+ validate_incoming::ValidatedRequestDetails,
123
+ };
124
+ use axum::{http::StatusCode, Router, routing::get, serve};
125
+ use tokio::net::TcpListener;
126
+
127
+ // This request will run no matter what the authorization status is
128
+ async fn foo() -> StatusCode {
129
+ StatusCode::OK
130
+ }
131
+
132
+ // If there is not a valid mauth signature, this function will never run at all, and
133
+ // the request will return an empty 401 Unauthorized
134
+ async fn bar(_: ValidatedRequestDetails) -> StatusCode {
135
+ StatusCode::OK
136
+ }
137
+
138
+ // In addition to returning a 401 Unauthorized without running if there is not a valid
139
+ // MAuth signature, this also makes the validated requesting app UUID available to
140
+ // the function
141
+ async fn baz(details: ValidatedRequestDetails) -> StatusCode {
142
+ println!("Got a request from app with UUID: {}", details.app_uuid);
143
+ StatusCode::OK
144
+ }
145
+
146
+ // This request will run whether or not there is a valid mauth signature, but the Option
147
+ // provided can be used to tell you whether there was a valid signature, so you can
148
+ // implement things like multiple possible types of authentication or behavior other than
149
+ // a 401 return if there is no authentication
150
+ async fn bam(optional_details: Option<ValidatedRequestDetails>) -> StatusCode {
151
+ match optional_details {
152
+ Some(details) => println!("Got a request from app with UUID: {}", details.app_uuid),
153
+ None => println!("Got a request without a valid mauth signature"),
154
+ }
155
+ StatusCode::OK
156
+ }
157
+
158
+ let router = Router::new()
159
+ .route("/foo", get(foo))
160
+ .route("/bar", get(bar))
161
+ .route("/baz", get(baz))
162
+ .route("/bam", get(bam))
163
+ .layer(OptionalMAuthValidationLayer::from_default_file().unwrap());
164
+ let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
165
+ serve(listener, router).await.unwrap();
166
+ # }
167
+ ` ` `
168
+
169
+ # ## Error Handling
170
+
171
+ Both the `RequiredMAuthValidationLayer` and the `OptionalMAuthValidationLayer` layers will
172
+ log errors encountered via `tracing` under the `mauth_client::validate_incoming` target.
173
+
174
+ The Required layer returns the 401 response immediately, so there is no convenient way to
175
+ retrieve the error in order to do anything more sophisticated with it.
176
+
177
+ The Optional layer, in addition to loging the error, will also add the `MAuthValidationError`
178
+ to the request extensions. If desired, any request handlers or middlewares can retrieve it
179
+ from there in order to take further actions based on the error type. This error type also
180
+ implements Axum's `OptionalFromRequestParts`, so you can more easily retrieve it using
181
+ ` Option<MAuthValidationError>` anywhere that supports extractors.
182
+
183
+ # ## OpenTelemetry Integration
55
184
56
185
There are also optional features `tracing-otel-26` and `tracing-otel-27` that pair with
57
186
the `axum-service` feature to ensure that any outgoing requests for credentials that take
0 commit comments