Abhishek
Abhishek11mo ago

getUserIdentity() resulting none

So I am calling http action from a 3rd party service in my case LinkedIn and following there 3 steps legged oauth. But while calling
const identity = await ctx.auth.getUserIdentity();
console.log(
"Response Data--------XXXXXXXXXX----->",
JSON.stringify(identity)
);
const identity = await ctx.auth.getUserIdentity();
console.log(
"Response Data--------XXXXXXXXXX----->",
JSON.stringify(identity)
);
Identity is returning null, how can I authenticate in this scenario? Sharing my full code for more ref : https://github.com/imaxisXD/AIvy-Post/blob/main/convex/oauth.ts
GitHub
AIvy-Post/convex/oauth.ts at main · imaxisXD/AIvy-Post
Contribute to imaxisXD/AIvy-Post development by creating an account on GitHub.
15 Replies
ian
ian11mo ago
HTTP Actions | Convex Developer Hub
HTTP actions allow you to build an HTTP API right in Convex!
ian
ian11mo ago
As in, passing the bearer token from the third party? If they’re calling directly, like a webhook, then there won’t be any auth there. You’ll have to pass user identifiers through in state (but make sure you do so securely - Google for strategies for oauth “state”). This is one reason to redirect back to the website instead of directly to convex- the website knows which user is logged in and can make an authenticated call.
Abhishek
AbhishekOP11mo ago
@ian quick doubt if a third party calls my http action then ctx auth will not be present in ctx right? And if I call some function from this http action then again these functions will not have auth right? So if I understood correctly, first redirect back to my nextjs route there I can check to auth with convex and then make an authenticated call. right ?
ian
ian11mo ago
That's right If the third party isn't specifying the Authorization header then you'll have to get the user from any data they send you. For example, once you swap the oauth code for tokens, you can decode those tokens and look at the data. Or use the token to fetch user data from the LinkedIn api. If you also want to know what logged-in-via-clerk user started the request, you need to channel the request back through the user's browser (vs. directly linkedin server -> convex server). make sense?
Abhishek
AbhishekOP11mo ago
after reading it multiple times, it makes some sense. Will it be alright to keep this chat open on more similar doubts related to this? If you also want to know what logged-in-via-clerk user started the request, you need to channel the request back through the user's browser (vs. directly linkedin server -> convex server). make sense? @ian so to do this, to know the user that started the request . Will it be possible to achieve this via nextjs route handlers?
ian
ian11mo ago
yes - if it's redirecting the user, the next nextjs route will be serving a request from the users's browser. This means it'll have access to the browser cookies to do server-side auth. You could alternatively handle this on the client-side on the page it redirected to - taking the query params from the page's location and passing them as arguments to convex when convex has the user logged in.
Abhishek
AbhishekOP11mo ago
So in conclusion @ian this is how it will be done : User clicks the btn -> goto linkedin server -> linkedin server calls your nextjs route (here we do user authentication ) -> then call linked in server to get access token through the code we received from previous call -> and store the user data through a convex function
ian
ian11mo ago
That works. Alternatively you can do
call linked in server to get access token through the code we received from previous call -> and store the user data through a convex function
As "call convex function with the code and get access token from linkedin from the convex function, get user data, and store it". If you need the token on the client to do other things, your approach makes sense. If you only need the token server-side, you can do the code exchange from Convex which will have a faster connection to LinkedIn. I'm assuming here that your user is "authenticated" with something other than LinkedIn. If here you're hoping to use LinkedIn as the primary login mechanism, let me know.
Abhishek
AbhishekOP11mo ago
So right now I'm not using LinkedIn as the primary login mechanism. But what I want to achieve is to store the access token with the user data(ie coming from the clerk) . In this case what approach will be good ?
ian
ian11mo ago
User clicks the btn -> goto linkedin server -> linkedin server calls your nextjs route (here we do user authentication ) -> call a convex action with the code we received from previous call -> from action, call linkedIn server to get access token from the code -> from the action, call an internalMutation via ctx.runMutation to store the access token. the internalMutation can call ctx.auth.getUserIdentity().
Abhishek
AbhishekOP11mo ago
@ian I tried this approach but still in action ctx.auth.getUserIdentity() is still returning a null If you could review the code, it would mean a lot to me. The Flow is -> UI (Calling through server action) -> Nextjs route -> Action UI: https://github.com/imaxisXD/AIvy-Post/blob/main/components/social-media-linkers/linkedin-connect-btn.tsx#L5 Route: https://github.com/imaxisXD/AIvy-Post/blob/main/app/api/linkedIn/oauth/route.ts Action: https://github.com/imaxisXD/AIvy-Post/blob/main/convex/oauth.ts#L63 Internal Mutation: https://github.com/imaxisXD/AIvy-Post/blob/main/convex/users.ts#L104 Thank you
ian
ian11mo ago
Ah, sorry I should have specified you need to call the convex action from the client where you're logged in, or pass a third argument to fetchAction with the token, if you can access the clerk auth state in that route the websocket client (useMutation) has the token set by the convex provider (but you should make sure it has fully authenticated first ) but from the nextjs server you need to manually specify it for each request
Abhishek
AbhishekOP11mo ago
@ian this got me So confused xD
ian
ian11mo ago
When you call fetchAction it doesn’t have any logged in state. You need to pass it as the third parameter. It’s on your api endpoint, server side. Clerk has nextjs middleware to make the token available there but you’ll have to look that up
John
John11mo ago
I think these are the docs for what you're tryinging to do. for example, here's a snippet for an nextjs api route where I used the auth token from clerk to call a mutation that requires a logged in user:
import { NextRequest, NextResponse } from "next/server";
import { api } from "@/convex/_generated/api";
import { fetchMutation } from "convex/nextjs";
import { auth } from "@clerk/nextjs";

// Clerk auth token for server-side requests
export async function getAuthToken() {
return (await auth().getToken({ template: "convex" })) ?? undefined;
}

export async function POST(req: NextRequest) {
const token = await getAuthToken();

///... do stuff

const postUrl = await fetchMutation(api.images.generateUploadUrl);

const result = await fetch(postUrl, {
method: "POST",
headers: { "Content-Type": "image/jpeg" },
body: blobImage,
});

const { storageId } = await result.json();

// pass in token here
await fetchMutation(api.images.storeStorageId, { storageId }, { token });

return new NextResponse(...);
}
import { NextRequest, NextResponse } from "next/server";
import { api } from "@/convex/_generated/api";
import { fetchMutation } from "convex/nextjs";
import { auth } from "@clerk/nextjs";

// Clerk auth token for server-side requests
export async function getAuthToken() {
return (await auth().getToken({ template: "convex" })) ?? undefined;
}

export async function POST(req: NextRequest) {
const token = await getAuthToken();

///... do stuff

const postUrl = await fetchMutation(api.images.generateUploadUrl);

const result = await fetch(postUrl, {
method: "POST",
headers: { "Content-Type": "image/jpeg" },
body: blobImage,
});

const { storageId } = await result.json();

// pass in token here
await fetchMutation(api.images.storeStorageId, { storageId }, { token });

return new NextResponse(...);
}
I can't remember if you also need a middleware.ts file for clerk, but heres the one I used for reference:
import { authMiddleware } from "@clerk/nextjs";

export default authMiddleware({
publicRoutes: ["/", "/images-pool"],
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
import { authMiddleware } from "@clerk/nextjs";

export default authMiddleware({
publicRoutes: ["/", "/images-pool"],
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
Next.js Server Rendering | Convex Developer Hub
Next.js automatically renders both Client and Server Components on the server

Did you find this page helpful?