@sekyro/node 0.1.0-rc.5 → 0.1.0-rc.7

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.
@@ -0,0 +1 @@
1
+ "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var express_exports={};__export(express_exports,{requireLevel:()=>requireLevel});module.exports=__toCommonJS(express_exports);var KNOWN_FLAGS=["face_mismatch","duplicate","revoked"];function isRecord(v){return typeof v=="object"&&v!==null&&!Array.isArray(v)}function isFiniteNumber(v){return typeof v=="number"&&Number.isFinite(v)}function build(c){let out={v:1,level:c.level,assurance:c.assurance,verified_at:c.verified_at};c.rechecked_at!==void 0&&(out.rechecked_at=c.rechecked_at),out.session_id=c.session_id,c.subject_id!==void 0&&(out.subject_id=c.subject_id),out.env=c.env;let flags=new Set;for(let f of c.flags??[])KNOWN_FLAGS.includes(f)&&flags.add(f);return flags.size>0&&(out.flags=[...flags].sort()),out}function parseClaim(value){let v=value;if(typeof v=="string")try{v=JSON.parse(v)}catch{return null}if(!isRecord(v)||!isFiniteNumber(v.v))return null;if(v.v>1)return v;if(v.v!==1)return null;let level=v.level;return level!==0&&level!==1&&level!==2&&level!==3||typeof v.assurance!="string"||!isFiniteNumber(v.verified_at)||typeof v.session_id!="string"||v.env!=="live"&&v.env!=="sandbox"?null:build({level,assurance:v.assurance,verified_at:v.verified_at,rechecked_at:isFiniteNumber(v.rechecked_at)?v.rechecked_at:void 0,session_id:v.session_id,subject_id:typeof v.subject_id=="string"?v.subject_id:void 0,env:v.env,flags:Array.isArray(v.flags)?v.flags:void 0})}var DEFAULT_DENY_FLAGS=["revoked","face_mismatch"],fail=(reason,action,claim)=>({ok:!1,reason,action,claim});function evaluateGate(claim,policy,nowSec=Math.floor(Date.now()/1e3)){let parsed=parseClaim(claim);if(!parsed)return fail("no_claim","verify",null);if(parsed.v>1)return fail("unsupported_version","verify",parsed);if(policy.env!==void 0&&parsed.env!==policy.env)return fail("wrong_env","verify",parsed);let deny2=policy.denyFlags??DEFAULT_DENY_FLAGS;return parsed.flags?.some(f=>deny2.includes(f))?fail("flagged","block",parsed):parsed.level<policy.level?fail("level_too_low",parsed.level>=1?"step_up":"verify",parsed):policy.maxAgeDays!==void 0&&nowSec-parsed.verified_at>policy.maxAgeDays*86400?fail("stale","recheck",parsed):policy.maxRecheckAgeMinutes!==void 0&&nowSec-(parsed.rechecked_at??parsed.verified_at)>policy.maxRecheckAgeMinutes*60?fail("recheck_stale","recheck",parsed):policy.assurance&&!policy.assurance.includes(parsed.assurance)?fail("assurance_not_allowed","step_up",parsed):{ok:!0,claim:parsed}}function sekyroGate(source,tokenClaims,policy){let claims=tokenClaims??{};if("gate"in source)return source.gate(claims,policy);let effective=source.env!==void 0&&policy.env===void 0?{...policy,env:source.env}:policy;return evaluateGate(source.provider.readClaimFromToken(claims),effective)}function deny(res,result){let body={error:"sekyro_gate",reason:result.reason,action:result.action},r=res;if(typeof r.status=="function"){r.status(403).json(body);return}let n=res;n.statusCode=403,n.setHeader("content-type","application/json"),n.end(JSON.stringify(body))}function requireLevel(source,policy,opts){return(req,res,next)=>{Promise.resolve().then(()=>opts.claims(req)).then(claims=>{let result=sekyroGate(source,claims,policy);if(typeof req=="object"&&req!==null&&(req.sekyro=result),result.ok)return next();if(opts.onFail)return opts.onFail(result,req,res);deny(res,result)},err=>next(err))}}0&&(module.exports={requireLevel});
@@ -0,0 +1,35 @@
1
+ import { C as Connector, P as ProviderAdapter, G as GatePolicy, a as GateResult } from '../../connector-D1bhSLBl.cjs';
2
+ import '../../client-DUMp_I-1.cjs';
3
+
4
+ /**
5
+ * `@sekyro/node/connect/express`: a route gate for Express (and any
6
+ * `(req, res, next)` framework). No dependency on Express or its types.
7
+ */
8
+
9
+ /**
10
+ * Express middleware that lets a request through only when the claim in the
11
+ * person's session token meets the policy. The gate result is attached as
12
+ * `req.sekyro`. On failure it answers 403 with the JSON body
13
+ * `{ "error": "sekyro_gate", "reason": …, "action": … }`, or calls `onFail`
14
+ * instead; an error thrown by `claims` goes to `next(err)`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * app.get("/payouts", requireLevel(connector, { level: 2, maxAgeDays: 365 }, { claims: (req) => (req as { auth?: Record<string, unknown> }).auth }), handler);
19
+ * ```
20
+ * @param source - A connector, or `{ provider, env? }`.
21
+ * @param policy - What the route requires.
22
+ * @param opts - `claims`: reads the decoded session token's claims from the request; `onFail`: your own response.
23
+ * @public
24
+ */
25
+ declare function requireLevel(source: Connector | {
26
+ provider: ProviderAdapter;
27
+ env?: "live" | "sandbox";
28
+ }, policy: GatePolicy, opts: {
29
+ claims: (req: unknown) => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
30
+ onFail?: (result: Extract<GateResult, {
31
+ ok: false;
32
+ }>, req: unknown, res: unknown) => void;
33
+ }): (req: unknown, res: unknown, next: (err?: unknown) => void) => void;
34
+
35
+ export { requireLevel };
@@ -0,0 +1,35 @@
1
+ import { C as Connector, P as ProviderAdapter, G as GatePolicy, a as GateResult } from '../../connector-CMHuC-CU.js';
2
+ import '../../client-DUMp_I-1.js';
3
+
4
+ /**
5
+ * `@sekyro/node/connect/express`: a route gate for Express (and any
6
+ * `(req, res, next)` framework). No dependency on Express or its types.
7
+ */
8
+
9
+ /**
10
+ * Express middleware that lets a request through only when the claim in the
11
+ * person's session token meets the policy. The gate result is attached as
12
+ * `req.sekyro`. On failure it answers 403 with the JSON body
13
+ * `{ "error": "sekyro_gate", "reason": …, "action": … }`, or calls `onFail`
14
+ * instead; an error thrown by `claims` goes to `next(err)`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * app.get("/payouts", requireLevel(connector, { level: 2, maxAgeDays: 365 }, { claims: (req) => (req as { auth?: Record<string, unknown> }).auth }), handler);
19
+ * ```
20
+ * @param source - A connector, or `{ provider, env? }`.
21
+ * @param policy - What the route requires.
22
+ * @param opts - `claims`: reads the decoded session token's claims from the request; `onFail`: your own response.
23
+ * @public
24
+ */
25
+ declare function requireLevel(source: Connector | {
26
+ provider: ProviderAdapter;
27
+ env?: "live" | "sandbox";
28
+ }, policy: GatePolicy, opts: {
29
+ claims: (req: unknown) => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
30
+ onFail?: (result: Extract<GateResult, {
31
+ ok: false;
32
+ }>, req: unknown, res: unknown) => void;
33
+ }): (req: unknown, res: unknown, next: (err?: unknown) => void) => void;
34
+
35
+ export { requireLevel };
@@ -0,0 +1 @@
1
+ import{sekyroGate}from"../../chunk-TAPXGI3Q.js";import"../../chunk-O5GJ3YJV.js";function deny(res,result){let body={error:"sekyro_gate",reason:result.reason,action:result.action},r=res;if(typeof r.status=="function"){r.status(403).json(body);return}let n=res;n.statusCode=403,n.setHeader("content-type","application/json"),n.end(JSON.stringify(body))}function requireLevel(source,policy,opts){return(req,res,next)=>{Promise.resolve().then(()=>opts.claims(req)).then(claims=>{let result=sekyroGate(source,claims,policy);if(typeof req=="object"&&req!==null&&(req.sekyro=result),result.ok)return next();if(opts.onFail)return opts.onFail(result,req,res);deny(res,result)},err=>next(err))}}export{requireLevel};
@@ -0,0 +1 @@
1
+ "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var connect_exports={};__export(connect_exports,{CLAIM_VERSION:()=>CLAIM_VERSION,SekyroConnectError:()=>SekyroConnectError,createConnector:()=>createConnector,evaluateGate:()=>evaluateGate,memoryStore:()=>memoryStore,mergeClaim:()=>mergeClaim,parseClaim:()=>parseClaim});module.exports=__toCommonJS(connect_exports);var CLAIM_VERSION=1,KNOWN_FLAGS=["face_mismatch","duplicate","revoked"];function isRecord(v){return typeof v=="object"&&v!==null&&!Array.isArray(v)}function isFiniteNumber(v){return typeof v=="number"&&Number.isFinite(v)}function build(c){let out={v:1,level:c.level,assurance:c.assurance,verified_at:c.verified_at};c.rechecked_at!==void 0&&(out.rechecked_at=c.rechecked_at),out.session_id=c.session_id,c.subject_id!==void 0&&(out.subject_id=c.subject_id),out.env=c.env;let flags=new Set;for(let f of c.flags??[])KNOWN_FLAGS.includes(f)&&flags.add(f);return flags.size>0&&(out.flags=[...flags].sort()),out}function parseClaim(value){let v=value;if(typeof v=="string")try{v=JSON.parse(v)}catch{return null}if(!isRecord(v)||!isFiniteNumber(v.v))return null;if(v.v>1)return v;if(v.v!==1)return null;let level=v.level;return level!==0&&level!==1&&level!==2&&level!==3||typeof v.assurance!="string"||!isFiniteNumber(v.verified_at)||typeof v.session_id!="string"||v.env!=="live"&&v.env!=="sandbox"?null:build({level,assurance:v.assurance,verified_at:v.verified_at,rechecked_at:isFiniteNumber(v.rechecked_at)?v.rechecked_at:void 0,session_id:v.session_id,subject_id:typeof v.subject_id=="string"?v.subject_id:void 0,env:v.env,flags:Array.isArray(v.flags)?v.flags:void 0})}function toUnix(isoTime,now){let ms=Date.parse(isoTime);return Math.floor((Number.isFinite(ms)?ms:now())/1e3)}var same=(claim,rule)=>({claim,changed:!1,rule}),wrote=(claim,rule)=>({claim,changed:!0,rule});function mergeClaim(previous,event,opts){let now=opts.now??Date.now,prev=parseClaim(previous);if(prev&&prev.v>1)return same(prev,"unsupported_version");if(event.kind==="revoked"||event.kind==="deleted"){if(!prev)return same(null,"no_change");if(!(prev.subject_id===void 0?event.userInExternalRefs===!0:prev.subject_id===event.subjectId))return same(prev,"subject_mismatch");if(prev.level===0&&prev.flags?.includes("revoked"))return same(prev,"no_change");let{rechecked_at:_cleared,...kept}=prev;return wrote(build({...kept,level:0,subject_id:event.subjectId,flags:["revoked"]}),"revoked")}let r=event.result;if(r.env!==opts.env)return same(prev,"env_mismatch");let t=toUnix(r.verifiedAt,now);if(prev&&t<prev.verified_at)return same(prev,"stale_ignored");let revokedPrev=prev?.flags?.includes("revoked")===!0;if(prev&&revokedPrev&&(r.subjectId===void 0||r.subjectId===prev.subject_id))return same(prev,"revoked_ignored");if(r.outcome==="rejected"&&r.subjectConflict===!0)return!prev||prev.flags?.includes("face_mismatch")?same(prev,"no_change"):wrote(build({...prev,flags:[...prev.flags??[],"face_mismatch"]}),"face_mismatch");if(r.outcome!=="approved"||r.level===null||r.level===0)return same(prev,"not_approved");let level=r.level,assurance=r.assurance??"none",duplicate=r.duplicates===!0?["duplicate"]:[];if(!prev)return wrote(build({level,assurance,verified_at:t,session_id:r.sessionId,subject_id:r.subjectId,env:r.env,flags:duplicate}),"created");if(!revokedPrev&&(r.reuseMode==="recheck"||r.returning===!0&&level<=prev.level))return prev.rechecked_at!==void 0&&t<=prev.rechecked_at?same(prev,t<prev.rechecked_at?"stale_ignored":"no_change"):wrote(build({...prev,rechecked_at:t,subject_id:prev.subject_id??r.subjectId}),"rechecked");if(level>prev.level){let kept=(prev.flags??[]).filter(f=>f!=="face_mismatch"&&f!=="revoked");return wrote(build({...prev,level,assurance,verified_at:t,session_id:r.sessionId,subject_id:r.subjectId??prev.subject_id,flags:[...kept,...duplicate]}),"raised")}return level===prev.level&&t>prev.verified_at?wrote(build({...prev,assurance,verified_at:t,session_id:r.sessionId,subject_id:prev.subject_id??r.subjectId,flags:[...prev.flags??[],...duplicate]}),"renewed"):same(prev,"no_change")}var DEFAULT_DENY_FLAGS=["revoked","face_mismatch"],fail=(reason,action,claim)=>({ok:!1,reason,action,claim});function evaluateGate(claim,policy,nowSec=Math.floor(Date.now()/1e3)){let parsed=parseClaim(claim);if(!parsed)return fail("no_claim","verify",null);if(parsed.v>1)return fail("unsupported_version","verify",parsed);if(policy.env!==void 0&&parsed.env!==policy.env)return fail("wrong_env","verify",parsed);let deny=policy.denyFlags??DEFAULT_DENY_FLAGS;return parsed.flags?.some(f=>deny.includes(f))?fail("flagged","block",parsed):parsed.level<policy.level?fail("level_too_low",parsed.level>=1?"step_up":"verify",parsed):policy.maxAgeDays!==void 0&&nowSec-parsed.verified_at>policy.maxAgeDays*86400?fail("stale","recheck",parsed):policy.maxRecheckAgeMinutes!==void 0&&nowSec-(parsed.rechecked_at??parsed.verified_at)>policy.maxRecheckAgeMinutes*60?fail("recheck_stale","recheck",parsed):policy.assurance&&!policy.assurance.includes(parsed.assurance)?fail("assurance_not_allowed","step_up",parsed):{ok:!0,claim:parsed}}var import_jose=require("jose"),import_result_token=require("@sekyro/node"),import_webhooks=require("@sekyro/node");var MESSAGES={token_session_mismatch:"The result token is for another session than the webhook",external_ref_mismatch:"The session belongs to another user",session_not_terminal:"The session has not finished yet",invalid_options:"Invalid connector options",unavailable:"The Sekyro AI public key set could not be fetched; try again"},SekyroConnectError=class extends Error{name="SekyroConnectError";code;constructor(code,message=MESSAGES[code],cause){super(message,cause===void 0?void 0:{cause}),this.code=code}};function factsOf(d,tokenDuplicates){let out={};return d.returning===!0&&(out.returning=!0),d.subjectConflict===!0&&(out.subjectConflict=!0),(Array.isArray(d.duplicates)&&d.duplicates.length>0||(tokenDuplicates?.length??0)>0)&&(out.duplicates=!0),out}function fromVerifiedResult(v,d){let out={sessionId:v.sessionId,outcome:v.outcome,level:v.level,assurance:v.assurance,verifiedAt:v.verifiedAt,env:v.env,...factsOf(d,v.duplicates)};return v.subjectId!==void 0&&(out.subjectId=v.subjectId),v.reuseMode!==void 0&&(out.reuseMode=v.reuseMode),out}function fromWebhookData(d,env,nowMs){let out={sessionId:d.sessionId,outcome:d.outcome??null,level:d.level??null,assurance:d.assurance??null,verifiedAt:d.verifiedAt??new Date(nowMs).toISOString(),env,...factsOf(d)};return d.subjectId!==void 0&&(out.subjectId=d.subjectId),d.reuseMode!==void 0&&(out.reuseMode=d.reuseMode),out}function memoryStore(opts={}){let now=opts.now??Date.now,ids=new Map,subjects=new Map,evict=t=>{for(let[id,expiresAt]of ids)expiresAt<=t&&ids.delete(id)};return{async seen(id,ttlSec){let t=now();return evict(t),ids.has(id)?!0:(ids.set(id,t+ttlSec*1e3),!1)},async forget(id){ids.delete(id)},async setSubjectUser(subjectId,userId){subjects.delete(subjectId),subjects.set(subjectId,userId),subjects.size>1e5&&subjects.delete(subjects.keys().next().value)},async getSubjectUser(subjectId){return subjects.get(subjectId)},size(){return evict(now()),ids.size}}}var DEDUP_TTL_SEC=24*3600,JTI_TTL_SEC=600;function isApiStatus(err,statuses){if(typeof err!="object"||err===null)return!1;let e=err;return e.name==="SekyroApiError"&&typeof e.status=="number"&&statuses.includes(e.status)}function linkOf(o){if(o.returnUrl===void 0&&o.state===void 0)return;let link={};return o.returnUrl!==void 0&&(link.returnUrl=o.returnUrl),o.state!==void 0&&(link.state=o.state),link}function perKeyLock(){let tails=new Map;return(key,fn)=>{let run=(tails.get(key)??Promise.resolve()).then(fn,fn),tail=run.then(()=>{},()=>{});return tails.set(key,tail),tail.then(()=>{tails.get(key)===tail&&tails.delete(key)}),run}}function assertUserId(method,userId){if(typeof userId!="string"||userId.length===0)throw new TypeError(`connector.${method}: \`userId\` must be a non-empty string`)}function createConnector(opts){let appIds=[opts?.appId].flat();if(appIds.length===0||appIds.some(id=>typeof id!="string"||id.length===0))throw new SekyroConnectError("invalid_options","`appId` must be a non-empty string or a non-empty array of them");if(opts.env!=="live"&&opts.env!=="sandbox")throw new SekyroConnectError("invalid_options","`env` must be 'live' or 'sandbox'");if(typeof opts.webhookSecret!="string"||opts.webhookSecret.length===0)throw new SekyroConnectError("invalid_options","`webhookSecret` is required");if(!opts.sekyro||!opts.provider)throw new SekyroConnectError("invalid_options","`sekyro` and `provider` are required");let{sekyro,provider,env}=opts,now=opts.now??Date.now,store=opts.store??memoryStore({now}),ids=Object.freeze([...appIds]),userLock=perKeyLock();function updateClaim(userId,fn){return typeof provider.update=="function"?provider.update(userId,fn):userLock(userId,async()=>{let out=fn(await provider.readUser(userId));return out.changed&&out.claim&&await provider.writeUser(userId,out.claim),out})}let log=event=>{if(opts.logger)try{opts.logger(event)}catch{}},tokenOptions={appId:[...ids],env,now};opts.issuer!==void 0&&(tokenOptions.issuer=opts.issuer),opts.jwksUrl!==void 0&&(tokenOptions.jwksUrl=opts.jwksUrl),opts.fetch!==void 0&&(tokenOptions.fetch=opts.fetch);async function verifiedFor(token,sessionId,userId){let v;try{v=await(0,import_result_token.verifyResultToken)(token,tokenOptions)}catch(err){let e=err;throw e?.name==="SekyroResultTokenError"&&e.code==="jwks_unavailable"?new SekyroConnectError("unavailable",void 0,err):err}if(v.sessionId!==sessionId)throw new SekyroConnectError("token_session_mismatch");if(v.externalRef!==void 0&&v.externalRef!==userId)throw new SekyroConnectError("external_ref_mismatch");return v}async function applyResult(event,userId,result){let out=await updateClaim(userId,previous=>mergeClaim(previous,{kind:"result",result},{env,now}));return out.rule==="env_mismatch"?(log({type:"env_mismatch",sessionId:result.sessionId}),{handled:!1,event,reason:"env_mismatch"}):!out.changed||!out.claim?{handled:!0,event,userId,claim:out.claim,rule:out.rule}:(out.claim.subject_id!==void 0&&store.setSubjectUser&&await store.setSubjectUser(out.claim.subject_id,userId),log({type:"write",userId,sessionId:result.sessionId,rule:out.rule}),{handled:!0,event,userId,claim:out.claim,rule:out.rule})}async function handleTerminal(event,d,envelopeEnv,remember){if(d.appId===void 0||!ids.includes(d.appId))return{handled:!1,event,reason:"other_app"};let userId=d.externalRef;if(typeof userId!="string"||userId.length===0)return{handled:!1,event,reason:"no_external_ref"};let result;if(typeof d.resultToken=="string"&&d.resultToken.length>0){let v=await verifiedFor(d.resultToken,d.sessionId,userId),jti=(0,import_jose.decodeJwt)(d.resultToken).jti;if(typeof jti=="string"&&jti.length>0&&await remember(`jti:${jti}`,JTI_TTL_SEC))return{handled:!1,event,reason:"duplicate_delivery"};result=fromVerifiedResult(v,d)}else{if(opts.requireResultToken===!0)return{handled:!1,event,reason:"result_token_required"};log({type:"result_token_absent",sessionId:d.sessionId}),result=fromWebhookData(d,envelopeEnv,now())}return applyResult(event,userId,result)}async function resolveUsers(event,subjectId,refs){let clean=list=>Array.isArray(list)?[...new Set(list.filter(r=>typeof r=="string"&&r.length>0))]:[],fromEvent=clean(refs);if(fromEvent.length>0)return{users:fromEvent,fromRefs:!0};if(event==="subject.updated"){let fromApi=clean((await sekyro.subjects.get(subjectId)).externalRefs);if(fromApi.length>0)return{users:fromApi,fromRefs:!0}}let fromStore=await store.getSubjectUser?.(subjectId);return{users:fromStore?[fromStore]:[],fromRefs:!1}}async function handleSubjectGone(event,subjectId,refs){let{users,fromRefs}=await resolveUsers(event,subjectId,refs);if(users.length===0)return log({type:"revocation_unresolved",subjectId}),{handled:!1,event,reason:"revocation_unresolved"};let kind=event==="subject.updated"?"revoked":"deleted",first;for(let userId of users){let out=await updateClaim(userId,previous=>mergeClaim(previous,{kind,subjectId,userInExternalRefs:fromRefs},{env,now}));out.changed&&out.claim&&log({type:"write",userId,rule:out.rule}),first??={claim:out.claim,rule:out.rule}}return{handled:!0,event,userId:users[0],claim:first.claim,rule:first.rule}}async function routeEnvelope(envelope,remember){let event=envelope.event;switch(envelope.event){case"session.completed":case"session.failed":return await handleTerminal(envelope.event,envelope.data,envelope.env,remember);case"subject.updated":{let d=envelope.data;return d.status!=="revoked"||typeof d.subjectId!="string"?{handled:!1,event,reason:"ignored_event"}:await handleSubjectGone("subject.updated",d.subjectId,d.externalRefs)}case"subject.deleted":{let d=envelope.data;return typeof d.subjectId!="string"||d.subjectId.length===0?{handled:!1,event,reason:"ignored_event"}:await handleSubjectGone("subject.deleted",d.subjectId,d.externalRefs)}default:return{handled:!1,event,reason:"ignored_event"}}}async function handleEnvelope(envelope){let event=envelope.event,canForget=typeof store.forget=="function",recorded=[],deferred=[],remember=async(id,ttl)=>{if(!canForget)return deferred.push([id,ttl]),!1;let dup=await store.seen(id,ttl);return dup||recorded.push(id),dup};if(await remember(`wh:${envelope.id}`,DEDUP_TTL_SEC))return{handled:!1,event,reason:"duplicate_delivery"};let out;try{out=await routeEnvelope(envelope,remember)}catch(err){for(let id of recorded)await store.forget(id).catch(()=>{});throw err}for(let[id,ttl]of deferred)await store.seen(id,ttl);return out}async function createVia(userId,mode,o){assertUserId(mode==="recheck"?"recheck":"stepUp",userId);let input={mode};mode==="step_up"&&o.flow!==void 0&&(input.flow=o.flow),o.locale!==void 0&&(input.locale=o.locale),o.ttlMinutes!==void 0&&(input.ttlMinutes=o.ttlMinutes);let link=linkOf(o);return link&&(input.link=link),sekyro.subjects.createSession({externalRef:userId},input)}return{env,appIds:ids,async webhook(rawBody,headers){let envelope=(0,import_webhooks.verifyWebhook)(rawBody,(0,import_webhooks.signatureFromHeaders)(headers),opts.webhookSecret,{now}),out=await handleEnvelope(envelope);if(!out.handled&&out.reason!=="env_mismatch"&&out.reason!=="revocation_unresolved"){let data=envelope.data,skip={type:"skip",reason:out.reason};typeof data.sessionId=="string"&&(skip.sessionId=data.sessionId),typeof data.subjectId=="string"&&(skip.subjectId=data.subjectId),log(skip)}return out},async sync(userId,sessionId){assertUserId("sync",userId);let s=await sekyro.sessions.get(sessionId);if(s.externalRef!==userId)throw new SekyroConnectError("external_ref_mismatch");if(s.status!=="completed"&&s.status!=="failed")throw new SekyroConnectError("session_not_terminal");let{token}=await sekyro.sessions.resultToken(sessionId),v=await verifiedFor(token,sessionId,userId),out=await applyResult(s.status==="completed"?"session.completed":"session.failed",userId,fromVerifiedResult(v,s));return out.handled?{claim:out.claim,rule:out.rule}:{claim:null,rule:"env_mismatch"}},async start(userId,o={}){assertUserId("start",userId);let input={};o.flow!==void 0&&(input.flow=o.flow),o.locale!==void 0&&(input.locale=o.locale),o.ttlMinutes!==void 0&&(input.ttlMinutes=o.ttlMinutes),input.subject={externalRef:userId};let link=linkOf(o);return link&&(input.link=link),sekyro.sessions.create(input)},recheck(userId,o={}){return createVia(userId,"recheck",o)},stepUp(userId,o){return createVia(userId,"step_up",o)},async userDeleted(userId){assertUserId("userDeleted",userId);let policy=opts.onUserDeleted??"ignore";if(policy==="ignore")return{policy,subjects:0};let subjects=await sekyro.subjects.list({externalRef:userId});for(let s of subjects)try{policy==="revoke"?await sekyro.subjects.revoke(s.id):await sekyro.subjects.delete(s.id)}catch(err){if(!isApiStatus(err,[404,409]))throw err}return{policy,subjects:subjects.length}},async claimOf(userId){return parseClaim(await provider.readUser(userId))},async clearFlag(userId,flag){let out=await updateClaim(userId,previous=>{let claim=parseClaim(previous);return!claim||claim.v>1||!claim.flags?.includes(flag)?{claim,changed:!1,rule:"no_change"}:{claim:parseClaim({...claim,flags:claim.flags.filter(f=>f!==flag)}),changed:!0,rule:"no_change"}});return out.changed&&log({type:"flag_cleared",userId,flag}),out.claim},gate(tokenClaims,policy){return evaluateGate(provider.readClaimFromToken(tokenClaims),{...policy,env:policy.env??env},Math.floor(now()/1e3))}}}0&&(module.exports={CLAIM_VERSION,SekyroConnectError,createConnector,evaluateGate,memoryStore,mergeClaim,parseClaim});
@@ -0,0 +1,29 @@
1
+ export { b as CLAIM_VERSION, c as ClaimFlag, C as Connector, d as ConnectorLogEvent, e as ConnectorOptions, f as ConnectorStore, g as GateAction, G as GatePolicy, h as GateReason, a as GateResult, M as MemoryStore, i as MergeEvent, j as MergeOutcome, k as MergeRule, P as ProviderAdapter, S as SekyroClaim, l as StartOptions, V as VerificationResultInput, W as WebhookOutcome, m as WebhookSkipReason, n as createConnector, o as evaluateGate, p as memoryStore, q as mergeClaim, r as parseClaim } from '../connector-D1bhSLBl.cjs';
2
+ import '../client-DUMp_I-1.cjs';
3
+
4
+ /**
5
+ * Why a connector refused to act:
6
+ * - `token_session_mismatch`: the webhook's result token is for another session;
7
+ * - `external_ref_mismatch`: the session (or its result token) belongs to another user;
8
+ * - `session_not_terminal`: {@link Connector.sync} on a session that has not finished;
9
+ * - `invalid_options`: {@link createConnector} options are missing or invalid;
10
+ * - `unavailable`: the Sekyro AI public key set could not be fetched (the
11
+ * `jwks_unavailable` `SekyroResultTokenError` is its `cause`). It is
12
+ * retryable: answer the webhook with a 503 so Sekyro delivers it again.
13
+ * @public
14
+ */
15
+ type SekyroConnectErrorCode = "token_session_mismatch" | "external_ref_mismatch" | "session_not_terminal" | "invalid_options" | "unavailable";
16
+ /**
17
+ * Thrown by the connector when an input fails a trust check, the options are
18
+ * invalid, or the key set is unreachable. Nothing is written when it is
19
+ * thrown; answer the webhook with a 503 for `unavailable` (retryable) and a
20
+ * 4xx for every other code.
21
+ * @public
22
+ */
23
+ declare class SekyroConnectError extends Error {
24
+ readonly name = "SekyroConnectError";
25
+ readonly code: SekyroConnectErrorCode;
26
+ constructor(code: SekyroConnectErrorCode, message?: string, cause?: unknown);
27
+ }
28
+
29
+ export { SekyroConnectError, type SekyroConnectErrorCode };
@@ -0,0 +1,29 @@
1
+ export { b as CLAIM_VERSION, c as ClaimFlag, C as Connector, d as ConnectorLogEvent, e as ConnectorOptions, f as ConnectorStore, g as GateAction, G as GatePolicy, h as GateReason, a as GateResult, M as MemoryStore, i as MergeEvent, j as MergeOutcome, k as MergeRule, P as ProviderAdapter, S as SekyroClaim, l as StartOptions, V as VerificationResultInput, W as WebhookOutcome, m as WebhookSkipReason, n as createConnector, o as evaluateGate, p as memoryStore, q as mergeClaim, r as parseClaim } from '../connector-CMHuC-CU.js';
2
+ import '../client-DUMp_I-1.js';
3
+
4
+ /**
5
+ * Why a connector refused to act:
6
+ * - `token_session_mismatch`: the webhook's result token is for another session;
7
+ * - `external_ref_mismatch`: the session (or its result token) belongs to another user;
8
+ * - `session_not_terminal`: {@link Connector.sync} on a session that has not finished;
9
+ * - `invalid_options`: {@link createConnector} options are missing or invalid;
10
+ * - `unavailable`: the Sekyro AI public key set could not be fetched (the
11
+ * `jwks_unavailable` `SekyroResultTokenError` is its `cause`). It is
12
+ * retryable: answer the webhook with a 503 so Sekyro delivers it again.
13
+ * @public
14
+ */
15
+ type SekyroConnectErrorCode = "token_session_mismatch" | "external_ref_mismatch" | "session_not_terminal" | "invalid_options" | "unavailable";
16
+ /**
17
+ * Thrown by the connector when an input fails a trust check, the options are
18
+ * invalid, or the key set is unreachable. Nothing is written when it is
19
+ * thrown; answer the webhook with a 503 for `unavailable` (retryable) and a
20
+ * 4xx for every other code.
21
+ * @public
22
+ */
23
+ declare class SekyroConnectError extends Error {
24
+ readonly name = "SekyroConnectError";
25
+ readonly code: SekyroConnectErrorCode;
26
+ constructor(code: SekyroConnectErrorCode, message?: string, cause?: unknown);
27
+ }
28
+
29
+ export { SekyroConnectError, type SekyroConnectErrorCode };
@@ -0,0 +1 @@
1
+ import{CLAIM_VERSION,evaluateGate,mergeClaim,parseClaim}from"../chunk-O5GJ3YJV.js";import{decodeJwt}from"jose";import{verifyResultToken}from"@sekyro/node";import{signatureFromHeaders,verifyWebhook}from"@sekyro/node";var MESSAGES={token_session_mismatch:"The result token is for another session than the webhook",external_ref_mismatch:"The session belongs to another user",session_not_terminal:"The session has not finished yet",invalid_options:"Invalid connector options",unavailable:"The Sekyro AI public key set could not be fetched; try again"},SekyroConnectError=class extends Error{name="SekyroConnectError";code;constructor(code,message=MESSAGES[code],cause){super(message,cause===void 0?void 0:{cause}),this.code=code}};function factsOf(d,tokenDuplicates){let out={};return d.returning===!0&&(out.returning=!0),d.subjectConflict===!0&&(out.subjectConflict=!0),(Array.isArray(d.duplicates)&&d.duplicates.length>0||(tokenDuplicates?.length??0)>0)&&(out.duplicates=!0),out}function fromVerifiedResult(v,d){let out={sessionId:v.sessionId,outcome:v.outcome,level:v.level,assurance:v.assurance,verifiedAt:v.verifiedAt,env:v.env,...factsOf(d,v.duplicates)};return v.subjectId!==void 0&&(out.subjectId=v.subjectId),v.reuseMode!==void 0&&(out.reuseMode=v.reuseMode),out}function fromWebhookData(d,env,nowMs){let out={sessionId:d.sessionId,outcome:d.outcome??null,level:d.level??null,assurance:d.assurance??null,verifiedAt:d.verifiedAt??new Date(nowMs).toISOString(),env,...factsOf(d)};return d.subjectId!==void 0&&(out.subjectId=d.subjectId),d.reuseMode!==void 0&&(out.reuseMode=d.reuseMode),out}function memoryStore(opts={}){let now=opts.now??Date.now,ids=new Map,subjects=new Map,evict=t=>{for(let[id,expiresAt]of ids)expiresAt<=t&&ids.delete(id)};return{async seen(id,ttlSec){let t=now();return evict(t),ids.has(id)?!0:(ids.set(id,t+ttlSec*1e3),!1)},async forget(id){ids.delete(id)},async setSubjectUser(subjectId,userId){subjects.delete(subjectId),subjects.set(subjectId,userId),subjects.size>1e5&&subjects.delete(subjects.keys().next().value)},async getSubjectUser(subjectId){return subjects.get(subjectId)},size(){return evict(now()),ids.size}}}var DEDUP_TTL_SEC=24*3600,JTI_TTL_SEC=600;function isApiStatus(err,statuses){if(typeof err!="object"||err===null)return!1;let e=err;return e.name==="SekyroApiError"&&typeof e.status=="number"&&statuses.includes(e.status)}function linkOf(o){if(o.returnUrl===void 0&&o.state===void 0)return;let link={};return o.returnUrl!==void 0&&(link.returnUrl=o.returnUrl),o.state!==void 0&&(link.state=o.state),link}function perKeyLock(){let tails=new Map;return(key,fn)=>{let run=(tails.get(key)??Promise.resolve()).then(fn,fn),tail=run.then(()=>{},()=>{});return tails.set(key,tail),tail.then(()=>{tails.get(key)===tail&&tails.delete(key)}),run}}function assertUserId(method,userId){if(typeof userId!="string"||userId.length===0)throw new TypeError(`connector.${method}: \`userId\` must be a non-empty string`)}function createConnector(opts){let appIds=[opts?.appId].flat();if(appIds.length===0||appIds.some(id=>typeof id!="string"||id.length===0))throw new SekyroConnectError("invalid_options","`appId` must be a non-empty string or a non-empty array of them");if(opts.env!=="live"&&opts.env!=="sandbox")throw new SekyroConnectError("invalid_options","`env` must be 'live' or 'sandbox'");if(typeof opts.webhookSecret!="string"||opts.webhookSecret.length===0)throw new SekyroConnectError("invalid_options","`webhookSecret` is required");if(!opts.sekyro||!opts.provider)throw new SekyroConnectError("invalid_options","`sekyro` and `provider` are required");let{sekyro,provider,env}=opts,now=opts.now??Date.now,store=opts.store??memoryStore({now}),ids=Object.freeze([...appIds]),userLock=perKeyLock();function updateClaim(userId,fn){return typeof provider.update=="function"?provider.update(userId,fn):userLock(userId,async()=>{let out=fn(await provider.readUser(userId));return out.changed&&out.claim&&await provider.writeUser(userId,out.claim),out})}let log=event=>{if(opts.logger)try{opts.logger(event)}catch{}},tokenOptions={appId:[...ids],env,now};opts.issuer!==void 0&&(tokenOptions.issuer=opts.issuer),opts.jwksUrl!==void 0&&(tokenOptions.jwksUrl=opts.jwksUrl),opts.fetch!==void 0&&(tokenOptions.fetch=opts.fetch);async function verifiedFor(token,sessionId,userId){let v;try{v=await verifyResultToken(token,tokenOptions)}catch(err){let e=err;throw e?.name==="SekyroResultTokenError"&&e.code==="jwks_unavailable"?new SekyroConnectError("unavailable",void 0,err):err}if(v.sessionId!==sessionId)throw new SekyroConnectError("token_session_mismatch");if(v.externalRef!==void 0&&v.externalRef!==userId)throw new SekyroConnectError("external_ref_mismatch");return v}async function applyResult(event,userId,result){let out=await updateClaim(userId,previous=>mergeClaim(previous,{kind:"result",result},{env,now}));return out.rule==="env_mismatch"?(log({type:"env_mismatch",sessionId:result.sessionId}),{handled:!1,event,reason:"env_mismatch"}):!out.changed||!out.claim?{handled:!0,event,userId,claim:out.claim,rule:out.rule}:(out.claim.subject_id!==void 0&&store.setSubjectUser&&await store.setSubjectUser(out.claim.subject_id,userId),log({type:"write",userId,sessionId:result.sessionId,rule:out.rule}),{handled:!0,event,userId,claim:out.claim,rule:out.rule})}async function handleTerminal(event,d,envelopeEnv,remember){if(d.appId===void 0||!ids.includes(d.appId))return{handled:!1,event,reason:"other_app"};let userId=d.externalRef;if(typeof userId!="string"||userId.length===0)return{handled:!1,event,reason:"no_external_ref"};let result;if(typeof d.resultToken=="string"&&d.resultToken.length>0){let v=await verifiedFor(d.resultToken,d.sessionId,userId),jti=decodeJwt(d.resultToken).jti;if(typeof jti=="string"&&jti.length>0&&await remember(`jti:${jti}`,JTI_TTL_SEC))return{handled:!1,event,reason:"duplicate_delivery"};result=fromVerifiedResult(v,d)}else{if(opts.requireResultToken===!0)return{handled:!1,event,reason:"result_token_required"};log({type:"result_token_absent",sessionId:d.sessionId}),result=fromWebhookData(d,envelopeEnv,now())}return applyResult(event,userId,result)}async function resolveUsers(event,subjectId,refs){let clean=list=>Array.isArray(list)?[...new Set(list.filter(r=>typeof r=="string"&&r.length>0))]:[],fromEvent=clean(refs);if(fromEvent.length>0)return{users:fromEvent,fromRefs:!0};if(event==="subject.updated"){let fromApi=clean((await sekyro.subjects.get(subjectId)).externalRefs);if(fromApi.length>0)return{users:fromApi,fromRefs:!0}}let fromStore=await store.getSubjectUser?.(subjectId);return{users:fromStore?[fromStore]:[],fromRefs:!1}}async function handleSubjectGone(event,subjectId,refs){let{users,fromRefs}=await resolveUsers(event,subjectId,refs);if(users.length===0)return log({type:"revocation_unresolved",subjectId}),{handled:!1,event,reason:"revocation_unresolved"};let kind=event==="subject.updated"?"revoked":"deleted",first;for(let userId of users){let out=await updateClaim(userId,previous=>mergeClaim(previous,{kind,subjectId,userInExternalRefs:fromRefs},{env,now}));out.changed&&out.claim&&log({type:"write",userId,rule:out.rule}),first??={claim:out.claim,rule:out.rule}}return{handled:!0,event,userId:users[0],claim:first.claim,rule:first.rule}}async function routeEnvelope(envelope,remember){let event=envelope.event;switch(envelope.event){case"session.completed":case"session.failed":return await handleTerminal(envelope.event,envelope.data,envelope.env,remember);case"subject.updated":{let d=envelope.data;return d.status!=="revoked"||typeof d.subjectId!="string"?{handled:!1,event,reason:"ignored_event"}:await handleSubjectGone("subject.updated",d.subjectId,d.externalRefs)}case"subject.deleted":{let d=envelope.data;return typeof d.subjectId!="string"||d.subjectId.length===0?{handled:!1,event,reason:"ignored_event"}:await handleSubjectGone("subject.deleted",d.subjectId,d.externalRefs)}default:return{handled:!1,event,reason:"ignored_event"}}}async function handleEnvelope(envelope){let event=envelope.event,canForget=typeof store.forget=="function",recorded=[],deferred=[],remember=async(id,ttl)=>{if(!canForget)return deferred.push([id,ttl]),!1;let dup=await store.seen(id,ttl);return dup||recorded.push(id),dup};if(await remember(`wh:${envelope.id}`,DEDUP_TTL_SEC))return{handled:!1,event,reason:"duplicate_delivery"};let out;try{out=await routeEnvelope(envelope,remember)}catch(err){for(let id of recorded)await store.forget(id).catch(()=>{});throw err}for(let[id,ttl]of deferred)await store.seen(id,ttl);return out}async function createVia(userId,mode,o){assertUserId(mode==="recheck"?"recheck":"stepUp",userId);let input={mode};mode==="step_up"&&o.flow!==void 0&&(input.flow=o.flow),o.locale!==void 0&&(input.locale=o.locale),o.ttlMinutes!==void 0&&(input.ttlMinutes=o.ttlMinutes);let link=linkOf(o);return link&&(input.link=link),sekyro.subjects.createSession({externalRef:userId},input)}return{env,appIds:ids,async webhook(rawBody,headers){let envelope=verifyWebhook(rawBody,signatureFromHeaders(headers),opts.webhookSecret,{now}),out=await handleEnvelope(envelope);if(!out.handled&&out.reason!=="env_mismatch"&&out.reason!=="revocation_unresolved"){let data=envelope.data,skip={type:"skip",reason:out.reason};typeof data.sessionId=="string"&&(skip.sessionId=data.sessionId),typeof data.subjectId=="string"&&(skip.subjectId=data.subjectId),log(skip)}return out},async sync(userId,sessionId){assertUserId("sync",userId);let s=await sekyro.sessions.get(sessionId);if(s.externalRef!==userId)throw new SekyroConnectError("external_ref_mismatch");if(s.status!=="completed"&&s.status!=="failed")throw new SekyroConnectError("session_not_terminal");let{token}=await sekyro.sessions.resultToken(sessionId),v=await verifiedFor(token,sessionId,userId),out=await applyResult(s.status==="completed"?"session.completed":"session.failed",userId,fromVerifiedResult(v,s));return out.handled?{claim:out.claim,rule:out.rule}:{claim:null,rule:"env_mismatch"}},async start(userId,o={}){assertUserId("start",userId);let input={};o.flow!==void 0&&(input.flow=o.flow),o.locale!==void 0&&(input.locale=o.locale),o.ttlMinutes!==void 0&&(input.ttlMinutes=o.ttlMinutes),input.subject={externalRef:userId};let link=linkOf(o);return link&&(input.link=link),sekyro.sessions.create(input)},recheck(userId,o={}){return createVia(userId,"recheck",o)},stepUp(userId,o){return createVia(userId,"step_up",o)},async userDeleted(userId){assertUserId("userDeleted",userId);let policy=opts.onUserDeleted??"ignore";if(policy==="ignore")return{policy,subjects:0};let subjects=await sekyro.subjects.list({externalRef:userId});for(let s of subjects)try{policy==="revoke"?await sekyro.subjects.revoke(s.id):await sekyro.subjects.delete(s.id)}catch(err){if(!isApiStatus(err,[404,409]))throw err}return{policy,subjects:subjects.length}},async claimOf(userId){return parseClaim(await provider.readUser(userId))},async clearFlag(userId,flag){let out=await updateClaim(userId,previous=>{let claim=parseClaim(previous);return!claim||claim.v>1||!claim.flags?.includes(flag)?{claim,changed:!1,rule:"no_change"}:{claim:parseClaim({...claim,flags:claim.flags.filter(f=>f!==flag)}),changed:!0,rule:"no_change"}});return out.changed&&log({type:"flag_cleared",userId,flag}),out.claim},gate(tokenClaims,policy){return evaluateGate(provider.readClaimFromToken(tokenClaims),{...policy,env:policy.env??env},Math.floor(now()/1e3))}}}export{CLAIM_VERSION,SekyroConnectError,createConnector,evaluateGate,memoryStore,mergeClaim,parseClaim};
@@ -0,0 +1 @@
1
+ "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var next_exports={};__export(next_exports,{gateRoute:()=>gateRoute,sekyroGate:()=>sekyroGate});module.exports=__toCommonJS(next_exports);var KNOWN_FLAGS=["face_mismatch","duplicate","revoked"];function isRecord(v){return typeof v=="object"&&v!==null&&!Array.isArray(v)}function isFiniteNumber(v){return typeof v=="number"&&Number.isFinite(v)}function build(c){let out={v:1,level:c.level,assurance:c.assurance,verified_at:c.verified_at};c.rechecked_at!==void 0&&(out.rechecked_at=c.rechecked_at),out.session_id=c.session_id,c.subject_id!==void 0&&(out.subject_id=c.subject_id),out.env=c.env;let flags=new Set;for(let f of c.flags??[])KNOWN_FLAGS.includes(f)&&flags.add(f);return flags.size>0&&(out.flags=[...flags].sort()),out}function parseClaim(value){let v=value;if(typeof v=="string")try{v=JSON.parse(v)}catch{return null}if(!isRecord(v)||!isFiniteNumber(v.v))return null;if(v.v>1)return v;if(v.v!==1)return null;let level=v.level;return level!==0&&level!==1&&level!==2&&level!==3||typeof v.assurance!="string"||!isFiniteNumber(v.verified_at)||typeof v.session_id!="string"||v.env!=="live"&&v.env!=="sandbox"?null:build({level,assurance:v.assurance,verified_at:v.verified_at,rechecked_at:isFiniteNumber(v.rechecked_at)?v.rechecked_at:void 0,session_id:v.session_id,subject_id:typeof v.subject_id=="string"?v.subject_id:void 0,env:v.env,flags:Array.isArray(v.flags)?v.flags:void 0})}var DEFAULT_DENY_FLAGS=["revoked","face_mismatch"],fail=(reason,action,claim)=>({ok:!1,reason,action,claim});function evaluateGate(claim,policy,nowSec=Math.floor(Date.now()/1e3)){let parsed=parseClaim(claim);if(!parsed)return fail("no_claim","verify",null);if(parsed.v>1)return fail("unsupported_version","verify",parsed);if(policy.env!==void 0&&parsed.env!==policy.env)return fail("wrong_env","verify",parsed);let deny=policy.denyFlags??DEFAULT_DENY_FLAGS;return parsed.flags?.some(f=>deny.includes(f))?fail("flagged","block",parsed):parsed.level<policy.level?fail("level_too_low",parsed.level>=1?"step_up":"verify",parsed):policy.maxAgeDays!==void 0&&nowSec-parsed.verified_at>policy.maxAgeDays*86400?fail("stale","recheck",parsed):policy.maxRecheckAgeMinutes!==void 0&&nowSec-(parsed.rechecked_at??parsed.verified_at)>policy.maxRecheckAgeMinutes*60?fail("recheck_stale","recheck",parsed):policy.assurance&&!policy.assurance.includes(parsed.assurance)?fail("assurance_not_allowed","step_up",parsed):{ok:!0,claim:parsed}}function sekyroGate(source,tokenClaims,policy){let claims=tokenClaims??{};if("gate"in source)return source.gate(claims,policy);let effective=source.env!==void 0&&policy.env===void 0?{...policy,env:source.env}:policy;return evaluateGate(source.provider.readClaimFromToken(claims),effective)}function gateRoute(result,routes){if(result.ok)return null;switch(result.action){case"verify":return routes.verify;case"step_up":return routes.stepUp;case"recheck":return routes.recheck;case"block":return routes.block}}0&&(module.exports={gateRoute,sekyroGate});
@@ -0,0 +1,43 @@
1
+ import { a as GateResult, C as Connector, P as ProviderAdapter, G as GatePolicy } from '../../connector-D1bhSLBl.cjs';
2
+ import '../../client-DUMp_I-1.cjs';
3
+
4
+ /**
5
+ * `@sekyro/node/connect/next`: gate helpers for Next.js middleware, route
6
+ * handlers and server components. Pure, with no Node built-in: safe in the
7
+ * edge runtime.
8
+ */
9
+
10
+ /**
11
+ * Evaluate the claim inside a decoded session token: through the connector
12
+ * (`connector.gate`: its environment and clock apply) or a provider
13
+ * adapter with an optional environment. A missing token is `no_claim` / `verify`.
14
+ *
15
+ * @param source - A connector, or `{ provider, env? }` where no connector is built (middleware).
16
+ * @param tokenClaims - The decoded session token's claims, or nothing.
17
+ * @param policy - What the route requires; its `env` wins over the source's.
18
+ * @public
19
+ */
20
+ declare function sekyroGate(source: Connector | {
21
+ provider: ProviderAdapter;
22
+ env?: "live" | "sandbox";
23
+ }, tokenClaims: Record<string, unknown> | null | undefined, policy: GatePolicy): GateResult;
24
+ /**
25
+ * The route for a failed gate's action, or `null` when the gate passed.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const to = gateRoute(sekyroGate(source, claims, { level: 2 }), {
30
+ * verify: "/verify", stepUp: "/verify/step-up", recheck: "/verify/recheck", block: "/blocked",
31
+ * });
32
+ * if (to) return NextResponse.redirect(new URL(to, request.url));
33
+ * ```
34
+ * @public
35
+ */
36
+ declare function gateRoute(result: GateResult, routes: {
37
+ verify: string;
38
+ stepUp: string;
39
+ recheck: string;
40
+ block: string;
41
+ }): string | null;
42
+
43
+ export { gateRoute, sekyroGate };
@@ -0,0 +1,43 @@
1
+ import { a as GateResult, C as Connector, P as ProviderAdapter, G as GatePolicy } from '../../connector-CMHuC-CU.js';
2
+ import '../../client-DUMp_I-1.js';
3
+
4
+ /**
5
+ * `@sekyro/node/connect/next`: gate helpers for Next.js middleware, route
6
+ * handlers and server components. Pure, with no Node built-in: safe in the
7
+ * edge runtime.
8
+ */
9
+
10
+ /**
11
+ * Evaluate the claim inside a decoded session token: through the connector
12
+ * (`connector.gate`: its environment and clock apply) or a provider
13
+ * adapter with an optional environment. A missing token is `no_claim` / `verify`.
14
+ *
15
+ * @param source - A connector, or `{ provider, env? }` where no connector is built (middleware).
16
+ * @param tokenClaims - The decoded session token's claims, or nothing.
17
+ * @param policy - What the route requires; its `env` wins over the source's.
18
+ * @public
19
+ */
20
+ declare function sekyroGate(source: Connector | {
21
+ provider: ProviderAdapter;
22
+ env?: "live" | "sandbox";
23
+ }, tokenClaims: Record<string, unknown> | null | undefined, policy: GatePolicy): GateResult;
24
+ /**
25
+ * The route for a failed gate's action, or `null` when the gate passed.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const to = gateRoute(sekyroGate(source, claims, { level: 2 }), {
30
+ * verify: "/verify", stepUp: "/verify/step-up", recheck: "/verify/recheck", block: "/blocked",
31
+ * });
32
+ * if (to) return NextResponse.redirect(new URL(to, request.url));
33
+ * ```
34
+ * @public
35
+ */
36
+ declare function gateRoute(result: GateResult, routes: {
37
+ verify: string;
38
+ stepUp: string;
39
+ recheck: string;
40
+ block: string;
41
+ }): string | null;
42
+
43
+ export { gateRoute, sekyroGate };
@@ -0,0 +1 @@
1
+ import{gateRoute,sekyroGate}from"../../chunk-TAPXGI3Q.js";import"../../chunk-O5GJ3YJV.js";export{gateRoute,sekyroGate};