Home
Support Forums

Missing identity client context in rust netlify function

Hello,

I am working on a project that uses a rust Netlify function and have enabled identity service on the project. I expected that using identity with functions would work similar in rust as it is in js and go (identity/user objects inside context.clientContext as described here, but it seems that the client_context passed to the rust handler function is empty even when “Authorization” header is set in the request.

In my http request to the function, I set the “Authorization” header with the jwt obtained from netlify identity widget.

In the rust function, I attempt to get the client_context from the lambda_runtime::Context, but it is always None.

pub(crate) async fn my_handler(event: ApiGatewayProxyRequest, ctx: Context) -> Result<ApiGatewayProxyResponse, Error> {
    let path = event.path.unwrap();

    log::info!("context: {:?}", ctx);
    
    let resp = ApiGatewayProxyResponse {
        status_code: 200,
        headers: HeaderMap::new(),
        multi_value_headers: HeaderMap::new(),
        body: Some(Body::Text(format!("Hello from '{}'", path))),
        is_base64_encoded: Some(false),
    };
    Ok(resp)
}

7:01:12 PM: 2022-01-01 00:01:12,798 INFO [rust_identity] context: Context { client_context: None, identity: None }

When sending the same request to a function written in javascript, I am able to obtain the user from the context. So I believe the request is correct and the issue appears to be on the rust side.

This can be seen by creating the example/template rust netlify function (following guide here), logging the context passed to the handler, and making a request to the function with authorize header set with netlify identity widget jwt.

Any guidance as to how to use identity with a function in rust would be appreciated. I understand rust support is still experimental, so perhaps this is something that hasn’t been fully implemented yet?

Thanks!

I was able to get the user by calling the identity endpoint /user API. Here is a snippet in case anyone else stumbles on this also.

use aws_lambda_events::encodings::Body;
use aws_lambda_events::event::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse};
use chrono::{DateTime, Utc};
use http::header::HeaderMap;
use lambda_runtime::{handler_fn, Context, Error};
use log::LevelFilter;
use serde::{Deserialize, Serialize};
use simple_logger::SimpleLogger;

static IDENTITY_ENDPOINT: &str = "[app url goes here]/.netlify/identity";

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: String,                          // "id": "11111111-2222-3333-4444-5555555555555",
    email: String,                       // "email": "email@example.com",
    confirmation_sent_at: DateTime<Utc>, // "confirmation_sent_at": "2016-05-15T20:49:40.882805774-07:00",
    created_at: DateTime<Utc>,           // "created_at": "2016-05-15T19:53:12.368652374-07:00",
    updated_at: DateTime<Utc>,           // "updated_at": "2016-05-15T19:53:12.368652374-07:00"
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    SimpleLogger::new()
        .with_level(LevelFilter::Info)
        .init()
        .unwrap();

    let func = handler_fn(my_handler);
    lambda_runtime::run(func).await?;
    Ok(())
}

pub(crate) async fn my_handler(
    request: ApiGatewayProxyRequest,
    ctx: Context,
) -> Result<ApiGatewayProxyResponse, Error> {
    let path = request.path.unwrap();

    // even though using netlify identity, client_context is not set
    log::info!("context: {:?}", ctx);

    // re-use the authorization header from the request, and call identity
    match request.headers.get("Authorization") {
        None => {
            log::info!("authorization header not found, skipping identity call");
        }
        Some(jwt) => {
            log::info!("placing identity call");
            let token = jwt.to_str().unwrap().trim_start_matches("Bearer ");
            log::info!("token: {}", token);
            let client = reqwest::blocking::Client::new();
            let identity_request = client
                .get(format!("{}/user", IDENTITY_ENDPOINT))
                .bearer_auth(token)
                .build()
                .unwrap();
            let identity_response = client.execute(identity_request)?;
            let user: User = identity_response.json()?;
            log::info!("user: {:?}", user);
        }
    }

    let resp = ApiGatewayProxyResponse {
        status_code: 200,
        headers: HeaderMap::new(),
        multi_value_headers: HeaderMap::new(),
        body: Some(Body::Text(format!("Hello from '{}'", path))),
        is_base64_encoded: Some(false),
    };
    Ok(resp)
}

Thanks