├── .gitignore
├── README.md
├── api
├── authenticated
│ └── greeting.js
└── public
│ └── greeting.js
├── package.json
├── pnpm-lock.yaml
└── vercel.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Auth.js 3rd Party Backend
5 | Authentication for the Web.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | This repo contains example code for third party backends to authenticate against.
23 |
24 | ## Getting Started
25 |
26 | The guide for authenticating against third party backends can be found [here](https://authjs.dev/guides/integrating-third-party-backends).
27 |
28 | ## Frontend Example
29 |
30 | You can find the frontend example of making authenticated requests to these backends in the `next-auth` example app.
31 | - Code: [`apps/examples/nextjs/app/api/authenticated/greeting/route.tsx`](https://github.com/nextauthjs/next-auth/blob/main/apps/examples/nextjs/app/api/authenticated/greeting/route.tsx)
32 | - Demo: https://next-auth-example.vercel.app/client-example
33 | - Example PR: https://github.com/nextauthjs/next-auth/pull/10761/files
34 |
35 |
--------------------------------------------------------------------------------
/api/authenticated/greeting.js:
--------------------------------------------------------------------------------
1 | import * as jose from "jose";
2 |
3 | export const config = {
4 | runtime: "edge",
5 | };
6 |
7 | const JWKS_URI =
8 | "https://keycloak.authjs.dev/realms/master/protocol/openid-connect/certs";
9 | const ISSUER = "https://keycloak.authjs.dev/realms/master";
10 | const ALLOWED_ORIGINS = [
11 | "http://localhost:3000",
12 | "https://next-auth-example.vercel.app",
13 | ];
14 |
15 | async function verifyToken(token) {
16 | try {
17 | const JWKS = jose.createRemoteJWKSet(new URL(JWKS_URI));
18 | return await jose.jwtVerify(token, JWKS, {
19 | issuer: ISSUER,
20 | algorithms: ["RS256"],
21 | });
22 | } catch (error) {
23 | console.error("Token verification failed:", error);
24 | return null;
25 | }
26 | }
27 |
28 | function corsHeaders(origin) {
29 | const headers = new Headers();
30 | if (origin && ALLOWED_ORIGINS.includes(origin)) {
31 | headers.set("Access-Control-Allow-Origin", origin);
32 | }
33 | headers.set("Access-Control-Allow-Methods", "GET, OPTIONS");
34 | headers.set("Access-Control-Allow-Headers", "Authorization, Content-Type");
35 | return headers;
36 | }
37 |
38 | export default async function handler(req) {
39 | const origin = req.headers.get("origin");
40 |
41 | // Handle CORS preflight requests
42 | if (req.method === "OPTIONS") {
43 | return Response.json(null, { headers: corsHeaders(origin) });
44 | }
45 |
46 | const url = new URL(req.url);
47 |
48 | const authHeader = req.headers.get("authorization");
49 | if (!authHeader || !authHeader.startsWith("Bearer ")) {
50 | return Response.json("Unauthorized", {
51 | status: 401,
52 | headers: corsHeaders(origin),
53 | });
54 | }
55 |
56 | const token = authHeader.split(" ")[1];
57 | const verifiedToken = await verifyToken(token);
58 |
59 | if (!verifiedToken) {
60 | return Response.json("Invalid token", {
61 | status: 401,
62 | headers: corsHeaders(origin),
63 | });
64 | }
65 |
66 | const name = verifiedToken.payload.preferred_username || "unknown name";
67 | return Response.json(
68 | { greeting: `Hello, ${name}!` },
69 | { headers: corsHeaders(origin) }
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/api/public/greeting.js:
--------------------------------------------------------------------------------
1 | export default function handler(_, res) {
2 | res.status(200).json({ greeting: 'Greetings, mysterious traveller.' });
3 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authjs-third-party-backend",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "author": "Falco Winkler",
7 | "license": "ISC",
8 | "dependencies": {
9 | "jose": "^5.9.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | jose:
12 | specifier: ^5.9.3
13 | version: 5.9.3
14 |
15 | packages:
16 |
17 | jose@5.9.3:
18 | resolution: {integrity: sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==}
19 |
20 | snapshots:
21 |
22 | jose@5.9.3: {}
23 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [{ "source": "/:path*", "destination": "/api/:path*" }]
3 | }
4 |
--------------------------------------------------------------------------------