@obfious/js 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @obfious/js
2
2
 
3
- Device identity protection for JavaScript applications.
3
+ Anti-bot device intelligence for JavaScript — Cloudflare Workers, Next.js, Express, Fastify, Lambda.
4
4
 
5
5
  ## Install
6
6
 
@@ -18,24 +18,26 @@ npm install @obfious/js
18
18
  | `@obfious/js/fastify` | Fastify |
19
19
  | `@obfious/js/lambda` | AWS Lambda (API Gateway) |
20
20
 
21
- ## Quick Start
21
+ ## Quick start
22
22
 
23
23
  ```typescript
24
24
  import { Obfious } from "@obfious/js";
25
25
 
26
26
  const obfious = new Obfious({
27
- includePaths: ["/api"],
27
+ stableString: "my-app:3000",
28
+ includePaths: ["/api/"],
28
29
  });
29
30
 
31
+ const creds = { keyId: process.env.OBFIOUS_KEY_ID, secret: process.env.OBFIOUS_SECRET };
32
+
30
33
  // In your request handler:
31
- const result = await obfious.protect(request, {
32
- keyId: process.env.OBFIOUS_KEY_ID,
33
- secret: process.env.OBFIOUS_SECRET,
34
- });
34
+ const result = await obfious.protect(request, creds);
35
35
  if (result.response) return result.response;
36
+ // result.deviceId is set when token is valid
36
37
 
37
- // Inject the client script in your HTML <head>:
38
- // <script src="${obfious.clientScript}" nonce="${obfious.cspNonce}" defer></script>
38
+ // Script tag for HTML <head>:
39
+ const tag = await obfious.scriptTag({ nonce: "abc123" });
40
+ // → <script src="/a3f7c9d4e5.js" nonce="abc123" defer></script>
39
41
  ```
40
42
 
41
43
  ### Next.js
@@ -46,10 +48,11 @@ import { NextResponse } from "next/server";
46
48
 
47
49
  const obfious = createObfiousMiddleware({
48
50
  creds: { keyId: process.env.OBFIOUS_KEY_ID!, secret: process.env.OBFIOUS_SECRET! },
51
+ stableString: "my-nextjs-app",
49
52
  includePaths: ["/api/"],
50
53
  });
51
54
 
52
- export async function middleware(request) {
55
+ export async function middleware(request: NextRequest) {
53
56
  const response = await obfious(request);
54
57
  if (response) return response;
55
58
  return NextResponse.next();
@@ -65,10 +68,52 @@ import { obfiousMiddleware } from "@obfious/js/express";
65
68
  const app = express();
66
69
  app.use(obfiousMiddleware({
67
70
  creds: { keyId: process.env.OBFIOUS_KEY_ID!, secret: process.env.OBFIOUS_SECRET! },
71
+ stableString: `my-app:${PORT}`,
68
72
  includePaths: ["/api/"],
69
73
  }));
70
74
  ```
71
75
 
76
+ ### Fastify
77
+
78
+ ```typescript
79
+ import Fastify from "fastify";
80
+ import { obfiousPlugin } from "@obfious/js/fastify";
81
+
82
+ const app = Fastify();
83
+ app.register(obfiousPlugin, {
84
+ creds: { keyId: process.env.OBFIOUS_KEY_ID!, secret: process.env.OBFIOUS_SECRET! },
85
+ stableString: `my-app:${PORT}`,
86
+ includePaths: ["/api/"],
87
+ });
88
+ ```
89
+
90
+ ### Lambda
91
+
92
+ ```typescript
93
+ import { obfiousHandler } from "@obfious/js/lambda";
94
+
95
+ export const handler = obfiousHandler({
96
+ creds: { keyId: process.env.OBFIOUS_KEY_ID!, secret: process.env.OBFIOUS_SECRET! },
97
+ stableString: "my-lambda-api",
98
+ includePaths: ["/api/"],
99
+ }, async (event, context) => {
100
+ return { statusCode: 200, body: JSON.stringify({ ok: true }), headers: {} };
101
+ });
102
+ ```
103
+
104
+ ## Configuration
105
+
106
+ | Option | Type | Default | Description |
107
+ |--------|------|---------|-------------|
108
+ | `apiUrl` | string | `https://api.obfious.com` | API base URL |
109
+ | `stableString` | string | `"obfious-default"` | Stable string for script path derivation |
110
+ | `scriptPath` | string | (derived) | Override the derived script path |
111
+ | `includePaths` | string[] | (all) | Only guard these path prefixes |
112
+ | `excludePaths` | string[] | (none) | Always pass through these prefixes |
113
+ | `privateKey` | string | — | HMAC key for user ID encryption |
114
+ | `getClientIp` | callback | (auto) | Custom client IP extraction |
115
+ | `getPlatformSignals` | callback | (CF default) | Custom platform signal headers |
116
+
72
117
  ## License
73
118
 
74
119
  See LICENSE file.
package/dist/express.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
- import { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
3
- export { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
2
+ import { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
3
+ export { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
4
4
  export interface ObfiousExpressOptionsV2 extends ObfiousConfigV2 {
5
5
  creds: ObfiousCredsV2;
6
6
  getUser?: (req: IncomingMessage) => string | undefined;
package/dist/express.js CHANGED
@@ -1 +1 @@
1
- var b="x-obfious-key",P="x-obfious-sig",v="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/,l=class{config;creds=null;scriptPathCache=null;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let e=this.config.stableString||"obfious-default";return this.scriptPathCache=await C(e),this.scriptPathCache}async scriptTag(e){let t=await this.getScriptPath(),s=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${s} defer></script>`}async protect(e,t,s){let r={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return r;let n=new URL(e.url);if(e.method==="GET"){let p=await this.getScriptPath();if(n.pathname===p){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&R.test(n.pathname)){let p=e.clone(),u=new Uint8Array(await p.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(n.pathname,u)}}if(this.config.excludePaths?.some(p=>n.pathname.startsWith(p))||this.config.includePaths&&!this.config.includePaths.some(p=>n.pathname.startsWith(p)))return r;let a=e.headers.get("x-req-auth");if(!a)return{response:new Response(null,{status:401})};let o=a.indexOf(".");if(o<1)return{response:new Response(null,{status:401})};let c=a.slice(0,o),h=a.slice(o+1),d=A(c);if(!d)return{response:new Response(null,{status:401})};let w=s&&this.config.privateKey?await S(s,this.config.privateKey):void 0,f=await this.validateToken(d,c,h,w);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t){return this.authedFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:t.buffer})}async validateToken(e,t,s,r){try{let n={tokenHex:e,signature:s,payload:t};r&&(n.encryptedUser=r);let a=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok)return{valid:!1};let o=await a.json();return{valid:o.valid===!0,deviceId:o.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let s=`${this.config.apiUrl}${e}`,r=Date.now().toString(),n=(t.method||"GET").toUpperCase(),a=`${r}.${n}.${e}`,o=await x(this.creds.secret,a),c=new Headers(t.headers);return c.set(b,this.creds.keyId),c.set(P,o),c.set(v,r),fetch(s,{...t,headers:c})}};async function C(i){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(i));return`/${Array.from(new Uint8Array(t),r=>r.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function x(i,e){let t=new TextEncoder().encode(i),s=await crypto.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode(e));return Array.from(new Uint8Array(r),n=>n.toString(16).padStart(2,"0")).join("")}async function S(i,e){let t=new TextEncoder().encode(e),s=await crypto.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode(i));return Array.from(new Uint8Array(r),n=>n.toString(16).padStart(2,"0")).join("")}function A(i){try{let e=i.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),s=>s.charCodeAt(0));return t.length<9||t[0]!==33?null:Array.from(t.slice(1,9),s=>s.toString(16).padStart(2,"0")).join("")}catch{return null}}import{Readable as g}from"node:stream";function y(i){let e=i.headers["x-forwarded-proto"]||"http",t=i.headers.host||"localhost",s=`${e}://${t}${i.url}`,r=new Headers;for(let[a,o]of Object.entries(i.headers))o&&r.set(a,Array.isArray(o)?o.join(", "):o);let n=i.method!=="GET"&&i.method!=="HEAD";return new Request(s,{method:i.method,headers:r,body:n?g.toWeb(g.from(i)):null,duplex:"half"})}async function m(i,e){let t={};if(e.headers.forEach((s,r)=>{t[r]=s}),i.writeHead(e.status,t),e.body){let s=e.body.getReader();try{for(;;){let{done:r,value:n}=await s.read();if(r)break;i.write(n)}}finally{s.releaseLock()}}i.end()}function k(i){let{creds:e,getUser:t,...s}=i,r=new l({...s,getClientIp:s.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});return async(n,a,o)=>{try{let c=y(n),h=t?.(n),d=await r.protect(c,e,h);if(d.response){await m(a,d.response);return}d.deviceId&&(n.obfiousDeviceId=d.deviceId),o()}catch(c){o(c)}}}export{l as ObfiousV2,k as obfiousMiddleware};
1
+ var b="x-obfious-key",P="x-obfious-sig",v="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/,l=class{config;creds=null;scriptPathCache=null;constructor(e={}){this.config={...e,apiUrl:e.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let e=this.config.stableString||"obfious-default";return this.scriptPathCache=await C(e),this.scriptPathCache}async scriptTag(e){let t=await this.getScriptPath(),s=e?.nonce?` nonce="${e.nonce}"`:"";return`<script src="${t}"${s} defer></script>`}async protect(e,t,s){let r={response:null};if(t&&!this.creds&&(this.creds=t),!this.creds)return r;let n=new URL(e.url);if(e.method==="GET"){let p=await this.getScriptPath();if(n.pathname===p){let u=await this.fetchBundle();if(u)return{response:new Response(u,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(e.method==="POST"&&R.test(n.pathname)){let p=e.clone(),u=new Uint8Array(await p.arrayBuffer());if(u.length>0&&u[0]===91)return{response:await this.forwardToApi(n.pathname,u)}}if(this.config.excludePaths?.some(p=>n.pathname.startsWith(p))||this.config.includePaths&&!this.config.includePaths.some(p=>n.pathname.startsWith(p)))return r;let a=e.headers.get("x-req-auth");if(!a)return{response:new Response(null,{status:401})};let o=a.indexOf(".");if(o<1)return{response:new Response(null,{status:401})};let c=a.slice(0,o),h=a.slice(o+1),d=A(c);if(!d)return{response:new Response(null,{status:401})};let w=s&&this.config.privateKey?await S(s,this.config.privateKey):void 0,f=await this.validateToken(d,c,h,w);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(e){return this.config.getClientIp?this.config.getClientIp(e):e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||e.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let e=await this.authedFetch("/b",{method:"GET"});return e.ok?await e.text():null}catch{return null}}async forwardToApi(e,t){return this.authedFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:t.buffer})}async validateToken(e,t,s,r){try{let n={tokenHex:e,signature:s,payload:t};r&&(n.encryptedUser=r);let a=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok)return{valid:!1};let o=await a.json();return{valid:o.valid===!0,deviceId:o.deviceId}}catch{return{valid:!1}}}async authedFetch(e,t){let s=`${this.config.apiUrl}${e}`,r=Date.now().toString(),n=(t.method||"GET").toUpperCase(),a=`${r}.${n}.${e}`,o=await x(this.creds.secret,a),c=new Headers(t.headers);return c.set(b,this.creds.keyId),c.set(P,o),c.set(v,r),fetch(s,{...t,headers:c})}};async function C(i){let e=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),t=await crypto.subtle.sign("HMAC",e,new TextEncoder().encode(i));return`/${Array.from(new Uint8Array(t),r=>r.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function x(i,e){let t=new TextEncoder().encode(i),s=await crypto.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode(e));return Array.from(new Uint8Array(r),n=>n.toString(16).padStart(2,"0")).join("")}async function S(i,e){let t=new TextEncoder().encode(e),s=await crypto.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=await crypto.subtle.sign("HMAC",s,new TextEncoder().encode(i));return Array.from(new Uint8Array(r),n=>n.toString(16).padStart(2,"0")).join("")}function A(i){try{let e=i.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";let t=Uint8Array.from(atob(e),s=>s.charCodeAt(0));return t.length<9||t[0]!==33?null:Array.from(t.slice(1,9),s=>s.toString(16).padStart(2,"0")).join("")}catch{return null}}import{Readable as g}from"node:stream";function y(i){let e=i.headers["x-forwarded-proto"]||"http",t=i.headers.host||"localhost",s=`${e}://${t}${i.url}`,r=new Headers;for(let[a,o]of Object.entries(i.headers))o&&r.set(a,Array.isArray(o)?o.join(", "):o);let n=i.method!=="GET"&&i.method!=="HEAD";return new Request(s,{method:i.method,headers:r,body:n?g.toWeb(g.from(i)):null,duplex:"half"})}async function m(i,e){let t={};if(e.headers.forEach((s,r)=>{t[r]=s}),i.writeHead(e.status,t),e.body){let s=e.body.getReader();try{for(;;){let{done:r,value:n}=await s.read();if(r)break;i.write(n)}}finally{s.releaseLock()}}i.end()}function k(i){let{creds:e,getUser:t,...s}=i,r=new l({...s,getClientIp:s.getClientIp??(n=>n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});return async(n,a,o)=>{try{let c=y(n),h=t?.(n),d=await r.protect(c,e,h);if(d.response){await m(a,d.response);return}d.deviceId&&(n.obfiousDeviceId=d.deviceId),o()}catch(c){o(c)}}}export{l as Obfious,k as obfiousMiddleware};
package/dist/fastify.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { IncomingMessage } from "node:http";
2
- import { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
3
- export { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
2
+ import { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
3
+ export { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
4
4
  export interface ObfiousFastifyOptionsV2 extends ObfiousConfigV2 {
5
5
  creds: ObfiousCredsV2;
6
6
  getUser?: (req: IncomingMessage) => string | undefined;
package/dist/fastify.js CHANGED
@@ -1 +1 @@
1
- var w="x-obfious-key",b="x-obfious-sig",P="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/,f=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await v(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let i=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(i.pathname===u){let p=await this.fetchBundle();if(p)return{response:new Response(p,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&R.test(i.pathname)){let u=t.clone(),p=new Uint8Array(await u.arrayBuffer());if(p.length>0&&p[0]===91)return{response:await this.forwardToApi(i.pathname,p)}}if(this.config.excludePaths?.some(u=>i.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>i.pathname.startsWith(u)))return s;let o=t.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let a=o.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let c=o.slice(0,a),g=o.slice(a+1),d=S(c);if(!d)return{response:new Response(null,{status:401})};let h=n&&this.config.privateKey?await x(n,this.config.privateKey):void 0,l=await this.validateToken(d,c,g,h);return l.valid?{response:null,deviceId:l.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let i={tokenHex:t,signature:n,payload:e};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let a=await o.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),i=(e.method||"GET").toUpperCase(),o=`${s}.${i}.${t}`,a=await C(this.creds.secret,o),c=new Headers(e.headers);return c.set(w,this.creds.keyId),c.set(b,a),c.set(P,s),fetch(n,{...e,headers:c})}};async function v(r){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(r));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function C(r,t){let e=new TextEncoder().encode(r),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),i=>i.toString(16).padStart(2,"0")).join("")}async function x(r,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(r));return Array.from(new Uint8Array(s),i=>i.toString(16).padStart(2,"0")).join("")}function S(r){try{let t=r.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}import{Readable as y}from"node:stream";function m(r){let t=r.headers["x-forwarded-proto"]||"http",e=r.headers.host||"localhost",n=`${t}://${e}${r.url}`,s=new Headers;for(let[o,a]of Object.entries(r.headers))a&&s.set(o,Array.isArray(a)?a.join(", "):a);let i=r.method!=="GET"&&r.method!=="HEAD";return new Request(n,{method:r.method,headers:s,body:i?y.toWeb(y.from(r)):null,duplex:"half"})}async function V(r,t){let{creds:e,getUser:n,...s}=t,i=new f({...s,getClientIp:s.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});r.addHook("onRequest",async(o,a)=>{let c=m(o.raw),g=n?.(o.raw),d=await i.protect(c,e,g);if(d.response){let h={};d.response.headers.forEach((u,p)=>{h[p]=u});let l=await d.response.text();a.code(d.response.status).headers(h).send(l);return}d.deviceId&&(o.obfiousDeviceId=d.deviceId)})}export{f as ObfiousV2,V as obfiousPlugin};
1
+ var w="x-obfious-key",b="x-obfious-sig",P="x-obfious-ts",R=/\.(json|js|gif|png|woff2|css)$/,f=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await v(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let i=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(i.pathname===u){let p=await this.fetchBundle();if(p)return{response:new Response(p,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&R.test(i.pathname)){let u=t.clone(),p=new Uint8Array(await u.arrayBuffer());if(p.length>0&&p[0]===91)return{response:await this.forwardToApi(i.pathname,p)}}if(this.config.excludePaths?.some(u=>i.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>i.pathname.startsWith(u)))return s;let o=t.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let a=o.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let c=o.slice(0,a),g=o.slice(a+1),d=S(c);if(!d)return{response:new Response(null,{status:401})};let h=n&&this.config.privateKey?await x(n,this.config.privateKey):void 0,l=await this.validateToken(d,c,g,h);return l.valid?{response:null,deviceId:l.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let i={tokenHex:t,signature:n,payload:e};s&&(i.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!o.ok)return{valid:!1};let a=await o.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),i=(e.method||"GET").toUpperCase(),o=`${s}.${i}.${t}`,a=await C(this.creds.secret,o),c=new Headers(e.headers);return c.set(w,this.creds.keyId),c.set(b,a),c.set(P,s),fetch(n,{...e,headers:c})}};async function v(r){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(r));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function C(r,t){let e=new TextEncoder().encode(r),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),i=>i.toString(16).padStart(2,"0")).join("")}async function x(r,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(r));return Array.from(new Uint8Array(s),i=>i.toString(16).padStart(2,"0")).join("")}function S(r){try{let t=r.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}import{Readable as y}from"node:stream";function m(r){let t=r.headers["x-forwarded-proto"]||"http",e=r.headers.host||"localhost",n=`${t}://${e}${r.url}`,s=new Headers;for(let[o,a]of Object.entries(r.headers))a&&s.set(o,Array.isArray(a)?a.join(", "):a);let i=r.method!=="GET"&&r.method!=="HEAD";return new Request(n,{method:r.method,headers:s,body:i?y.toWeb(y.from(r)):null,duplex:"half"})}async function V(r,t){let{creds:e,getUser:n,...s}=t,i=new f({...s,getClientIp:s.getClientIp??(o=>o.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||o.headers.get("x-real-ip")||"unknown"),getPlatformSignals:s.getPlatformSignals??(()=>({}))});r.addHook("onRequest",async(o,a)=>{let c=m(o.raw),g=n?.(o.raw),d=await i.protect(c,e,g);if(d.response){let h={};d.response.headers.forEach((u,p)=>{h[p]=u});let l=await d.response.text();a.code(d.response.status).headers(h).send(l);return}d.deviceId&&(o.obfiousDeviceId=d.deviceId)})}export{f as Obfious,V as obfiousPlugin};
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ export interface ProtectResultV2 {
16
16
  response: Response | null;
17
17
  deviceId?: string;
18
18
  }
19
- export declare class ObfiousV2 {
19
+ export declare class Obfious {
20
20
  constructor(config?: ObfiousConfigV2);
21
21
  getScriptPath(): Promise<string>;
22
22
  scriptTag(opts?: { nonce?: string }): Promise<string>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/,u=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await b(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let r=new URL(t.url);if(t.method==="GET"){let h=await this.getScriptPath();if(r.pathname===h){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&P.test(r.pathname)){let h=t.clone(),l=new Uint8Array(await h.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(r.pathname,l)}}if(this.config.excludePaths?.some(h=>r.pathname.startsWith(h))||this.config.includePaths&&!this.config.includePaths.some(h=>r.pathname.startsWith(h)))return s;let i=t.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let o=i.indexOf(".");if(o<1)return{response:new Response(null,{status:401})};let c=i.slice(0,o),g=i.slice(o+1),p=x(c);if(!p)return{response:new Response(null,{status:401})};let f=n&&this.config.privateKey?await v(n,this.config.privateKey):void 0,d=await this.validateToken(p,c,g,f);return d.valid?{response:null,deviceId:d.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let r={tokenHex:t,signature:n,payload:e};s&&(r.encryptedUser=s);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!i.ok)return{valid:!1};let o=await i.json();return{valid:o.valid===!0,deviceId:o.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),r=(e.method||"GET").toUpperCase(),i=`${s}.${r}.${t}`,o=await C(this.creds.secret,i),c=new Headers(e.headers);return c.set(y,this.creds.keyId),c.set(w,o),c.set(m,s),fetch(n,{...e,headers:c})}};async function b(a){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(a));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function C(a,t){let e=new TextEncoder().encode(a),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}async function v(a,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(a));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}function x(a){try{let t=a.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}export{u as ObfiousV2};
1
+ var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",P=/\.(json|js|gif|png|woff2|css)$/,u=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await b(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let r=new URL(t.url);if(t.method==="GET"){let h=await this.getScriptPath();if(r.pathname===h){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&P.test(r.pathname)){let h=t.clone(),l=new Uint8Array(await h.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(r.pathname,l)}}if(this.config.excludePaths?.some(h=>r.pathname.startsWith(h))||this.config.includePaths&&!this.config.includePaths.some(h=>r.pathname.startsWith(h)))return s;let i=t.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let o=i.indexOf(".");if(o<1)return{response:new Response(null,{status:401})};let c=i.slice(0,o),g=i.slice(o+1),p=x(c);if(!p)return{response:new Response(null,{status:401})};let f=n&&this.config.privateKey?await v(n,this.config.privateKey):void 0,d=await this.validateToken(p,c,g,f);return d.valid?{response:null,deviceId:d.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let r={tokenHex:t,signature:n,payload:e};s&&(r.encryptedUser=s);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!i.ok)return{valid:!1};let o=await i.json();return{valid:o.valid===!0,deviceId:o.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),r=(e.method||"GET").toUpperCase(),i=`${s}.${r}.${t}`,o=await C(this.creds.secret,i),c=new Headers(e.headers);return c.set(y,this.creds.keyId),c.set(w,o),c.set(m,s),fetch(n,{...e,headers:c})}};async function b(a){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(a));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function C(a,t){let e=new TextEncoder().encode(a),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}async function v(a,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(a));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}function x(a){try{let t=a.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}export{u as Obfious};
package/dist/lambda.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
2
- export { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
1
+ import { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
2
+ export { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
3
3
  export interface APIGatewayProxyEvent {
4
4
  httpMethod: string;
5
5
  path: string;
package/dist/lambda.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",m="x-obfious-sig",w="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/,g=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await P(t),this.scriptPathCache}async scriptTag(t){let s=await this.getScriptPath(),r=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${s}"${r} defer></script>`}async protect(t,s,r){let n={response:null};if(s&&!this.creds&&(this.creds=s),!this.creds)return n;let o=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(o.pathname===u){let h=await this.fetchBundle();if(h)return{response:new Response(h,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&b.test(o.pathname)){let u=t.clone(),h=new Uint8Array(await u.arrayBuffer());if(h.length>0&&h[0]===91)return{response:await this.forwardToApi(o.pathname,h)}}if(this.config.excludePaths?.some(u=>o.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>o.pathname.startsWith(u)))return n;let i=t.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let c=i.indexOf(".");if(c<1)return{response:new Response(null,{status:401})};let a=i.slice(0,c),p=i.slice(c+1),d=R(a);if(!d)return{response:new Response(null,{status:401})};let l=r&&this.config.privateKey?await C(r,this.config.privateKey):void 0,f=await this.validateToken(d,a,p,l);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,s){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:s.buffer})}async validateToken(t,s,r,n){try{let o={tokenHex:t,signature:r,payload:s};n&&(o.encryptedUser=n);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!i.ok)return{valid:!1};let c=await i.json();return{valid:c.valid===!0,deviceId:c.deviceId}}catch{return{valid:!1}}}async authedFetch(t,s){let r=`${this.config.apiUrl}${t}`,n=Date.now().toString(),o=(s.method||"GET").toUpperCase(),i=`${n}.${o}.${t}`,c=await x(this.creds.secret,i),a=new Headers(s.headers);return a.set(y,this.creds.keyId),a.set(m,c),a.set(w,n),fetch(r,{...s,headers:a})}};async function P(e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return`/${Array.from(new Uint8Array(s),n=>n.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function x(e,t){let s=new TextEncoder().encode(e),r=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(t));return Array.from(new Uint8Array(n),o=>o.toString(16).padStart(2,"0")).join("")}async function C(e,t){let s=new TextEncoder().encode(t),r=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(e));return Array.from(new Uint8Array(n),o=>o.toString(16).padStart(2,"0")).join("")}function R(e){try{let t=e.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let s=Uint8Array.from(atob(t),r=>r.charCodeAt(0));return s.length<9||s[0]!==33?null:Array.from(s.slice(1,9),r=>r.toString(16).padStart(2,"0")).join("")}catch{return null}}function S(e){let t=e.headers["x-forwarded-proto"]||e.headers["X-Forwarded-Proto"]||"https",s=e.headers.host||e.headers.Host||"localhost",r=`${t}://${s}${e.path}`;if(e.queryStringParameters){let c=new URLSearchParams;for(let[p,d]of Object.entries(e.queryStringParameters))d!=null&&c.set(p,d);let a=c.toString();a&&(r+=`?${a}`)}let n=new Headers;for(let[c,a]of Object.entries(e.headers))a&&n.set(c,a);let i=e.httpMethod!=="GET"&&e.httpMethod!=="HEAD"&&e.body!=null?e.isBase64Encoded?atob(e.body):e.body:null;return new Request(r,{method:e.httpMethod,headers:n,body:i})}async function A(e){let t={};return e.headers.forEach((s,r)=>{t[r]=s}),{statusCode:e.status,headers:t,body:await e.text()}}function H(e,t){let{creds:s,getUser:r,...n}=e,o=new g({...n,getClientIp:n.getClientIp??(i=>i.headers.get("x-lambda-source-ip")||i.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});return async(i,c)=>{let a=S(i),p=i.requestContext?.identity?.sourceIp||i.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";a.headers.set("x-lambda-source-ip",p);let d=r?.(i),l=await o.protect(a,s,d);return l.response?A(l.response):(l.deviceId&&(i.headers["x-obfious-device-id"]=String(l.deviceId)),t(i,c))}}export{g as ObfiousV2,H as obfiousHandler};
1
+ var y="x-obfious-key",m="x-obfious-sig",w="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/,g=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await P(t),this.scriptPathCache}async scriptTag(t){let s=await this.getScriptPath(),r=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${s}"${r} defer></script>`}async protect(t,s,r){let n={response:null};if(s&&!this.creds&&(this.creds=s),!this.creds)return n;let o=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(o.pathname===u){let h=await this.fetchBundle();if(h)return{response:new Response(h,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&b.test(o.pathname)){let u=t.clone(),h=new Uint8Array(await u.arrayBuffer());if(h.length>0&&h[0]===91)return{response:await this.forwardToApi(o.pathname,h)}}if(this.config.excludePaths?.some(u=>o.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>o.pathname.startsWith(u)))return n;let i=t.headers.get("x-req-auth");if(!i)return{response:new Response(null,{status:401})};let c=i.indexOf(".");if(c<1)return{response:new Response(null,{status:401})};let a=i.slice(0,c),p=i.slice(c+1),d=R(a);if(!d)return{response:new Response(null,{status:401})};let l=r&&this.config.privateKey?await C(r,this.config.privateKey):void 0,f=await this.validateToken(d,a,p,l);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,s){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:s.buffer})}async validateToken(t,s,r,n){try{let o={tokenHex:t,signature:r,payload:s};n&&(o.encryptedUser=n);let i=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!i.ok)return{valid:!1};let c=await i.json();return{valid:c.valid===!0,deviceId:c.deviceId}}catch{return{valid:!1}}}async authedFetch(t,s){let r=`${this.config.apiUrl}${t}`,n=Date.now().toString(),o=(s.method||"GET").toUpperCase(),i=`${n}.${o}.${t}`,c=await x(this.creds.secret,i),a=new Headers(s.headers);return a.set(y,this.creds.keyId),a.set(m,c),a.set(w,n),fetch(r,{...s,headers:a})}};async function P(e){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(e));return`/${Array.from(new Uint8Array(s),n=>n.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function x(e,t){let s=new TextEncoder().encode(e),r=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(t));return Array.from(new Uint8Array(n),o=>o.toString(16).padStart(2,"0")).join("")}async function C(e,t){let s=new TextEncoder().encode(t),r=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,new TextEncoder().encode(e));return Array.from(new Uint8Array(n),o=>o.toString(16).padStart(2,"0")).join("")}function R(e){try{let t=e.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let s=Uint8Array.from(atob(t),r=>r.charCodeAt(0));return s.length<9||s[0]!==33?null:Array.from(s.slice(1,9),r=>r.toString(16).padStart(2,"0")).join("")}catch{return null}}function S(e){let t=e.headers["x-forwarded-proto"]||e.headers["X-Forwarded-Proto"]||"https",s=e.headers.host||e.headers.Host||"localhost",r=`${t}://${s}${e.path}`;if(e.queryStringParameters){let c=new URLSearchParams;for(let[p,d]of Object.entries(e.queryStringParameters))d!=null&&c.set(p,d);let a=c.toString();a&&(r+=`?${a}`)}let n=new Headers;for(let[c,a]of Object.entries(e.headers))a&&n.set(c,a);let i=e.httpMethod!=="GET"&&e.httpMethod!=="HEAD"&&e.body!=null?e.isBase64Encoded?atob(e.body):e.body:null;return new Request(r,{method:e.httpMethod,headers:n,body:i})}async function A(e){let t={};return e.headers.forEach((s,r)=>{t[r]=s}),{statusCode:e.status,headers:t,body:await e.text()}}function H(e,t){let{creds:s,getUser:r,...n}=e,o=new g({...n,getClientIp:n.getClientIp??(i=>i.headers.get("x-lambda-source-ip")||i.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown"),getPlatformSignals:n.getPlatformSignals??(()=>({}))});return async(i,c)=>{let a=S(i),p=i.requestContext?.identity?.sourceIp||i.headers["x-forwarded-for"]?.split(",")[0]?.trim()||"unknown";a.headers.set("x-lambda-source-ip",p);let d=r?.(i),l=await o.protect(a,s,d);return l.response?A(l.response):(l.deviceId&&(i.headers["x-obfious-device-id"]=String(l.deviceId)),t(i,c))}}export{g as Obfious,H as obfiousHandler};
package/dist/nextjs.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
2
- export { ObfiousV2, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
1
+ import { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 } from "@obfious/js";
2
+ export { Obfious, ObfiousConfigV2, ObfiousCredsV2, ProtectResultV2 };
3
3
  export interface ObfiousMiddlewareConfigV2 extends ObfiousConfigV2 {
4
4
  creds: ObfiousCredsV2;
5
5
  }
6
6
  export declare function createObfiousMiddleware(config: ObfiousMiddlewareConfigV2): (request: Request) => Promise<Response | null>;
7
- export declare function obfiousScriptTag(obfious: ObfiousV2, nonce?: string): Promise<string>;
7
+ export declare function obfiousScriptTag(obfious: Obfious, nonce?: string): Promise<string>;
package/dist/nextjs.js CHANGED
@@ -1 +1 @@
1
- var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/,p=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await C(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let r=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(r.pathname===u){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&b.test(r.pathname)){let u=t.clone(),l=new Uint8Array(await u.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(r.pathname,l)}}if(this.config.excludePaths?.some(u=>r.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>r.pathname.startsWith(u)))return s;let o=t.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let a=o.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let c=o.slice(0,a),h=o.slice(a+1),d=v(c);if(!d)return{response:new Response(null,{status:401})};let g=n&&this.config.privateKey?await x(n,this.config.privateKey):void 0,f=await this.validateToken(d,c,h,g);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let r={tokenHex:t,signature:n,payload:e};s&&(r.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!o.ok)return{valid:!1};let a=await o.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),r=(e.method||"GET").toUpperCase(),o=`${s}.${r}.${t}`,a=await P(this.creds.secret,o),c=new Headers(e.headers);return c.set(y,this.creds.keyId),c.set(w,a),c.set(m,s),fetch(n,{...e,headers:c})}};async function C(i){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(i));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function P(i,t){let e=new TextEncoder().encode(i),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}async function x(i,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(i));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}function v(i){try{let t=i.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}function T(i){let{creds:t,...e}=i,n=new p({...e,getClientIp:e.getClientIp??(s=>s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||"unknown")});return async s=>(await n.protect(s,t)).response}async function A(i,t){return i.scriptTag({nonce:t})}export{p as ObfiousV2,T as createObfiousMiddleware,A as obfiousScriptTag};
1
+ var y="x-obfious-key",w="x-obfious-sig",m="x-obfious-ts",b=/\.(json|js|gif|png|woff2|css)$/,p=class{config;creds=null;scriptPathCache=null;constructor(t={}){this.config={...t,apiUrl:t.apiUrl??"https://api.obfious.com"}}async getScriptPath(){if(this.config.scriptPath)return this.config.scriptPath;if(this.scriptPathCache)return this.scriptPathCache;let t=this.config.stableString||"obfious-default";return this.scriptPathCache=await C(t),this.scriptPathCache}async scriptTag(t){let e=await this.getScriptPath(),n=t?.nonce?` nonce="${t.nonce}"`:"";return`<script src="${e}"${n} defer></script>`}async protect(t,e,n){let s={response:null};if(e&&!this.creds&&(this.creds=e),!this.creds)return s;let r=new URL(t.url);if(t.method==="GET"){let u=await this.getScriptPath();if(r.pathname===u){let l=await this.fetchBundle();if(l)return{response:new Response(l,{headers:{"Content-Type":"application/javascript","Cache-Control":"no-store"}})}}}if(t.method==="POST"&&b.test(r.pathname)){let u=t.clone(),l=new Uint8Array(await u.arrayBuffer());if(l.length>0&&l[0]===91)return{response:await this.forwardToApi(r.pathname,l)}}if(this.config.excludePaths?.some(u=>r.pathname.startsWith(u))||this.config.includePaths&&!this.config.includePaths.some(u=>r.pathname.startsWith(u)))return s;let o=t.headers.get("x-req-auth");if(!o)return{response:new Response(null,{status:401})};let a=o.indexOf(".");if(a<1)return{response:new Response(null,{status:401})};let c=o.slice(0,a),h=o.slice(a+1),d=v(c);if(!d)return{response:new Response(null,{status:401})};let g=n&&this.config.privateKey?await x(n,this.config.privateKey):void 0,f=await this.validateToken(d,c,h,g);return f.valid?{response:null,deviceId:f.deviceId}:{response:new Response(null,{status:401})}}getIp(t){return this.config.getClientIp?this.config.getClientIp(t):t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")?.split(",")[0]?.trim()||t.headers.get("X-Real-IP")||"unknown"}async fetchBundle(){try{let t=await this.authedFetch("/b",{method:"GET"});return t.ok?await t.text():null}catch{return null}}async forwardToApi(t,e){return this.authedFetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:e.buffer})}async validateToken(t,e,n,s){try{let r={tokenHex:t,signature:n,payload:e};s&&(r.encryptedUser=s);let o=await this.authedFetch("/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!o.ok)return{valid:!1};let a=await o.json();return{valid:a.valid===!0,deviceId:a.deviceId}}catch{return{valid:!1}}}async authedFetch(t,e){let n=`${this.config.apiUrl}${t}`,s=Date.now().toString(),r=(e.method||"GET").toUpperCase(),o=`${s}.${r}.${t}`,a=await P(this.creds.secret,o),c=new Headers(e.headers);return c.set(y,this.creds.keyId),c.set(w,a),c.set(m,s),fetch(n,{...e,headers:c})}};async function C(i){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode("obfious-script-v1"),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e=await crypto.subtle.sign("HMAC",t,new TextEncoder().encode(i));return`/${Array.from(new Uint8Array(e),s=>s.toString(16).padStart(2,"0")).join("").slice(0,10)}.js`}async function P(i,t){let e=new TextEncoder().encode(i),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(t));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}async function x(i,t){let e=new TextEncoder().encode(t),n=await crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),s=await crypto.subtle.sign("HMAC",n,new TextEncoder().encode(i));return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}function v(i){try{let t=i.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";let e=Uint8Array.from(atob(t),n=>n.charCodeAt(0));return e.length<9||e[0]!==33?null:Array.from(e.slice(1,9),n=>n.toString(16).padStart(2,"0")).join("")}catch{return null}}function T(i){let{creds:t,...e}=i,n=new p({...e,getClientIp:e.getClientIp??(s=>s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||"unknown")});return async s=>(await n.protect(s,t)).response}async function A(i,t){return i.scriptTag({nonce:t})}export{p as Obfious,T as createObfiousMiddleware,A as obfiousScriptTag};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obfious/js",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Obfious anti-bot protection for JavaScript — CF Workers, Next.js, Express, Fastify, Lambda",
5
5
  "type": "module",
6
6
  "exports": {