UNPKG

express-tailscale-auth

Version:

Express middleware for Tailscale authentication

15 lines (10 loc) 2.86 kB
'use strict';var P=require('picomatch'),R=require('proxy-addr'),tailscaleLocalApi=require('tailscale-local-api'),zod=require('zod');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var P__default=/*#__PURE__*/_interopDefault(P);var R__default=/*#__PURE__*/_interopDefault(R);var d=zod.z.enum(["GET","POST","PUT","DELETE","*"]),m=zod.z.object({routes:zod.z.array(zod.z.object({route:zod.z.string(),methods:zod.z.array(d)})).optional()});var b=s=>{let n=s.app.get("trust proxy");return !!(s.headers["x-forwarded-for"]||s.headers["x-forwarded-host"]||s.headers["x-forwarded-proto"])&&(n===false||n===void 0)?(console.warn(` \u26A0\uFE0F Tailscale Auth Middleware Warning \u26A0\uFE0F Detected X-Forwarded-* headers but Express 'trust proxy' is not configured. This may result in incorrect IP address detection and authentication failures. To fix this, add one of the following to your Express app: // For most reverse proxy setups (recommended) app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) See: https://expressjs.com/en/guide/behind-proxies.html `),true):false};var x=false,O=(s={})=>{let n=new tailscaleLocalApi.TailscaleLocalApi(s),a=s.debug||false,g=e=>{a&&console.debug("all addrs of req",R.all(e)),!x&&b(e)&&(x=true);let t=e.app.get("trust proxy");if(t===false||t===void 0)return a&&console.debug("Not trusting reverse proxy, returning socket address",e.socket.remoteAddress),e.socket.remoteAddress;if(typeof t=="boolean"&&t===true)return R.all(e).at(-1);let c=R.compile(t),i=R__default.default(e,c);return a&&console.debug("trusting remote address",i),i};return async(e,t,c)=>{let i=g(e);if(!i){a&&console.debug("No client IP found",i),t.status(404).send(`Cannot ${e.method} ${e.path}`);return}let h=n.isInTailscaleIpRange(i);if(!h){a&&console.debug("Request IP not from Tailscale range",i),t.status(404).send(`Cannot ${e.method} ${e.path}`);return}let u;try{let o=await n.whoIs(h),f=s.capabilitiesNamespace?o.capMap[s.capabilitiesNamespace]?.[0]:{routes:[{route:"**",methods:["*"]}]},l=m.safeParse(f);if(u=l.data,e.tailscaleUser={...o.userProfile,capabilities:u??{}},!l.success){a&&console.debug("Couldn't find or parse capabilities",l.error),t.status(401).send(`Cannot ${e.method} ${e.path}`);return}}catch(o){console.error(o),t.status(401).send(`Cannot ${e.method} ${e.path}`);return}let C=e.originalUrl,p=d.safeParse(e.method);if(!p.success){a&&console.debug("Invalid method",p.error),t.status(401).send(`Cannot ${e.method} ${e.path}`);return}let T=p.data;if(!u?.routes?.some(o=>P__default.default(o.route)(C)?o.methods.includes("*")||o.methods.includes(T):false)){a&&console.debug("Requester has no capability for route",e.path,e.method),t.status(401).send(`Cannot ${e.method} ${e.path}`);return}c();}};exports.createTailscaleAuthMw=O;//# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map