How to Re-Authenticate a User with Token?

A typical authentication flow for realm-web is:

  1. Authenticate a user, and get a token
  2. Call Realm’s GraphQL with Authorization: Bearer <token> header

This is no problem at all. Now, my question is how to use your own api, instead of Realm’s GraphQL endpoint.

  1. Client-side: Authenticate a user, and get a token
  2. Client-side: Call a self-provided api endpoint with the token
  3. Server-side: Receive the token, and identify the user (→ How?)

(FYI: I’m using Google OAuth for the step#1)

I faced this usecase with Next.js. Logins happen on client side, but later some logics may run on server side. The same user identities/roles wanted to be used.

Ideally something like:

// on server side

// receive the token in request header (or in GET query?)
const token = req.headers.token;
// can we do like this?
const credentials = Realm.Credentials.tokenAlreadyIssuedByRealm(token);
const user = await app.logIn(credentials);

Do I have to use the User Api Key for this kind of usecase, or am I missing something?

1 Like

Hi @Toshi,

Why not to use Realm Google Auth to get a token and then use it for graphQL?

Any auth provider can be used to get an access token.

Best
Pavel

Thanks for response!
Before trying your suggestion, please allow me to clarify what I’m doing.

So, I already used Realm Google Auth, and made it work successfully on the client-side (with realm-web SDK). Then, please assume I have to use a separate 3rd party serverless function. Not a GraphQL but a REST one. (More precisely, it’s a Next.js api fuction, but it doesn’t matter)

User workflow is like this:
First, a user clicks “Login with Google” button. Redirects to Google’s login screen. Then redirects back to the original page (localhost), successfully logged in.
Then, the browser fetches an external api.

My question – now on the API server, how I can tell the request comes from (on behalf of) the user?
Do you still think I should somehow use Google Realm Auth on the server side again?

Hi @Toshi,

So if I understand you correctly you want to call a http webhook on behalf of the already authenticated user?

If my assumption is correct I advise you to review my detailed example here on how to use payload user_id field to run a webhook by this user

Best
Pavel

Thank you, the post itself looks informative, but hmm…

So if I understand you correctly you want to call a http webhook on behalf of the already authenticated user?

For this question, the answer is yes and no – yes, I want to send a http request on behalf of the already authenticated user, but not toward the realm cloud, but to a normal NodeJS server I set up.

An analogy may be described like this: So you have “React for frontend + Rails for backend” combination, but the authentication is done on React (=browser) side. You want to allow the backend to access the user object to do some CRUD operations respecting the roles.
(*I’m not using Rails, but NodeJS of course)

How can the backend (NodeJS) get access to the user object?
Is your post mentioned above also applicable to this problem?

@Toshi, if this node js code need to get access to the Realm resources and respect the defined roles you will need to authenticate it through realm .

Now for webhooks you csn have the NodeJS code call them with the realm user_id, with the script auth this should be enough to set the excutor.

If you have some unrelated to realm tasks for this node code its up to you how to store/identify the user.

Pavel

Hmm, let me take some time to understand what you said.

OK, let me seek for another workaround… after all I want to re-authenticate a user from my next.js code.

So from the post you mentioned above, I found you said:

Basically any provider can be authenticated via the following url:
https://realm.mongodb.com/api/client/v2.0/app/<yourappid-abcde>/auth/providers/<provider type>/login

I’m curious about this. Can it be used with Google Auth as the provider type? Could you refer to any doc about it if any?

Hi @Toshi,

It seems to be possible if you will investigate what payload is expected by google and you con grab from Realm side…

Basically it seems that it looks for one of the following:

$ curl --location --request POST 'https://realm.mongodb.com/api/client/v2.0/app/app-123/auth/providers/oauth2-google/login' \
>   --header 'Content-Type: application/json' \
>   --data-raw '{
>   authCode : ""
>   }'
{"error":"failed to load auth request","er-location --request POST 'https://realm.mongodb.com/api/client/v2.0/app/friendlychat-amzgp/auth/providers/oauth2-google/login'   --header 'Content-Type: application/json'   --data-raw '{
  }'
{"error":"expected either accessToken, id_token or authCode in payload","error_code":"AuthError"

So if you have accessToken, id_token or authCode from your initial auth you should be able to reauthenticate

Also read:

Best regards,
Pavel

Thank you! But unfortunately the requirements of authCode seems not a simple solution to the problem.

Meanwhile, I just found an article which does exactly what I want to do, although it features AWS Amplify.

Please check the first 2-3 sections. Those sentences really well explain how I (and other Next.js developers) want to use authentication.

In short, all I want is Realm version of this:

import { withSSRContext } from 'aws-amplify'
// ...
export async function getServerSideProps(context) {
  const { Auth } = withSSRContext(context)
  const user = await Auth.currentAuthenticatedUser() //← this line
  // ...
}

But I now suspect Realm currently does not have similar capability yet. Why I think so is because Amplify team shipped this feature only 6 days ago. You can find the discussion in this github issue.

I’ve been watching the issue for weeks, but until today I didn’t think it relates to authentication. I now start thinking that implementing a client-server integrated auth system is a difficult task…

What are your thoughts?

1 Like

Thank you! But unfortunately the requirements of authCode seems not a simple solution to the problem.

Can you explain why this isn’t simple? Is this because you can’t re-use the auth code that’s generated from a log-in twice?

At the moment it looks like a possible workaround is using JWT Authentication w/ Realm + Auth0/Cognito/any other Auth Service.

You can:

  1. login w/ google using the 3rd part auth provider and get the JWT
  2. provide the JWT to Realm via the client to log the user in
  3. provide the JWT to your server via the auth header in your request
  4. use the Node SDK/ POST request w/ the JWT
1 Like

Hi Sumedha, thank you. To explain this, I need details, so you’ll have to read a little long post – but let me do:

Can you explain why this isn’t simple? Is this because you can’t re-use the auth code that’s generated from a log-in twice?

One reason is that I’m not using Google’s SDK, so in my current code no authCode and id_token appear.

What does that mean? The first thing to mention, how I use Realm.Credentials.google() func. In Realm’s official doc, you find Realm.Credentials.google(accessToken). The argument is accessToken acquired via Google’s SDK. But I found another blog post which shows me I can use Realm.Credentials.google(redirectUri) instead. It looked simpler so I adopted this approach.

The code looks something like this:

const redirectUri = process.env.NEXT_PUBLIC_REALM_LOGIN_REDIRECT_URL;
//...
const logIn = async () => {
  const credentials = Realm.Credentials.google(redirectUri);
  await app.logIn(credentials);
};
// in JSX
<button onClick={logIn}>Log In with Google</button> 

This code successfully redirects user to Google’s login page. (I also used handleAuthRedirect from realm-web SDK to handle the user redirected back)

So, in my current code, I don’t know about Google’s id_token or authCode. They are nicely hidden behind. Yes if I write some code I will get access to them, but I want to skip the steps if possible. Does it make sense to you?


So next, the latter half, about JWT – yes, it looked very attractive so I have been trying it as a plan B in parallel. Actually, Next.js has a library called next-auth which can issue JWT as a sort of 3rd party authenticator. It also beautifully handles the client-server shared authentication.

The main problem I faced was that it seemed I have to manage two different logins accordingly. For example, a user clicks “Log out” button, it triggers next-auth’s (3rd party’s) sign-out function. It doesn’t trigger Realm’s sing-out automatically and the user may remain signed-in with Realm. I may write my own code to handle this, but if there’s a bug it’s serious, so I gave up with this plan B. (I’ll maybe try again, I might be wrong)

Now you might come up with further questions, if so, please ask.

1 Like

Hey Toshi – your approach for Google seems fine for only client-side authentication, but probably will not work for the case of trying to re-authenticate from the server. Even if you took the Auth Code approach with the Google SDK, an Auth Code would not work given that you’re only allowed to use it once. I just wanted to confirm how you were going about it.

I also want to warn you that using the redirect_uri approach may not be the best choice if you’re taking your application into production. The reason we’ve left it out of the documentation is we’re in the process of understanding a recent change in Google’s API which no longer lets users verify their application on Google Cloud when using the realm/stitch as their Redirect URI.


As for using the Realm logout simultaneously with next-auth’s 3rd party sign-out function, I don’t see why you wouldn’t be able to call the logout function after the user clicks the ‘Logout’ Button, unless there is something else that I’m not catching

Hi Sumedha,

but probably will not work for the case of trying to re-authenticate from the server.

Yes, I definitely agree with you. And that is the reason why I’m seeking (in this thread) for a general, provider-agnostic way to re-authenticate a user on server, who already logged in on browser.

I also want to warn you that using the redirect_uri approach may not be the best choice

Hmm that’s a very interesting story. Thank you for letting me know. I’ll definitely follow your advice.

I don’t see why you wouldn’t be able to call the logout function after the user clicks the ‘Logout’ Button

That’s a good question, you may be right – last time I tried implementing it, I smelled some future complexity and just stopped, but maybe I was wrong. I’ll have to try again.


So now – I’m feeling I’m taking too much time of you (and Pavel) for this issue. I appreciate your responsive feedbacks! At this moment it seems there’s no super elegant way to achieve client-server synced authentication, but I think I can find a way to do without it, thanks to Realm providing different approaches. (While I wanted to use remoteMongoClient on the server, Realm also allows us to run MQL on client side so next I will try make use of it)

So I’ll use my brain a bit more to find a way to achieve the same user experience without server-side re-authentication. Yet just in case, may I post another feature-request regarding this on feedback.mongodb.com?

1 Like