grafbase_sdk/extension/
authentication.rs

1use crate::{
2    component::AnyExtension,
3    types::{Configuration, Error, ErrorResponse, GatewayHeaders, PublicMetadataEndpoint, RequestContext, Token},
4};
5
6/// An authentication extension is called before any request processing, authenticating a user with
7/// a token or returning an error response.
8///
9/// # Example
10///
11/// You can initialize a new authentication extension with the Grafbase CLI:
12///
13/// ```bash
14/// grafbase extension init --type authentication my-auth
15/// ```
16///
17/// This will generate the following:
18///
19/// ```rust
20/// use grafbase_sdk::{
21///     AuthenticationExtension,
22///     types::{GatewayHeaders, Configuration, ErrorResponse, Token, Error, RequestContext}
23/// };
24///
25/// #[derive(AuthenticationExtension)]
26/// struct MyAuth {
27///     config: Config
28/// }
29///
30/// #[derive(serde::Deserialize)]
31/// struct Config {
32///     my_custom_key: String
33/// }
34///
35/// impl AuthenticationExtension for MyAuth {
36///     fn new(config: Configuration) -> Result<Self, Error> {
37///         let config: Config = config.deserialize()?;
38///         Ok(Self { config })
39///     }
40///
41///     fn authenticate(&mut self, ctx: &RequestContext, headers: &GatewayHeaders) -> Result<Token, ErrorResponse> {
42///         todo!()
43///     }
44/// }
45/// ```
46/// ## Configuration
47///
48/// The configuration provided in the `new` method is the one defined in the `grafbase.toml`
49/// file by the extension user:
50///
51/// ```toml
52/// [extensions.my-auth.config]
53/// my_custom_key = "value"
54/// ```
55///
56/// Once your business logic is written down you can compile your extension with:
57///
58/// ```bash
59/// grafbase extension build
60/// ```
61///
62/// It will generate all the necessary files in a `build` directory which you can specify in the
63/// `grafbase.toml` configuration with:
64///
65/// ```toml
66/// [extensions.my-auth]
67/// path = "<project path>/build"
68/// ```
69///
70pub trait AuthenticationExtension: Sized + 'static {
71    /// Creates a new instance of the extension. The [Configuration] will contain all the
72    /// configuration defined in the `grafbase.toml` by the extension user in a serialized format.
73    ///
74    /// # Example
75    ///
76    /// The following TOML configuration:
77    /// ```toml
78    /// [extensions.my-auth.config]
79    /// my_custom_key = "value"
80    /// ```
81    ///
82    /// can be easily deserialized with:
83    ///
84    /// ```rust
85    /// # use grafbase_sdk::types::{Configuration, Error};
86    /// # fn dummy(config: Configuration) -> Result<(), Error> {
87    /// #[derive(serde::Deserialize)]
88    /// struct Config {
89    ///     my_custom_key: String
90    /// }
91    ///
92    /// let config: Config = config.deserialize()?;
93    /// # Ok(())
94    /// # }
95    /// ```
96    fn new(config: Configuration) -> Result<Self, Error>;
97
98    /// Authenticate the user with a [Token] or return an [ErrorResponse]. It is called before any
99    /// GraphQL processing and an error will stop any further actions.
100    ///
101    /// The [HttpHeaders] are the headers received by the gateway before any header rules.
102    fn authenticate(&mut self, ctx: &RequestContext, headers: &GatewayHeaders) -> Result<Token, ErrorResponse>;
103
104    /// Define endpoints on the gateway that expose authentication related metadata. This can be used to implement [OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728), for example. This method is only called once, on gateway initialization. The endpoints are available on the gateway for GET requests at a custom path, and they return a static payload with custom headers.
105    ///
106    /// See the docs on [`public-metadata-endpoint`](public-metadata-endpoint) for details.
107    fn public_metadata(&mut self) -> Result<Vec<PublicMetadataEndpoint>, Error> {
108        Ok(vec![])
109    }
110}
111
112#[doc(hidden)]
113pub fn register<T: AuthenticationExtension>() {
114    pub(super) struct Proxy<T: AuthenticationExtension>(T);
115
116    impl<T: AuthenticationExtension> AnyExtension for Proxy<T> {
117        fn authenticate(&mut self, ctx: &RequestContext, headers: &GatewayHeaders) -> Result<Token, ErrorResponse> {
118            self.0.authenticate(ctx, headers)
119        }
120
121        fn public_metadata(&mut self) -> Result<Vec<PublicMetadataEndpoint>, Error> {
122            AuthenticationExtension::public_metadata(&mut self.0)
123        }
124    }
125
126    crate::component::register_extension(Box::new(|_, config| {
127        <T as AuthenticationExtension>::new(config).map(|extension| Box::new(Proxy(extension)) as Box<dyn AnyExtension>)
128    }))
129}