Creating a database adapter for an external backend REST API #11571
Replies: 9 comments
-
I have done the same exact thing for my app. It was the only solution |
Beta Was this translation helpful? Give feedback.
-
I couldn't reach you on any other platform so I'll ask my questions here, I like your approach and would want to implement the same thing in my app, but still want to use Nestjs guards or maybe a brief example of how to authorize on Nestjs side or any further config that is required. Great work Basem |
Beta Was this translation helpful? Give feedback.
-
Hi @basememara this is perfect solution so we only have one api to communicate with. I'm thinking of also building a mobile app and a sveltekit for the web. I was thinking the same solution but, my nest.js protected routes are only accessible via JWT. But we are encoding the JWT on the next.js side because we're just returning the user from nest.js? Is this correct? I have a dedicated login/registration mechanism on nest js but they only spit out accessToken and refreshToken instead of user. Can't wrap my head around on how can next.js talk to the nest.js protected routes api if the JWT was not from the nest app. |
Beta Was this translation helpful? Give feedback.
-
@SaadiSidali the only additional things I can think of that may help further is the following... I added a new middleware called @Injectable()
export class UserMiddleware implements NestMiddleware {
async use(req: Request, _res: Response, next: NextFunction) {
// Validate token if provided
const token = await getToken({ req });
if (token?.sub) {
req.user = {
id: token.sub,
name: token.name || null,
email: token.email || null,
custom1: token.custom1
};
return next();
}
throw new UnauthorizedException('Credentials not provided or incorrect');
}
} After attaching the user details to all // User.decorator.ts
export const User = createParamDecorator((data: keyof AuthUser, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return data ? request.user?.[data] : request.user;
});
// AuthUser.ts
export interface AuthUser {
id: string;
name: string | null;
email: string | null;
custom1: string | null;
}
// types.d.ts
declare module 'express-serve-static-core' {
interface Request {
user?: AuthUser;
}
}
declare module 'next-auth/jwt' {
interface JWT extends AuthUser {}
} |
Beta Was this translation helpful? Give feedback.
-
@kingroho the reason the NestJS can read tokens generated from the Next.js app is because they share the same env variable |
Beta Was this translation helpful? Give feedback.
-
Wow, I did not know you can use the same secret to decode/encode JWTs. I'll use your code as a reference then. Thank you. |
Beta Was this translation helpful? Give feedback.
-
@basememara Thanks for sharing, this is the missing documentation that I'm looking for. I hope we can bring this into official documentation of how to make a customised Adapter for an existing backend. My question, does this implementation also work with for example login with social media account like Facebook/GitHub? I guess yes because looks like the adapter passes all the requests to the backend. |
Beta Was this translation helpful? Give feedback.
-
Hey @sangdth, yes I have this fully functioning in production with Google login as one of my providers. Right that's the beauty is everything else falls in place because of the NextAuth adapter architecture. |
Beta Was this translation helpful? Give feedback.
-
Hey @basememara, thanks for sharing your approach - super helpful. Should we not include session tokens in the request URL to avoid man-in-the-middle attacks or leaking of session ids elsewhere? Is this a valid concern? |
Beta Was this translation helpful? Give feedback.
-
Description 📓
I would like my frontend to handle the authentication but use a backend for database access.
For context, I have a Next.js app on the frontend that communicates to a NestJS app for the backend. I would like all the authentication to happen on the Next.js frontend app which has the NextAuth installation, but the data access handled by the NestJS app. This is so I don't have to share the database between 2 servers but instead only the backend has database access. To achieve this, I need to create a custom database adapter that makes REST API requests to the NestJS backend app.
Also note that I do plan on using JWT which does not require a database, but I want to still leverage a database adapter with JWT so NextAuth can handle the create/update user, social account flows, and email verifications. I believe only the session functions will be unused in the adapter + JWT scenario.
I tried implementing an adapter that connects an external backend API and it was under-estimated based on other discussions. The most difficult part was the serialization and deserialization of the objects between frontend and backend servers.
I would like to share my implementation to help others and hopefully I can also get peer reviewed by the community, and eventually contribute it as a REST API adapter. Here's what I came up with:
Frontend / Next.js:
pages/api/auth/[next-auth].ts
:utils/auth-reset-adapter.ts
:Note the following with the adapter:
NEXTAUTH_SECRET
to be passed to the backend to verify the requestsBackend / NestJS:
src/api/auth/auth.controller.ts
:src/api/auth/auth.controller.ts
:Note the following with the service:
To secure the communication between the frontend and backend, I'm using a middleware in the backend app:
src/middlewares/auth.middleware.ts
:app.module.ts
:The only drawback I've encountered so far is a little bit of latency. Are there any other thoughts or concerns about this approach and implementation? Any experience or insights would be greatly appreciated!
Related discussion:
How to reproduce ☕️
Add
AuthRestAdapter()
to your NextAuth options and implement the controller/service on the NestJS backend.Contributing 🙌🏽
Yes, I am willing to help implement this feature in a PR
Beta Was this translation helpful? Give feedback.
All reactions