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}