Inject the current user in a TS function

Hey there !

We’re using OSDK in native mobile apps (+ web clients as well)

I have a typescript function that executes a certain logic for a user.
For obvious security reasons, I’d like this logic to only be applied to the user that is currently authenticated through the OSDK (using a Foundry token).

Is there a way to, inside a typescript function (a Query one more specifically), inject the current user ? (A possible “workaround” would be to have a userId as a param for the said function, but that means anyone can call the function with any id, and I’d like to avoid that)

Thanks a lot !

1 Like

I am guessing from your question that you are using confidential client (service user) and not a public client application?
If you create a public client application it will require each user to have a Foundry user, and will take the user through a simple login process, after-which each call would be made on behalf of the user.
I am not sure how you plan to use the user context in the functions you call but in the process above you are gurenteed that the function is executed using the user’s permission.
Note that if you are using npm create @osdk/app@2.1.0-beta.19 you will have the option to generate an Expo (React Native) template to use with OSDK which might help you as a reference.

1 Like

Thanks for your reply!

Hmm seems like my initial post wasn’t clear enough so let me explain a bit further:

  • Mobile app: using client OSDK ( so not a service user, a public client)

  • TS repo on foundry: typescript functions, exposed to clients through OSDK.

Here’s a fictional scenario + pseudo code to explain the situation:

I want to expose a function that executes a certain logic depending on the current user, for example creating a customer in an external system.

So we’re gonna have a function like this:

async createCustomerInExternalSystem() {
  const theInfoIwant =  await fetch("externalSystemUrl/customers", { method: "POST" })
  return theInfoIwant
}

Now image the externalSystemUrl/customers endpoint requires a certain information from the user, for example his email. I’d then like to do something like this:

async createCustomerInExternalSystem() {
  const currentUser = getCurrentUser() // gotten from the token that's in the http call for this method
  const theInfoIwant =  await fetch("externalSystemUrl/customers", { 
      method: "POST"; 
      body: { 
           email: currentUser.email
      } 
  })
  return theInfoIwant
}

or even using injection if possible as a few backend frameworks do, something like this:

 // gotten from the token that's in the http call for this method
async createCustomerInExternalSystem(currentUser?: Principal) {
  const theInfoIwant =  await fetch("externalSystemUrl/customers", { 
      method: "POST"; 
      body: { 
           email: currentUser.email
      } 
  })
  return theInfoIwant
}

Then, the mobile app would call this function using the public OSDK client:

client(createMeasurementV2).applyAction()

and the TS function would get the current user based on the authenticated user from the app, as he sends a Foundry token in the request.

I hope it’s a bit clearer now.
I currently can’t really inject the current user into a function.
Is that currently possible ?

Thanks !

So you want the current user context in the functions.
You have 2 options for this:

  1. If you are using an action, in the rules page you can pick current user as an input parameter and not data coming from the OSDK client, then the action can use the client id to do whatever you want.
  2. You can use PSDK to get the current user details in the OSDK application and send it as parameter to the function without requiring the user to set it.
import { getCurrent, User, profilePicture } from "@osdk/foundry.admin/User";
import { platformClient } from '@/Foundry/client';

const PsdkUser: React.FC = () => {
    const [user, setUser] = useState<User | undefined>();

    const getProfile = useCallback(async () => {
        const result = await getCurrent(platformClient, { preview: true });

HTH,

  • Regarding option 1: I need my function to return things, so it’s not an Ontology Edit Function, but a Query. (@Query({ apiName: ...})), which means it’s not really an action, in the sense that queries can only be edited from the repo, not from the UI, and rules can’t be configured.

  • Regarding option 2: that means there’s an exposed api endpoint that takes a user param, which to me seems like a security issue as anyone with a token can call the endpoint (in our context a token is easily obtainable as it’s a B2C app). For now I’m just using a usedId as a param for that function, which reduces the risk, ideally I’d just get the user calling the function inside the TS function, but I’m guessing from your answer that it’s not currently possible ?

You are correct on #1 I just wasn’t sure on what was your use case.
Regarding #2 this is less of an OSDK question and more of a Functions questions but you can’t get the calling user id from a function (Query) you can have a parameter of the userID and then use that parameter to query for other user’s attributes like email so you reduce the risk but not eliminate it.

import { Function, Users } from "@foundry/functions-api";

export class UserFunctions {
    @Function()
    public async getUserEmail(userId: string): Promise<string | undefined> {
        try {
            const user = await Users.getUserByIdAsync(userId);
            return user.email;
        } catch (error) {
            console.error(`Error fetching user: ${error}`);
            return undefined;
        }
    }
}

BTW, it does make sense to make it into a function backed action and not use a Query because query do not expect to have a side effect of writing into some system.
if you make it into an action like I suggested in #1 you both make it clear that this will write data, you get the current user for free and you are able to write the result of the request.
I would look into webhook for this.

Yeah for now I’m using a userId as parameter, so I’m reducing the risk but not eliminating it. So you’re saying there’s currently no way to eliminate it (for example a mechanism that injects the current user in the function) ?

The reason I’m using a query and not an Ontology Edit Function is that I need the call to immediately return something (ephemeral payment data that should not be stored anywhere), and since Queries can return and OEF can’t, I have to use queries.

tagging functions as you want to get these details in Functions.

1 Like

We do not expose any APIs in Typescript functions to get the calling user’s ID. I think you will need to use a user ID parameter.

The security implications of exposing such a function is context-dependent. In general, I would expect this to be safe since the function will use the token provided in the request for any Foundry API calls (e.g., loading user information, loading objects, executing webhooks, etc.).

In your case, it might make sense to just perform the POST request to an external system outside of the function (i.e., just use functions to do any necessary pre- or post-processing).

That said, we are working towards a new OSDK-based template for Typescript functions which would allow you to get the current user’s information.

The POST requests involves using sensitive information regarding payments and admin API keys that are not safe to use or store on the frontend, so unfortunately I can’t really make the call there and only pre-process/post-process in a function.

For now, I’m passing the userId as parameter of the query, fetching an object that’s protected by a restricted view regarding this userId (this call is unnecessary to the rest of the logic, I’m basically using it to trigger a permission check => if you can’t fetch the object with this userId then it’s not you), the performing the necessary POST requests and returning from the query.

BTW, queries don’t support web hooks with side effects, but I need an immediate return so I “tricked” the query by setting the POST web hook as a read web hook (it’s not, it mutates things in external systems). Not sure if there’s a “cleaner” way of doing this.

That said, we are working towards a new OSDK-based template for Typescript functions which would allow you to get the current user’s information.

Looking forward to it, thanks !

Hi all,

I have had experience implementing this in TS function, I think that solution will work for you too.

You will need to create a dataset with all user ids for this. For production application, you’d need to build a pipeline for this, but there are ways to set it up in a secure manner.

Once you have a dataset with all user ids, create a Restricted view on top of it with a policy where user’s can only see rows where their user ID matches the row’s value of user ID. You may see where I am leading with this. Create an object type backed by this restricted view. Now, whenever you query for all objects of that type with user’s permissions, you’ll get only one object back, and it will have a property with user id in it. It’s an unfortunate side effect, and will likely make your function a bit slower (though you can do this async)

@alaa-patient1st , you were on the right track with the Restricted view, hope this solution works for you!

Cheers,
Yurii

1 Like