├── .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 | TypeScript 9 | 10 | 11 | npm 12 | 13 | 14 | Downloads 15 | 16 | 17 | Github Stars 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 | --------------------------------------------------------------------------------