@authowl/core 0.10.0
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/LICENSE +21 -0
- package/README.md +231 -0
- package/THIRD_PARTY_NOTICES.md +33 -0
- package/dist/chunk-KPXCQZUX.js +2 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +1282 -0
- package/dist/index.d.ts +1282 -0
- package/dist/index.js +3 -0
- package/dist/server.cjs +2 -0
- package/dist/server.d.cts +2757 -0
- package/dist/server.d.ts +2757 -0
- package/dist/server.js +1 -0
- package/dist/transport-BObDKlIh.d.cts +131 -0
- package/dist/transport-BObDKlIh.d.ts +131 -0
- package/package.json +66 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {d,c,u,s,g as g$1,f,e,q as q$1}from'./chunk-KPXCQZUX.js';export{e as resolveConfig,b as sessionCookieName}from'./chunk-KPXCQZUX.js';var i=class extends Error{code;constructor(t,n="TOKEN_VERIFICATION_FAILED"){super(t),this.name="TokenVerificationError",this.code=n;}},X=300*1e3,Z=5e3,Q=64*1024,ee=64,te=60*1e3,W=new Map,$=new Map;function j(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t.length%4===0?t:t+"=".repeat(4-t.length%4),r=typeof atob=="function"?atob(n):globalThis.Buffer.from(n,"base64").toString("binary"),s=new Uint8Array(r.length);for(let o=0;o<r.length;o+=1)s[o]=r.charCodeAt(o);return s}function D(e){try{if(!/^[A-Za-z0-9_-]+$/.test(e))throw new i("Malformed JWT segment.","TOKEN_MALFORMED");let t=new TextDecoder().decode(j(e)),n=JSON.parse(t);if(!n||typeof n!="object"||Array.isArray(n))throw new i("Malformed JWT segment.","TOKEN_MALFORMED");return n}catch(t){throw t instanceof i?t:new i("Malformed JWT segment.","TOKEN_MALFORMED")}}function q(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function J(e){return typeof e=="string"&&/^[A-Za-z0-9_-]{43}$/.test(e)&&j(e).byteLength===32}function ne(e){if(!q(e))throw new i("JWKS contains a non-object key.","JWKS_KEY_INVALID");let t=new Set(["alg","crv","kid","kty","use","x","y"]),n=["d","p","q","dp","dq","qi","k","oth"];if("key_ops"in e||n.some(r=>r in e)||Object.keys(e).some(r=>!t.has(r)))throw new i("JWKS contains private, key_ops, or unexpected key members.","JWKS_KEY_INVALID");if(e.kty!=="EC"||e.crv!=="P-256"||e.alg!=="ES256"||e.use!=="sig"||typeof e.kid!="string"||e.kid.length===0||e.kid.length>128||!/^[A-Za-z0-9_-]+$/.test(e.kid)||!J(e.x)||!J(e.y))throw new i("JWKS contains a key outside the AuthOwl ES256 public-key schema.","JWKS_KEY_INVALID");return {alg:e.alg,crv:e.crv,kid:e.kid,kty:e.kty,use:e.use,x:e.x,y:e.y}}function re(e){if(!q(e)||Object.keys(e).length!==1||!Array.isArray(e.keys))throw new i("JWKS response must be an object containing only a keys array.","JWKS_DOCUMENT_INVALID");if(e.keys.length>ee)throw new i("JWKS response exceeds the 64-key limit.","JWKS_TOO_MANY_KEYS");let t=e.keys.map(ne),n=new Set;for(let r of t){if(n.has(r.kid))throw new i("JWKS response contains duplicate kid values.","JWKS_DUPLICATE_KID");n.add(r.kid);}return t}async function V(e,t){let n=W.get(e);if(!t&&n&&Date.now()-n.fetchedAt<X)return n.keys;try{let r=await g$1({fetchImpl:fetch,url:e,init:{headers:{accept:"application/json"}},timeoutMs:Z,maxResponseBytes:Q,allowHttpLoopback:new URL(e).protocol==="http:",decode:o=>q$1(o)});if(!r.response.ok)throw new i(`JWKS fetch returned ${r.response.status}.`,"JWKS_HTTP_ERROR");let s=re(r.data);return W.set(e,{keys:s,fetchedAt:Date.now()}),s}catch(r){if(r instanceof i)throw r;if(r instanceof f)switch(r.kind){case "timeout":throw new i("JWKS fetch timed out.","JWKS_FETCH_TIMEOUT");case "response_too_large":throw new i("JWKS response exceeds the 64 KiB limit.","JWKS_RESPONSE_TOO_LARGE");case "invalid_response":throw new i("JWKS response is invalid.","JWKS_DOCUMENT_INVALID");}throw new i("Failed to fetch JWKS.","JWKS_FETCH_FAILED")}}async function se(e,t){let n=s=>t?s.find(o=>o.kid===t):s[0],r=n(await V(e,false));if(!r){let s=$.get(e)??0;Date.now()-s>=te&&($.set(e,Date.now()),r=n(await V(e,true)));}if(!r)throw new i("No matching JWKS key for the token kid.","JWKS_KEY_NOT_FOUND");return r}function oe(e){let t=globalThis.crypto?.subtle;if(!t)throw new i("WebCrypto is unavailable in this runtime.","WEBCRYPTO_UNAVAILABLE");return t.importKey("jwk",{kty:e.kty,crv:e.crv,x:e.x,y:e.y},{name:"ECDSA",namedCurve:"P-256"},false,["verify"])}function ie(e,t){return typeof e=="string"?e===t:Array.isArray(e)?e.includes(t):false}function ae(e){let t=e.membership;if(!t||typeof t!="object"||Array.isArray(t))return null;let n=t,r=typeof n.role=="string"?n.role:"",s=Array.isArray(n.permissions)?n.permissions.filter(a=>typeof a=="string"):[],o=Array.isArray(n.teams)?n.teams.filter(a=>typeof a=="string"):void 0;return r===""&&s.length===0&&!o?.length?null:{role:r,permissions:s,...o===void 0?{}:{teams:o}}}async function pe(e,t){return _(e,I(t))}function I(e,t=false){if(!e||typeof e!="object")throw new i("Token verification options are required.","TOKEN_CONFIG_INVALID");let n;try{n=d(e.issuer,e.jwksUri,{allowHttpLoopback:t});}catch{throw new i("Token verifier issuer or JWKS URL is invalid.","TOKEN_CONFIG_INVALID")}if(typeof e.audience!="string"||e.audience.length===0||e.audience.length>256)throw new i("Token verifier audience is invalid.","TOKEN_CONFIG_INVALID");if(e.clockToleranceSeconds!==void 0&&(!Number.isInteger(e.clockToleranceSeconds)||e.clockToleranceSeconds<0||e.clockToleranceSeconds>300))throw new i("clockToleranceSeconds must be an integer from 0 through 300.","TOKEN_CONFIG_INVALID");return {...e,...n}}async function _(e,t){if(typeof e!="string"||e.length===0)throw new i("A token string is required.","TOKEN_MALFORMED");let n=e.split(".");if(n.length!==3)throw new i("Malformed JWT.","TOKEN_MALFORMED");let[r,s,o]=n,a=D(r);if(a.alg!=="ES256")throw new i("Unsupported JWT algorithm.","TOKEN_ALGORITHM_UNSUPPORTED");let p=await se(t.jwksUri,typeof a.kid=="string"?a.kid:void 0),d;try{d=await oe(p);}catch(S){throw S instanceof i?S:new i("JWKS key could not be imported.","JWKS_KEY_INVALID")}let m;try{if(!/^[A-Za-z0-9_-]{86}$/.test(o))throw new Error("invalid signature encoding");if(m=j(o),m.byteLength!==64)throw new Error("invalid signature length")}catch{throw new i("Malformed JWT signature.","TOKEN_MALFORMED")}let y=new TextEncoder().encode(`${r}.${s}`),f;try{f=await globalThis.crypto.subtle.verify({name:"ECDSA",hash:"SHA-256"},d,m,y);}catch{throw new i("Token signature verification failed.","TOKEN_SIGNATURE_INVALID")}if(!f)throw new i("Invalid token signature.","TOKEN_SIGNATURE_INVALID");let c=D(s),l=t.clockToleranceSeconds??60,h=Math.floor(Date.now()/1e3);if(typeof c.exp!="number")throw new i("Token is missing a valid exp claim.","TOKEN_CLAIM_INVALID");if(c.exp+l<h)throw new i("Token has expired.","TOKEN_CLAIM_INVALID");if(typeof c.nbf=="number"&&c.nbf-l>h)throw new i("Token is not yet valid.","TOKEN_CLAIM_INVALID");if(typeof c.iss!="string"||c.iss!==t.issuer)throw new i("Token issuer missing or mismatched.","TOKEN_CLAIM_INVALID");if(!ie(c.aud,t.audience))throw new i("Token audience mismatch.","TOKEN_CLAIM_INVALID");return {sub:typeof c.sub=="string"?c.sub:null,membership:ae(c),claims:c}}var ce="dc1953d9d9b9e1f7002fa045f12788280e5865bacfda94b1d133e9580aa87d62",A={getOpenApiDocument:{method:"GET",path:"/openapi.json",successStatuses:[200],responseSchemas:{200:{type:"object"}}},listUsers:{method:"GET",path:"/users",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/UserPage"}}},createUser:{method:"POST",path:"/users",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/User"}}},startUserExport:{method:"POST",path:"/users/export",successStatuses:[202],responseSchemas:{202:{$ref:"#/components/schemas/ExportJob"}}},getUser:{method:"GET",path:"/users/{userId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/User"}}},updateUser:{method:"PATCH",path:"/users/{userId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/User"}}},deleteUser:{method:"DELETE",path:"/users/{userId}",successStatuses:[204],responseSchemas:{204:null}},listUserSessions:{method:"GET",path:"/users/{userId}/sessions",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/SessionPage"}}},revokeUserSessions:{method:"DELETE",path:"/users/{userId}/sessions",successStatuses:[204],responseSchemas:{204:null}},updateUserMetadata:{method:"PATCH",path:"/users/{userId}/metadata",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/UserMetadata"}}},verifySession:{method:"POST",path:"/sessions/verify",successStatuses:[200],responseSchemas:{200:{type:"object",required:["user","session"],properties:{user:{$ref:"#/components/schemas/User"},session:{$ref:"#/components/schemas/Session"}}}}},getSession:{method:"GET",path:"/sessions/{sessionId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/Session"}}},updateSessionMetadata:{method:"PATCH",path:"/sessions/{sessionId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/SessionMetadata"}}},revokeSession:{method:"DELETE",path:"/sessions/{sessionId}",successStatuses:[204],responseSchemas:{204:null}},listOrganizations:{method:"GET",path:"/organizations",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/OrganizationPage"}}},createOrganization:{method:"POST",path:"/organizations",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/Organization"}}},getOrganization:{method:"GET",path:"/organizations/{organizationId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/Organization"}}},updateOrganization:{method:"PATCH",path:"/organizations/{organizationId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/Organization"}}},deleteOrganization:{method:"DELETE",path:"/organizations/{organizationId}",successStatuses:[204],responseSchemas:{204:null}},listOrganizationMembers:{method:"GET",path:"/organizations/{organizationId}/members",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/MemberPage"}}},addOrganizationMember:{method:"POST",path:"/organizations/{organizationId}/members",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/OrganizationMember"}}},updateOrganizationMember:{method:"PATCH",path:"/organizations/{organizationId}/members/{userId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/OrganizationMember"}}},removeOrganizationMember:{method:"DELETE",path:"/organizations/{organizationId}/members/{userId}",successStatuses:[204],responseSchemas:{204:null}},listOrganizationRoles:{method:"GET",path:"/organizations/{organizationId}/roles",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/RolePage"}}},createOrganizationRole:{method:"POST",path:"/organizations/{organizationId}/roles",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/OrganizationRole"}}},updateOrganizationRole:{method:"PATCH",path:"/organizations/{organizationId}/roles/{roleId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/OrganizationRole"}}},deleteOrganizationRole:{method:"DELETE",path:"/organizations/{organizationId}/roles/{roleId}",successStatuses:[204],responseSchemas:{204:null}},listInvitations:{method:"GET",path:"/invitations",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/InvitationPage"}}},createInvitation:{method:"POST",path:"/invitations",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/Invitation"}}},getInvitation:{method:"GET",path:"/invitations/{invitationId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/Invitation"}}},revokeInvitation:{method:"DELETE",path:"/invitations/{invitationId}",successStatuses:[204],responseSchemas:{204:null}},listEvents:{method:"GET",path:"/events",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/EventPage"}}},getStatsOverview:{method:"GET",path:"/stats/overview",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/StatsOverview"}}},listWebhookEndpoints:{method:"GET",path:"/webhooks",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookPage"}}},createWebhookEndpoint:{method:"POST",path:"/webhooks",successStatuses:[201],responseSchemas:{201:{$ref:"#/components/schemas/WebhookWithSecret"}}},getWebhookEndpoint:{method:"GET",path:"/webhooks/{webhookId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookEndpoint"}}},updateWebhookEndpoint:{method:"PATCH",path:"/webhooks/{webhookId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookEndpoint"}}},deleteWebhookEndpoint:{method:"DELETE",path:"/webhooks/{webhookId}",successStatuses:[204],responseSchemas:{204:null}},rotateWebhookSecret:{method:"POST",path:"/webhooks/{webhookId}/rotate-secret",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookSecret"}}},pauseWebhookEndpoint:{method:"POST",path:"/webhooks/{webhookId}/pause",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookEndpoint"}}},resumeWebhookEndpoint:{method:"POST",path:"/webhooks/{webhookId}/resume",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookEndpoint"}}},testWebhookEndpoint:{method:"POST",path:"/webhooks/{webhookId}/test",successStatuses:[202],responseSchemas:{202:{$ref:"#/components/schemas/WebhookDelivery"}}},listWebhookDeliveries:{method:"GET",path:"/webhook-deliveries",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/DeliveryPage"}}},getWebhookDelivery:{method:"GET",path:"/webhook-deliveries/{deliveryId}",successStatuses:[200],responseSchemas:{200:{$ref:"#/components/schemas/WebhookDelivery"}}},replayWebhookDelivery:{method:"POST",path:"/webhook-deliveries/{deliveryId}/replay",successStatuses:[202],responseSchemas:{202:{$ref:"#/components/schemas/WebhookDelivery"}}}},L={Problem:{type:"object",required:["type","title","status","detail","instance","code"],properties:{type:{type:"string",format:"uri"},title:{type:"string"},status:{type:"integer"},detail:{type:"string"},instance:{type:"string",format:"uri"},code:{type:"string"}},additionalProperties:true},User:{type:"object",required:["id","email","phone","name","image","email_verified","banned","public_metadata","private_metadata","unsafe_metadata","metadata_version","created_at","updated_at"],properties:{id:{type:"string"},email:{type:"string",format:"email"},phone:{type:["string","null"]},name:{type:["string","null"]},image:{type:["string","null"],format:"uri"},email_verified:{type:"boolean"},banned:{type:"boolean"},public_metadata:{$ref:"#/components/schemas/MetadataObject"},private_metadata:{$ref:"#/components/schemas/MetadataObject"},unsafe_metadata:{$ref:"#/components/schemas/MetadataObject"},metadata_version:{type:"integer",minimum:0},created_at:{type:"string",format:"date-time"},updated_at:{type:"string",format:"date-time"}},additionalProperties:false},CreateUser:{type:"object",required:["email"],properties:{email:{type:"string",format:"email",maxLength:320},password:{type:"string",minLength:8,maxLength:128},name:{type:"string",minLength:1,maxLength:200},phone:{type:"string",minLength:1,maxLength:32}},additionalProperties:false},UpdateUser:{type:"object",minProperties:1,properties:{email:{type:"string",format:"email",maxLength:320},name:{type:["string","null"],minLength:1,maxLength:200},phone:{type:["string","null"],minLength:1,maxLength:32},banned:{type:"boolean"}},additionalProperties:false},MetadataObject:{type:"object",maxProperties:100,description:"JSON metadata object. Reserved prototype keys and the authowl_ prefix are rejected at every depth. Encoded object size is limited to 8192 bytes.",additionalProperties:true},UserMetadata:{type:"object",required:["public_metadata","private_metadata","unsafe_metadata","metadata_version"],properties:{public_metadata:{$ref:"#/components/schemas/MetadataObject"},private_metadata:{$ref:"#/components/schemas/MetadataObject"},unsafe_metadata:{$ref:"#/components/schemas/MetadataObject"},metadata_version:{type:"integer",minimum:0}},additionalProperties:false},UpdateUserMetadata:{type:"object",required:["expected_version"],minProperties:2,properties:{expected_version:{type:"integer",minimum:0,maximum:2147483647},public_metadata:{$ref:"#/components/schemas/MetadataObject"},private_metadata:{$ref:"#/components/schemas/MetadataObject"},unsafe_metadata:{$ref:"#/components/schemas/MetadataObject"}},additionalProperties:false},Session:{type:"object",required:["id","user_id","expires_at","created_at","metadata","metadata_version"],properties:{id:{type:"string"},user_id:{type:"string"},expires_at:{type:"string",format:"date-time"},created_at:{type:"string",format:"date-time"},ip_address:{type:["string","null"]},user_agent:{type:["string","null"]},metadata:{$ref:"#/components/schemas/MetadataObject"},metadata_version:{type:"integer",minimum:0}},additionalProperties:false},SessionMetadata:{type:"object",required:["metadata","metadata_version"],properties:{metadata:{$ref:"#/components/schemas/MetadataObject"},metadata_version:{type:"integer",minimum:0}},additionalProperties:false},UpdateSessionMetadata:{type:"object",required:["expected_version","metadata"],properties:{expected_version:{type:"integer",minimum:0,maximum:2147483647},metadata:{$ref:"#/components/schemas/MetadataObject"}},additionalProperties:false},Organization:{type:"object",required:["id","name","slug","created_at"],properties:{id:{type:"string"},name:{type:"string"},slug:{type:"string"},logo:{type:["string","null"],format:"uri"},metadata:{type:["object","null"]},created_at:{type:"string",format:"date-time"}},additionalProperties:false},CreateOrganization:{type:"object",required:["name","slug"],properties:{name:{type:"string",minLength:1,maxLength:200},slug:{type:"string",minLength:1,maxLength:100,pattern:"^[a-z0-9]+(?:-[a-z0-9]+)*$"},logo:{type:"string",format:"uri",maxLength:2048},metadata:{type:"object"}},additionalProperties:false},UpdateOrganization:{type:"object",minProperties:1,properties:{name:{type:"string",minLength:1,maxLength:200},slug:{type:"string",minLength:1,maxLength:100,pattern:"^[a-z0-9]+(?:-[a-z0-9]+)*$"},logo:{type:["string","null"],format:"uri",maxLength:2048},metadata:{type:["object","null"]}},additionalProperties:false},OrganizationMember:{type:"object",required:["organization_id","user_id","role","created_at"],properties:{organization_id:{type:"string"},user_id:{type:"string"},role:{type:"string"},created_at:{type:"string",format:"date-time"}},additionalProperties:false},AddMember:{type:"object",required:["user_id","role"],properties:{user_id:{type:"string"},role:{type:"string",minLength:1,maxLength:100}},additionalProperties:false},OrganizationRole:{type:"object",required:["id","organization_id","name","permissions"],properties:{id:{type:"string"},organization_id:{type:"string"},name:{type:"string"},permissions:{type:"array",items:{$ref:"#/components/schemas/OrganizationPermissionId"}}},additionalProperties:false},OrganizationPermissionId:{type:"string",enum:["organization:update","organization:delete","member:create","member:update","member:delete","invitation:create","invitation:cancel","team:create","team:update","team:delete","ac:create","ac:read","ac:update","ac:delete"]},RoleInput:{type:"object",required:["name","permissions"],properties:{name:{type:"string",minLength:1,maxLength:100,pattern:"^[a-z][a-z0-9_-]*$"},permissions:{type:"array",maxItems:100,items:{$ref:"#/components/schemas/OrganizationPermissionId"},uniqueItems:true}},additionalProperties:false},Invitation:{type:"object",required:["id","organization_id","email","role","status","expires_at"],properties:{id:{type:"string"},organization_id:{type:"string"},email:{type:"string",format:"email",maxLength:320},role:{type:"string",minLength:1,maxLength:100},status:{type:"string"},expires_at:{type:"string",format:"date-time"}},additionalProperties:false},CreateInvitation:{type:"object",required:["organization_id","email","role"],properties:{organization_id:{type:"string"},email:{type:"string",format:"email",maxLength:320},role:{type:"string",minLength:1,maxLength:100}},additionalProperties:false},Event:{type:"object",required:["id","type","status","created_at"],properties:{id:{type:"string"},type:{type:"string"},status:{type:"string"},user_id:{type:["string","null"]},metadata:{type:"object"},created_at:{type:"string",format:"date-time"}},additionalProperties:false},StatsOverview:{type:"object",required:["users_total","monthly_active_users","recent_sign_ins"],properties:{users_total:{type:"integer",minimum:0},monthly_active_users:{type:"integer",minimum:0},recent_sign_ins:{type:"integer",minimum:0}},additionalProperties:false},WebhookInput:{type:"object",required:["url","events"],properties:{url:{type:"string",format:"uri",maxLength:2048},description:{type:"string",maxLength:500},events:{type:"array",minItems:1,maxItems:20,items:{$ref:"#/components/schemas/WebhookSubscriptionEventType"},uniqueItems:true}},additionalProperties:false},WebhookSubscriptionEventType:{type:"string",enum:["user.created","user.updated","user.deleted","user.banned","session.created","session.updated","session.revoked","organization.created","organization.updated","organization.deleted","organization_membership.created","organization_membership.deleted","mfa.enrolled","mfa.reset"]},WebhookEventType:{type:"string",enum:["user.created","user.updated","user.deleted","user.banned","session.created","session.updated","session.revoked","organization.created","organization.updated","organization.deleted","organization_membership.created","organization_membership.deleted","mfa.enrolled","mfa.reset","webhook.test"]},WebhookEndpoint:{type:"object",required:["id","url","events","status","created_at","updated_at"],properties:{id:{type:"string",format:"uuid"},url:{type:"string",format:"uri"},description:{type:["string","null"]},events:{type:"array",items:{$ref:"#/components/schemas/WebhookSubscriptionEventType"}},status:{type:"string",enum:["active","paused","auto_paused"]},created_at:{type:"string",format:"date-time"},updated_at:{type:"string",format:"date-time"}},additionalProperties:false},WebhookSecret:{type:"object",required:["secret","overlap_expires_at"],properties:{secret:{type:"string"},overlap_expires_at:{type:"string",format:"date-time"}},additionalProperties:false},WebhookWithSecret:{type:"object",required:["endpoint","secret"],properties:{endpoint:{$ref:"#/components/schemas/WebhookEndpoint"},secret:{type:"string"}},additionalProperties:false},WebhookDelivery:{type:"object",required:["id","event_id","attempt_id","attempt_number","webhook_id","event_type","status","response_status","response_body","error_code","created_at","delivered_at"],properties:{id:{type:"string",format:"uuid"},event_id:{type:"string",format:"uuid"},attempt_id:{type:"string",format:"uuid"},attempt_number:{type:"integer",minimum:1},webhook_id:{type:"string",format:"uuid"},event_type:{$ref:"#/components/schemas/WebhookEventType"},status:{type:"string",enum:["queued","delivering","succeeded","failed"]},response_status:{type:["integer","null"]},response_body:{type:["string","null"]},error_code:{type:["string","null"]},created_at:{type:"string",format:"date-time"},delivered_at:{type:["string","null"],format:"date-time"}},additionalProperties:false},ExportJob:{type:"object",required:["id","status","created_at"],properties:{id:{type:"string",format:"uuid"},status:{type:"string",enum:["pending","processing","completed","failed"]},created_at:{type:"string",format:"date-time"}},additionalProperties:false},Page:{type:"object",required:["data","next_cursor"],properties:{data:{type:"array"},next_cursor:{type:["string","null"]}},additionalProperties:false},UserPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/User"}}}}]},SessionPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/Session"}}}}]},OrganizationPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/Organization"}}}}]},MemberPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/OrganizationMember"}}}}]},RolePage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/OrganizationRole"}}}}]},InvitationPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/Invitation"}}}}]},EventPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/Event"}}}}]},WebhookPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/WebhookEndpoint"}}}}]},DeliveryPage:{allOf:[{$ref:"#/components/schemas/Page"},{type:"object",properties:{data:{type:"array",items:{$ref:"#/components/schemas/WebhookDelivery"}}}}]}};var de=new Set(["__proto__","constructor","prototype"]),me=64,ue=5e4;function B(e,t,n){let s=A[e].responseSchemas;Object.hasOwn(s,String(t))||P();let o=s[String(t)];if(o===null){n!==null&&P();return}let a={nodes:0};return g(n,O(o),0,a)||P(),n}function g(e,t,n,r){if(r.nodes+=1,n>me||r.nodes>ue)return false;if(typeof t.$ref=="string"){let o=Se(t.$ref);if(o===null||!g(e,o,n+1,r))return false}if(Array.isArray(t.allOf)&&!t.allOf.every(o=>g(e,O(o),n+1,r))||Array.isArray(t.enum)&&!t.enum.some(o=>Object.is(o,e)))return false;let s=Array.isArray(t.type)?t.type:[t.type];return t.type!==void 0&&!s.some(o=>fe(e,o))?false:e===null?t.type===void 0||s.includes("null"):typeof e=="string"?le(e,t):typeof e=="number"?he(e,t):Array.isArray(e)?ge(e,t,n,r):w(e)?be(e,t,n,r):typeof e=="boolean"}function fe(e,t){switch(t){case "null":return e===null;case "object":return w(e);case "array":return Array.isArray(e);case "string":return typeof e=="string";case "integer":return Number.isSafeInteger(e);case "number":return typeof e=="number"&&Number.isFinite(e);case "boolean":return typeof e=="boolean";default:return false}}function le(e,t){if(typeof t.minLength=="number"&&e.length<t.minLength||typeof t.maxLength=="number"&&e.length>t.maxLength||typeof t.pattern=="string"&&!ye(e,t.pattern))return false;switch(t.format){case void 0:return true;case "uuid":return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e);case "date-time":return /^\d{4}-\d{2}-\d{2}T/.test(e)&&Number.isFinite(Date.parse(e));case "email":return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);case "uri":try{return new URL(e).protocol.length>1}catch{return false}default:return false}}function ye(e,t){switch(t){case "^[a-z0-9]+(?:-[a-z0-9]+)*$":return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e);case "^[a-z][a-z0-9_-]*$":return /^[a-z][a-z0-9_-]*$/.test(e);default:return false}}function he(e,t){return Number.isFinite(e)&&!(typeof t.minimum=="number"&&e<t.minimum)&&!(typeof t.maximum=="number"&&e>t.maximum)}function ge(e,t,n,r){if(typeof t.minItems=="number"&&e.length<t.minItems||typeof t.maxItems=="number"&&e.length>t.maxItems||t.uniqueItems===true&&new Set(e.map(o=>JSON.stringify(o))).size!==e.length)return false;if(t.items===void 0)return true;let s=O(t.items);return e.every(o=>g(o,s,n+1,r))}function be(e,t,n,r){let s=Object.keys(e);if(s.some(a=>de.has(a))||typeof t.minProperties=="number"&&s.length<t.minProperties||typeof t.maxProperties=="number"&&s.length>t.maxProperties)return false;let o=w(t.properties)?t.properties:{};if(Array.isArray(t.required)&&t.required.some(a=>typeof a!="string"||!Object.hasOwn(e,a)))return false;for(let[a,p]of Object.entries(e)){let d=o[a];if(d!==void 0){if(!g(p,O(d),n+1,r))return false;continue}if(t.additionalProperties===false||w(t.additionalProperties)&&!g(p,t.additionalProperties,n+1,r))return false}return true}function Se(e){let t="#/components/schemas/";if(!e.startsWith(t))return null;let n=e.slice(t.length);return Object.hasOwn(L,n)?O(L[n]):null}function O(e){return w(e)||P(),e}function w(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function P(){throw new TypeError("Admin API response does not match its generated contract.")}var _e=/^sk_(?:live|test)_[0-9a-f-]{36}_[A-Za-z0-9]{20,}$/i,Ae=1024*1024,we=1e4,E=class extends Error{status;code;requestId;problem;retryAfter;constructor(t){super(t.problem.detail),this.name="AuthOwlAdminApiError",this.status=t.status,this.code=t.code,this.requestId=t.requestId,this.problem=t.problem,this.retryAfter=t.retryAfter;}},b=class extends Error{kind;requestId;constructor(t,n){super(t==="aborted"?"The AuthOwl Admin API request was aborted.":t==="timeout"?"The AuthOwl Admin API request timed out.":t==="response_too_large"?"The AuthOwl Admin API response exceeded the allowed size.":t==="invalid_response"?"The AuthOwl Admin API returned an invalid response.":"The AuthOwl Admin API request could not be completed."),this.name="AuthOwlAdminNetworkError",this.kind=t,this.requestId=n;}};function Oe(e){Ee();let t=ke(e?.secretKey),n=Te(e?.apiUrl),r=e?.fetch??globalThis.fetch;if(typeof r!="function")throw new TypeError("A fetch implementation is required in this server runtime.");let s=async(a,p)=>{let d=p??{},m=A[a],y=new URL(Ie(m.path,d).replace(/^\/+/,""),n);Pe(y,d);let f$1=new Headers({accept:"application/json, application/problem+json",authorization:`Bearer ${t}`}),c="body"in d?JSON.stringify(d.body):void 0;c!==void 0&&f$1.set("content-type","application/json");let l;try{l=await g$1({fetchImpl:r,url:y,init:{method:m.method,headers:f$1,body:c,signal:p?.signal},timeoutMs:we,maxResponseBytes:Ae,allowHttpLoopback:n.protocol==="http:",decode:(u,G)=>B(a,G.status,u)});}catch(u){throw u instanceof E?u:u instanceof f?new b(u.kind,u.requestId):new b("network")}let{response:h,requestId:S,data:R}=l;if(!h.ok)throw xe(h,R,S);if(!m.successStatuses.some(u=>u===h.status))throw new b("invalid_response",S);return R},o=Object.assign(Object.create(null),{request:s});for(let a of Object.keys(A))o[a]=p=>s(a,p);return Object.freeze(o)}function Ee(){if(typeof window<"u"&&typeof window.document<"u")throw new Error("createAdminClient must not be called in a browser context.")}function ke(e){if(typeof e!="string"||!_e.test(e))throw new TypeError("secretKey is malformed; expected sk_(live|test)_<uuid>_<random>.");return e}function Te(e){if(typeof e!="string"||e.length===0)throw new TypeError("apiUrl is required.");let t=c(e,{allowHttpLoopback:true});if(t.username||t.password||t.search||t.hash)throw new TypeError("apiUrl must not contain credentials, a query, or a fragment.");let n=t.pathname.replace(/\/+$/,"");if(n!==""&&n!=="/api/v1")throw new TypeError("apiUrl path must be empty or /api/v1.");return t.pathname="/api/v1/",t}function Ie(e,t){let n="path"in t&&N(t.path)?t.path:{};return e.replace(/\{([^}]+)\}/g,(r,s)=>{let o=n[s];if(typeof o!="string"&&typeof o!="number")throw new TypeError(`Missing Admin API path parameter: ${s}.`);return encodeURIComponent(String(o))})}function Pe(e,t){if(!(!("query"in t)||!N(t.query)))for(let[n,r]of Object.entries(t.query)){if(r==null)continue;let s=Array.isArray(r)?r:[r];for(let o of s)e.searchParams.append(n,String(o));}}function xe(e,t,n){let r=n??H(e.headers.get("x-request-id"),256),s=H(e.headers.get("retry-after"),128),o=je(e.status,r),a=Le(t)?t:o;return new E({status:e.status,code:a.code,requestId:r,problem:a,retryAfter:s})}function je(e,t){return {type:"about:blank",title:"AuthOwl Admin API error",status:e,detail:"The AuthOwl Admin API rejected the request.",instance:`urn:authowl:request:${t??"unknown"}`,code:"UNKNOWN_ERROR"}}function Le(e){return N(e)?typeof e.type=="string"&&typeof e.title=="string"&&typeof e.status=="number"&&typeof e.detail=="string"&&typeof e.instance=="string"&&typeof e.code=="string":false}function N(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function H(e,t){let n=e?.trim();if(!(!n||n.length>t||!/^[\x21-\x7e]+$/.test(n)))return n}var Ne=/^v1=([a-f0-9]{64})$/i,Ke=/^whsec_[A-Za-z0-9_-]{1,256}$/,Re=/^(0|[1-9]\d{0,10})$/;async function ve(e){if(!e||typeof e!="object")throw new TypeError("Webhook verification input must be an object.");let t=Ce(e.secrets),n=e.toleranceSeconds??300;if(!Number.isSafeInteger(n)||n<0||n>3600)throw new TypeError("Webhook toleranceSeconds must be an integer from 0 to 3600.");let r=e.now??Math.floor(Date.now()/1e3);if(!Number.isSafeInteger(r)||r<0)throw new TypeError("Webhook now must be a non-negative Unix timestamp.");let s=ze(e.rawBody);if(s.byteLength>1048576||typeof e.timestamp!="string"||!Re.test(e.timestamp))return false;let o=Number(e.timestamp);if(!Number.isSafeInteger(o)||Math.abs(r-o)>n)return false;let a=Ue(e.signatureHeader);if(a.length===0)return false;let p=new TextEncoder().encode(`${e.timestamp}.`),d=new Uint8Array(p.byteLength+s.byteLength);d.set(p),d.set(s,p.byteLength);let m=false;for(let y of t){let f=await crypto.subtle.importKey("raw",new TextEncoder().encode(y),{name:"HMAC",hash:"SHA-256"},false,["sign"]),c=new Uint8Array(await crypto.subtle.sign("HMAC",f,d));for(let l of a)m=We(c,l)||m;}return m}function Ce(e){if(!Array.isArray(e)||e.length<1||e.length>2||e.some(t=>typeof t!="string"||!Ke.test(t))||new Set(e).size!==e.length)throw new TypeError("Webhook secrets must contain one or two unique whsec_ values.");return e}function Ue(e){if(typeof e!="string"||e.length>1024)return [];let t=e.split(",");if(t.length>4)return [];let n=[];for(let r of t){let s=Ne.exec(r.trim());s&&n.push(Me(s[1]));}return n}function ze(e){if(typeof e=="string")return new TextEncoder().encode(e);if(e instanceof ArrayBuffer)return new Uint8Array(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new TypeError("Webhook rawBody must be a string, ArrayBuffer, or ArrayBuffer view.")}function Me(e){let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n+=1)t[n]=Number.parseInt(e.slice(n*2,n*2+2),16);return t}function We(e,t){if(e.byteLength!==t.byteLength)return false;let n=0;for(let r=0;r<e.byteLength;r+=1)n|=e[r]^t[r];return n===0}var F=["publishableKey","apiUrl","issuer","jwksUri","audience"],$e=new Set([...F,"clockToleranceSeconds"]);function K(e$1){if(e$1!==void 0&&(!e$1||typeof e$1!="object"))throw new Error("AuthOwl token verification config must be an object.");if(e$1&&Object.keys(e$1).some(p=>!$e.has(p)))throw new Error("AuthOwl token verification config contains an unsupported field.");let t=new Set(F.filter(p=>Object.prototype.hasOwnProperty.call(e$1??{},p))),n=t.has("publishableKey")||t.has("apiUrl"),r=t.has("issuer")||t.has("jwksUri")||t.has("audience");if(n&&r)throw new Error("AuthOwl token verification config cannot mix publishableKey/apiUrl with issuer/jwksUri/audience.");if(r){if(typeof e$1?.issuer!="string"||typeof e$1.jwksUri!="string"||typeof e$1.audience!="string")throw new Error("Explicit AuthOwl token verification requires issuer, jwksUri, and audience together.");return I({issuer:e$1.issuer,jwksUri:e$1.jwksUri,audience:e$1.audience,clockToleranceSeconds:e$1.clockToleranceSeconds})}let s,o;if(n){if(typeof e$1?.publishableKey!="string"||typeof e$1.apiUrl!="string")throw new Error("Derived AuthOwl token verification requires publishableKey and apiUrl together.");s=e$1.publishableKey,o=e$1.apiUrl;}else s=process.env.AUTHOWL_PUBLISHABLE_KEY,o=process.env.AUTHOWL_API_URL;if(!s||!o)throw new Error("AuthOwl token verification is not configured. Pass { publishableKey, apiUrl } (or issuer/jwksUri/audience), or set AUTHOWL_PUBLISHABLE_KEY and AUTHOWL_API_URL.");let a=e({publishableKey:s,apiUrl:o});return I({issuer:a.projectBaseURL,jwksUri:`${a.projectBaseURL}/jwks`,audience:a.decoded.projectId,clockToleranceSeconds:e$1?.clockToleranceSeconds},a.decoded.env==="test")}async function st(e,t){return _(e,K(t))}async function ot(e,t,n){let r=K(n);try{let s=await _(e,r);return u(s.membership,t)}catch{return false}}async function it(e,t,n){let r=K(n);try{let s$1=await _(e,r);return s(s$1.membership,t.permission)}catch{return false}}export{ce as ADMIN_API_SPEC_SHA256,E as AuthOwlAdminApiError,b as AuthOwlAdminNetworkError,i as TokenVerificationError,Oe as createAdminClient,ot as has,it as hasPermission,pe as verifyProjectToken,st as verifyToken,ve as verifyWebhook};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
type DecodedPublishableKey = {
|
|
2
|
+
prefix: 'pk_live' | 'pk_test';
|
|
3
|
+
env: 'live' | 'test';
|
|
4
|
+
projectId: string;
|
|
5
|
+
};
|
|
6
|
+
declare function decodePublishableKey(key: string): DecodedPublishableKey;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The exact session-cookie name the auth server sets for a given project.
|
|
10
|
+
*
|
|
11
|
+
* Single source of truth for the cookie name across the SDK. The server
|
|
12
|
+
* (the AuthOwl server project factory) configures
|
|
13
|
+
* `advanced.cookiePrefix = "p_" + <projectId without dashes>` and
|
|
14
|
+
* `useSecureCookies` in production; the auth engine then names the session
|
|
15
|
+
* cookie `${securePrefix}${cookiePrefix}.session_token`, where `securePrefix`
|
|
16
|
+
* is `__Secure-` for secure cookies and empty otherwise:
|
|
17
|
+
*
|
|
18
|
+
* dev (http): p_<idNoDashes>.session_token
|
|
19
|
+
* prod (https): __Secure-p_<idNoDashes>.session_token
|
|
20
|
+
*
|
|
21
|
+
* Verified 2026-07-06 against the auth engine's `getCookies()` with the server's
|
|
22
|
+
* real config (see CONTRACTS section 5 and `cookie.test.ts`). Note the engine
|
|
23
|
+
* joins the prefix and name with a dot (`.`), not an underscore, and uses the
|
|
24
|
+
* `__Secure-` (not `__Host-`) prefix - both are easy to get wrong by hand, and
|
|
25
|
+
* getting them wrong makes `auth()` forward a cookie the server never set. If an
|
|
26
|
+
* auth-engine upgrade changes cookie naming, re-verify and update this function,
|
|
27
|
+
* the `cookie.test.ts` fixtures, and CONTRACTS section 5 together.
|
|
28
|
+
*
|
|
29
|
+
* `secure` must reflect the SERVER's cookie mode: callers holding the auth API
|
|
30
|
+
* URL derive it from the protocol (`https:` => secure). Callers that cannot
|
|
31
|
+
* know it (the UX-only redirect middleware) should check both variants.
|
|
32
|
+
*/
|
|
33
|
+
declare function sessionCookieName(projectId: string, opts?: {
|
|
34
|
+
secure?: boolean;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
type AuthConfig = {
|
|
38
|
+
publishableKey: string;
|
|
39
|
+
apiUrl: string;
|
|
40
|
+
/** Optional fetch override (e.g. for testing). */
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
};
|
|
43
|
+
type ResolvedAuthConfig = AuthConfig & {
|
|
44
|
+
decoded: DecodedPublishableKey;
|
|
45
|
+
/** Fully-resolved base URL pointing at the per-project auth endpoint. */
|
|
46
|
+
projectBaseURL: string;
|
|
47
|
+
};
|
|
48
|
+
declare function resolveConfig(input: AuthConfig): ResolvedAuthConfig;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pure, dependency-free evaluators for an organization membership's advisory
|
|
52
|
+
* permission claim. Shared by the CLIENT `has()` (organization-client.ts, over
|
|
53
|
+
* the browser session) and the SERVER `has()` (server.ts, over a verified JWT),
|
|
54
|
+
* so the two paths can never disagree on what a membership grants.
|
|
55
|
+
*
|
|
56
|
+
* The membership carries the SAME `permissions` array AuthOwl emits into the
|
|
57
|
+
* session and the JWT claim (plan §4/§5): the relabelled `org:sys_*` system ids
|
|
58
|
+
* (plus their legacy bare forms during the dual-emit window) AND the operator's
|
|
59
|
+
* custom `org:<feature>:<action>` ids. Evaluation is a pure array/string check
|
|
60
|
+
* over that local claim - it NEVER calls a statement-only `/organization/has-
|
|
61
|
+
* permission` route, which only knows the 14 static statements and would wrongly
|
|
62
|
+
* report `false` for any custom permission.
|
|
63
|
+
*/
|
|
64
|
+
/** The active-membership shape carried on the session / decoded from a token. */
|
|
65
|
+
interface OrganizationMembership {
|
|
66
|
+
/** The member's canonical role key (built-in `owner`/`admin`/`member` or a project role). */
|
|
67
|
+
role: string;
|
|
68
|
+
/**
|
|
69
|
+
* The member's effective permission ids: `org:sys_*` system claims (with
|
|
70
|
+
* their legacy bare forms during dual-emit) plus custom `org:<feature>:<action>`
|
|
71
|
+
* ids. Advisory only - the real boundary is server-side over the verified token.
|
|
72
|
+
*/
|
|
73
|
+
permissions: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Team ids the member holds inside the ACTIVE organization, as emitted by
|
|
76
|
+
* AuthOwl into both the session and the JWT claim. Teams are pure grouping:
|
|
77
|
+
* belonging to one grants nothing on its own, so this is for the application's
|
|
78
|
+
* own gating, never an authority check.
|
|
79
|
+
*
|
|
80
|
+
* Optional because a token minted before teams shipped carries no `teams` claim.
|
|
81
|
+
* `has({ teamId })` then returns false rather than guessing - it can only ever
|
|
82
|
+
* confirm a team the claim actually proves.
|
|
83
|
+
*/
|
|
84
|
+
teams?: string[];
|
|
85
|
+
}
|
|
86
|
+
/** Clerk-style `has()` query: match the role, the permission, the team, or a combination (AND). */
|
|
87
|
+
interface HasParams {
|
|
88
|
+
role?: string;
|
|
89
|
+
permission?: string;
|
|
90
|
+
/** Require membership of this team within the active organization. */
|
|
91
|
+
teamId?: string;
|
|
92
|
+
}
|
|
93
|
+
/** True when the membership's permission claim includes `permission`. Pure. */
|
|
94
|
+
declare function membershipHasPermission(membership: OrganizationMembership | null | undefined, permission: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* True when the membership's team claim includes `teamId`. Pure.
|
|
97
|
+
*
|
|
98
|
+
* False when the claim carries no `teams` at all, which is what a token minted
|
|
99
|
+
* before teams shipped looks like - an absent claim is never read as "any team".
|
|
100
|
+
*/
|
|
101
|
+
declare function membershipHasTeam(membership: OrganizationMembership | null | undefined, teamId: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Clerk-style `has()`: true when the membership satisfies EVERY provided
|
|
104
|
+
* criterion - the role matches AND the permission is included AND the team is
|
|
105
|
+
* held. Returns false when there is no membership, or when no criterion at all is
|
|
106
|
+
* given. Pure: no I/O, evaluated entirely against the local claim.
|
|
107
|
+
*/
|
|
108
|
+
declare function membershipHas(membership: OrganizationMembership | null | undefined, params: HasParams): boolean;
|
|
109
|
+
/** Bind the pure evaluators to one membership (drives the client / hook `has`). */
|
|
110
|
+
declare function createMembershipHas(membership: OrganizationMembership | null | undefined): {
|
|
111
|
+
has: (params: HasParams) => boolean;
|
|
112
|
+
hasPermission: (params: {
|
|
113
|
+
permission: string;
|
|
114
|
+
}) => boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
type TransportErrorKind = 'aborted' | 'timeout' | 'network' | 'response_too_large' | 'invalid_response';
|
|
118
|
+
/**
|
|
119
|
+
* Stable, secret-safe failure from the shared HTTP boundary.
|
|
120
|
+
*
|
|
121
|
+
* Deliberately does not retain the request URL, headers, body, or underlying
|
|
122
|
+
* error. Server clients may carry secret authorization headers and hostile
|
|
123
|
+
* fetch implementations may echo those values in their error messages.
|
|
124
|
+
*/
|
|
125
|
+
declare class TransportError extends Error {
|
|
126
|
+
readonly kind: TransportErrorKind;
|
|
127
|
+
readonly requestId?: string;
|
|
128
|
+
constructor(kind: TransportErrorKind, requestId?: string);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { type AuthConfig as A, type DecodedPublishableKey as D, type HasParams as H, type OrganizationMembership as O, type ResolvedAuthConfig as R, TransportError as T, type TransportErrorKind as a, membershipHasPermission as b, createMembershipHas as c, decodePublishableKey as d, membershipHasTeam as e, membershipHas as m, resolveConfig as r, sessionCookieName as s };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
type DecodedPublishableKey = {
|
|
2
|
+
prefix: 'pk_live' | 'pk_test';
|
|
3
|
+
env: 'live' | 'test';
|
|
4
|
+
projectId: string;
|
|
5
|
+
};
|
|
6
|
+
declare function decodePublishableKey(key: string): DecodedPublishableKey;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The exact session-cookie name the auth server sets for a given project.
|
|
10
|
+
*
|
|
11
|
+
* Single source of truth for the cookie name across the SDK. The server
|
|
12
|
+
* (the AuthOwl server project factory) configures
|
|
13
|
+
* `advanced.cookiePrefix = "p_" + <projectId without dashes>` and
|
|
14
|
+
* `useSecureCookies` in production; the auth engine then names the session
|
|
15
|
+
* cookie `${securePrefix}${cookiePrefix}.session_token`, where `securePrefix`
|
|
16
|
+
* is `__Secure-` for secure cookies and empty otherwise:
|
|
17
|
+
*
|
|
18
|
+
* dev (http): p_<idNoDashes>.session_token
|
|
19
|
+
* prod (https): __Secure-p_<idNoDashes>.session_token
|
|
20
|
+
*
|
|
21
|
+
* Verified 2026-07-06 against the auth engine's `getCookies()` with the server's
|
|
22
|
+
* real config (see CONTRACTS section 5 and `cookie.test.ts`). Note the engine
|
|
23
|
+
* joins the prefix and name with a dot (`.`), not an underscore, and uses the
|
|
24
|
+
* `__Secure-` (not `__Host-`) prefix - both are easy to get wrong by hand, and
|
|
25
|
+
* getting them wrong makes `auth()` forward a cookie the server never set. If an
|
|
26
|
+
* auth-engine upgrade changes cookie naming, re-verify and update this function,
|
|
27
|
+
* the `cookie.test.ts` fixtures, and CONTRACTS section 5 together.
|
|
28
|
+
*
|
|
29
|
+
* `secure` must reflect the SERVER's cookie mode: callers holding the auth API
|
|
30
|
+
* URL derive it from the protocol (`https:` => secure). Callers that cannot
|
|
31
|
+
* know it (the UX-only redirect middleware) should check both variants.
|
|
32
|
+
*/
|
|
33
|
+
declare function sessionCookieName(projectId: string, opts?: {
|
|
34
|
+
secure?: boolean;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
type AuthConfig = {
|
|
38
|
+
publishableKey: string;
|
|
39
|
+
apiUrl: string;
|
|
40
|
+
/** Optional fetch override (e.g. for testing). */
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
};
|
|
43
|
+
type ResolvedAuthConfig = AuthConfig & {
|
|
44
|
+
decoded: DecodedPublishableKey;
|
|
45
|
+
/** Fully-resolved base URL pointing at the per-project auth endpoint. */
|
|
46
|
+
projectBaseURL: string;
|
|
47
|
+
};
|
|
48
|
+
declare function resolveConfig(input: AuthConfig): ResolvedAuthConfig;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pure, dependency-free evaluators for an organization membership's advisory
|
|
52
|
+
* permission claim. Shared by the CLIENT `has()` (organization-client.ts, over
|
|
53
|
+
* the browser session) and the SERVER `has()` (server.ts, over a verified JWT),
|
|
54
|
+
* so the two paths can never disagree on what a membership grants.
|
|
55
|
+
*
|
|
56
|
+
* The membership carries the SAME `permissions` array AuthOwl emits into the
|
|
57
|
+
* session and the JWT claim (plan §4/§5): the relabelled `org:sys_*` system ids
|
|
58
|
+
* (plus their legacy bare forms during the dual-emit window) AND the operator's
|
|
59
|
+
* custom `org:<feature>:<action>` ids. Evaluation is a pure array/string check
|
|
60
|
+
* over that local claim - it NEVER calls a statement-only `/organization/has-
|
|
61
|
+
* permission` route, which only knows the 14 static statements and would wrongly
|
|
62
|
+
* report `false` for any custom permission.
|
|
63
|
+
*/
|
|
64
|
+
/** The active-membership shape carried on the session / decoded from a token. */
|
|
65
|
+
interface OrganizationMembership {
|
|
66
|
+
/** The member's canonical role key (built-in `owner`/`admin`/`member` or a project role). */
|
|
67
|
+
role: string;
|
|
68
|
+
/**
|
|
69
|
+
* The member's effective permission ids: `org:sys_*` system claims (with
|
|
70
|
+
* their legacy bare forms during dual-emit) plus custom `org:<feature>:<action>`
|
|
71
|
+
* ids. Advisory only - the real boundary is server-side over the verified token.
|
|
72
|
+
*/
|
|
73
|
+
permissions: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Team ids the member holds inside the ACTIVE organization, as emitted by
|
|
76
|
+
* AuthOwl into both the session and the JWT claim. Teams are pure grouping:
|
|
77
|
+
* belonging to one grants nothing on its own, so this is for the application's
|
|
78
|
+
* own gating, never an authority check.
|
|
79
|
+
*
|
|
80
|
+
* Optional because a token minted before teams shipped carries no `teams` claim.
|
|
81
|
+
* `has({ teamId })` then returns false rather than guessing - it can only ever
|
|
82
|
+
* confirm a team the claim actually proves.
|
|
83
|
+
*/
|
|
84
|
+
teams?: string[];
|
|
85
|
+
}
|
|
86
|
+
/** Clerk-style `has()` query: match the role, the permission, the team, or a combination (AND). */
|
|
87
|
+
interface HasParams {
|
|
88
|
+
role?: string;
|
|
89
|
+
permission?: string;
|
|
90
|
+
/** Require membership of this team within the active organization. */
|
|
91
|
+
teamId?: string;
|
|
92
|
+
}
|
|
93
|
+
/** True when the membership's permission claim includes `permission`. Pure. */
|
|
94
|
+
declare function membershipHasPermission(membership: OrganizationMembership | null | undefined, permission: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* True when the membership's team claim includes `teamId`. Pure.
|
|
97
|
+
*
|
|
98
|
+
* False when the claim carries no `teams` at all, which is what a token minted
|
|
99
|
+
* before teams shipped looks like - an absent claim is never read as "any team".
|
|
100
|
+
*/
|
|
101
|
+
declare function membershipHasTeam(membership: OrganizationMembership | null | undefined, teamId: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Clerk-style `has()`: true when the membership satisfies EVERY provided
|
|
104
|
+
* criterion - the role matches AND the permission is included AND the team is
|
|
105
|
+
* held. Returns false when there is no membership, or when no criterion at all is
|
|
106
|
+
* given. Pure: no I/O, evaluated entirely against the local claim.
|
|
107
|
+
*/
|
|
108
|
+
declare function membershipHas(membership: OrganizationMembership | null | undefined, params: HasParams): boolean;
|
|
109
|
+
/** Bind the pure evaluators to one membership (drives the client / hook `has`). */
|
|
110
|
+
declare function createMembershipHas(membership: OrganizationMembership | null | undefined): {
|
|
111
|
+
has: (params: HasParams) => boolean;
|
|
112
|
+
hasPermission: (params: {
|
|
113
|
+
permission: string;
|
|
114
|
+
}) => boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
type TransportErrorKind = 'aborted' | 'timeout' | 'network' | 'response_too_large' | 'invalid_response';
|
|
118
|
+
/**
|
|
119
|
+
* Stable, secret-safe failure from the shared HTTP boundary.
|
|
120
|
+
*
|
|
121
|
+
* Deliberately does not retain the request URL, headers, body, or underlying
|
|
122
|
+
* error. Server clients may carry secret authorization headers and hostile
|
|
123
|
+
* fetch implementations may echo those values in their error messages.
|
|
124
|
+
*/
|
|
125
|
+
declare class TransportError extends Error {
|
|
126
|
+
readonly kind: TransportErrorKind;
|
|
127
|
+
readonly requestId?: string;
|
|
128
|
+
constructor(kind: TransportErrorKind, requestId?: string);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { type AuthConfig as A, type DecodedPublishableKey as D, type HasParams as H, type OrganizationMembership as O, type ResolvedAuthConfig as R, TransportError as T, type TransportErrorKind as a, membershipHasPermission as b, createMembershipHas as c, decodePublishableKey as d, membershipHasTeam as e, membershipHas as m, resolveConfig as r, sessionCookieName as s };
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@authowl/core",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Framework-agnostic client for AuthOwl, the multi-tenant auth SaaS.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/mstfash/authowl-sdk.git",
|
|
12
|
+
"directory": "packages/auth-core"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/mstfash/authowl-sdk#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"authentication",
|
|
17
|
+
"auth",
|
|
18
|
+
"authowl",
|
|
19
|
+
"oauth",
|
|
20
|
+
"passkey",
|
|
21
|
+
"sdk"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"default": "./dist/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./server": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/server.d.ts",
|
|
39
|
+
"default": "./dist/server.js"
|
|
40
|
+
},
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/server.d.cts",
|
|
43
|
+
"default": "./dist/server.cjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"main": "./dist/index.cjs",
|
|
48
|
+
"module": "./dist/index.js",
|
|
49
|
+
"types": "./dist/index.d.ts",
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"THIRD_PARTY_NOTICES.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
],
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"tsup": "^8.3.5",
|
|
57
|
+
"typescript": "^5.7.2",
|
|
58
|
+
"vitest": "^4.1.10"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup && node ../../scripts/gen-notices.mjs src/index.ts src/server.ts",
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "vitest run --passWithNoTests"
|
|
65
|
+
}
|
|
66
|
+
}
|