openapi_lambda/middleware.rs
1use crate::{ApiGatewayProxyRequestContext, HeaderMap, HttpResponse, LambdaContext};
2
3use async_trait::async_trait;
4
5use std::future::Future;
6
7/// Middleware interface for handling request authentication and optionally wrapping each request
8/// (e.g., to perform logging/telemetry).
9///
10/// This trait is intended to be used with the [`#[async_trait]`](async_trait::async_trait)
11/// attribute.
12#[async_trait]
13pub trait Middleware {
14 /// Type returned by a successful call to [`authenticate`](Middleware::authenticate).
15 ///
16 /// This might represent a user, authentication session, or other abstraction relevant to
17 /// your API. If none of the API endpoints require authentication, simply use the unit type
18 /// (`()`).
19 type AuthOk: Send;
20
21 /// Authenticate the current request.
22 ///
23 /// # Arguments
24 ///
25 /// * `operation_id` - Operation ID associated with the current request (as defined in the OpenAPI
26 /// definition).
27 /// * `headers` - HTTP request headers (e.g., `Authorization`, `Cookie`, etc.).
28 /// * `request_context` - Amazon API Gateway request context containing information to identify
29 /// the AWS account and resources invoking the Lambda function. It also includes Cognito
30 /// identity information for the caller (see the
31 /// [`identity`](ApiGatewayProxyRequestContext::identity) field).
32 /// * `lambda_context` - Lambda function execution context.
33 async fn authenticate(
34 &self,
35 operation_id: &str,
36 headers: &HeaderMap,
37 request_context: &ApiGatewayProxyRequestContext,
38 lambda_context: &LambdaContext,
39 ) -> Result<Self::AuthOk, HttpResponse>;
40
41 /// Wrap an authenticated request.
42 ///
43 /// This method serves as an optional hook for running arbitrary code before and/or after each
44 /// request handler is invoked. For example, it may be used to implement logging or telemetry, or
45 /// to add HTTP response headers prior to returning the handler's [`HttpResponse`] to the client.
46 ///
47 /// If implemented, this method should invoke the `api_handler` argument as follows:
48 /// ```rust,ignore
49 /// api_handler(headers, request_context, lambda_context, auth_ok)
50 /// ```
51 ///
52 /// # Arguments
53 ///
54 /// * `api_handler` - API handler function to invoke.
55 /// * `operation_id` - Operation ID associated with the current request (as defined in the OpenAPI
56 /// definition).
57 /// * `headers` - HTTP request headers (e.g., `Authorization`, `Cookie`, etc.).
58 /// * `request_context` - Amazon API Gateway request context containing information to identify
59 /// the AWS account and resources invoking the Lambda function. It also includes Cognito
60 /// identity information for the caller (see the
61 /// [`identity`](ApiGatewayProxyRequestContext::identity) field).
62 /// * `lambda_context` - Lambda function execution context.
63 /// * `auth_ok` - Output of successful call to [`authenticate`](Middleware::authenticate) method.
64 async fn wrap_handler_authed<F, Fut>(
65 &self,
66 api_handler: F,
67 operation_id: &str,
68 headers: HeaderMap,
69 request_context: ApiGatewayProxyRequestContext,
70 lambda_context: LambdaContext,
71 auth_ok: Self::AuthOk,
72 ) -> HttpResponse
73 where
74 F: FnOnce(HeaderMap, ApiGatewayProxyRequestContext, LambdaContext, Self::AuthOk) -> Fut + Send,
75 Fut: Future<Output = HttpResponse> + Send,
76 {
77 let _ = operation_id;
78 api_handler(headers, request_context, lambda_context, auth_ok).await
79 }
80
81 /// Wrap an unauthenticated request.
82 ///
83 /// This method serves as an optional hook for running arbitrary code before and/or after each
84 /// request handler is invoked. For example, it may be used to implement logging or telemetry, or
85 /// to add HTTP response headers prior to returning the handler's [`HttpResponse`] to the client.
86 ///
87 /// If implemented, this method should invoke the `api_handler` argument as follows:
88 /// ```rust,ignore
89 /// api_handler(headers, request_context, lambda_context)
90 /// ```
91 ///
92 /// # Arguments
93 ///
94 /// * `api_handler` - API handler function to invoke.
95 /// * `operation_id` - Operation ID associated with the current request (as defined in the OpenAPI
96 /// definition).
97 /// * `headers` - HTTP request headers (e.g., `Authorization`, `Cookie`, etc.).
98 /// * `request_context` - Amazon API Gateway request context containing information to identify
99 /// the AWS account and resources invoking the Lambda function. It also includes Cognito
100 /// identity information for the caller (see the
101 /// [`identity`](ApiGatewayProxyRequestContext::identity) field).
102 /// * `lambda_context` - Lambda function execution context.
103 async fn wrap_handler_unauthed<F, Fut>(
104 &self,
105 api_handler: F,
106 operation_id: &str,
107 headers: HeaderMap,
108 request_context: ApiGatewayProxyRequestContext,
109 lambda_context: LambdaContext,
110 ) -> HttpResponse
111 where
112 F: FnOnce(HeaderMap, ApiGatewayProxyRequestContext, LambdaContext) -> Fut + Send,
113 Fut: Future<Output = HttpResponse> + Send,
114 {
115 let _ = operation_id;
116 api_handler(headers, request_context, lambda_context).await
117 }
118}
119
120/// Convenience middleware that performs no request authentication.
121///
122/// This middleware is intended for two use cases:
123/// * APIs without any authenticated endpoints.
124/// * APIs with authentication requirements that cannot be handled by
125/// [`authenticate`](Middleware::authenticate)
126/// (e.g., webhook handlers that require access to the raw request body in order to compute an
127/// HMAC). For this use case, each handler function should perform its own authentication rather
128/// than via the middleware.
129pub struct UnauthenticatedMiddleware;
130
131#[async_trait]
132impl Middleware for UnauthenticatedMiddleware {
133 type AuthOk = ();
134
135 async fn authenticate(
136 &self,
137 _operation_id: &str,
138 _headers: &HeaderMap,
139 _request_context: &ApiGatewayProxyRequestContext,
140 _lambda_context: &LambdaContext,
141 ) -> Result<Self::AuthOk, HttpResponse> {
142 Ok(())
143 }
144}