using the medplum sdk in node with BFF
# support
l
we're starting a spike on how we might implement medplum at miga, and desire to use the backend for frontend pattern in our implementations. (see this article if you're not familiar with that pattern: https://samnewman.io/patterns/architectural/bff/). the reason we want to implement bff are that 1) it really simplifies our front-end architectures (one api call per page, less complex client side state) and 2) we can mix in non-medplum data from other databases or api calls in the bff, for example we will want to implement one time login codes via SMS for our patients) it looks like the medplum sdk is intended to run on the browser, and not on a server/backend -- although the docs are a little conflicting on this, on one page it says browser or node.js, and on another it says browser only. (we're specifically trying to first create/auth a user through a backend layer) before we go too deep into this on our end, wanted to check in with medplum and ask what the vision is for your sdk, and whether the bff approach is not well suited to medplum.
r
Good question! So we do run the SDK on the backend - in Bots, but do not use this pattern ourselves in production for the Server. We do have customers who use it this way though, and it does work. Let me get back to you shortly on the the philosophy. Prefer not to give a glib answer of course!
Updated thought: we do plan to support this BFF pattern and make it a first class citizen. There are known rough edges with the experience today. Question - what kind of auth do you plan to support/enable?
v
Hello! I'm the engineer working on this with @limbera 🙂 We're trying to use google auth. I've also tried email+password. Both hit the error:
crypto is not defined
. This looks to be because the auth functions in the SDK depend on the web crypto API.
r
2 thoughts: 1) I'm guessing this is an issue with dependencies, and so I will call a lifeline here @rahul1 2) I think we should debug 2 things separately - the dependencies and the auth. Assuming you are running this code on the server, could I ask you to try basicAuth in the SDK https://www.medplum.com/docs/sdk/classes/MedplumClient#setbasicauth ??
r
Hi @vork_jmc - what kind of server environment are you running in? NodeJS should have the crypto library available as well, but it can depend on module versions, tsconfig (if using typescript), and a few other thigns
v
(1) I also need to know how to call functions as a user. It looks like this is possible in the
options
parameter, but I'd like to confirm this and get an example. (2) is that new? I believe I used
startClientLogin
to auth the client and perform actions. Is
setBasicAuth
the same?
@rahul1 NodeJS written in TypeScript. The error gets thrown here: https://github.com/medplum/medplum/blob/66b51d6e16c03c24aadebcf9c7eeed25b836e697/packages/core/src/crypto.ts#L8. NodeJS 18 has an experimental webcrypto api, but still requires an import in the file. This is what I see in their docs: https://nodejs.dev/en/api/v18/webcrypto/#encryption-and-decryption
r
> (2) is that new? I believe I used startClientLogin to auth the client and perform actions. Is setBasicAuth the same? Yes, the SDK support for basicAuth is new, though the server has supported it for some time.
> I believe I used startClientLogin to auth the client and perform actions. Is setBasicAuth the same? Just wanted to give a clarification here: - startClientLogin performs the OAuth2 Client Credentials exchange (https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow), which involves a an HTTP handshake to the auth server and storing an access token locally - setBasicAuth doesn't perform a handshake, it just stores an HTTP Basic Auth header (https://www.twilio.com/docs/glossary/what-is-basic-authentication) that is sent with every request. I'm still looking into the
crypto
issue to solve your problem of running a function AS the user, but Basic Auth / Client Credentials should at least unblock you from being able to make Medplum API calls
c
Hi @vork_jmc - we might need to do a deeper exploration to use Google Auth with the BFF pattern. To do that in a way to complies with Google's policies, it will require a series of redirects between (1) your client, (2) your server, (3) medplum server, (4) google auth, and all the way back. It is technically possible, although perhaps not easily with
MedplumClient
today.
v
What if I used the sdk directly in the browser to auth and then send the auth token to the bff for subsequent actions?
r
@vork_jmc Apologies because I'm not very familiar with the BFF pattern, but I've been doing some research and posted some resources below. It seems like most BFF auth patterns involve creating encrypted first-party cookies between the BFF server and the browser. That cookie could contain the medplum login state (
medplum.getActiveLogin()
) , which could be cached server side https://cloudentity.com/developers/blog/adding-oauth-proxy-bff-component-to-spa/ https://dev.to/damikun/web-app-security-understanding-the-meaning-of-the-bff-pattern-i85 https://abstarreveld.medium.com/what-is-a-bff-and-how-to-build-one-e2a2b78cfc43
Question for clarification, does you app use a custom login page with the Google Auth button embedded, or is it using the medplum SignInForm component, or some other solution?
v
I used @react-oauth/google:
Copy code
import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from "@react-oauth/google"
import { trpc } from "../utils"

const SignUp = () => {
  const signUp = trpc.signUp.useMutation();

  const handleSuccess = ({ clientId, credential }: CredentialResponse) => {
    if (clientId && credential) signUp.mutate({ googleClientId: clientId, googleCredential: credential })
  }

  return (
    <GoogleOAuthProvider clientId="936888910715-fbs96p0heu961p6a6jmakq8gkaquoebo.apps.googleusercontent.com">
      <div style={{ minHeight: '100vh' }}>
        <GoogleLogin onSuccess={handleSuccess} onError={console.error} />
      </div>
    </GoogleOAuthProvider>
  )
}

export default SignUp
r
Ah I see! Yes, since you're already getting the google credential client-side, I'd recommend running the
medplum.startGoogleLogin
on the client as well, and then potentially caching the login state in your BFF
You can then use
medplum.setActiveLogin
to set those credientials on your API client and make the call
I will say that BFF is new ground for our team, so there might be some unforseen issues - but we'll work through it with you!
v
Thank you! I'm digging into next-auth (https://next-auth.js.org). I think I can use it to auth with medplum on the client side and share that context with the server side. I'll let you know how I go!
r
Great! We'd love to know how it goes. Feel free to start another thread about NextAuth.js <> Medplum if you have questions about that integration - I'm sure a lot of people will be interested
m
@vork_jmc could you share how you managed 1) integration with nextAuth + medplum 2) the error on your node backend "crypto is not defined" and how you solved it? we currently have a similar situation / the same problem 🤔
153 Views