@crossauth/backend 0.0.38 → 0.0.40
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/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +627 -483
- package/dist/oauth/authserver.d.ts +75 -4
- package/dist/oauth/authserver.d.ts.map +1 -1
- package/dist/oauth/tokenconsumer.d.ts +1 -1
- package/dist/oauth/tokenconsumer.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var pe=Object.defineProperty;var Se=(C,a,r)=>a in C?pe(C,a,{enumerable:!0,configurable:!0,writable:!0,value:r}):C[a]=r;var d=(C,a,r)=>Se(C,typeof a!="symbol"?a+"":a,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@crossauth/common"),B=require("@prisma/client"),de=require("@mbakereth/ldapjs"),I=require("node:crypto"),Te=require("node:util"),V=require("nunjucks"),we=require("nodemailer"),ve=require("twilio"),_e=require("qrcode"),oe=require("otplib"),X=require("jsonwebtoken"),ke=require("crypto"),G=require("node:fs"),be=require("jose");function Ue(C){const a=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(C){for(const r in C)if(r!=="default"){const t=Object.getOwnPropertyDescriptor(C,r);Object.defineProperty(a,r,t.get?t:{enumerable:!0,get:()=>C[r]})}}return a.default=C,Object.freeze(a)}const Ae=Ue(be);var u=(C=>(C[C.String=0]="String",C[C.Number=1]="Number",C[C.Boolean=2]="Boolean",C[C.Json=3]="Json",C[C.JsonArray=4]="JsonArray",C))(u||{});function Pe(C,a){let r=C.split("."),t=a;for(let s in r){const i=r[s];if(!(i in t)||t[i]==null)return;t=t[i]}return t}function ue(C,a){let r=C.split("."),t=a;for(let s in r){const i=r[s];if(!(i in t)||t[i]==null)return!1;t=t[i]}return!0}function Ie(C,a,r){const t=Pe(a,r);C[a.replace(".","_")]=t}function Oe(C,a,r,t){var i;const s=a.replace(".","_");switch(r){case 0:C[s]=process.env[t]=="null"?null:process.env[t];break;case 1:C[s]=process.env[t]=="null"?null:Number(process.env[t]);break;case 2:C[s]=["1","true"].includes(((i=process.env[t])==null?void 0:i.toLowerCase())??"");break;case 3:C[s]=JSON.parse(process.env[t]??"{}");break;case 4:C[s]=JSON.parse(process.env[t]??"[]");break}}function h(C,a,r,t,s,i=!1){const o="CROSSAUTH_"+s;if(i&&!ue(C,t)&&!(o&&o in process.env))throw new e.CrossauthError(e.ErrorCode.Configuration,C+" is required");ue(C,t)?Ie(r,C,t):s&&o in process.env&&process.env[o]!=null&&Oe(r,C,a,o)}class F{constructor(a={}){d(this,"userEditableFields",[]);d(this,"adminEditableFields",[]);d(this,"normalizeUsername",!0);d(this,"normalizeEmail",!0);h("userEditableFields",u.JsonArray,this,a,"USER_EDITABLE_FIELDS"),h("adminEditableFields",u.JsonArray,this,a,"ADMIN_EDITABLE_FIELDS"),h("normalizeUsername",u.JsonArray,this,a,"NORMALIZE_USERNAME"),h("normalizeEmail",u.JsonArray,this,a,"NORMALIZE_EMAIL")}createUser(a,r){throw new e.CrossauthError(e.ErrorCode.Configuration)}static normalize(a){return a.normalize("NFD").replace(new RegExp("\\p{Diacritic}","gu"),"").toLowerCase()}}class K{static decodeData(a){return a==null||a==""?{}:JSON.parse(a)}static encodeData(a){return a?JSON.stringify(a):"{}"}updateDataInternal(a,r,t){if(r.indexOf(".")>0){let s=r.split("."),i=a;for(let o=0;o<s.length-1&&a;o++)i=i[s[o]];if(i)return i[s[s.length-1]]=t,a}else return a[r]=t,a}deleteDataInternal(a,r){if(r.indexOf(".")>0){let t=r.split("."),s=a;for(let i=0;i<t.length-1&&a;i++)s=s[t[i]];return s&&s[t[t.length-1]]?(delete s[t[t.length-1]],!0):!1}else return r in a?(delete a[r],!0):!1}}class se{constructor(a={}){}}class ie{constructor(a={}){}}class $ extends F{constructor(r={}){super(r);d(this,"userTable","user");d(this,"userSecretsTable","userSecrets");d(this,"idColumn","id");d(this,"useridForeignKeyColumn","userid");d(this,"prismaClient");d(this,"includes",["secrets"]);d(this,"includesObject",{});d(this,"forceIdToNumber",!0);h("userTable",u.String,this,r,"USER_TABLE"),h("userSecretsTable",u.String,this,r,"USER_SECRETS_TABLE"),h("idColumn",u.String,this,r,"USER_ID_COLUMN"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),h("includes",u.String,this,r,"USER_INCLUDES"),h("forceIdToNumber",u.String,this,r,"USER_FORCE_ID_TO_NUMBER"),this.includes.forEach(t=>{this.includesObject[t]=!0}),r&&r.prismaClient?this.prismaClient=r.prismaClient:this.prismaClient=new B.PrismaClient}async getUser(r,t,s){let i,o;try{o=await this.prismaClient[this.userTable].findUniqueOrThrow({where:{[r]:t},include:this.includesObject})}catch{i=new e.CrossauthError(e.ErrorCode.UserNotExist)}if(this.prismaClient||(i=new e.CrossauthError(e.ErrorCode.Connection)),i)throw i;if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);if((s==null?void 0:s.skipEmailVerifiedCheck)!=!0&&o.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User has not verified email"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"User must change password"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&(o.state==e.UserState.passwordResetNeeded||o.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"User must reset password"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);const n=o.secrets||{};return o.secrets&&(delete n[this.useridForeignKeyColumn],delete o.secrets),{user:{...o,id:o[this.idColumn]},secrets:{userid:o[this.idColumn],...n}}}async getUserByUsername(r,t){const s=$.normalize(r);return this.getUser("username_normalized",s,t)}async getUserBy(r,t,s){return this.getUser(r,t,s)}async getUserByEmail(r,t){const s=$.normalize(r);return this.getUser("email_normalized",s,t)}async getUserById(r,t){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.getUser(this.idColumn,Number(r),t)}catch(s){if(e.CrossauthError.asCrossauthError(s).code==e.ErrorCode.UserNotExist)return await this.getUser(this.idColumn,r,t);throw e.CrossauthLogger.logger.debug(e.j({err:s})),s}return await this.getUser(this.idColumn,r,t)}async updateUser(r,t){if(!r.id)throw new e.CrossauthError(e.ErrorCode.InvalidKey);t&&!t.userid&&(t={...t,userid:r[this.idColumn]});try{let{id:s,...i}=r,{userid:o,...n}=t??{};"email"in i&&i.email&&(i={email_normalized:$.normalize(i.email),...i}),"username"in i&&i.username&&(i={username_normalized:$.normalize(i.username),...i}),t?await this.prismaClient.$transaction(async l=>{let c={};try{c=await l[this.userSecretsTable].findUniqueOrThrow({where:{[this.useridForeignKeyColumn]:r.id}})}catch{}let{userid:g,...f}=c??{};n={...f,...n},await l[this.userTable].update({where:{[this.idColumn]:r.id},data:{...i}}),await l[this.userSecretsTable].upsert({where:{[this.useridForeignKeyColumn]:r.id},update:n,create:{[this.useridForeignKeyColumn]:r.id,...n}})}):await this.prismaClient[this.userTable].update({where:{[this.idColumn]:r.id},data:i})}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating user")}}async createUser(r,t){let s;if(t&&!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordFormat,"Password required when creating user");let i,o="",n="";try{"email"in r&&r.email&&(n=$.normalize(r.email)),"username"in r&&r.username&&(o=$.normalize(r.username)),t?i=await this.prismaClient[this.userTable].create({data:{...r,email_normalized:n,username_normalized:o,secrets:{create:t}},include:{secrets:!0}}):i=await this.prismaClient[this.userTable].create({data:{...r,email_normalized:n,username_normalized:o}})}catch(l){e.CrossauthLogger.logger.debug(e.j({err:l})),s=new e.CrossauthError(e.ErrorCode.Connection,"Error creating user"),(l instanceof B.Prisma.PrismaClientKnownRequestError||l instanceof Object&&"code"in l)&&l.code==="P2002"&&(s=new e.CrossauthError(e.ErrorCode.UserExists))}if(s)throw s;return i}async deleteUserByUsername(r){let t;try{return this.prismaClient[this.userTable].deleteMany({where:{username:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting user")}if(t)throw t}async deleteUserById(r){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.deleteUserById_internal(Number(r))}catch{e.CrossauthLogger.logger.debug(e.j({msg:"Failed forcing id to number when deleting user"}))}return await this.deleteUserById_internal(r)}async deleteUserById_internal(r){let t;try{return this.prismaClient[this.userTable].delete({where:{id:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting user")}if(t)throw t}async getUsers(r,t){let s={};r&&(s.skip=r),t&&(s.take=t);try{return await this.prismaClient[this.userTable].findMany({...s,orderBy:[{username_normalized:"asc"}],include:this.includesObject})}catch(i){throw e.CrossauthLogger.logger.error(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Couldn't select from user table")}}}class Ke extends K{constructor(r={}){super();d(this,"keyTable","key");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"useridForeignKeyColumn","userid");h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.Number,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.keyTable&&(this.keyTable=r.keyTable),r.prismaClient==null?this.prismaClient=new B.PrismaClient:this.prismaClient=r.prismaClient}async getKey(r){return await this.getKeyWithTransaction(r,this.prismaClient)}async getKeyWithTransaction(r,t){let s={userid:0,value:"",created:new Date,expires:void 0},i;try{let o=await t[this.keyTable].findUniqueOrThrow({where:{value:r}});s={...o,userid:o[this.useridForeignKeyColumn]},this.useridForeignKeyColumn!="userid"&&delete s[this.useridForeignKeyColumn]}catch(o){e.CrossauthLogger.logger.debug(e.j({err:o})),i=new e.CrossauthError(e.ErrorCode.InvalidKey)}if(i)throw i;return s}async saveKey(r,t,s,i,o,n={}){let l;try{let c={[this.useridForeignKeyColumn]:r,value:t,created:s,expires:i??null,data:o,...n};await this.prismaClient[this.keyTable].create({data:c})}catch(c){c instanceof B.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.warn(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.KeyExists)):(e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key")):(e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key"))}if(l)throw l}async deleteKey(r){let t;try{return this.prismaClient[this.keyTable].deleteMany({where:{value:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(t)throw t}async deleteAllForUser(r,t,s){let i;try{return s?this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}},{value:{not:s}}]}}):this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}}]}})}catch(o){e.CrossauthLogger.logger.debug(e.j({err:o})),i=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(i)throw i}async deleteMatching(r){try{let t=[];for(let s in r)s=="userid"?t.push({[this.useridForeignKeyColumn]:r[s]}):t.push({[s]:r[s]});return this.prismaClient[this.keyTable].deleteMany({where:{AND:t}})}catch(t){throw e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Error deleting keys")}}async deleteWithPrefix(r,t){let s;try{return this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}}]}})}catch(i){e.CrossauthLogger.logger.debug(e.j({err:i})),s=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(s)throw s}async getAllForUser(r){let t=[],s;try{t=(await this.prismaClient[this.keyTable].findMany({where:{[this.useridForeignKeyColumn]:r??null}})).map(o=>{let n={...o,userid:o[this.useridForeignKeyColumn]};return this.useridForeignKeyColumn!="userid"&&delete n[this.useridForeignKeyColumn],n})}catch{s=new e.CrossauthError(e.ErrorCode.InvalidKey)}if(s)throw s;return t}async updateKey(r){await this.updateKeyWithTransaction(r,this.prismaClient)}async updateKeyWithTransaction(r,t){let s;if(!r.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey);try{let i={...r};delete i.value,await t[this.keyTable].update({where:{value:r.value},data:i})}catch(i){s=new e.CrossauthError(e.ErrorCode.Connection,String(i))}if(s)throw e.CrossauthLogger.logger.debug(e.j({err:s})),s}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){try{await this.prismaClient.$transaction(async s=>{const i=await this.getKeyWithTransaction(r,s);let o;if(!i.data||i.data=="")o={};else try{o=JSON.parse(i.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let n of t){let l=this.updateDataInternal(o,n.dataName,n.value);if(!l)throw new e.CrossauthError(e.ErrorCode.BadRequest,`Parents of ${n.dataName} not found in key data`);o=l}await this.updateKeyWithTransaction({value:i.value,data:JSON.stringify(o)},s)},{timeout:this.transactionTimeout})}catch(s){throw s&&typeof s=="object"&&!("isCrossauthError"in s)?(e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):s}}async deleteData(r,t){try{let s=!1;await this.prismaClient.$transaction(async i=>{let o={};const n=await this.getKeyWithTransaction(r,i);if(n.data&&n.data!=""){try{o=JSON.parse(n.data)}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),new e.CrossauthError(e.ErrorCode.DataFormat)}s=this.deleteDataInternal(o,t)}s&&await this.updateKeyWithTransaction({value:n.value,data:JSON.stringify(o)},i)},{timeout:this.transactionTimeout})}catch(s){throw s&&typeof s=="object"&&!("isCrossauthError"in s)?(e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):s}}}class Fe extends se{constructor(r={}){super();d(this,"clientTable","oAuthClient");d(this,"redirectUriTable","OAuthClientRedirectUri");d(this,"validFlowTable","OAuthClientValidFlow");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"updateMode","DeleteAndInsert");d(this,"useridForeignKeyColumn","userid");h("clientTable",u.String,this,r,"OAUTH_CLIENT_TABLE"),h("redirectUriTable",u.String,this,r,"OAUTH_REDIRECTURI_TABLE"),h("validFlowTable",u.String,this,r,"OAUTH_VALID_FLOW_TABLE"),h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("updateMode",u.String,this,r,"OAUTHCLIENT_UPDATE_MODE"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.prismaClient==null?this.prismaClient=new B.PrismaClient:this.prismaClient=r.prismaClient}async getClientById(r){return(await this.getClientWithTransaction("client_id",r,this.prismaClient,!0,void 0))[0]}async getClientByName(r,t){return await this.getClientWithTransaction("client_name",r,this.prismaClient,!1,t)}async getClientWithTransaction(r,t,s,i,o){const n=o==null&&o!==null?{}:{[this.useridForeignKeyColumn]:o};try{if(i){const l=await s[this.clientTable].findUniqueOrThrow({where:{[r]:t,...n},include:{redirect_uri:!0,valid_flow:!0}}),c=l.redirect_uri,g=l.valid_flow;let f=l[this.useridForeignKeyColumn];return f===null&&(f=void 0),this.useridForeignKeyColumn!="userid"&&delete l[this.useridForeignKeyColumn],[{...l,userid:f,client_secret:l.client_secret??void 0,redirect_uri:c.map(w=>w.uri),valid_flow:g.map(w=>w.flow)}]}else{const l=await s[this.clientTable].findMany({where:{[r]:t,...n},include:{redirect_uri:!0,valid_flow:!0}});for(let c of l){const g=c.redirect_uri,f=c.valid_flow;let w=c[this.useridForeignKeyColumn];w==null&&(w=void 0),c.userid=w,this.useridForeignKeyColumn!="userid"&&delete c[this.useridForeignKeyColumn],c.client_secret=c.client_secret??void 0,c.redirect_uri=g.map(E=>E.uri),c.valid_flow=f.map(E=>E.flow)}return l}}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid OAuth client",[r]:t,cerr:l})),new e.CrossauthError(e.ErrorCode.InvalidClientId)}}async createClient(r){try{return this.prismaClient.$transaction(async t=>{try{throw await this.getClientWithTransaction("client_id",r.client_id,t,!0,r.userid),new e.CrossauthError(e.ErrorCode.ClientExists)}catch{}return await this.createClientWithTransaction(r,t)},{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed creating client")):t}}async createClientWithTransaction(r,t){const{redirect_uri:s,valid_flow:i,userid:o,...n}=r;let l;if(o&&(n[this.useridForeignKeyColumn]=o),this.useridForeignKeyColumn!="userid"&&delete r[this.useridForeignKeyColumn],s)for(let c=0;c<s.length;++c){if(s[c].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[c])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[c]} is not valid`)}}if(i){for(let c=0;c<i.length;++c)if(!e.OAuthFlows.isValidFlow(i[c]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Invalid flow "+i[c])}try{l=await t[this.clientTable].create({data:n})}catch(c){throw c instanceof B.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.ClientExists,"Attempt to create an OAuth client with a client_id that already exists. Maximum attempts failed")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}if(!l)throw e.CrossauthLogger.logger.error(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),new e.CrossauthError(e.ErrorCode.KeyExists);if(s)try{for(let c=0;c<s.length;++c)await t[this.redirectUriTable].create({data:{client_id:l.client_id,uri:s[c]}})}catch(c){throw c instanceof B.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Attempt to create an OAuth client with a redirect uri that already belongs to another client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}if(i)try{for(let c=0;c<i.length;++c)await t[this.validFlowTable].create({data:{client_id:l.client_id,flow:i[c]}})}catch(c){throw c instanceof B.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}return{...l,redirect_uri:s,valid_flow:i}}async deleteClient(r){try{return this.prismaClient.$transaction(async t=>await this.deleteClientWithTransaction(r,t),{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed deleting client")):t}}async deleteClientWithTransaction(r,t){try{await t[this.clientTable].deleteMany({where:{client_id:r}})}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Error deleting OAuth client")}}async updateClient(r){try{return this.prismaClient.$transaction(async t=>this.updateMode=="Update"?await this.updateClientWithTransaction_update(r,t):await this.updateClientWithTransaction_deleteAndInsert(r,t),{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating client")):t}}async updateClientWithTransaction_update(r,t){if(!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const s=r.redirect_uri,i=r.valid_flow;if(s)for(let o=0;o<s.length;++o){if(s[o].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[o])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[o]} is not valid`)}}if(i){for(let o=0;o<i.length;++o)if(!e.OAuthFlows.isValidFlow(i[o]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Redirect Uri's may not contain page fragments")}try{let o={...r};delete o.client_id,delete o.redirect_uri,delete o.valid_flow,"userid"in o&&this.useridForeignKeyColumn!="userid"&&(o[this.useridForeignKeyColumn]=o.userid,delete o.userid),Object.keys(o).length>0&&await t[this.clientTable].update({where:{client_id:r.client_id},data:o})}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")}if(s!=null)try{await this.prismaClient[this.redirectUriTable].deleteMany({where:{client_id:r.client_id}});for(let o=0;o<s.length;++o)await t[this.redirectUriTable].create({data:{client_id:r.client_id,uri:s[o]}})}catch(o){throw o instanceof B.Prisma.PrismaClientKnownRequestError||o instanceof Object&&"code"in o?o.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.KeyExists,"Attempt to update an OAuth client with a redirect Uri that already belongs to another client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client"))}if(i!=null)try{await this.prismaClient[this.validFlowTable].deleteMany({where:{client_id:r.client_id}});for(let o=0;o<i.length;++o)await t[this.validFlowTable].create({data:{client_id:r.client_id,flow:i[o]}})}catch(o){throw o instanceof B.Prisma.PrismaClientKnownRequestError||o instanceof Object&&"code"in o?(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client"))}}async updateClientWithTransaction_deleteAndInsert(r,t){if(!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const i={...(await this.getClientWithTransaction("client_id",r.client_id,this.prismaClient,!0,void 0))[0],...r};"userid"in i&&this.useridForeignKeyColumn!="userid"&&(i[this.useridForeignKeyColumn]=i.userid,delete i.userid),await this.deleteClientWithTransaction(r.client_id,t),await this.createClientWithTransaction(i,t)}async getClients(r,t,s){let i={};r&&(i.skip=r),t&&(i.take=t);try{let o=[];return s||s===null?o=await this.prismaClient[this.clientTable].findMany({...i,where:{[this.useridForeignKeyColumn]:s},orderBy:[{client_name:"asc"}]}):o=await this.prismaClient[this.clientTable].findMany({...i,orderBy:[{client_name:"asc"}]}),o.forEach(n=>(this.useridForeignKeyColumn!="userid"&&(n.userid=n[this.useridForeignKeyColumn],delete n[this.useridForeignKeyColumn]),n.userid=n.userid===null?void 0:n.userid,n)),o}catch(o){throw e.CrossauthLogger.logger.error(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Couldn't select from client table")}}}class Le extends ie{constructor(r={}){super();d(this,"authorizationTable","oAuthAuthorization");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"useridForeignKeyColumn","userid");h("authorizationTable",u.String,this,r,"OAUTH_CLIENT_TABLE"),h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.prismaClient==null?this.prismaClient=new B.PrismaClient:this.prismaClient=r.prismaClient}async getAuthorizations(r,t){try{return(await this.prismaClient[this.authorizationTable].findMany({where:{client_id:r,[this.useridForeignKeyColumn]:t??null},select:{scope:!0}})).map(i=>i.scope)}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection)}}async updateAuthorizations(r,t,s){return this.prismaClient.$transaction(async i=>await this.updateAuthorizationsWithTransaction(r,t,s,i),{timeout:this.transactionTimeout})}async updateAuthorizationsWithTransaction(r,t,s,i){try{await i[this.authorizationTable].deleteMany({where:{client_id:r,[this.useridForeignKeyColumn]:t??null}});const o=[];s.forEach(n=>{o.push(i[this.authorizationTable].create({data:{client_id:r,[this.useridForeignKeyColumn]:t??null,scope:n}}))}),await Promise.all(o)}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating OAuth authorizations")}}}class Ne extends F{constructor(r={}){super(r);d(this,"usersByUsername",{});d(this,"usersByEmail",{});d(this,"secretsByUsername",{});d(this,"secretsByEmail",{})}async createUser(r,t){if(r.username_normalized=F.normalize(r.username),r.username_normalized in this.usersByUsername)throw new e.CrossauthError(e.ErrorCode.UserExists);if("email"in r&&r.email&&(r.email_normalized=F.normalize(r.email),r.email_normalized in this.getUserByEmail))throw new e.CrossauthError(e.ErrorCode.UserExists);const s={id:r.username,...r};return this.usersByUsername[r.username_normalized]=s,this.secretsByUsername[r.username_normalized]=t??{},"email"in r&&r.email&&(this.usersByEmail[r.email_normalized]=s),"email"in r&&r.email&&(this.secretsByEmail[r.email_normalized]=t??{}),{id:r.username,...r}}async getUserByUsername(r,t){const s=F.normalize(r);if(s in this.usersByUsername){const i=this.usersByUsername[s];if(!i)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"Password change required"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&(i.state==e.UserState.passwordResetNeeded||i.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"Password reset required"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((t==null?void 0:t.skipEmailVerifiedCheck)!=!0&&i.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User email not verified"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);const o=this.secretsByUsername[s];return{user:{...i},secrets:{userid:i.id,...o}}}throw e.CrossauthLogger.logger.debug(e.j({msg:"User does not exist"})),new e.CrossauthError(e.ErrorCode.UserNotExist)}async getUserByEmail(r,t){const s=F.normalize(r);if(s in this.usersByEmail){const i=this.usersByEmail[s];if(!i)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if((t==null?void 0:t.skipEmailVerifiedCheck)!=!0&&i.state=="awaitingemailverification")throw e.CrossauthLogger.logger.debug(e.j({msg:"User email not verified"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state!="active")throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);const o=this.secretsByEmail[s];return{user:{...i},secrets:{userid:i.id,...o}}}throw e.CrossauthLogger.logger.debug(e.j({msg:"User does not exist"})),new e.CrossauthError(e.ErrorCode.UserNotExist)}async getUserById(r,t){return this.getUserByUsername(r,t)}async getUserBy(r,t,s){throw new e.CrossauthError(e.ErrorCode.NotImplemented)}async updateUser(r,t){let s={...r};if("username"in s&&s.username?s.username_normalized=F.normalize(s.username):"id"in s&&s.id&&(s.username_normalized=F.normalize(String(s.id))),"email"in s&&s.email&&(s.email_normalized=F.normalize(s.email)),s.username_normalized&&s.username_normalized in this.usersByUsername){for(let i in s)this.usersByUsername[s.username_normalized][i]=s[i];t&&(this.secretsByUsername[s.username_normalized]={...this.secretsByUsername[s.username_normalized],...t})}}async deleteUserByUsername(r){const t=F.normalize(String(r));if(t in this.usersByUsername){const s=this.usersByUsername[t];delete this.usersByUsername[t],delete this.secretsByUsername[t];const i=F.normalize(String(s.email));i in this.usersByEmail&&(delete this.usersByEmail[i],delete this.secretsByEmail[i])}}async deleteUserById(r){return await this.deleteUserByUsername(String(r))}async getUsers(r,t){const s=Object.keys(this.usersByUsername).sort();let i=[];r||(r=0);let o=t||s.length;o>=s.length-r&&(o=s.length-r);for(let n=r;n<o;++n)i.push(this.usersByUsername[s[n]]);return i}}class xe extends K{constructor(){super();d(this,"keys",{});d(this,"keysByUserId",{});d(this,"nonUserKeys",[])}async getKey(r){if(this.keys&&r in this.keys)return this.keys[r];e.CrossauthLogger.logger.debug(e.j({msg:"Key does not exist in key storage"}));let t=new e.CrossauthError(e.ErrorCode.InvalidKey);throw e.CrossauthLogger.logger.debug(e.j({err:t})),t}async saveKey(r,t,s,i,o,n){const l={value:t,userid:r,created:s,expires:i,data:o,...n};this.keys[t]=l,r?r in this.keysByUserId?this.keysByUserId[r].push(l):this.keysByUserId[r]=[l]:this.nonUserKeys.push(l)}async deleteKey(r){if(r in this.keys){const t=this.keys[r];t.userid?delete this.keysByUserId[t.userid]:this.nonUserKeys=this.nonUserKeys.filter(s=>s.value!=r),delete this.keys[r]}}async deleteAllForUser(r,t,s=void 0){for(const i in this.keys)this.keys[i].userid==r&&(!s||i!=s)&&i.startsWith(t)&&delete this.keys[i];r?r in this.keysByUserId&&delete this.keysByUserId[r]:this.nonUserKeys=[]}async getAllForUser(r){return r?r in this.keysByUserId?this.keysByUserId[r]:[]:this.nonUserKeys}async deleteMatching(r){for(let t in this.keys){let s=!0;const i=this.keys[t];for(let o in r)if(o in i&&i[o]!=r[o]){s=!1;break}s&&delete this.keys[t]}for(let t in this.keysByUserId){const s=this.keysByUserId[t];for(let i=0;i<s.length;++i){let o=!0,n=0;const l=s[i];for(let c in r)if(c in l&&l[c]!=r[c]){o=!1,n=i;break}o&&(this.keysByUserId[t]=this.keysByUserId[t].splice(n,1))}}for(let t=0;t<this.nonUserKeys.length;++t){let s=!0,i=0;const o=this.nonUserKeys[t];for(let n in r)if(n in o&&o[n]!=r[n]){s=!1,i=t;break}s&&(this.nonUserKeys=this.nonUserKeys.splice(i,1))}}async updateKey(r){if(r.value&&r.value in this.keys){let t=r.value??"";for(let s in r)this.keys[t][s]=r[s]}}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){const s=await this.getKey(r);let i;if(!s.data||s.data=="")i={};else try{i=JSON.parse(s.data)}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let o of t)if(this.updateDataInternal(i,o.dataName,o.value))s.data=JSON.stringify(i);else throw new e.CrossauthError(e.ErrorCode.BadRequest,`parents of ${o.dataName} not found in key data`)}async deleteData(r,t){const s=await this.getKey(r);let i;if(!s.data||s.data=="")return;try{i=JSON.parse(s.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}this.deleteDataInternal(i,t)&&(s.data=JSON.stringify(i))}}class Re extends se{constructor(r={}){super();d(this,"clients",{});d(this,"clientsByName",{})}async getClientById(r){if(this.clients&&r in this.clients)return this.clients[r];e.CrossauthLogger.logger.debug(e.j({msg:"Client does not exist in client storage"}));let t=new e.CrossauthError(e.ErrorCode.InvalidClientId);throw e.CrossauthLogger.logger.debug(e.j({err:t})),t}async getClientByName(r,t){if(this.clientsByName&&r in this.clientsByName){const s=this.clientsByName[r];if(t==null&&t!==null)return s;const i=[];for(let o of s)o.userid===t&&i.push(o);return i}return[]}async createClient(r){return"userid"in r||(r.userid=null),r.client_name in this.clientsByName||(this.clientsByName[r.client_name]=[]),this.clientsByName[r.client_name].push(r),this.clients[r.client_id]=r}async deleteClient(r){if(r in this.clients){const t=this.clients[r].client_name;if(t in this.clientsByName){let s=this.clientsByName[t];for(let i=0;i<s.length;++i)if(s[i].client_id==r){s.splice(i,1);break}}delete this.clients[r]}}async updateClient(r){if(r.client_id&&r.client_id in this.clients){const t=this.clients[r.client_id];this.clients[r.client_id]={...t,...r}}}async getClients(r,t,s){const i=Object.keys(this.clients).sort();let o=[];r||(r=0);let n=t||i.length;n>=i.length-r&&(n=i.length-r);for(let l=r;l<n;++l)s===null?this.clients[i[l]].userid==null&&o.push(this.clients[i[l]]):s!=null&&s!==null?this.clients[i[l]].userid==s&&o.push(this.clients[i[l]]):o.push(this.clients[i[l]]);return o}}class De extends ie{constructor(r={}){super();d(this,"byClientAndUser",{});d(this,"byClient",{})}async getAuthorizations(r,t){if(t){if(r in this.byClientAndUser){const s=this.byClientAndUser[r];if(t in s)return s[t]}}else if(r in this.byClient)return this.byClient[r];return[]}async updateAuthorizations(r,t,s){if(t){r in this.byClientAndUser||(this.byClientAndUser[r]={});const i=this.byClientAndUser[r];i[t]=[...s]}else this.byClient[r]=[...s]}}function je(C,a){return{username:Array.isArray(a.uid)?a.uid[0]:a.uid,state:"active",...C}}class Z extends F{constructor(r,t={}){super(t);d(this,"localStorage");d(this,"ldapUrls",[]);d(this,"ldapUserSearchBase","");d(this,"ldapUsernameAttribute","cn");d(this,"createUserFn",je);this.localStorage=r,h("ldapUrls",u.JsonArray,this,t,"LDAP_URL",!0),h("ldapUserSearchBase",u.String,this,t,"LDAP_USER_SEARCH_BASE"),h("ldapUsernameAttribute",u.String,this,t,"LDAP_USENAME_ATTRIBUTE"),t.createUserFn&&(this.createUserFn=t.createUserFn)}async createUser(r,t){if(!(t!=null&&t.password))throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);const s=await this.getLdapUser(r.username,t.password);return r=this.createUserFn(r,s),await this.localStorage.createUser(r,{password:"pbkdf2:sha256:32:600000:0:DISABLED:DISABLED"})}async getUserByUsername(r,t){return await this.localStorage.getUserByUsername(r,t)}async getUserById(r,t){return await this.localStorage.getUserById(r,t)}async getUserByEmail(r,t){return await this.localStorage.getUserByEmail(r,t)}async getUserBy(r,t,s){return await this.localStorage.getUserBy(r,t,s)}async getUsers(r,t){return await this.localStorage.getUsers(r,t)}async updateUser(r,t){return await this.localStorage.updateUser(r,void 0)}async deleteUserByUsername(r){await this.localStorage.deleteUserByUsername(r)}async deleteUserById(r){await this.localStorage.deleteUserById(r)}async getLdapUser(r,t){let s;try{const i=Z.sanitizeLdapDnForSerach(r),o=[this.ldapUsernameAttribute+"="+i,this.ldapUserSearchBase].join(",");if(!t)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);return e.CrossauthLogger.logger.debug(e.j({msg:"LDAP search "+o})),s=await this.ldapBind(o,t),await this.searchUser(s,o)}catch(i){e.CrossauthLogger.logger.debug(e.j({err:i}));const o=e.CrossauthError.asCrossauthError(i);throw i instanceof de.InvalidCredentialsError?new e.CrossauthError(e.ErrorCode.UsernameOrPasswordInvalid):o.code!=e.ErrorCode.UnknownError?o:new e.CrossauthError(e.ErrorCode.Connection,"LDAP error getting user")}}ldapBind(r,t){return new Promise((s,i)=>{let o=de.createClient({url:this.ldapUrls});o.on("connect",function(){o.bind(r,t,function(n){if(n){i(n),o.unbind();return}s(o)})}),o.on("timeout",n=>{i(n)}),o.on("connectTimeout",n=>{i(n)}),o.on("error",n=>{i(n)}),o.on("connectError",function(n){if(n){i(n);return}})})}async searchUser(r,t,s){return new Promise(function(i,o){let n={scope:"base"};s&&(n.attributes=s),r.search(t,n,function(l,c){let g;if(l){o(l),r.unbind();return}c.on("searchEntry",function(f){g=Z.searchResultToUser(f.pojo)}),c.on("error",function(f){o(f),r.unbind()}),c.on("end",function(f){f.status!=0?o(new e.CrossauthError(e.ErrorCode.Connection,"LDAP onnection failed")):g?i(g):o(new e.CrossauthError(e.ErrorCode.UsernameOrPasswordInvalid)),r.unbind()})})})}static searchResultToUser(r){let t={dn:r.objectName,state:"active"};return r.attributes.forEach(s=>{t[s.type]=s.values.length==1?s.values[0]:s.values}),t}static sanitizeLdapDn(r){return r.replace("\\","\\\\").replace(",",",").replace("+","+").replace('"','"').replace("<","<").replace(">",">").replace("#","#").trim()}static sanitizeLdapDnForSerach(r){return Z.sanitizeLdapDn(r).replace("*","*").replace("(","(").replace(")",")")}}class q extends F{constructor(r,t={}){super(t);d(this,"userTable","users");d(this,"userSecretsTable","usersecrets");d(this,"idColumn","id");d(this,"useridForeignKeyColumn","userid");d(this,"forceIdToNumber",!0);d(this,"dbPool");this.dbPool=r,h("userTable",u.String,this,t,"USER_TABLE"),h("userSecretsTable",u.String,this,t,"USER_SECRETS_TABLE"),h("idColumn",u.String,this,t,"USER_ID_COLUMN"),h("forceIdToNumber",u.String,this,t,"USER_FORCE_ID_TO_NUMBER"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN")}async getUserById(r,t){return await this.getUser(this.idColumn,r,t)}async getUserByUsername(r,t){const s=this.normalizeUsername?q.normalize(r):r;return await this.getUser("username_normalized",s,t)}async getUserByEmail(r,t){const s=this.normalizeEmail?q.normalize(r):r;return this.getUser("email_normalized",s,t)}async getUserBy(r,t,s){return await this.getUser(r,t,s)}async getUser(r,t,s){let i=await this.dbPool.connect(),o,n,l=this.dbPool.parameters();try{await i.startTransaction();let c=`select * from ${this.userTable} where ${r} = `+l.nextParameter(),g=await i.execute(c,[t]);if(g.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);let f,w,E;if(this.idColumn in g[0])f=g[0][this.idColumn];else throw new e.CrossauthError(e.ErrorCode.Configuration,"ID column "+this.idColumn+" not present in user table");if("username"in g[0])w=g[0].username;else throw new e.CrossauthError(e.ErrorCode.Configuration,"username column "+this.idColumn+" not present in user table");if("state"in g[0])E=g[0].state;else throw new e.CrossauthError(e.ErrorCode.Configuration,"state column "+this.idColumn+" not present in user table");if(o={...g[0],id:f,username:w,state:E},!o)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(l=this.dbPool.parameters(),c=`select * from ${this.userSecretsTable} where ${this.useridForeignKeyColumn} = `+l.nextParameter(),g=await i.execute(c,[o.id]),g.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(g.length>0?n={userid:o.id,...g[0]}:n={userid:o.id},!n)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(this.useridForeignKeyColumn!="userid"&&this.useridForeignKeyColumn in n&&delete n[this.useridForeignKeyColumn],await i.commit(),(s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);if((s==null?void 0:s.skipEmailVerifiedCheck)!=!0&&o.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User has not verified email"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"User must change password"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&(o.state==e.UserState.passwordResetNeeded||o.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"User must reset password"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);return{user:o,secrets:n}}catch(c){throw await i.rollback(),c}finally{i.release()}}async getUsers(r,t){const s=await this.dbPool.connect();let i=[],o=[],n="",l="",c=this.dbPool.parameters();r&&(l="OFFSET "+c.nextParameter()),t&&(o.push(t),n="LIMIT "+c.nextParameter());try{let g=`select * from ${this.userTable} ${n} ${l} order by username_normalized asc`,f=await s.execute(g,o);if(f.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);for(let w of f){let E,m,y;if(this.idColumn in w)E=w[this.idColumn];else throw new e.CrossauthError(e.ErrorCode.Configuration,"ID column "+this.idColumn+" not present in user table");if("username"in w)m=w.username;else throw new e.CrossauthError(e.ErrorCode.Configuration,"username column "+this.idColumn+" not present in user table");if("state"in w)y=w.state;else throw new e.CrossauthError(e.ErrorCode.Configuration,"state column "+this.idColumn+" not present in user table");let S={...w,id:E,username:m,state:y};i.push(S)}return i}catch(g){throw g}finally{s.release()}}async updateUser(r,t){if(!(this.idColumn in r))throw new e.CrossauthError(e.ErrorCode.InvalidKey);t&&!t.userid&&(t={...t,userid:r[this.idColumn]}),t&&this.useridForeignKeyColumn!="userid"&&this.useridForeignKeyColumn in t&&delete t[this.useridForeignKeyColumn];const s=await this.dbPool.connect();try{await s.startTransaction();let i=this.dbPool.parameters(),o=`select * from ${this.userTable} where ${this.idColumn} = `+i.nextParameter();if((await s.execute(o,[r.id])).length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);let l={...r},c=t?{...t}:void 0;"email"in l&&l.email&&(l={email_normalized:this.normalizeEmail?q.normalize(l.email):l.email,...l}),"username"in l&&l.username&&(l={username_normalized:this.normalizeUsername?q.normalize(l.username):l.username,...l}),i=this.dbPool.parameters();let g=[],f=[];for(let w in l)l[w]!=null&&w!="id"&&(g.push(w+"= "+i.nextParameter()),f.push(l[w]));if(g.length>0){let w=g.join(", ");f.push(r.id);let E=`update ${this.userTable} set ${w} where ${this.idColumn} = `+i.nextParameter();await s.execute(E,f)}if(t){g=[],f=[],i=this.dbPool.parameters();for(let w in c)c[w]!=null&&w!="userid"&&(g.push(w+"= "+i.nextParameter()),f.push(c[w]));if(g.length>0){let w=g.join(", ");f.push(r.id);let E=`update ${this.userSecretsTable} set ${w} where userid = `+i.nextParameter();await s.execute(E,f)}}await s.commit()}catch(i){throw await s.rollback(),i}finally{s.release()}}async createUser(r,t){if(t&&(t={...t}),t&&!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordFormat,"Password required when creating user");t&&this.useridForeignKeyColumn in t&&delete t[this.useridForeignKeyColumn],t&&"userid"in t&&delete t.userid;const s=await this.dbPool.connect();let i;try{await s.startTransaction();let o={...r},n=t?{...t}:void 0;"email"in o&&o.email&&(o={email_normalized:this.normalizeEmail?q.normalize(o.email):o.email,...o}),"username"in o&&o.username&&(o={username_normalized:this.normalizeUsername?q.normalize(o.username):o.username,...o});let l=[],c=[],g=[];const f=this.dbPool.parameters();for(let E in o)o[E]!=null&&E!="id"&&(l.push(E),c.push(f.nextParameter()),g.push(o[E]));if(l.length>0){let E=l.join(", "),m=c.join(", ");const y=`insert into ${this.userTable} (${E}) values (${m}) returning ${this.idColumn}`,S=await s.execute(y,g);if(S.length==0||!S[0][this.idColumn])throw new e.CrossauthError(e.ErrorCode.Connection,"Couldn't create user");i=S[0][this.idColumn]}if(!i)throw new e.CrossauthError(e.ErrorCode.Connection,"Couldn't create user");if(t){l=[],c=[],g=[];const E=this.dbPool.parameters();l.push("userid"),c.push(E.nextParameter()),g.push(i);for(let m in n)n[m]!=null&&m!="userid"&&(l.push(m),c.push(E.nextParameter()),g.push(n[m]));if(l.length>0){let m=l.join(", "),y=c.join(", ");const S=`insert into ${this.userSecretsTable} (${m}) values (${y})`;e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:S})),await s.execute(S,g)}}return await s.commit(),(await this.getUserById(i)).user}catch(o){await s.rollback();const n=e.CrossauthError.asCrossauthError(o);throw e.CrossauthLogger.logger.debug(e.j({err:n})),n.code==e.ErrorCode.ConstraintViolation?new e.CrossauthError(e.ErrorCode.UserExists,"User already exists"):n}finally{s.release()}}async deleteUserByUsername(r){const t=await this.dbPool.connect();let{user:s}=await this.getUserByUsername(r),i=s.id;try{await t.startTransaction();let o=this.dbPool.parameters(),n=`delete from ${this.userSecretsTable} where ${this.useridForeignKeyColumn}=`+o.nextParameter();await t.execute(n,[i]),o=this.dbPool.parameters(),n=`delete from ${this.userTable} where username=`+o.nextParameter(),await t.execute(n,[r]),await t.commit()}catch(o){throw await t.rollback(),o}finally{t.release()}}async deleteUserById(r){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.deleteUserById_internal(Number(r))}catch{e.CrossauthLogger.logger.debug(e.j({msg:"Failed forcing id to number when deleting user"}))}return await this.deleteUserById_internal(r)}async deleteUserById_internal(r){const t=await this.dbPool.connect();try{await t.startTransaction();let s=this.dbPool.parameters(),i=`delete from ${this.userSecretsTable} where ${this.useridForeignKeyColumn}=`+s.nextParameter();await t.execute(i,[r]),s=this.dbPool.parameters(),i=`delete from ${this.userTable} where ${this.idColumn}=`+s.nextParameter(),await t.execute(i,[r]),await t.commit()}catch(s){throw await t.rollback(),s}finally{t.release()}}}class Be extends K{constructor(r,t={}){super();d(this,"keyTable","keys");d(this,"dbPool");d(this,"useridForeignKeyColumn","userid");h("transactionTimeout",u.Number,this,t,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),t.keyTable&&(this.keyTable=t.keyTable),this.dbPool=r}async getKey(r){const t=await this.dbPool.connect();try{await t.startTransaction();const s=await this.getKeyInTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async getKeyInTransaction(r,t){const s=this.dbPool.parameters();let i=`select * from ${this.keyTable} where value = `+s.nextParameter(),o=await r.execute(i,[t]);if(o.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidKey);return this.makeKey(o[0])}makeKey(r){r={...r};let t,s=null,i,o;if(this.useridForeignKeyColumn in r&&(s=r[this.useridForeignKeyColumn],this.useridForeignKeyColumn!="userid"&&delete r[this.useridForeignKeyColumn]),r.value)t=r.value;else throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No value in key");if(r.created)i=r.created;else throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No creation date in key");return r.expires&&(o=r.expires),r.userid||r.userid,{value:t,userid:s,created:i,expires:o,...r}}async saveKey(r,t,s,i,o,n={}){let l,c=[this.useridForeignKeyColumn,"value","created","expires","data"],g=this.dbPool.parameters(),f=[];for(let S=0;S<5;++S)f.push(g.nextParameter());let w=[r??null,t,s,i??null,o??""];for(let S in n)c.push(S),f.push(g.nextParameter()),w.push(n[S]);let E=c.join(", "),m=f.join(", ");const y=await this.dbPool.connect();try{const S=`insert into ${this.keyTable} (${E}) values (${m})`;await y.execute(S,w)}catch(S){e.CrossauthError.asCrossauthError(S).code==e.ErrorCode.ConstraintViolation?(e.CrossauthLogger.logger.warn(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),e.CrossauthLogger.logger.debug(e.j({err:S})),l=new e.CrossauthError(e.ErrorCode.KeyExists)):(e.CrossauthLogger.logger.debug(e.j({err:S})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key"))}finally{y.release()}if(l)throw l}async deleteKey(r){const t=await this.dbPool.connect();try{let s=this.dbPool.parameters(),i=`delete from ${this.keyTable} where value=`;i+=s.nextParameter(),e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:i})),await t.execute(i,[r])}finally{t.release()}}async deleteAllForUser(r,t,s){const i=await this.dbPool.connect();try{let o,n=[],l="",c=this.dbPool.parameters();if(r){const g=c.nextParameter(),f=c.nextParameter();o=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} = ${g} and value like ${f} `,n=[r]}else{const g=c.nextParameter();o=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} is null and value like ${g}`}n.push(t+"%"),s&&(l="and value != "+c.nextParameter(),n.push(s)),o+=" "+l,e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:o})),await i.execute(o,n)}catch(o){throw o}finally{i.release()}}async deleteMatching(r){const t=await this.dbPool.connect();try{let s=[],i=[];const o=this.dbPool.parameters();for(let c in r){let g=c=="userid"?this.useridForeignKeyColumn:c;r[c]==null?s.push(g+" is null"):(s.push(g+" = "+o.nextParameter()),i.push(r[c]))}let n=s.join(" and "),l=`delete from ${this.keyTable} where ${n}`;await t.execute(l,i)}catch(s){throw s}finally{t.release()}}async deleteWithPrefix(r,t){const s=await this.dbPool.connect();try{let i,o=[];const n=this.dbPool.parameters();if(r){let l=n.nextParameter(),c=n.nextParameter();i=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} = ${l} and value like ${c}`,o.push(r)}else{let l=n.nextParameter();i=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} is null and value like ${l}`}o.push(t+"%"),await s.execute(i,o)}catch(i){throw i}finally{s.release()}}async getAllForUser(r){const t=await this.dbPool.connect();try{let s=[],i,o=[];const n=this.dbPool.parameters();r?(i=`select * from ${this.keyTable} where ${this.useridForeignKeyColumn} = `+n.nextParameter(),o=[r]):i=`select * from ${this.keyTable} where ${this.useridForeignKeyColumn} is null`,e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:i}));let l=await t.execute(i,o);if(l.length==0)return[];for(let c of l){let g=this.makeKey(c);this.useridForeignKeyColumn!="userid"&&(g.userid=g[this.useridForeignKeyColumn],delete g[this.useridForeignKeyColumn]),s.push(g)}return s}catch(s){throw s}finally{t.release()}}async updateKey(r){const t=await this.dbPool.connect();try{await t.startTransaction(),await this.updateKeyInTransaction(t,r),await t.commit()}catch(s){throw await t.rollback(),s}finally{t.release()}}async updateKeyInTransaction(r,t){let s={...t};if(!t.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey);delete s.value;let i=[],o=[],n=this.dbPool.parameters();for(let l in s){let c=l;s[l]!=null&&l=="userid"&&this.useridForeignKeyColumn!="userid"&&(c=this.useridForeignKeyColumn),i.push(l+"= "+n.nextParameter()),o.push(s[c])}if(o.push(t.value),i.length>0){let l=i.join(", "),c=`update ${this.keyTable} set ${l} where value = `+n.nextParameter();e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:c})),await r.execute(c,o)}}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){const s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getKeyInTransaction(s,r);let o;if(!i.data||i.data=="")o={};else try{o=JSON.parse(i.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let n of t){let l=this.updateDataInternal(o,n.dataName,n.value);if(!l)throw new e.CrossauthError(e.ErrorCode.BadRequest,`Parents of ${n.dataName} not found in key data`);o=l}await this.updateKeyInTransaction(s,{value:i.value,data:JSON.stringify(o)}),await s.commit()}catch(i){throw await s.rollback(),i&&typeof i=="object"&&!("isCrossauthError"in i)?(e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):i}finally{s.release()}}async deleteData(r,t){const s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getKeyInTransaction(s,r);let o={},n=!1;if(i.data&&i.data!=""){try{o=JSON.parse(i.data)}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),new e.CrossauthError(e.ErrorCode.DataFormat)}n=this.deleteDataInternal(o,t)}n&&await this.updateKeyInTransaction(s,{value:i.value,data:JSON.stringify(o)}),await s.commit()}catch(i){throw await s.rollback(),i&&typeof i=="object"&&!("isCrossauthError"in i)?(e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):i}finally{s.release()}}}class ze extends se{constructor(r,t={}){super();d(this,"clientTable","oauthclient");d(this,"redirectUriTable","oauthclientredirecturi");d(this,"validFlowTable","oauthclientvalidflow");d(this,"dbPool");d(this,"useridForeignKeyColumn","userid");h("clientTable",u.String,this,t,"OAUTH_CLIENT_TABLE"),h("redirectUriTable",u.String,this,t,"OAUTH_REDIRECTURI_TABLE"),h("validFlowTable",u.String,this,t,"OAUTH_VALID_FLOW_TABLE"),h("updateMode",u.String,this,t,"OAUTHCLIENT_UPDATE_MODE"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),this.dbPool=r}async getClientById(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=await this.getClientWithTransaction(t,"client_id",r,void 0);if(await t.commit(),s.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return s[0]}catch(s){throw await t.rollback(),s}finally{t.release()}}async getClientByName(r,t){let s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getClientWithTransaction(s,"client_name",r,t);if(await s.commit(),i.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return i}catch(i){throw await s.rollback(),i}finally{s.release()}}makeClient(r){let t,s,i,o=!1,n=[],l=[];if("client_id"in r&&(t=r.client_id),!t)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);if("client_name"in r&&(s=r.client_name),!s)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return"client_secret"in r&&(i=r.client_secret),"confidential"in r&&(o=r.confidential),"redirect_uri"in r&&r.redirect_uri&&(n=r.redirect_uri),"valid_flow"in r&&r.valid_flow&&(l=r.valid_flow),{client_id:t,client_name:s,client_secret:i,confidential:o,redirect_uri:n,valid_flow:l,...r}}async getClientWithTransaction(r,t,s,i,o,n){let l=[],c=this.dbPool.parameters(),g=[],f=`select c.*, r.uri as uri, null as flow from ${this.clientTable} as c left join ${this.redirectUriTable} r on c.client_id = r.client_id `,w="";t&&s&&(w=`where c.${t} = `+c.nextParameter(),g.push(s)),i!==null&&i==null||(w==""?w="where ":w+=" and ",i==null?w+="userid is null":(w+=`${this.useridForeignKeyColumn} = `+c.nextParameter(),g.push(i)));let E=`select c.*, null as uri, f.flow as flow from ${this.clientTable} as c left join ${this.validFlowTable} f on c.client_id = f.client_id `,m="";t&&s&&(m=`where c.${t} = `+c.nextParameter(),g.push(s)),i!==null&&i==null||(m==""?m="where ":m+=" and ",i==null?m+="userid is null":(m+=`${this.useridForeignKeyColumn} = `+c.nextParameter(),g.push(i))),n&&(o||(o=0),o=Number(o),n=Number(n),w==""?w="where ":w+=" and ",w+=` c.client_id in (select client_id from ${this.clientTable} limit ${n} offset ${o})`,m==""?m="where ":m+=" and ",m+=` c.client_id in (select client_id from ${this.clientTable} limit ${n} offset ${o})`),f+=w,E+=m;let y=f+" union "+E+" order by client_id";const S=await r.execute(y,g);let T;for(let N of S)(!T||N.client_id!=T.client_id)&&(T&&l.push(T),T=this.makeClient(N),T.valid_flow=[],T.redirect_uri=[]),N.uri&&T.redirect_uri.push(N.uri),N.flow&&T.valid_flow.push(N.flow);return T&&l.push(T),l}async createClient(r){let t=await this.dbPool.connect();try{if(await t.startTransaction(),!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);if((await this.getClientWithTransaction(t,"client_id",r.client_id,r.userid)).length!=0)throw new e.CrossauthError(e.ErrorCode.ClientExists);let i=await this.createClientWithTransaction(t,r);return await t.commit(),i}catch(s){throw await t.rollback(),s}finally{t.release()}}async createClientWithTransaction(r,t){const{redirect_uri:s,valid_flow:i,userid:o,...n}=t;if(o&&(n[this.useridForeignKeyColumn]=o),s)for(let m=0;m<s.length;++m){if(s[m].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[m])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[m]} is not valid`)}}if(i){for(let m=0;m<i.length;++m)if(!e.OAuthFlows.isValidFlow(i[m]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Invalid flow "+i[m])}let l=[],c=[],g=[],f=this.dbPool.parameters();try{for(let m in n)l.push(m),c.push(f.nextParameter()),g.push(n[m]);if(l.length>0){let m=l.join(", "),y=c.join(", ");const S=`insert into ${this.clientTable} (${m}) values (${y})`;await r.execute(S,g)}}catch(m){throw typeof m=="object"&&m!=null&&"code"in m&&typeof m.code=="string"&&(m.code.startsWith("22")||m.code.startsWith("23"))?(e.CrossauthLogger.logger.debug(e.j({err:m})),new e.CrossauthError(e.ErrorCode.InvalidClientId,"Attempt to create an OAuth client with a client_id that already exists. Maximum attempts failed")):(e.CrossauthLogger.logger.debug(e.j({err:m})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}let w=await this.getClientWithTransaction(r,"client_id",t.client_id,t.userid);if(w.length==0)throw e.CrossauthLogger.logger.error(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),new e.CrossauthError(e.ErrorCode.KeyExists);let E=w[0];if(s)for(let m=0;m<s.length;++m){g=[],f=this.dbPool.parameters();let y=`insert into ${this.redirectUriTable} (client_id, uri) values (`+f.nextParameter()+", "+f.nextParameter()+")";g.push(E.client_id),g.push(s[m]),await r.execute(y,g)}if(i)for(let m=0;m<i.length;++m){g=[],f=this.dbPool.parameters();let y=`insert into ${this.validFlowTable} (client_id, flow) values (`+f.nextParameter()+", "+f.nextParameter()+")";g.push(E.client_id),g.push(i[m]),await r.execute(y,g)}return{...E,redirect_uri:s,valid_flow:i}}async deleteClient(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=this.deleteClientWithTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async deleteClientWithTransaction(r,t){let s=[],o=this.dbPool.parameters().nextParameter(),n=`delete from ${this.redirectUriTable} where client_id = ${o}`;s.push(t),await r.execute(n,s),n=`delete from ${this.validFlowTable} where client_id = ${o}`,await r.execute(n,s),n=`delete from ${this.clientTable} where client_id = ${o}`,await r.execute(n,s)}async updateClient(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=this.updateClientWithTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async updateClientWithTransaction(r,t){if(!t.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const s=t.redirect_uri,i=t.valid_flow;if(s)for(let y=0;y<s.length;++y){if(s[y].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[y])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[y]} is not valid`)}}if(i){for(let y=0;y<i.length;++y)if(!e.OAuthFlows.isValidFlow(i[y]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Redirect Uri's may not contain page fragments")}if(!t.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId,"No client ig given");let{client_id:o,redirect_uri:n,valid_flow:l,...c}=t;n||(n=[]),l||(l=[]);let g=this.dbPool.parameters(),f=`delete from ${this.redirectUriTable} where client_id = `+g.nextParameter();await r.execute(f,[t.client_id]),g=this.dbPool.parameters(),f=`delete from ${this.validFlowTable} where client_id = `+g.nextParameter(),await r.execute(f,[t.client_id]);let w=[],E=[],m=[];g=this.dbPool.parameters(),f=`delete from ${this.validFlowTable} where client_id = `+g.nextParameter();for(let y in c)w.push(y),E.push(g.nextParameter()),m.push(c[y]);if(w.length>0){let y=w.join(", "),S=E.join(", ");f=`update ${this.clientTable} set (${y}) values (${S})`,await r.execute(f,m)}if(n)for(let y=0;y<n.length;++y){m=[],g=this.dbPool.parameters();let S=`insert into ${this.redirectUriTable} (client_id, uri) values (`+g.nextParameter()+", "+g.nextParameter()+")";m.push(t.client_id),m.push(n[y]),await r.execute(S,m)}if(l)for(let y=0;y<l.length;++y){m=[],g=this.dbPool.parameters();let S=`insert into ${this.validFlowTable} (client_id, flow) values (`+g.nextParameter()+", "+g.nextParameter()+")";m.push(t.client_id),m.push(l[y]),await r.execute(S,m)}}async getClients(r,t,s){let i=await this.dbPool.connect();try{await i.startTransaction();const o=this.getClientWithTransaction(i,void 0,void 0,s,r,t);return await i.commit(),o}catch(o){throw await i.rollback(),o}finally{i.release()}}}class He extends ie{constructor(r,t={}){super();d(this,"authorizationTable","oauthauthorization");d(this,"useridForeignKeyColumn","userid");d(this,"dbPool");h("authorizationTable",u.String,this,t,"OAUTH_CLIENT_TABLE"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),this.dbPool=r}async getAuthorizations(r,t){let s=await this.dbPool.connect();try{const i=this.dbPool.parameters(),o=[];let n=`select scope from ${this.authorizationTable} where client_id = `+i.nextParameter();return o.push(r),t===null?n+=` and ${this.useridForeignKeyColumn} is null`:t&&(n+=` and ${this.useridForeignKeyColumn} = `+i.nextParameter(),o.push(t)),(await s.execute(n,o)).map(g=>g.scope)}catch(i){throw i}finally{s.release()}}async updateAuthorizations(r,t,s){let i=await this.dbPool.connect();try{await i.startTransaction();let o=this.dbPool.parameters(),n=[],l=`delete from ${this.authorizationTable} where client_id = `+o.nextParameter();n.push(r),t?(l+=` and ${this.useridForeignKeyColumn} = `+o.nextParameter(),n.push(t)):l+=` and ${this.useridForeignKeyColumn} is null`,await i.execute(l,n);for(let c of s)o=this.dbPool.parameters(),n=[],l=`insert into ${this.authorizationTable} (client_id, userid, scope) values (`+o.nextParameter()+", "+o.nextParameter()+", "+o.nextParameter()+")",n.push(r),n.push(t),n.push(c),await i.execute(l,n);await i.commit()}catch(o){throw await i.rollback(),o}finally{i.release()}}}class Me{constructor(){}}class Ve{constructor(){}}class $e{}class ae extends Me{constructor(r){super();d(this,"pgPool");this.pgPool=r}async connect(){const r=await this.pgPool.connect();return e.CrossauthLogger.logger.debug(e.j({msg:"DB connect"})),new qe(r)}parameters(){return new We}}class qe extends $e{constructor(r){super();d(this,"pgClient");this.pgClient=r}crossauthErrorFromPostgresError(r){let t,s;if(r&&typeof r=="object"&&"code"in r&&typeof r.code=="string"&&(t=r.code),r&&typeof r=="object"&&"detail"in r&&typeof r.detail=="string"&&(s=r.detail),t!=null&&t.startsWith("23")){const o=t+" : "+(s??"Constraint violation during database insert/update");return new e.CrossauthError(e.ErrorCode.ConstraintViolation,o)}const i=t?t+" : "+(s??"Constraint violation during database insert/update"):"Couldn't execute database query";return new e.CrossauthError(e.ErrorCode.Connection,i)}async execute(r,t=[]){try{return e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:r})),(await this.pgClient.query({text:r,values:t})).rows}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),this.crossauthErrorFromPostgresError(s)}}release(){e.CrossauthLogger.logger.debug(e.j({msg:"DB release"})),this.pgClient.release()}async startTransaction(){e.CrossauthLogger.logger.debug(e.j({msg:"DB start transaction"})),await this.pgClient.query("BEGIN")}async commit(){e.CrossauthLogger.logger.debug(e.j({msg:"DB commit"})),await this.pgClient.query("COMMIT")}async rollback(){e.CrossauthLogger.logger.debug(e.j({msg:"DB rollback"})),await this.pgClient.query("ROLLBACK")}}class We extends Ve{constructor(){super();d(this,"nextParam",1)}nextParameter(){return"$"+this.nextParam++}}class Je extends q{constructor(a,r={}){super(new ae(a),r)}}class Ye extends Be{constructor(a,r={}){super(new ae(a),r)}}class Ge extends ze{constructor(a,r={}){super(new ae(a),r)}}class Ze extends He{constructor(a,r={}){super(new ae(a),r)}}class Y{constructor(a){d(this,"friendlyName");d(this,"factorName","");if(!(a!=null&&a.friendlyName))throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator must have a friendly name");this.friendlyName=a==null?void 0:a.friendlyName}capabilities(){return{canCreateUser:this.canCreateUser(),canUpdateUser:this.canUpdateUser(),canUpdateSecrets:this.canUpdateSecrets()}}requireUserEntry(){return!0}}class le extends Y{secretNames(){return["password"]}transientSecretNames(){return[]}mfaType(){return"none"}mfaChannel(){return"none"}}const he=process.env.PBKDF2_DIGEST||"sha256",ge=Number(process.env.PBKDF2_ITERATIONS||6e5),fe=Number(process.env.PBKDF2_KEYLENGTH||32),Xe=Number(process.env.PBKDF2_KEYLENGTH||16),Q="sha256",H=class H{static async passwordsEqual(a,r,t){let s=H.decodePasswordHash(r),i=await H.passwordHash(a,{salt:s.salt,encode:!1,secret:s.useSecret?t:void 0,iterations:s.iterations,keyLen:s.keyLen,digest:s.digest});if(i.length!=s.hashedPassword.length)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);return I.timingSafeEqual(Buffer.from(i),Buffer.from(s.hashedPassword))}static base64Decode(a){return Buffer.from(a,"base64url").toString("utf-8")}static base64Encode(a){return Buffer.from(a,"utf-8").toString("base64url")}static decodePasswordHash(a){const r=a.split(":");if(r.length!=7)throw new e.CrossauthError(e.ErrorCode.InvalidHash);try{return{hashedPassword:r[6],salt:r[5],useSecret:r[4]!="0",iterations:Number(r[3]),keyLen:Number(r[2]),digest:r[1]}}catch{throw new e.CrossauthError(e.ErrorCode.InvalidHash)}}static encodePasswordHash(a,r,t,s,i,o){return"pbkdf2:"+o+":"+String(i)+":"+String(s)+":"+(t?1:0)+":"+r+":"+a}static randomSalt(){return H.randomValue(Xe)}static randomValue(a){return I.randomBytes(a).toString("base64url")}static randomBase32(a,r){var i;const s=[...I.randomBytes(a)].map(o=>H.Base32[o%32]).join("");return r?((i=s.match(/(.{1,4})/g))==null?void 0:i.join("-"))??s:s}static uuid(){return I.randomUUID()}static hash(a){return this.sha256(a)}static sha256(a){return I.createHash("sha256").update(a).digest("base64url")}static async passwordHash(a,r={}){let{salt:t,secret:s,encode:i}={...r};t||(t=H.randomSalt());let o=s!=null,n=o?t+"!"+s:t;i==null&&(i=!1);let g=(await Te.promisify(I.pbkdf2)(a,n,r.iterations??ge,r.keyLen??fe,r.digest??he)).toString("base64url");return i&&(g=this.encodePasswordHash(g,t,o,r.iterations??ge,r.keyLen??fe,r.digest??he)),g}static signableToken(a,r,t){return r==null&&(r=H.randomSalt()),t||(t=new Date().getTime()),Buffer.from(JSON.stringify({...a,t,s:r})).toString("base64url")}static sign(a,r,t,s){const i=H.signableToken(a,t,s),o=I.createHmac(Q,r);return i+"."+o.update(i).digest("base64url")}static signSecureToken(a,r){const t=I.createHmac(Q,r);return a+"."+t.update(a).digest("base64url")}static unsign(a,r,t){const s=a.split(".");if(s.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const i=s[0],o=s[1],n=JSON.parse(Buffer.from(i,"base64url").toString());if(t&&n.t+t*1e3>new Date().getTime())throw new e.CrossauthError(e.ErrorCode.Expired);const c=I.createHmac(Q,r).update(i).digest("base64url");if(c.length!=o.length)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");if(!I.timingSafeEqual(Buffer.from(c),Buffer.from(o)))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");return n}static unsignSecureToken(a,r){const t=a.split(".");if(t.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const s=t[0],i=t[1],o=s,l=I.createHmac(Q,r).update(s).digest("base64url");if(l.length!=i.length)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");if(!I.timingSafeEqual(Buffer.from(l),Buffer.from(i)))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");return o}static xor(a,r){const t=Buffer.from(a,"base64url"),s=Buffer.from(r,"base64url"),i=t.map((o,n)=>o^s[n]);return Buffer.from(i).toString("base64url")}static symmetricEncrypt(a,r,t=void 0){t||(t=I.randomBytes(16));let s=Buffer.from(r,"base64url");var i=I.createCipheriv("aes-256-cbc",s,t);let o=i.update(a);return o=Buffer.concat([o,i.final()]),t.toString("base64url")+"."+o.toString("base64url")}static symmetricDecrypt(a,r){let t=Buffer.from(r,"base64url");const s=a.split(".");if(s.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidHash,"Not AES-256-CBC ciphertext");let i=Buffer.from(s[0],"base64url"),o=Buffer.from(s[1],"base64url");var n=I.createDecipheriv("aes-256-cbc",t,i);let l=n.update(o);return l=Buffer.concat([l,n.final()]),l.toString()}};d(H,"Base32","ABCDEFGHJKLMNPQRSTUVWXYZ23456789".split(""));let p=H;function Qe(C){let a=[];if(!C.password)a.push("Password not provided");else{const r=C.password;r.length<8&&a.push("Password must be at least 8 characters"),r.match(/[a-z]/)==null&&a.push("Password must contain at least one lowercase character"),r.match(/[A-Z]/)==null&&a.push("Password must contain at least one uppercase character"),r.match(/[0-9]/)==null&&a.push("Password must contain at least one digit")}return a}const te=class te extends le{constructor(r,t={}){super({friendlyName:"Local password",...t});d(this,"secret");d(this,"enableSecretForPasswords",!1);d(this,"pbkdf2Digest","sha256");d(this,"pbkdf2Iterations",6e5);d(this,"pbkdf2SaltLength",16);d(this,"pbkdf2KeyLength",32);d(this,"validatePasswordFn",Qe);h("secret",u.String,this,t,"HASHER_SECRET"),h("enableSecretForPasswordHash",u.Boolean,this,t,"ENABLE_SECRET_FOR_PASSWORDS"),h("pbkdf2Digest",u.String,this,t,"PASSWORD_PBKDF2_DIGEST"),h("pbkdf2Iterations",u.String,this,t,"PASSWORD_PBKDF2_ITERATIONS"),h("pbkdf2SaltLength",u.String,this,t,"PASSWORD_PBKDF2_SALTLENGTH"),h("pbkdf2KeyLength",u.String,this,t,"PASSWORD_PBKDF2_KEYLENGTH"),t.validatePasswordFn&&(this.validatePasswordFn=t.validatePasswordFn)}async authenticateUser(r,t,s){if(!s.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid,"Password not provided");if(!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);if(!await p.passwordsEqual(s.password,t.password,this.secret))throw e.CrossauthLogger.logger.debug(e.j({msg:"Invalid password hash",user:r.username})),new e.CrossauthError(e.ErrorCode.PasswordInvalid);if(r.state=="awaitingtwofactorsetup")throw new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if(r.state=="awaitingemailverification")throw new e.CrossauthError(e.ErrorCode.EmailNotVerified);if(r.state=="deactivated")throw new e.CrossauthError(e.ErrorCode.UserNotActive)}validateSecrets(r){return this.validatePasswordFn(r)}async createPasswordHash(r,t){return await p.passwordHash(r,{salt:t,encode:!0,secret:this.enableSecretForPasswords?this.secret:void 0,iterations:this.pbkdf2Iterations,keyLen:this.pbkdf2KeyLength,digest:this.pbkdf2Digest})}async createPasswordForStorage(r){return this.createPasswordHash(r)}async passwordMatchesHash(r,t,s){return t==te.NoPassword?!1:await p.passwordsEqual(r,t,s)}async createPersistentSecrets(r,t,s){if(!t.password)throw new e.CrossauthError(e.ErrorCode.Unauthorized,"No password provided");if(s&&s.password!=t.password)throw new e.CrossauthError(e.ErrorCode.PasswordMatch);return{password:await this.createPasswordHash(t.password)}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!0}skipEmailVerificationOnSignup(){return!1}async prepareConfiguration(r){}async reprepareConfiguration(r,t){}};d(te,"NoPassword","********");let ne=te;class W extends Y{constructor(r={}){super({friendlyName:"Email otp",...r});d(this,"views","views");d(this,"emailAuthenticatorTextBody","emailauthenticationtextbody.njk");d(this,"emailAuthenticatorHtmlBody");d(this,"emailAuthenticatorSubject","Login code");d(this,"emailFrom","");d(this,"smtpHost","");d(this,"smtpPort",587);d(this,"smtpUseTls",!0);d(this,"smtpUsername");d(this,"smtpPassword");d(this,"emailAuthenticatorTokenExpires",60*5);d(this,"render");h("views",u.String,this,r,"VIEWS"),h("emailAuthenticatorTextBody",u.String,this,r,"EMAIL_AUTHENTICATOR_TEXT_BODY"),h("emailAuthenticatorHtmlBody",u.String,this,r,"EMAIL_AUTHENTICATOR_HTML_BODY"),h("emailAuthenticatorSubject",u.String,this,r,"EMAIL_AUTHENTICATOR_SUBJECT"),h("emailFrom",u.String,this,r,"EMAIL_FROM",!0),h("smtpHost",u.String,this,r,"SMTP_HOST",!0),h("smtpPort",u.Number,this,r,"SMTP_PORT"),h("smtpUsername",u.String,this,r,"SMTP_USERNAME"),h("smtpPassword",u.String,this,r,"SMTP_PASSWORD"),h("smtpUseTls",u.Boolean,this,r,"SMTP_USE_TLS"),h("emailAuthenticatorTokenExpires",u.Number,this,r,"EMAIL_AUTHENTICATOR_TOKEN_EXPIRES"),r.render?this.render=r.render:V.configure(this.views,{autoescape:!0})}mfaType(){return"oob"}mfaChannel(){return"email"}createEmailer(){let r={};return this.smtpUsername&&(r.user=this.smtpUsername),this.smtpPassword&&(r.pass=this.smtpPassword),we.createTransport({host:this.smtpHost,port:this.smtpPort,secure:this.smtpUseTls,auth:r})}async sendToken(r,t){W.validateEmail(r),this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.emailAuthenticatorSubject},i={otp:t};return this.emailAuthenticatorTextBody&&(s.text=this.render?this.render(this.emailAuthenticatorTextBody,i):V.render(this.emailAuthenticatorTextBody,i)),this.emailAuthenticatorHtmlBody&&(s.html=this.render?this.render(this.emailAuthenticatorHtmlBody,i):V.render(this.emailAuthenticatorHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on EmailAuthenticator before using");const t=W.zeroPad(I.randomInt(999999),6),s=r.email?r.email:r.username;W.validateEmail(s);const i=new Date,o=new Date(i.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),n={username:r.username,email:s,factor2:this.factorName},l={username:r.username,factor2:this.factorName,expiry:o,otp:t},c=this.sendToken(s,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:c,email:s})),{userData:n,sessionData:l}}async reprepareConfiguration(r,t){const s=K.decodeData(t.data)["2fa"],i=W.zeroPad(I.randomInt(999999),6),o=new Date,n=new Date(o.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),l=this.sendToken(s.email,i);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:l,email:s.email})),{userData:{email:s.email,factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=W.zeroPad(I.randomInt(999999),6),s=new Date,i=new Date(s.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),o=r.email||r.username,n=this.sendToken(o,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:n,email:o})),{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!0}static isEmailValid(r){return String(r).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static validateEmail(r){if(r==null||!W.isEmailValid(r))throw new e.CrossauthError(e.ErrorCode.InvalidEmail)}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class J extends Y{constructor(r={}){super({friendlyName:"SMS otp",...r});d(this,"views","views");d(this,"smsAuthenticatorBody","smsauthenticationbody.njk");d(this,"smsAuthenticatorFrom","");d(this,"smsAuthenticatorTokenExpires",60*5);d(this,"render");h("views",u.String,this,r,"VIEWS"),h("smsAuthenticatorBody",u.String,this,r,"SMS_AUTHENTICATOR_BODY"),h("smsAuthenticatorFrom",u.String,this,r,"SMS_AUTHENTICATOR_FROM",!0),h("smsAuthenticatorTokenExpires",u.Number,this,r,"SMS_AUTHENTICATOR_TOKEN_EXPIRES"),r.render?this.render=r.render:V.configure(this.views,{autoescape:!0})}mfaType(){return"oob"}mfaChannel(){return"sms"}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on SmsAuthenticator before using");const t=J.zeroPad(I.randomInt(999999),6),s=r.phone;J.validatePhone(s);const i=new Date,o=new Date(i.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),n={username:r.username,phone:s,factor2:this.factorName},l={username:r.username,factor2:this.factorName,expiry:o,otp:t};let c={otp:t};const g=this.render?this.render(this.smsAuthenticatorBody,c):V.render(this.smsAuthenticatorBody,c),f=this.sendSms(s,g);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:f,phone:s})),{userData:n,sessionData:l}}async reprepareConfiguration(r,t){const s=K.decodeData(t.data)["2fa"],i=J.zeroPad(I.randomInt(999999),6),o=new Date,n=new Date(o.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),l=this.sendSms(s.phone,i);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:l,phone:s.phone})),{userData:{phone:s.phone,factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=J.zeroPad(I.randomInt(999999),6),s=new Date,i=new Date(s.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),o=r.phone,n=this.sendSms(o,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:n,phone:o})),{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}static isPhoneValid(r){return String(r).match(/^\+[1-9][0-9]{7,14}$/)!=null}static validatePhone(r){if(r==null||!J.isPhoneValid(r))throw new e.CrossauthError(e.ErrorCode.InvalidPhoneNumber)}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class ce extends J{constructor(r={}){super(r);d(this,"accountSid");d(this,"authToken");if(!process.env.TWILIO_ACCOUNT_SID||!process.env.TWILIO_AUTH_TOKEN)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in environment to use Twilio");this.accountSid=process.env.TWILIO_ACCOUNT_SID,this.authToken=process.env.TWILIO_AUTH_TOKEN}async sendSms(r,t){ce.validatePhone(r);let s={from:this.smsAuthenticatorFrom,to:r,body:t};return(await ve(this.accountSid,this.authToken).messages.create(s)).sid}}class er extends Y{constructor(r,t={}){super({friendlyName:"Dummy factor2",...t});d(this,"code");this.code=r}mfaType(){return"oob"}mfaChannel(){return"email"}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on DummyFactor2AuthenticatorOptions before using");const t=new Date,s=new Date(t.getTime()+1e3*60).getTime(),i={username:r.username,factor2:this.factorName},o={username:r.username,factor2:this.factorName,expiry:s,otp:this.code};return{userData:i,sessionData:o}}async reprepareConfiguration(r,t){const s=K.decodeData(t.data)["2fa"],i=this.code,o=new Date,n=new Date(o.getTime()+1e3*60).getTime();return{userData:{factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=this.code,s=new Date,i=new Date(s.getTime()+1e3*60).getTime();return{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}static isEmailValid(r){return String(r).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class rr extends le{constructor(r,t={}){super({friendlyName:"LDAP",...t});d(this,"ldapAutoCreateAccount",!1);d(this,"ldapStorage");d(this,"ldapAutoCreateFactor1","ldap");h("ldapAutoCreateAccount",u.Boolean,this,t,"LDAP_AUTO_CREATE_ACCOUNT"),h("ldapAutoCreateFactor1",u.Boolean,this,t,"LDAP_AUTO_CREATE_FACTOR1"),this.ldapStorage=r}async authenticateUser(r,t,s){if(!s.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid,"Password not provided");await this.ldapStorage.getLdapUser(r.username,s.password);let i;try{if(this.ldapAutoCreateAccount)try{i=(await this.ldapStorage.getUserByUsername(r.username)).user,i.factor1=this.ldapAutoCreateFactor1}catch{i=await this.ldapStorage.createUser({factor1:this.ldapAutoCreateFactor1,...r},s)}else i=(await this.ldapStorage.getUserByUsername(r.username)).user;if(i.state=="awaitingtwofactorsetup")throw new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if(i.state=="awaitingemailverification")throw new e.CrossauthError(e.ErrorCode.EmailNotVerified);if(i.state=="deactivated")throw new e.CrossauthError(e.ErrorCode.UserNotActive)}catch(o){throw console.log(o),e.CrossauthLogger.logger.debug(e.j({err:o})),o}}validateSecrets(r){return[]}requireUserEntry(){return!1}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}skipEmailVerificationOnSignup(){return!1}async prepareConfiguration(r){}async reprepareConfiguration(r,t){}}class tr extends Y{constructor(r,t){super({friendlyName:"Google Authenticator",...t});d(this,"appName");this.appName=r}mfaType(){return"otp"}mfaChannel(){return"none"}async createSecret(r,t){t||(t=oe.authenticator.generateSecret());let s="";return await _e.toDataURL(oe.authenticator.keyuri(r,this.appName,t)).then(i=>{s=i}).catch(i=>{throw e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.UnknownError,"Couldn't generate 2FA URL")}),{qrUrl:s,secret:t}}async getSecretFromSession(r,t){let s=K.decodeData(t.data);if(s&&s["2fa"]&&(s=s["2fa"]),!("totpsecret"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"TOTP data not in session");if(!("factor2"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"TOTP factor name not in session");const i=s.totpsecret,{qrUrl:o,secret:n}=await this.createSecret(r,i);return{qrUrl:o,secret:n,factor2:s.factor2}}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on TotpAuthenticator before using");const{qrUrl:t,secret:s}=await this.createSecret(r.username),i={username:r.username,qr:t,totpsecret:s,factor2:this.factorName},o={username:r.username,totpsecret:s,factor2:this.factorName};return{userData:i,sessionData:o}}async reprepareConfiguration(r,t){const{qrUrl:s,secret:i,factor2:o}=await this.getSecretFromSession(r,t);return{userData:{qr:s,totpsecret:i,factor2:o},secrets:{totpsecret:i},newSessionData:void 0}}async authenticateUser(r,t,s){if(!t.totpsecret||!s.otp)throw new e.CrossauthError(e.ErrorCode.InvalidToken,"TOTP secret or code not given");const i=s.otp,o=t.totpsecret;if(!oe.authenticator.check(i,o))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid TOTP code")}async createPersistentSecrets(r,t,s){const{secret:i}=await this.createSecret(r);return{totpsecret:i}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return["totpsecret"]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}}const ee=16;class P{constructor(a,r,t={}){d(this,"userStorage");d(this,"keyStorage");d(this,"views","views");d(this,"siteUrl");d(this,"prefix","/");d(this,"emailVerificationTextBody","emailverificationtextbody.njk");d(this,"emailVerificationHtmlBody");d(this,"emailVerificationSubject","Please verify your email");d(this,"passwordResetTextBody","passwordresettextbody.njk");d(this,"passwordResetHtmlBody");d(this,"passwordResetSubject","Password reset");d(this,"emailFrom","");d(this,"smtpHost","");d(this,"smtpPort",587);d(this,"smtpUseTls",!0);d(this,"smtpUsername");d(this,"smtpPassword");d(this,"verifyEmailExpires",60*60*24);d(this,"passwordResetExpires",60*60*24);d(this,"render");this.userStorage=a,this.keyStorage=r,h("siteUrl",u.String,this,t,"SITE_URL",!0),h("prefix",u.String,this,t,"PREFIX"),h("views",u.String,this,t,"VIEWS"),h("emailVerificationTextBody",u.String,this,t,"EMAIL_VERIFICATION_TEXT_BODY"),h("emailVerificationHtmlBody",u.String,this,t,"EMAIL_VERIFICATION_HTML_BODY"),h("emailVerificationSubject",u.String,this,t,"EMAIL_VERIFICATION_SUBJECT"),h("passwordResetTextBody",u.String,this,t,"PASSWORD_RESET_TEXT_BODY"),h("passwordResetHtmlBody",u.String,this,t,"PASSWORD_RESET_HTML_BODY"),h("passwordResetSubject",u.String,this,t,"PASSWORD_RESET_SUBJECT"),h("emailFrom",u.String,this,t,"EMAIL_FROM",!0),h("smtpHost",u.String,this,t,"SMTP_HOST",!0),h("smtpPort",u.Number,this,t,"SMTP_PORT"),h("smtpUsername",u.String,this,t,"SMTP_USERNAME"),h("smtpPassword",u.String,this,t,"SMTP_PASSWORD"),h("smtpUseTls",u.Boolean,this,t,"SMTP_USE_TLS"),h("verifyEmailExpires",u.Boolean,this,t,"VERIFY_EMAIL_EXPIRES"),h("passwordResetExpires",u.String,this,t,"PASSWORD_RESET_EXPIRES"),t.render?this.render=t.render:V.configure(this.views,{autoescape:!0})}createEmailer(){let a={};return this.smtpUsername&&(a.user=this.smtpUsername),this.smtpPassword&&(a.pass=this.smtpPassword),we.createTransport({host:this.smtpHost,port:this.smtpPort,secure:this.smtpUseTls,auth:a})}static hashEmailVerificationToken(a){return e.KeyPrefix.emailVerificationToken+p.hash(a)}static hashPasswordResetToken(a){return e.KeyPrefix.passwordResetToken+p.hash(a)}async createAndSaveEmailVerificationToken(a,r=""){let s=0;const i=new Date,o=new Date(i.getTime()+1e3*this.verifyEmailExpires);for(;s<10;){let n=p.randomValue(ee),l=P.hashEmailVerificationToken(n);try{return await this.keyStorage.saveKey(a,l,i,o,r),n}catch{n=p.randomValue(ee),l=P.hashEmailVerificationToken(n),s++}}throw new e.CrossauthError(e.ErrorCode.Connection,"failed creating a unique key")}async _sendEmailVerificationToken(a,r,t){this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.emailVerificationSubject},i={token:a,siteUrl:this.siteUrl,prefix:this.prefix};return t&&(i={...i,...t}),this.emailVerificationTextBody&&(s.text=this.render?this.render(this.emailVerificationTextBody,i):V.render(this.emailVerificationTextBody,i)),this.emailVerificationHtmlBody&&(s.html=this.render?this.render(this.emailVerificationHtmlBody,i):V.render(this.emailVerificationHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async sendEmailVerificationToken(a,r="",t={}){if(!this.emailVerificationTextBody&&!this.emailVerificationHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either emailVerificationTextBody or emailVerificationHtmlBody must be set to send email verification emails");let{user:s}=await this.userStorage.getUserById(a,{skipEmailVerifiedCheck:!0}),i=r;i!=""?P.validateEmail(i):(i=s.email??s.username,i||(i=s.username),P.validateEmail(i)),P.validateEmail(i);const o=await this.createAndSaveEmailVerificationToken(a,r),n=await this._sendEmailVerificationToken(o,i,t);e.CrossauthLogger.logger.info(e.j({msg:"Sent email verification email",emailMessageId:n,email:i}))}async verifyEmailVerificationToken(a){const r=P.hashEmailVerificationToken(a);let t=await this.keyStorage.getKey(r);try{if(!t.userid||!t.expires)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const{user:s}=await this.userStorage.getUserById(t.userid,{skipEmailVerifiedCheck:!0});let i=(s.email??s.username).toLowerCase();if(i||(i=s.username.toLowerCase()),P.validateEmail(i),new Date().getTime()>t.expires.getTime())throw new e.CrossauthError(e.ErrorCode.Expired);return{userid:t.userid,newEmail:t.data??""}}finally{}}async deleteEmailVerificationToken(a){try{const r=P.hashEmailVerificationToken(a);await this.keyStorage.deleteKey(r)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t}))}}async createAndSavePasswordResetToken(a){let t=0;const s=new Date,i=new Date(s.getTime()+1e3*this.passwordResetExpires);for(;t<10;){let o=p.randomValue(ee),n=P.hashPasswordResetToken(o);try{return await this.keyStorage.saveKey(a,n,s,i),o}catch{o=p.randomValue(ee),n=P.hashPasswordResetToken(o),t++}}throw new e.CrossauthError(e.ErrorCode.Connection,"failed creating a unique key")}async verifyPasswordResetToken(a){const r=P.hashPasswordResetToken(a);e.CrossauthLogger.logger.debug("verifyPasswordResetToken "+a+" "+r);let t=await this.keyStorage.getKey(r);if(!t.userid)throw new e.CrossauthError(e.ErrorCode.InvalidKey);if(!t.userid||!t.expires)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const{user:s}=await this.userStorage.getUserById(t.userid,{skipActiveCheck:!0});if(s.state!=e.UserState.active&&s.state!=e.UserState.passwordResetNeeded&&s.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);if(new Date().getTime()>t.expires.getTime())throw new e.CrossauthError(e.ErrorCode.Expired);return s}async _sendPasswordResetToken(a,r,t){if(!this.emailVerificationTextBody&&!this.emailVerificationHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either emailVerificationTextBody or emailVerificationHtmlBody must be set to send email verification emails");this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.passwordResetSubject},i={token:a,siteUrl:this.siteUrl,prefix:this.prefix};return t&&(i={...i,...t}),this.passwordResetTextBody&&(s.text=this.render?this.render(this.passwordResetTextBody,i):V.render(this.passwordResetTextBody,i)),this.passwordResetHtmlBody&&(s.html=this.render?this.render(this.passwordResetHtmlBody,i):V.render(this.passwordResetHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async sendPasswordResetToken(a,r={},t=!1){if(!this.passwordResetTextBody&&!this.passwordResetHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either passwordResetTextBody or passwordResetTextBody must be set to send email verification emails");let{user:s}=await this.userStorage.getUserById(a,{skipActiveCheck:!0});if(!t&&s.state!=e.UserState.active&&s.state!=e.UserState.passwordResetNeeded&&s.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);let i=(s.email??s.username).toLowerCase();i||(i=s.username.toLowerCase()),P.validateEmail(i);const o=await this.createAndSavePasswordResetToken(a),n=await this._sendPasswordResetToken(o,i,r);e.CrossauthLogger.logger.info(e.j({msg:"Sent password reset email",emailMessageId:n,email:i}))}static isEmailValid(a){return String(a).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static validateEmail(a){if(a==null||!P.isEmailValid(a))throw new e.CrossauthError(e.ErrorCode.InvalidEmail)}}const me=16,Ce=16;function sr(C){return{...C,path:C.path??"/"}}class ye{constructor(a={}){d(this,"headerName","X-CROSSAUTH-CSRF");d(this,"cookieName","CSRFTOKEN");d(this,"domain");d(this,"httpOnly",!1);d(this,"path","/");d(this,"secure",!0);d(this,"sameSite","lax");d(this,"secret","");h("headerName",u.String,this,a,"CSRF_HEADER_NAME"),h("cookieName",u.String,this,a,"CSRF_COOKIE_NAME"),h("domain",u.String,this,a,"CSRF_COOKIE_DOMAIN"),h("httpOnly",u.Boolean,this,a,"CSRF_COOKIE_HTTPONLY"),h("path",u.String,this,a,"CSRF_COOKIE_PATH"),h("secure",u.Boolean,this,a,"CSRF_COOKIE_SECURE"),h("sameSite",u.String,this,a,"CSRF_COOKIE_SAMESITE"),h("secret",u.String,this,a,"SECRET",!0)}createCsrfToken(){return p.randomValue(me)}makeCsrfCookie(a){const r=p.signSecureToken(a,this.secret);let t={};return this.domain&&(t.domain=this.domain),this.path&&(t.path=this.path),t.sameSite=this.sameSite,this.httpOnly&&(t.httpOnly=this.httpOnly),this.secure&&(t.secure=this.secure),{name:this.cookieName,value:r,options:t}}makeCsrfFormOrHeaderToken(a){return this.maskCsrfToken(a)}unsignCookie(a){return p.unsignSecureToken(a,this.secret)}makeCsrfCookieString(a){let r=this.cookieName+"="+a+"; SameSite="+this.sameSite;return this.domain&&(r+="; "+this.domain),this.path&&(r+="; "+this.path),this.httpOnly&&(r+="; httpOnly"),this.secure&&(r+="; secure"),r}maskCsrfToken(a){const r=p.randomValue(me),t=p.xor(a,r);return r+"."+t}unmaskCsrfToken(a){const r=a.split(".");if(r.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidCsrf,"CSRF token in header or form not in correct format");const t=r[0],s=r[1];return p.xor(s,t)}validateDoubleSubmitCsrfToken(a,r){const t=this.unmaskCsrfToken(r);let s;try{s=p.unsignSecureToken(a,this.secret)}catch(i){throw e.CrossauthLogger.logger.error(e.j({err:i})),new e.CrossauthError(e.ErrorCode.InvalidCsrf,"Invalid CSRF cookie")}if(s!=t)throw e.CrossauthLogger.logger.warn(e.j({msg:"Invalid CSRF token received - form/header value does not match",csrfCookieHash:p.hash(a)})),new e.CrossauthError(e.ErrorCode.InvalidCsrf)}validateCsrfCookie(a){try{return p.unsignSecureToken(a,this.secret)}catch(r){throw e.CrossauthLogger.logger.error(e.j({err:r})),new e.CrossauthError(e.ErrorCode.InvalidCsrf,"Invalid CSRF cookie")}}}class O{constructor(a,r={}){d(this,"userStorage");d(this,"keyStorage");d(this,"idleTimeout",0);d(this,"persist",!0);d(this,"filterFunction");d(this,"cookieName","SESSIONID");d(this,"maxAge",60*60*24*30);d(this,"domain");d(this,"httpOnly",!1);d(this,"path","/");d(this,"secure",!0);d(this,"sameSite","lax");d(this,"secret","");r.userStorage&&(this.userStorage=r.userStorage),this.keyStorage=a,h("idleTimeout",u.Number,this,r,"SESSION_IDLE_TIMEOUT"),h("persist",u.Boolean,this,r,"PERSIST_SESSION_ID"),this.filterFunction=r.filterFunction,h("cookieName",u.String,this,r,"SESSION_COOKIE_NAME"),h("maxAge",u.String,this,r,"SESSION_COOKIE_MAX_AGE"),h("domain",u.String,this,r,"SESSION_COOKIE_DOMAIN"),h("httpOnly",u.Boolean,this,r,"SESSIONCOOKIE_HTTPONLY"),h("path",u.String,this,r,"SESSION_COOKIE_PATH"),h("secure",u.Boolean,this,r,"SESSION_COOKIE_SECURE"),h("sameSite",u.String,this,r,"SESSION_COOKIE_SAMESITE"),h("secret",u.String,this,r,"SECRET",!0)}expiry(a){let r;return this.maxAge>0&&(r=new Date,r.setTime(a.getTime()+this.maxAge*1e3)),r}static hashSessionId(a){return e.KeyPrefix.session+p.hash(a)}async createSessionKey(a,r={}){let s=0,i=p.randomValue(Ce);const o=new Date;let n=this.expiry(o),l=!1;for(;s<10&&!l;){const c=O.hashSessionId(i);try{this.idleTimeout>0&&a&&(r={...r,lastActivity:new Date}),await this.keyStorage.saveKey(a,c,o,n,void 0,r),l=!0}catch(g){let f=e.CrossauthError.asCrossauthError(g);if(f.code==e.ErrorCode.KeyExists||f.code==e.ErrorCode.InvalidKey){if(s++,i=p.randomValue(Ce),s>10)throw e.CrossauthLogger.logger.error(e.j({msg:"Max attempts exceeded trying to create session ID"})),new e.CrossauthError(e.ErrorCode.KeyExists)}else throw e.CrossauthLogger.logger.debug(e.j({err:g})),g}}return{userid:a,value:i,created:o,expires:n}}makeCookie(a,r){let t=p.signSecureToken(a.value,this.secret),s={};return r==null&&(r=this.persist),this.domain&&(s.domain=this.domain),a.expires&&r&&(s.expires=a.expires),this.path&&(s.path=this.path),s.sameSite=this.sameSite,this.httpOnly&&(s.httpOnly=this.httpOnly),this.secure&&(s.secure=this.secure),{name:this.cookieName,value:t,options:s}}makeCookieString(a){let r=a.name+"="+a.value;return this.sameSite&&(r+="; SameSite="+this.sameSite),a.options.expires&&(r+="; expires="+new Date(a.options.expires).toUTCString()),this.domain&&(r+="; domain="+this.domain),this.path&&(r+="; path="+this.path),this.httpOnly&&(r+="; httpOnly"),this.secure&&(r+="; secure"),r}async updateSessionKey(a){if(!a.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No session when updating activity");a.value=O.hashSessionId(a.value),await this.keyStorage.updateKey(a)}unsignCookie(a){return p.unsignSecureToken(a,this.secret)}async getUserForSessionId(a,r){const t=await this.getSessionKey(a);if(!this.userStorage)return{key:t,user:void 0};if(t.userid){let{user:s}=await this.userStorage.getUserById(t.userid,r);return{user:s,key:t}}else return{user:void 0,key:t}}async getSessionKey(a){const r=Date.now(),t=O.hashSessionId(a),s=await this.keyStorage.getKey(t);if(s.value=a,s.expires&&r>s.expires.getTime())throw e.CrossauthLogger.logger.warn(e.j({msg:"Session id in cookie expired in key storage",hashedSessionCookie:p.hash(a)})),new e.CrossauthError(e.ErrorCode.Expired);if(s.userid&&this.idleTimeout>0&&s.lastactive&&r>s.lastactive.getTime()+this.idleTimeout*1e3)throw e.CrossauthLogger.logger.warn(e.j({msg:"Session cookie with expired idle time received",hashedSessionCookie:p.hash(a)})),new e.CrossauthError(e.ErrorCode.Expired);if(this.filterFunction&&!this.filterFunction(s))throw e.CrossauthLogger.logger.warn(e.j({msg:"Filter function on session id in cookie failed",hashedSessionCookie:p.hash(a)})),new e.CrossauthError(e.ErrorCode.InvalidKey);return s}async deleteAllForUser(a,r){r&&(r=O.hashSessionId(r)),await this.keyStorage.deleteAllForUser(a,e.KeyPrefix.session,r)}}class ir{constructor(a,r,t={}){d(this,"userStorage");d(this,"keyStorage");d(this,"emailTokenStorage");d(this,"csrfTokens");d(this,"session");d(this,"authenticators");d(this,"enableEmailVerification",!1);d(this,"enablePasswordReset",!1);d(this,"tokenEmailer");d(this,"allowedFactor2",[]);t.userStorage&&(this.userStorage=t.userStorage),this.keyStorage=a,this.authenticators=r;for(let s in this.authenticators)this.authenticators[s].factorName=s;if(this.session=new O(this.keyStorage,{...t==null?void 0:t.sessionCookieOptions,...t??{}}),this.csrfTokens=new ye({...t==null?void 0:t.doubleSubmitCookieOptions,...t??{}}),h("allowedFactor2",u.JsonArray,this,t,"ALLOWED_FACTOR2"),h("enableEmailVerification",u.Boolean,this,t,"ENABLE_EMAIL_VERIFICATION"),h("enablePasswordReset",u.Boolean,this,t,"ENABLE_PASSWORD_RESET"),this.emailTokenStorage=this.keyStorage,this.userStorage&&(this.enableEmailVerification||this.enablePasswordReset)){let s=this.keyStorage;t.emailTokenStorage&&(this.emailTokenStorage=t.emailTokenStorage),this.tokenEmailer=new P(this.userStorage,s,t)}}get sessionCookieName(){return this.session.cookieName}get sessionCookiePath(){return this.session.path}get csrfCookieName(){return this.csrfTokens.cookieName}get csrfCookiePath(){return this.csrfTokens.path}get csrfHeaderName(){return this.csrfTokens.headerName}async login(a,r,t={},s,i,o=!1){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call login if no user storage provided");let n={userid:""},l="";if(i)n=(await this.userStorage.getUserByUsername(i.username,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0})).secrets;else{let E={username:"",state:"active"};try{let y=await this.userStorage.getUserByUsername(a,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});n=y.secrets,i=y.user,E=y.user}catch{for(let S in this.authenticators)this.authenticators[S].requireUserEntry()||(E={username:r.username,state:"active"},l=S)}if(E.username=="")throw new e.CrossauthError(e.ErrorCode.UserNotExist);await this.authenticators[(i==null?void 0:i.factor1)??l].authenticateUser(E,n,r);let m=await this.userStorage.getUserByUsername(a,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});n=m.secrets,i=m.user}let c;if(i.state==e.UserState.passwordChangeNeeded)c=(await this.createAnonymousSession({data:JSON.stringify({passwordchange:{username:i.username}})})).sessionCookie;else if(i.state==e.UserState.factor2ResetNeeded)c=(await this.createAnonymousSession({data:JSON.stringify({factor2change:{username:i.username}})})).sessionCookie;else if(!o&&i.factor2&&i.factor2!=""){const{sessionCookie:E}=await this.initiateTwoFactorLogin(i);c=E}else{const E=await this.session.createSessionKey(i.id,t);c=this.session.makeCookie(E,s)}const g=this.csrfTokens.createCsrfToken(),f=this.csrfTokens.makeCsrfCookie(g),w=this.csrfTokens.makeCsrfFormOrHeaderToken(g);try{this.emailTokenStorage.deleteAllForUser(i.id,e.KeyPrefix.passwordResetToken)}catch(E){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:a})),e.CrossauthLogger.logger.debug(e.j({err:E}))}return{sessionCookie:c,csrfCookie:f,csrfFormOrHeaderValue:w,user:i,secrets:n}}async createAnonymousSession(a={}){const r=await this.session.createSessionKey(void 0,a),t=this.session.makeCookie(r,!1);let{csrfCookie:s,csrfFormOrHeaderValue:i}=await this.createCsrfToken();return{sessionCookie:t,csrfCookie:s,csrfFormOrHeaderValue:i}}async logout(a){const r=await this.session.getSessionKey(a);return await this.keyStorage.deleteKey(O.hashSessionId(r.value))}async logoutFromAll(a,r){return this.session.deleteAllForUser(a,r)}async userForSessionId(a){return await this.session.getUserForSessionId(a)}async dataStringForSessionId(a){try{let{key:r}=await this.session.getUserForSessionId(a);return r.data}catch(r){let t=e.CrossauthError.asCrossauthError(r);switch(t.code){case e.ErrorCode.Expired:return;default:throw t}}}async dataForSessionId(a){const r=await this.dataStringForSessionId(a);return!r||r.length==0?{}:JSON.parse(r)}async createCsrfToken(){this.csrfTokens.makeCsrfCookie(await this.csrfTokens.createCsrfToken());const a=this.csrfTokens.createCsrfToken(),r=this.csrfTokens.makeCsrfFormOrHeaderToken(a);return{csrfCookie:this.csrfTokens.makeCsrfCookie(a),csrfFormOrHeaderValue:r}}async createCsrfFormOrHeaderValue(a){const r=this.csrfTokens.unsignCookie(a);return this.csrfTokens.makeCsrfFormOrHeaderToken(r)}getSessionId(a){return this.session.unsignCookie(a)}validateDoubleSubmitCsrfToken(a,r){if(!a||!r)throw new e.CrossauthError(e.ErrorCode.InvalidCsrf,"CSRF missing from either cookie or form/header value");this.csrfTokens.validateDoubleSubmitCsrfToken(a,r)}validateCsrfCookie(a){this.csrfTokens.validateCsrfCookie(a)}async updateSessionActivity(a){const{key:r}=await this.session.getSessionKey(a);this.session.idleTimeout>0&&this.session.updateSessionKey({value:r.value,lastactive:new Date})}async updateSessionData(a,r,t){const s=O.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:`Updating session data value${r}`,hashedSessionCookie:p.hash(a)})),await this.keyStorage.updateData(s,r,t)}async updateManySessionData(a,r){const t=O.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:"Updating session data",hashedSessionCookie:p.hash(a)})),await this.keyStorage.updateManyData(t,r)}async deleteSessionData(a,r){const t=O.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:`Updating session data value${r}`,hashedSessionCookie:p.hash(a)})),await this.keyStorage.deleteData(t,r)}async deleteSession(a){return await this.keyStorage.deleteKey(O.hashSessionId(a))}async createUser(a,r,t,s=!1,i=!1){var l;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call createUser if no user storage provided");if(!this.authenticators[a.factor1])throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator cannot create users");this.authenticators[a.factor1].skipEmailVerificationOnSignup()==!0&&(s=!0);let o=i?void 0:await this.authenticators[a.factor1].createPersistentSecrets(a.username,r,t);const n=i?await this.userStorage.createUser(a):await this.userStorage.createUser(a,o);return!s&&this.enableEmailVerification&&this.tokenEmailer&&await((l=this.tokenEmailer)==null?void 0:l.sendEmailVerificationToken(n.id,void 0)),n}async deleteUserByUsername(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call deleteUser if no user storage provided");this.userStorage.deleteUserByUsername(a)}async initiateTwoFactorSignup(a,r,t,s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call initiateTwoFactorSignup if no user storage provided");if(!this.authenticators[a.factor1])throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator cannot create users");if(!this.authenticators[a.factor2])throw new e.CrossauthError(e.ErrorCode.Configuration,"Two factor authentication not enabled for user");const o=await this.authenticators[a.factor2].prepareConfiguration(a),n=o==null?{}:o.userData,l=o==null?{}:o.sessionData,c=await this.authenticators[a.factor1].createPersistentSecrets(a.username,r,s);return a.state="awaitingtwofactorsetup",await this.keyStorage.updateData(O.hashSessionId(t),"2fa",l),{userid:(await this.userStorage.createUser(a,c)).id,userData:n}}async initiateTwoFactorSetup(a,r,t){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call initiateTwOFactorSetup if no user storage provided");if(r&&r!="none"){if(!this.authenticators[r])throw new e.CrossauthError(e.ErrorCode.Configuration,"Two factor authentication not enabled for user");const i=await this.authenticators[r].prepareConfiguration(a),o=i==null?{}:i.userData,n=i==null?{}:i.sessionData;return n&&(n.userData=o),await this.keyStorage.updateData(O.hashSessionId(t),"2fa",n),o}return await this.userStorage.updateUser({id:a.id,factor2:r??""}),await this.keyStorage.updateData(O.hashSessionId(t),"2fa",void 0),{}}async repeatTwoFactorSignup(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call repeatTwoFactorSignup if no user storage provided");const r=(await this.dataForSessionId(a))["2fa"],t=r.username,s=r.factor2,i=O.hashSessionId(a),o=await this.keyStorage.getKey(i),l=await this.authenticators[s].reprepareConfiguration(t,o),c=l==null?{}:l.userData,g=l==null?{}:l.secrets,f=l==null?{}:l.newSessionData;f&&await this.keyStorage.updateData(i,"2fa",f);const{user:w}=await this.userStorage.getUserByUsername(t,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});return{userid:w.id,userData:c,secrets:g}}async completeTwoFactorSetup(a,r){var E;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorSetup if no user storage provided");let t=!1,{user:s,key:i}=await this.session.getUserForSessionId(r,{skipActiveCheck:!0});if(s&&s.state!=e.UserState.active&&s.state!=e.UserState.factor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);if(!i)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let o=K.decodeData(i.data)["2fa"];if(!(o!=null&&o.factor2)||!(o!=null&&o.username))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");let n=o.username;const l=this.authenticators[o.factor2];if(!l)throw new e.CrossauthError(e.ErrorCode.Configuration,"Unrecognised second factor authentication");const c={},g=l.secretNames();for(let m in o)g.includes(m)&&(c[m]=o[m]);await l.authenticateUser(void 0,o,a),s||(t=!0,s=(await this.userStorage.getUserByUsername(n,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0})).user);const f=l.skipEmailVerificationOnSignup()==!0;if(!s)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Couldn't fetch user");const w={id:s.id,state:!f&&this.enableEmailVerification?"awaitingemailverification":"active",factor2:o.factor2};return l.secretNames().length>0?await this.userStorage.updateUser(w,c):await this.userStorage.updateUser(w),!f&&t&&this.enableEmailVerification&&this.tokenEmailer&&await((E=this.tokenEmailer)==null?void 0:E.sendEmailVerificationToken(s.id,void 0)),await this.keyStorage.updateData(O.hashSessionId(i.value),"2fa",void 0),{...s,...w}}async initiateTwoFactorLogin(a){const t=await this.authenticators[a.factor2].createOneTimeSecrets(a),{sessionCookie:s}=await this.createAnonymousSession({data:JSON.stringify({"2fa":{username:a.username,twoFactorInitiated:!0,factor2:a.factor2,...t}})}),i=this.csrfTokens.createCsrfToken(),o=this.csrfTokens.makeCsrfCookie(i),n=this.csrfTokens.makeCsrfFormOrHeaderToken(i);return{sessionCookie:s,csrfCookie:o,csrfFormOrHeaderValue:n}}async initiateTwoFactorPageVisit(a,r,t,s,i){const n=await this.authenticators[a.factor2].createOneTimeSecrets(a);let l,c,g;const f=O.hashSessionId(r);e.CrossauthLogger.logger.debug("initiateTwoFactorPageVisit "+a.username+" "+r+" "+f);let w={username:a.username,factor2:a.factor2,secrets:n,body:t,url:s};return i&&(w["content-type"]=i),await this.keyStorage.updateData(f,"pre2fa",w),{sessionCookie:l,csrfCookie:c,csrfFormOrHeaderValue:g}}async completeTwoFactorPageVisit(a,r){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorPageVisit if no user storage provided");let{key:t}=await this.session.getUserForSessionId(r);if(!t)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let s=K.decodeData(t.data);if(!("pre2fa"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");const{secrets:i}=await this.userStorage.getUserByUsername(s.pre2fa.username),o=this.authenticators[s.pre2fa.factor2];if(!o)throw new e.CrossauthError(e.ErrorCode.Configuration,"Unrecognised second factor authentication");const n={},l=o.secretNames();for(let c in i)l.includes(c)&&c in i&&(n[c]=i[c]);await o.authenticateUser(void 0,{...n,...s.pre2fa.secrets},a),await this.keyStorage.updateData(O.hashSessionId(t.value),"pre2fa",void 0)}async cancelTwoFactorPageVisit(a){let{key:r}=await this.session.getUserForSessionId(a);if(!r)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let t=K.decodeData(r.data);if(!("pre2fa"in t))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");return await this.keyStorage.updateData(O.hashSessionId(r.value),"pre2fa",void 0),t.pre2fa}async completeTwoFactorLogin(a,r,t={},s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorLogin if no user storage provided");let{key:i}=await this.session.getUserForSessionId(r);if(!i||!i.data||i.data=="")throw new e.CrossauthError(e.ErrorCode.Unauthorized);let o=K.decodeData(i.data)["2fa"],n=o.username,l=o.factor2;const{user:c,secrets:g}=await this.userStorage.getUserByUsername(n),f=this.authenticators[l];if(!f)throw new e.CrossauthError(e.ErrorCode.Configuration,"Second factor "+l+" not enabled");await f.authenticateUser(c,{...g,...o},a);const w=await this.session.createSessionKey(c.id,t);await this.keyStorage.deleteKey(O.hashSessionId(i.value));const E=this.session.makeCookie(w,s),m=this.csrfTokens.createCsrfToken(),y=this.csrfTokens.makeCsrfCookie(m),S=this.csrfTokens.makeCsrfFormOrHeaderToken(m);try{this.emailTokenStorage.deleteAllForUser(c.id,e.KeyPrefix.passwordResetToken)}catch(T){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:n})),e.CrossauthLogger.logger.debug(e.j({err:T}))}return{sessionCookie:E,csrfCookie:y,csrfFormOrHeaderValue:S,user:c}}async requestPasswordReset(a){var t;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call requestPasswordReset if no user storage provided");const{user:r}=await this.userStorage.getUserByEmail(a,{skipActiveCheck:!0});if(r.state!=e.UserState.active&&r.state!=e.UserState.passwordResetNeeded&&r.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);await((t=this.tokenEmailer)==null?void 0:t.sendPasswordResetToken(r.id))}async applyEmailVerificationToken(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call applyEmailVerificationToken if no user storage provided");if(e.CrossauthLogger.logger.debug(e.j({msg:"applyEmailVerificationToken"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Email verification not enabled");try{let{userid:r,newEmail:t}=await this.tokenEmailer.verifyEmailVerificationToken(a),{user:s}=await this.userStorage.getUserById(r,{skipEmailVerifiedCheck:!0}),i;"email"in s&&s.email!=null?i=s.email:i=s.username;let o={id:s.id};return(s.state="awaitingemailverification")&&(o.state="active"),t!=""?o.email=t:i=void 0,await this.userStorage.updateUser(o),await this.tokenEmailer.deleteEmailVerificationToken(a),{...s,...o,oldEmail:i}}finally{}}async userForPasswordResetToken(a){if(e.CrossauthLogger.logger.debug(e.j({msg:"userForPasswordResetToken"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Password reset not enabled");return await this.tokenEmailer.verifyPasswordResetToken(a)}async changeSecrets(a,r,t,s,i){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call changeSecrets if no user storage provided");let{user:o,secrets:n}=await this.userStorage.getUserByUsername(a);const l=r==1?o.factor1:o.factor2;i!=null&&await this.authenticators[l].authenticateUser(o,n,i);const c=await this.authenticators[o.factor1].createPersistentSecrets(o.username,t,s);await this.userStorage.updateUser({id:o.id},c);try{this.emailTokenStorage.deleteAllForUser(o.id,e.KeyPrefix.passwordResetToken)}catch(g){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:a})),e.CrossauthLogger.logger.debug(e.j({err:g}))}return o}async updateUser(a,r,t=!1,s=!1){var f,w;let i;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call updateUser if no user storage provided");if(!("id"in a)||a.id==null)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Please specify a user id");if(!("username"in a)||a.username==null)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Please specify a userername");let{email:o,username:n,password:l,...c}=r;c.userid=a.userid;let g=!1;if(o)i=o,P.validateEmail(i),g=!0;else if(n){i=n;try{P.validateEmail(a.username),g=!0}catch{}g&&P.validateEmail(i)}return!t&&this.enableEmailVerification&&g?await((f=this.tokenEmailer)==null?void 0:f.sendEmailVerificationToken(a.id,i)):(o&&(c.email=o),n&&(c.username=n)),(r.state==e.UserState.passwordResetNeeded||r.state==e.UserState.passwordAndFactor2ResetNeeded)&&await((w=this.tokenEmailer)==null?void 0:w.sendPasswordResetToken(a.id,{},s)),await this.userStorage.updateUser(c),{emailVerificationTokenSent:!t&&this.enableEmailVerification&&g,passwordResetTokenSent:r.state==e.UserState.passwordResetNeeded||r.state==e.UserState.passwordAndFactor2ResetNeeded}}async resetSecret(a,r,t,s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call resetSecret if no user storage provided");if(e.CrossauthLogger.logger.debug(e.j({msg:"resetSecret"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Password reset not enabled");const i=await this.userForPasswordResetToken(a),o=r==1?i.factor1:i.factor2;if(!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration);let n=i.state==e.UserState.passwordAndFactor2ResetNeeded?e.UserState.factor2ResetNeeded:e.UserState.active;await this.userStorage.updateUser({id:i.id,state:n},await this.authenticators[o].createPersistentSecrets(i.username,t,s));try{await this.emailTokenStorage.deleteAllForUser(i.id,e.KeyPrefix.passwordResetToken)}catch(l){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:i.username})),e.CrossauthLogger.logger.debug(e.j({err:l}))}return{...i,state:n}}}class re{constructor(a,r={}){d(this,"apiKeyStorage");d(this,"keyLength",16);d(this,"secret","");d(this,"prefix",e.KeyPrefix.apiKey);d(this,"authScheme","ApiKey");this.apiKeyStorage=a,h("secret",u.String,this,r,"SECRET",!0),h("keyLength",u.String,this,r,"APIKEY_LENGTH"),h("prefix",u.String,this,r,"APIKEY_PREFIX"),h("authScheme",u.String,this,r,"APIKEY_AUTHSCHEME")}async createKey(a,r,t,s,i){const o=p.randomValue(this.keyLength),n=new Date,l=s?new Date(n.getTime()+s*1e3):void 0,c=re.hashApiKeyValue(o),g={name:a,value:o,userid:r,data:K.encodeData(t),expires:l,created:n,...i};await this.apiKeyStorage.saveKey(r,this.prefix+c,n,l,g.data,{name:a,...i});const f=this.signApiKeyValue(o);return{key:g,token:f}}static hashApiKeyValue(a){return p.hash(a)}static hashSignedApiKeyValue(a){return p.hash(a.split(".")[0])}unsignApiKeyValue(a){return p.unsign(a,this.secret).v}signApiKeyValue(a){return p.sign({v:a},this.secret)}async getKey(a){if(this.authScheme!=""&&a.startsWith(this.authScheme+" ")){const i=new RegExp(`^${this.authScheme} `);a=a.replace(i,"")}const r=this.unsignApiKeyValue(a),t=re.hashApiKeyValue(r),s=await this.apiKeyStorage.getKey(this.prefix+t);if(!("name"in s))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Not a valid API key");return{...s,name:s.name}}async validateToken(a){const r=a.split(" ");if(r.length!=2||r[0]!=this.authScheme)throw new e.CrossauthError(e.ErrorCode.InvalidKey,`Not a ${this.authScheme} token`);return await this.getKey(r[1])}}const ar=16,or=32;class M{constructor(a={}){d(this,"oauthPbkdf2Digest","sha256");d(this,"oauthPbkdf2Iterations",4e4);d(this,"oauthPbkdf2KeyLength",32);d(this,"clientStorage");if(!a.clientStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must specify clientStorage when adding a client manager");this.clientStorage=a.clientStorage,h("oauthPbkdf2Digest",u.String,this,a,"OAUTH_PBKDF2_DIGEST"),h("oauthPbkdf2KeyLength",u.String,this,a,"OAUTH_PBKDF2_KEYLENGTH"),h("requireRedirectUriRegistration",u.Boolean,this,a,"OAUTH_REQUIRE_REDIRECT_URI_REGISTRATION")}async createClient(a,r,t,s=!0,i){const o=M.randomClientId();let n,l;s&&(l=M.randomClientSecret(),n=await p.passwordHash(l,{encode:!0,iterations:this.oauthPbkdf2Iterations,keyLen:this.oauthPbkdf2KeyLength,digest:this.oauthPbkdf2Digest})),r.forEach(f=>{M.validateUri(f)}),t||(t=e.OAuthFlows.allFlows());const c={client_id:o,client_secret:n,client_name:a,redirect_uri:r,confidential:s,valid_flow:t,userid:i};let g;for(let f=0;f<5;++f)try{g=await this.clientStorage.createClient(c);break}catch(w){if(f==4){if(e.CrossauthError.asCrossauthError(w).code!=e.ErrorCode.ClientExists)throw w}else c.client_id=M.randomClientId()}if(!g)throw new e.CrossauthError(e.ErrorCode.ClientExists);return g.client_secret&&l&&(g.client_secret=l),g}async updateClient(a,r,t=!1){const s=await this.clientStorage.getClientById(a);let i=!1,o;r.confidential===!0&&!s.confidential||r.confidential===!0&&t?(o=M.randomClientSecret(),r.client_secret=await p.passwordHash(o,{encode:!0,iterations:this.oauthPbkdf2Iterations,keyLen:this.oauthPbkdf2KeyLength,digest:this.oauthPbkdf2Digest}),i=!0):r.confidential===!1&&(r.client_secret=null),r.redirect_uri&&r.redirect_uri.forEach(l=>{M.validateUri(l)}),r.client_id=a,await this.clientStorage.updateClient(r);const n=await this.clientStorage.getClientById(a);return o&&(n.client_secret=o),{client:n,newSecret:i}}static randomClientId(){return p.randomValue(ar)}static randomClientSecret(){return p.randomValue(or)}static validateUri(a){let r=!1;try{r=new URL(a).hash.length==0}catch(t){try{r=new URL(a).hash.length==0}catch{e.CrossauthLogger.logger.debug(e.j({err:t}))}}if(!r)throw e.CrossauthError.fromOAuthError("invalid_request",`Invalid redirect Uri ${a}`)}}function nr(C){switch(C){case"HS256":case"HS384":case"HS512":case"RS256":case"RS384":case"RS512":case"ES256":case"ES384":case"ES512":case"PS256":case"PS384":case"PS512":case"none":return C}throw new e.CrossauthError(e.ErrorCode.Configuration,"Invalid JWT signing algorithm "+C)}class lr{constructor(a,r,t,s={}){d(this,"clientStorage");d(this,"keyStorage");d(this,"userStorage");d(this,"authenticators",{});d(this,"authStorage");d(this,"clientManager");d(this,"oauthIssuer","");d(this,"audience",null);d(this,"requireRedirectUriRegistration",!0);d(this,"requireClientSecretOrChallenge",!0);d(this,"jwtAlgorithm","RS256");d(this,"jwtAlgorithmChecked","RS256");d(this,"codeLength",32);d(this,"jwtKeyType","");d(this,"jwtSecretKey","");d(this,"jwtPublicKey","");d(this,"jwtPrivateKey","");d(this,"jwtSecretKeyFile","");d(this,"jwtPublicKeyFile","");d(this,"jwtPrivateKeyFile","");d(this,"jwtKid","1");d(this,"secretOrPrivateKey","");d(this,"secretOrPublicKey","");d(this,"persistAccessToken",!1);d(this,"issueRefreshToken",!1);d(this,"opaqueAccessToken",!1);d(this,"accessTokenExpiry",60*60);d(this,"refreshTokenExpiry",60*60);d(this,"rollingRefreshToken",!0);d(this,"authorizationCodeExpiry",60*5);d(this,"mfaTokenExpiry",60*5);d(this,"clockTolerance",10);d(this,"emptyScopeIsValid",!0);d(this,"validateScopes",!1);d(this,"validScopes",[]);d(this,"idTokenClaims",{});d(this,"accessTokenClaims",{});d(this,"userCodeExpiry",60*5);d(this,"userCodeThrottle",1500);d(this,"deviceCodePollInterval",5);d(this,"userCodeLength",8);d(this,"deviceCodeLength",16);d(this,"userCodeDashEvery",4);d(this,"deviceCodeVerificationUri","");d(this,"authServerBaseUrl","");d(this,"validFlows",["all"]);d(this,"allowedFactor2",[]);this.clientStorage=a,this.keyStorage=r,this.userStorage=s.userStorage,this.authStorage=s.authStorage,t&&(this.authenticators=t),this.clientManager=new M({clientStorage:a,...s}),h("authServerBaseUrl",u.String,this,s,"AUTH_SERVER_BASE_URL",!0),h("oauthIssuer",u.String,this,s,"OAUTH_ISSUER"),this.oauthIssuer||(this.oauthIssuer=this.authServerBaseUrl),h("audience",u.String,this,s,"OAUTH_AUDIENCE"),h("oauthPbkdf2Iterations",u.String,this,s,"OAUTH_PBKDF2_ITERATIONS"),h("requireClientSecretOrChallenge",u.Boolean,this,s,"OAUTH_REQUIRE_CLIENT_SECRET_OR_CHALLENGE"),h("jwtAlgorithm",u.String,this,s,"JWT_ALGORITHM"),h("codeLength",u.Number,this,s,"OAUTH_CODE_LENGTH"),h("jwtKeyType",u.String,this,s,"JWT_KEY_TYPE"),h("jwtSecretKeyFile",u.String,this,s,"JWT_SECRET_KEY_FILE"),h("jwtPublicKeyFile",u.String,this,s,"JWT_PUBLIC_KEY_FILE"),h("jwtPrivateKeyFile",u.String,this,s,"JWT_PRIVATE_KEY_FILE"),h("jwtSecretKey",u.String,this,s,"JWT_SECRET_KEY"),h("jwtPublicKey",u.String,this,s,"JWT_PUBLIC_KEY"),h("jwtPrivateKey",u.String,this,s,"JWT_PRIVATE_KEY"),h("jwtKid",u.String,this,s,"JWT_KID"),h("persistAccessToken",u.String,this,s,"OAUTH_PERSIST_ACCESS_TOKEN"),h("issueRefreshToken",u.String,this,s,"OAUTH_ISSUE_REFRESH_TOKEN"),h("opaqueAccessToken",u.String,this,s,"OAUTH_OPAQUE_ACCESS_TOKEN"),h("accessTokenExpiry",u.Number,this,s,"OAUTH_ACCESS_TOKEN_EXPIRY"),h("refreshTokenExpiry",u.Number,this,s,"OAUTH_REFRESH_TOKEN_EXPIRY"),h("rollingRefreshToken",u.Boolean,this,s,"OAUTH_ROLLING_REFRESH_TOKEN"),h("authorizationCodeExpiry",u.Number,this,s,"OAUTH_AUTHORIZATION_CODE_EXPIRY"),h("mfaTokenExpiry",u.Number,this,s,"OAUTH_MFA_TOKEN_EXPIRY"),h("clockTolerance",u.Number,this,s,"OAUTH_CLOCK_TOLERANCE"),h("validateScopes",u.Boolean,this,s,"OAUTH_VALIDATE_SCOPES"),h("emptyScopeIsValid",u.Boolean,this,s,"OAUTH_EMPTY_SCOPE_VALID"),h("validScopes",u.JsonArray,this,s,"OAUTH_VALID_SCOPES"),h("validFlows",u.JsonArray,this,s,"OAUTH_validFlows"),h("idTokenClaims",u.Json,this,s,"OAUTH_ID_TOKEN_CLAIMS"),h("accessTokenClaims",u.Json,this,s,"OAUTH_ACCESS_TOKEN_CLAIMS"),h("allowedFactor2",u.JsonArray,this,s,"ALLOWED_FACTOR2"),h("userCodeExpiry",u.Number,this,s,"DEVICECODE_USERCODE_EXPIRY"),h("userCodeThrottle",u.Number,this,s,"DEVICECODE_USERCODE_THROTTLE"),h("deviceCodePollInterval",u.Number,this,s,"DEVICECODE_POLL_INTERVAL"),h("deviceCodeLength",u.Number,this,s,"DEVICECODE_LENGTH"),h("userCodeLength",u.Number,this,s,"DEVICECODE_USERCODE_LENGTH");let i={};if(h("userCodeDashEvery",u.String,i,s,"DEVICECODE_USERCODE_DASH_EVERY"),i.userCodeDashEvery)if(i.userCodeDashEvery==""||i.userCodeDashEvery.toLowerCase()=="null")this.userCodeDashEvery=null;else try{this.userCodeDashEvery=Number(i.userCodeDashEvery)}catch{throw new e.CrossauthError(e.ErrorCode.Configuration,"userCodeDashEvery must be a number or null")}if(h("deviceCodeVerificationUri",u.String,this,s,"DEVICECODE_VERIFICATION_URI"),this.validFlows.length==1&&this.validFlows[0]==e.OAuthFlows.All&&(this.validFlows=e.OAuthFlows.allFlows()),this.jwtAlgorithmChecked=nr(this.jwtAlgorithm),this.jwtSecretKey||this.jwtSecretKeyFile){if(this.jwtPublicKey||this.jwtPublicKeyFile||this.jwtPrivateKey||this.jwtPrivateKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric and public/private JWT keys");if(this.jwtSecretKey&&this.jwtSecretKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric key and file");this.jwtSecretKeyFile&&(this.jwtSecretKey=G.readFileSync(this.jwtSecretKeyFile,"utf8"))}else if((this.jwtPrivateKey||this.jwtPrivateKeyFile)&&(this.jwtPublicKey||this.jwtPublicKeyFile)){if(this.jwtPrivateKeyFile&&this.jwtPrivateKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both private key and private key file");if(this.jwtPrivateKeyFile&&(this.jwtPrivateKey=G.readFileSync(this.jwtPrivateKeyFile,"utf8")),this.jwtPublicKeyFile&&this.jwtPublicKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both public key and public key file");this.jwtPublicKeyFile&&(this.jwtPublicKey=G.readFileSync(this.jwtPublicKeyFile,"utf8"))}else throw new e.CrossauthError(e.ErrorCode.Configuration,"Must specify either a JWT secret key or a public and private key pair");if(this.jwtSecretKey?this.secretOrPrivateKey=this.secretOrPublicKey=this.jwtSecretKey:(this.secretOrPrivateKey=this.jwtPrivateKey,this.secretOrPublicKey=this.jwtPublicKey),(this.jwtPublicKey||this.jwtPrivateKey)&&!this.jwtKeyType)throw new e.CrossauthError(e.ErrorCode.Configuration,"If setting jwtPublicKey or jwtPrivate key, must also set jwtKeyType");if(this.opaqueAccessToken&&(this.persistAccessToken=!0),(this.validFlows.includes(e.OAuthFlows.Password)||this.validFlows.includes(e.OAuthFlows.PasswordMfa))&&(!this.userStorage||Object.keys(this.authenticators).length==0))throw new e.CrossauthError(e.ErrorCode.Configuration,"If password flow or password MFA flow is enabled, userStorage and authenticators must be provided");if((this.issueRefreshToken||this.persistAccessToken)&&!this.keyStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must have key storage if persisting access tokens or issuing refresh tokens")}async authorizeGetEndpoint({responseType:a,client_id:r,redirect_uri:t,scope:s,state:i,codeChallenge:o,codeChallengeMethod:n,user:l}){if(!this.responseTypesSupported().includes(a))return{error:"unsupported_response_type",error_description:"Unsupported response type "+a};let g;try{g=await this.clientStorage.getClientById(r)}catch(y){return e.CrossauthLogger.logger.debug(e.j({err:y})),{error:"unauthorized_client",error_description:"Client is not authorized"}}const{scopes:f,error:w,error_description:E}=await this.validateAndPersistScope(r,s,l);if(w)return{error:w,error_description:E};const m=this.inferFlowFromGet(a,f||[],o);if(!m||!this.validFlows.includes(m))return{error:"access_denied",error_description:"Unsupported flow type "+m};if(!g.valid_flow.includes(m))return{error:"unauthorized_client",error_description:"Client does not support "+m};try{this.validateState(i)}catch{return{error:"invalid_request",error_description:"Invalid state"}}return a=="code"?await this.getAuthorizationCode(g,t,f,i,o,n,l):{error:"unsupported_response_type",error_description:`Invalid response_type ${a}`}}async hasAllScopes(a,r,t){if(!this.authStorage)return!1;const s=await this.authStorage.getAuthorizations(a,r==null?void 0:r.id);return t.filter(o=>s.includes(o)).length==t.length}async validateAndPersistScope(a,r,t){let s,i;if(!r&&!this.emptyScopeIsValid)return{error:"invalid_scope",error_description:"Must provide at least one scope"};if(r){const{error:o,errorDescription:n,scopes:l}=this.validateScope(r);if(s=l,i=l,o)return{error:o,error_description:n??"Unknown error"}}else i=[null];if(this.authStorage)try{const o=i??[],n=await this.authStorage.getAuthorizations(a,t==null?void 0:t.id),l=[...new Set([...n,...o])];e.CrossauthLogger.logger.debug(e.j({msg:"Updating authorizations for "+a+" to "+l})),await this.authStorage.updateAuthorizations(a,(t==null?void 0:t.id)??null,l)}catch(o){return e.CrossauthLogger.logger.debug(e.j({err:o})),{error:"server_error",error_description:"Couldn't save scope"}}else if(r)return{error:"server_error",error_description:"Must provide auth storage in order to use scopes"};return{scopes:s}}async authenticateClient(a,r,t){let s=!1;switch(a){case e.OAuthFlows.AuthorizationCode:case e.OAuthFlows.AuthorizationCodeWithPKCE:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.ClientCredentials:s=!0;break;case e.OAuthFlows.Password:case e.OAuthFlows.PasswordMfa:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.RefreshToken:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.DeviceCode:s=r.confidential==!0||r.client_secret!=null||t!=null;break}return s&&(r.client_secret==null||t==null)?{error:"access_denied",error_description:"Client secret is required for this client"}:s&&(!t||!r.client_secret)?{error:"access_denied",error_description:"Client is confidential but either secret not passed or is missing in database"}:s&&!await p.passwordsEqual(t??"",r.client_secret??"")?{error:"access_denied",error_description:"Incorrect client secret"}:{}}async getClientById(a){let r;try{return r=await this.clientStorage.getClientById(a),{client:r}}catch{return{error:"access_denied",error_description:"client id does not exist"}}}async tokenEndpoint({grantType:a,client_id:r,scope:t,code:s,client_secret:i,codeVerifier:o,refreshToken:n,username:l,password:c,mfaToken:g,oobCode:f,bindingCode:w,otp:E,deviceCode:m}){var U,j,R;const y=this.inferFlowFromPost(a,o);if(!y)return{error:"server_error",error_description:"Unable to determine OAuth flow type"};const S=await this.getClientById(r);if(!S.client)return S;const T=S.client,N=await this.authenticateClient(y,T,i);if(N.error)return N;if(y==e.OAuthFlows.Password&&!this.validFlows.includes(y)&&!this.validFlows.includes(e.OAuthFlows.PasswordMfa))return{error:"access_denied",error_description:"Unsupported flow type "+y};if(!y||!this.validFlows.includes(y))return{error:"access_denied",error_description:"Unsupported flow type "+y};if(T&&!T.valid_flow.includes(y))return{error:"unauthorized_client",error_description:"Client does not support "+y};let x=!1;this.issueRefreshToken&&y!=e.OAuthFlows.RefreshToken&&(x=!0),this.issueRefreshToken&&y==e.OAuthFlows.RefreshToken&&this.rollingRefreshToken&&(x=!0);let k;if(a=="authorization_code")return this.requireClientSecretOrChallenge&&T&&T.client_secret&&!i&&!o?{error:"access_denied",error_description:"Must provide either a client secret or use PKCE"}:T&&T.client_secret&&!i?{error:"access_denied",error_description:"No client secret or code verifier provided for authorization coode flow"}:s?await this.makeAccessToken({client:T,code:s,client_secret:i,codeVerifier:o,issueRefreshToken:x}):{error:"access_denied",error_description:"No authorization code provided for authorization code flow"};if(a=="refresh_token"){const A=await this.getRefreshTokenData(n);if(!n||!A||!this.userStorage)return{error:"access_denied",error_description:"Refresh token is invalid"};let _;if(A.username)try{const{user:b}=await((U=this.userStorage)==null?void 0:U.getUserByUsername(A.username));_=b}catch(b){return e.CrossauthLogger.logger.error(e.j({err:b,msg:"Couldn't get user for refresh token. Doesn't exist?",username:A.username})),{error:"access_denied",error_description:"Refresh token is invalid"}}try{const b=e.KeyPrefix.refreshToken+p.hash(n);await this.keyStorage.deleteKey(b)}catch(b){const v=e.CrossauthError.asCrossauthError(b);e.CrossauthLogger.logger.debug(e.j({err:b})),e.CrossauthLogger.logger.warn(e.j({msg:"Cannot delete refresh token",cerr:v}))}return await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,issueRefreshToken:x,scopes:A.scope,user:_})}else if(a=="client_credentials"){const{scopes:A,error:_,error_description:b}=await this.validateAndPersistScope(r,t,void 0);return _?{error:_,error_description:b}:await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:x})}else if(a=="password"){if(!l||!c)return{error:"access_denied",error_description:"Username and/or password not provided for password flow"};try{if(!this.userStorage)return{error:"server_error",error_description:"Password authentication not configured"};const{user:v,secrets:z}=await this.userStorage.getUserByUsername(l),L=this.authenticators[v.factor1];if(!L||!L.secretNames().includes("password"))return{error:"access_denied",error_description:"Password flow used but factor 1 authenticator does not accept passwords"};await L.authenticateUser(v,z,{password:c}),k=v}catch(v){return e.CrossauthLogger.logger.debug(e.j({err:v})),{error:"access_denied",error_description:"Username and/or password do not match"}}const{scopes:A,error:_,error_description:b}=await this.validateAndPersistScope(r,t,k);return _?{error:_,error_description:b}:k.factor2?this.allowedFactor2.length>0&&(k.state==e.UserState.factor2ResetNeeded||!this.allowedFactor2.includes(k.factor2?k.factor2:"none"))?{error:"access_denied",error_description:"2FA method not allowed or needs to be reconfigured"}:await this.createMfaRequest(k):await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:x,user:k})}else if(a=="http://auth0.com/oauth/grant-type/mfa-otp"){const{scopes:A,error:_,error_description:b}=await this.validateAndPersistScope(r,t,void 0);if(_)return{error:_,error_description:b};if(!E)return{error:"access_denied",error_description:"OTP not provided"};if(!g)return{error:"access_denied",error_description:"MFA token not provided"};const v=await this.validateMfaToken(g),z=e.KeyPrefix.mfaToken+p.hash(g);if(!v.user||!v.key)return{error:"access_denied",error_description:"Invalid MFA token"};const L=this.authenticators[v.user.factor2];if(!L||!this.userStorage)return{error:"access_denied",error_description:"MFA type is not supported for OAuth"};try{const{secrets:D}=await this.userStorage.getUserById(v.user.id);await L.authenticateUser(v.user,D,{otp:E})}catch(D){return e.CrossauthLogger.logger.debug(e.j({err:D})),{error:"access_denied",error_description:"Invalid OTP"}}try{await this.keyStorage.deleteKey(z)}catch(D){e.CrossauthLogger.logger.debug(e.j({err:D})),e.CrossauthLogger.logger.warn(e.j({cerr:D,msg:"Couldn't delete mfa token",hashedMfaToken:v.key.value}))}return await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:x,user:v.user})}else if(a=="http://auth0.com/oauth/grant-type/mfa-oob"){const{scopes:A,error:_,error_description:b}=await this.validateAndPersistScope(r,t,void 0);if(_)return{error:_,error_description:b};if(!f||!w)return{error:"access_denied",error_description:"OOB code or binding code not provided"};if(!g)return{error:"access_denied",error_description:"MFA token not provided"};const v=await this.validateMfaToken(g);if(!v.user||!v.key)return{error:"access_denied",error_description:"Invalid MFA token"};const z=this.authenticators[v.user.factor2];if(!z||!this.userStorage)return{error:"access_denied",error_description:"MFA type is not supported for OAuth"};try{const{secrets:L}=await this.userStorage.getUserById(v.user.id),D=K.decodeData(v.key.data).omfa;if(!D||!D.otp||!D.oobCode)return{error:"server_error",error_description:"Cannot retrieve email OTP"};if(D.oobCode!=f)return{error:"access_denied",error_description:"Invalid OOB code"};await z.authenticateUser(v.user,{...L,otp:D.otp,expiry:(j=v.key.expires)==null?void 0:j.getTime()},{otp:w})}catch(L){return e.CrossauthLogger.logger.debug(e.j({err:L})),{error:"access_denied",error_description:"Invalid OTP"}}try{await this.keyStorage.deleteKey(v.key.value)}catch(L){e.CrossauthLogger.logger.debug(e.j({err:L})),e.CrossauthLogger.logger.warn(e.j({cerr:L,msg:"Couldn't delete mfa token",hashedMfaToken:v.key.value}))}return await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:x,user:v.user})}else if(a=="urn:ietf:params:oauth:grant-type:device_code"){if(!m)return{error:"invalid_request",error_description:"No device code given"};let A;try{A=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+m)}catch(_){const b=e.CrossauthError.asCrossauthError(_);return e.CrossauthLogger.logger.debug(e.j({err:b})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't get device code",cerr:b})),{error:"accerss_denied",error_description:"Invalid device code"}}try{const _=JSON.parse(A.data??"{}"),b=new Date().getTime();if(A.expires&&b>A.expires.getTime())return await this.deleteDeviceCode(m),{error:"expired_token",error_description:"Code has expired"};if(_.ok!=!0)return{error:"authorization_pending",error_description:"Waiting for user code to be entered"};{let v=_.scope?_.scope.split(" "):void 0,z=_.userid?await((R=this.userStorage)==null?void 0:R.getUserById(_.userid)):void 0;return await this.deleteDeviceCode(m),await this.makeAccessToken({client:T,client_secret:i,codeVerifier:o,scopes:v,issueRefreshToken:x,user:z==null?void 0:z.user})}}catch(_){const b=e.CrossauthError.asCrossauthError(_);return e.CrossauthLogger.logger.debug(e.j({err:b})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't get device code",cerr:b})),await this.deleteDeviceCode(m),{error:"accerss_denied",error_description:"Invalid device code"}}}else return{error:"invalid_request",error_description:`Invalid grant_type ${a}`}}async deleteDeviceCode(a){try{await this.keyStorage.deleteKey(e.KeyPrefix.deviceCode+a)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't delete device code",cerr:t}))}}async deleteUserCode(a){try{await this.keyStorage.deleteKey(e.KeyPrefix.userCode+a)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't delete user code",cerr:t}))}}async deviceAuthorizationEndpoint({client_id:a,scope:r,client_secret:t}){var m;if(this.deviceCodeVerificationUri=="")return{error:"invalid_request",error_description:"Must provide deviceCodeVerificationUri if using the device code flow"};try{new URL(this.deviceCodeVerificationUri)}catch{return{error:"invalid_request",error_description:"Invalid deviceCodeVerificationUri"}}const s=e.OAuthFlows.DeviceCode,i=await this.getClientById(a);if(!i.client)return i;const o=i.client,n=await this.authenticateClient(s,o,t);if(n.error)return n;if(!this.validFlows.includes(s))return{error:"access_denied",error_description:"Unsupported flow type "+s};if(r){const{error:y,errorDescription:S}=this.validateScope(r);if(y)return{error:y,error_description:S}}let l,c=!1;const g=new Date,f=this.userCodeExpiry,w=new Date(g.getTime()+this.userCodeExpiry*1e3+this.clockTolerance*1e3);for(let y=0;y<10&&!c;++y)try{l=p.randomValue(this.deviceCodeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.deviceCode+l,g,w,JSON.stringify({scope:r,client_id:a})),c=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!c||!l)return{error:"server_error",error_description:"Couldn't create device code"};let E;c=!1;for(let y=0;y<10&&!c;++y)try{E=p.randomBase32(this.userCodeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.userCode+E,g,w,JSON.stringify({deviceCode:l})),c=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!c||!E)return await this.deleteDeviceCode(l),{error:"server_error",error_description:"Couldn't create device code"};if(E&&this.userCodeDashEvery){const y=new RegExp(String.raw`(.{1,${this.userCodeDashEvery}})`,"g");E=(m=E.match(y))==null?void 0:m.join("-")}return{device_code:l,user_code:E,verification_uri:this.deviceCodeVerificationUri,verification_uri_complete:this.deviceCodeVerificationUri+"?user_code="+E,expires_in:f,interval:this.deviceCodePollInterval}}async deviceEndpoint({userCode:a,user:r}){var g;a=a.replace(/[ -]*/g,"");let t,s={};try{t=await this.keyStorage.getKey(e.KeyPrefix.userCode+a),s=JSON.parse((t==null?void 0:t.data)??"{}")}catch{return{ok:!1,error:"access_denied",error_description:"Invalid user code"}}if(!s.deviceCode)return e.CrossauthLogger.logger.error(e.j({msg:"No device code for user code",userCodeHash:p.hash(a)})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"No device code for user code"};let i;try{i=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+s.deviceCode)}catch(f){const w=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:w})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid device code for user code",userCodeHash:p.hash(a),deviceCodeHash:p.hash(s.deviceCode),cerr:w})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"Invalid device code user code"}}let o,n;try{if(!i.data)throw new e.CrossauthError(e.ErrorCode.UnknownError);const f=JSON.parse(i.data);if(o=f.scope,n=f.client_id,!n)throw new e.CrossauthError(e.ErrorCode.UnknownError)}catch{return await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"server_error",error_description:"Unexpected or incomplete data in device code key"}}if(new Date().getTime()>((g=s.expires)==null?void 0:g.getTime()))return await this.deleteUserCode(a),{ok:!1,error:"expired_token",error_description:"User code has expired",client_id:n};if(s.ok==!0)return{ok:!1,error:"access_denied",error_description:"User code has already been used",client_id:n};let c=!1;if(e.CrossauthLogger.logger.debug(e.j({msg:"Checking scopes have been authorized",scope:o})),o?c=await this.hasAllScopes(n,r,o.split(" ")):c=await this.hasAllScopes(n,r,[null]),!c){try{r!=null&&r.id&&await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"userid",r.id)}catch(f){const w=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:w})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update user id on user code entry - deleting",cerr:w})),await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:n}}return{ok:!0,scope:o,client_id:n,scopeAuthorizationNeeded:!0}}try{r!=null&&r.id&&await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"userid",r.id),await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"ok",!0)}catch(f){const w=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:w})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update status on user code entry - deleting",cerr:w})),await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:n}}return await this.deleteUserCode(a),{ok:!0,scope:o,client_id:n}}async authorizeDeviceFlowScopes(a){a=a.replace(/[ -]*/g,"");let r,t={};try{r=await this.keyStorage.getKey(e.KeyPrefix.userCode+a),t=JSON.parse((r==null?void 0:r.data)??"{}")}catch{return{ok:!1,error:"access_denied",error_description:"Invalid user code"}}if(!t.deviceCode)return e.CrossauthLogger.logger.error(e.j({msg:"No device code for user code",userCodeHash:p.hash(a)})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"No device code for user code"};let s;try{s=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+t.deviceCode)}catch(n){const l=e.CrossauthError.asCrossauthError(n);return e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid device code for user code",userCodeHash:p.hash(a),deviceCodeHash:p.hash(t.deviceCode),cerr:l})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"Invalid device code user code"}}let i,o;try{if(!s.data)throw new e.CrossauthError(e.ErrorCode.UnknownError);const n=JSON.parse(s.data);if(i=n.scope,o=n.client_id,!o)throw new e.CrossauthError(e.ErrorCode.UnknownError)}catch{return await this.deleteUserCode(a),await this.deleteDeviceCode(t.deviceCode),{ok:!1,error:"server_error",error_description:"Unexpected or incomplete data in device code key"}}try{await this.keyStorage.updateData(e.KeyPrefix.deviceCode+t.deviceCode,"ok",!0)}catch(n){const l=e.CrossauthError.asCrossauthError(n);return e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update status on user code entry - deleting",cerr:l})),await this.deleteUserCode(a),await this.deleteDeviceCode(t.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:o}}return await this.deleteUserCode(a),{ok:!0,scope:i,client_id:o}}async createMfaRequest(a){const r=p.randomValue(this.codeLength),t=e.KeyPrefix.mfaToken+p.hash(r),s=new Date;try{await this.keyStorage.saveKey(a.id,t,s,this.mfaTokenExpiry?new Date(s.getTime()+(this.mfaTokenExpiry+this.clockTolerance)*1e3):void 0,JSON.stringify({omfaaid:a.factor2}))}catch(i){const o=e.CrossauthError.asCrossauthError(i);e.CrossauthLogger.logger.debug(e.j({err:o})),e.CrossauthLogger.logger.error(e.j({cerr:o,msg:"Couldn't save MFA token"}))}return{mfa_token:r,error:"mfa_required",error_description:"Multifactor authentication required"}}async validateMfaToken(a){var s;let r,t;try{const i=e.KeyPrefix.mfaToken+p.hash(a);if(t=await this.keyStorage.getKey(i),!t.userid)return{error:"access_denied",error_description:"Invalid MFA token"};if(!this.userStorage)return{error:"server_error",error_description:"No user storage defined"};const{user:o}=await((s=this.userStorage)==null?void 0:s.getUserById(t.userid));r=o}catch(i){return e.CrossauthLogger.logger.debug(e.j({err:i})),e.CrossauthLogger.logger.error(e.j({cerr:i,msg:"Invalid MFA token"})),{error:"access_denied",error_description:"Invalid MFA token"}}if(!r)return{error:"access_denied",error_description:"Invalid MFA token"};try{if(K.decodeData(t.data).omfaaid!=r.factor2)return{error:"access_denied",error_description:"authenticatorId not valid for user"}}catch{return{error:"server_error",error_description:"Error getting data for MFA token"}}return{user:r,key:t}}async mfaAuthenticatorsEndpoint(a){const r=await this.validateMfaToken(a);if(!r.user)return r;const t=r.user;if(!t.factor2)return{authenticators:[]};const s=this.authenticators[t.factor2];if(!s)return{error:"server_error",error_description:"User has an unsupported MFA authenticator"};let i;if(s.mfaType()=="otp")i={id:t.factor2,authenticator_type:"otp",active:!0};else if(s.mfaType()=="oob")i={id:t.factor2,authenticator_type:"oob",active:!0,name:t.email??t.username,oob_channel:s.mfaChannel()};else return{error:"server_error",error_description:"User has an unsupported MFA authenticator"};return{authenticators:[i]}}async mfaChallengeEndpoint(a,r,t,s,i){const o=e.OAuthFlows.PasswordMfa,n=await this.getClientById(r);if(!n.client)return n;const l=n.client,c=await this.authenticateClient(o,l,t);if(c.error)return c;const g=await this.validateMfaToken(a);if(!g.user||!g.key)return g;if(g.user.factor2!=i)return{error:"access_denied",error_description:"Invalid MFA authenticator"};if(s!="otp"&&s!="oob")return{error:"invalid_request",error_description:"Invalid MFA challenge type"};let f={};s=="oob"&&(f={oobCode:p.randomValue(this.codeLength)});try{const w=this.authenticators[g.user.factor2];if(!w)throw new e.CrossauthError(e.ErrorCode.Configuration,"User's authenticator has not been loaded");const E=await w.createOneTimeSecrets(g.user);await this.keyStorage.updateData(g.key.value,"omfa",{...f,...E})}catch(w){return e.CrossauthLogger.logger.debug(e.j({err:w})),{error:"server_error",error_description:"Unable to initiate OOB authentication"}}return s=="otp"?{challenge_type:"otp"}:{challenge_type:"oob",oob_code:f==null?void 0:f.oobCode,binding_method:"prompt"}}inferFlowFromGet(a,r,t){if(a=="code"&&!r.includes("openid"))return t?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode;if(r.includes("openid")&&a=="code")return t?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode}inferFlowFromPost(a,r){if(a=="authorization_code")return r?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode;if(a=="client_credentials")return e.OAuthFlows.ClientCredentials;if(a=="refresh_token")return e.OAuthFlows.RefreshToken;if(a=="urn:ietf:params:oauth:grant-type:device_code")return e.OAuthFlows.DeviceCode;if(a=="password")return e.OAuthFlows.Password;if(a=="http://auth0.com/oauth/grant-type/mfa-otp")return e.OAuthFlows.PasswordMfa;if(a=="http://auth0.com/oauth/grant-type/mfa-oob")return e.OAuthFlows.PasswordMfa}async getAuthorizationCode(a,r,t,s,i,o,n){if(i&&(o||(o="S256"),o!="S256"&&o!="plain"))return{error:"invalid_request",error_description:"Code challenge method must be S256 or plain"};const l=r;if(M.validateUri(l),this.requireRedirectUriRegistration&&!a.redirect_uri.includes(l))return{error:"invalid_request",error_description:`The redirect uri ${r} is invalid`};const c=new Date,g=this.authorizationCodeExpiry?new Date(c.getTime()+this.authorizationCodeExpiry*1e3+this.clockTolerance*1e3):void 0,f={};t&&(f.scope=t),i&&(f.challengeMethod=o,f.challenge=p.hash(i)),n&&(f.username=n.username,f.id=n.id);const w=JSON.stringify(f);let E=!1,m="";for(let y=0;y<10&&!E;++y)try{m=p.randomValue(this.codeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.authorizationCode+p.hash(m),c,g,w),E=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!E)throw new e.CrossauthError(e.ErrorCode.KeyExists,"Couldn't create a authorization code");return{code:m,state:s}}async makeAccessToken({client:a,code:r,client_secret:t,codeVerifier:s,scopes:i,issueRefreshToken:o=!1,user:n}){var N,x;let l=!0;try{a.client_secret!=null&&(l=await p.passwordsEqual(t??"",a.client_secret??""))}catch(k){return e.CrossauthLogger.logger.error(e.j({err:k})),{error:"server_error",error_description:"Couldn't validate client"}}if(!l)return{error:"access_denied",error_description:"Invalid client secret"};let c={};if(r){let k;try{k=await this.keyStorage.getKey(e.KeyPrefix.authorizationCode+p.hash(r)),c=K.decodeData(k.data)}catch(U){return e.CrossauthLogger.logger.debug(e.j({err:U})),{error:"access_denied",error_description:"Invalid or expired authorization code"}}try{await this.keyStorage.deleteKey(k.value)}catch(U){e.CrossauthLogger.logger.warn(e.j({err:U,msg:"Couldn't delete authorization code from storatge",client_id:a==null?void 0:a.client_id}))}i=c.scope}if(n&&(c.username=n.username),c.challengeMethod&&!c.challenge&&c.challengeMethod!="plain"&&c.challengeMethod!="S256")return{error:"access_denied",error_description:"Invalid code challenge/code challenge method method for authorization code"};if(c.challenge){const k=c.challengeMethod=="plain"?s??"":p.sha256(s??"");if(p.hash(k)!=c.challenge)return{error:"access_denied",error_description:"Code verifier is incorrect"}}const g=new Date,f=Math.ceil(g.getTime()/1e3);let w;if((i&&i.includes("openid")||Object.keys(this.accessTokenClaims).length>0)&&this.userStorage&&c.username)try{const{user:k}=await this.userStorage.getUserByUsername(c.username);n=k}catch(k){return e.CrossauthLogger.logger.error(e.j({err:k})),{error:"server_error",error_description:"Couldn't load user data"}}const E=p.uuid();let m={jti:E,iat:f,iss:this.oauthIssuer,sub:c.username,type:"access"};m=this.addClaims(m,this.accessTokenClaims,i,n),i&&(m.scope=i),this.accessTokenExpiry!=null&&(m.exp=f+this.accessTokenExpiry,w=new Date(g.getTime()+this.accessTokenExpiry*1e3+this.clockTolerance*1e3)),this.audience&&(m.aud=this.audience);const y=await new Promise((k,U)=>{X.sign(m,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:"1"},(j,R)=>{R?k(R):U(j||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})});this.persistAccessToken&&this.keyStorage&&await((N=this.keyStorage)==null?void 0:N.saveKey(void 0,e.KeyPrefix.accessToken+p.hash(E),g,w));let S;if(i&&i.includes("openid")){const k=p.uuid();let U={aud:a.client_id,jti:k,iat:f,iss:this.oauthIssuer,sub:c.username,type:"id"};if(i.includes("email")&&(n!=null&&n.email)&&(U.email=n.email),i.includes("address")&&n&&"address"in n&&(U.address=n.address),i.includes("phone")&&n&&"phone"in n&&(U.phone=n.phone),i.includes("profile")&&n)for(let j of["name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"])U[j]=n[j];U=this.addClaims(U,this.idTokenClaims,i,n),U.scope=i,this.accessTokenExpiry!=null&&(U.exp=f+this.accessTokenExpiry),S=await new Promise((j,R)=>{X.sign(U,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:this.jwtKid},(A,_)=>{_?j(_):R(A||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})})}let T;if(o){const k={username:c.username,client_id:a.client_id};i&&(k.scope=i);let U;const R={jti:p.uuid(),iat:f,iss:this.oauthIssuer,sub:c.username,type:"refresh"};this.refreshTokenExpiry!=null&&(R.exp=f+this.refreshTokenExpiry,U=this.refreshTokenExpiry?new Date(f+this.refreshTokenExpiry*1e3+this.clockTolerance*1e3):void 0),this.oauthIssuer&&(R.aud=this.oauthIssuer),T=await new Promise((A,_)=>{X.sign(R,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:"1"},(b,v)=>{v?A(v):_(b||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})}),T&&await((x=this.keyStorage)==null?void 0:x.saveKey(void 0,e.KeyPrefix.refreshToken+p.hash(T),g,U,JSON.stringify(k)))}return{access_token:y,id_token:S,refresh_token:T,expires_in:this.accessTokenExpiry==null?void 0:this.accessTokenExpiry,token_type:"Bearer",scope:i?i.join(" "):void 0}}addClaims(a,r,t,s){if(s){if(t){for(let i of t)if(i in r)if(r[i]=="all")a={...a,...s};else{let o=r[i];typeof o=="string"&&(o=[o]);for(let n in o)a[n]=s[o[n]]}}if("all"in r){let i=r.all;if(typeof i=="string"&&(i=[i]),i=="all")a={...a,...s};else for(let o in i)a[o]=s[i[o]]}}return a}async validAuthorizationCode(a){try{const r=e.KeyPrefix.authorizationCode+p.hash(a);return await this.keyStorage.getKey(r),!0}catch(r){return e.CrossauthLogger.logger.debug(e.j({err:r})),!1}}async validRefreshToken(a){try{const r=e.KeyPrefix.refreshToken+p.hash(a);return await this.keyStorage.getKey(r),!0}catch(r){return e.CrossauthLogger.logger.debug(e.j({err:r})),!1}}async getRefreshTokenData(a){if(a)try{const r=e.KeyPrefix.refreshToken+p.hash(a),t=await this.keyStorage.getKey(r);return JSON.parse(t.data||"{}")}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validIdToken(a){try{return await this.validateJwt(a,"id")}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validAccessToken(a){try{const r=await this.validateJwt(a,"access");if(this.persistAccessToken){const t=e.KeyPrefix.accessToken+p.hash(r.payload.jti);await this.keyStorage.getKey(t)}return r}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validateJwt(a,r){return new Promise((t,s)=>{X.verify(a,this.secretOrPublicKey,{clockTolerance:this.clockTolerance,complete:!0},(i,o)=>{o?!r||o.payload.type==r?t(o):s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid JWT type")):i?(e.CrossauthLogger.logger.debug(e.j({err:i})),s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid JWT signature"))):s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})})}validateScope(a){let r=[];try{r=a.split(" ")}catch{const s="invalid_scope",i=`Invalid scope ${a}`;return e.CrossauthLogger.logger.debug(e.j({err:e.CrossauthError.fromOAuthError(s,i)})),{error:s,errorDescription:i}}if(this.validateScopes){let t;if(r.forEach(s=>{if(!this.validScopes.includes(s)){const i="invalid_scope",o=`Illegal scope ${s}`;e.CrossauthLogger.logger.debug(e.j({err:e.CrossauthError.fromOAuthError(i,o)})),t={error:i,errorDescription:o}}}),t)return t}return{scopes:r}}redirect_uri(a,r,t){const s=a.includes("?")?"&":"?";return`${a}${s}code=${r}&state=${t}`}responseTypesSupported(){let a=[];return(this.validFlows.includes(e.OAuthFlows.AuthorizationCode)||this.validFlows.includes(e.OAuthFlows.AuthorizationCodeWithPKCE)||this.validFlows.includes(e.OAuthFlows.OidcAuthorizationCode))&&a.push("code"),a}oidcConfiguration({authorizeEndpoint:a,tokenEndpoint:r,jwksUri:t,additionalClaims:s}){let i=[];this.validFlows.forEach(n=>{const l=e.OAuthFlows.grantType(n);l&&(i=[...i,...l])});const o=["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512","PS256","PS384","PS512"];return s||(s=[]),{issuer:this.oauthIssuer,authorization_endpoint:new URL(a??"authorize",this.oauthIssuer).toString(),token_endpoint:new URL(r??"token",this.oauthIssuer).toString(),token_endpoint_auth_methods_supported:["client_secret_post"],jwks_uri:new URL(t??"jwks",this.oauthIssuer).toString(),response_types_supported:this.responseTypesSupported(),response_modes_supported:["query"],grant_types_supported:i,token_endpoint_auth_signing_alg_values_supported:o,subject_types_supported:["public"],id_token_signing_alg_values_supported:o,claims_supported:["iss","sub","aud","jti","iat","type",...s],request_uri_parameter_supported:!0,require_request_uri_registration:this.requireRedirectUriRegistration}}jwks(){let a=[];if(this.jwtPublicKey){const r=ke.createPublicKey(this.jwtPublicKey).export({format:"jwk"});r.kid="1",r.alg=this.jwtKeyType,a.push(r)}return{keys:a}}validateState(a){if(!/^[A-Za-z0-9_-]+$/.test(a))throw e.CrossauthError.fromOAuthError("invalid_request")}validateAuthorizeParameters({response_type:a,client_id:r,redirect_uri:t,scope:s,state:i,code_challenge:o,code_challenge_method:n}){let l;/^[A-Za-z0-9_-]+$/.test(a)?/^[A-Za-z0-9_-]+$/.test(r)?s&&!/^[A-Za-z0-9_+ -]+$/.test(s)?l="scope is invalid":/^[A-Za-z0-9_-]+$/.test(i)?o&&!/^[A-Za-z0-9_-]+$/.test(o)?l="code_challenge is invalid":n&&!/^[A-Za-z0-9_-]+$/.test(n)&&(l="code_challenge_method is invalid"):l="state is invalid":l="client_id is invalid":l="response_type is invalid";try{new URL(t)}catch{l="redirect_uri is invalid"}return(!t||t.includes("#"))&&(l="redirect_uri is invalid"),l?{error:"invalid_request",error_description:l}:{}}}class Ee extends e.OAuthTokenConsumerBase{constructor(r,t={}){const s={};h("jwtKeyType",u.String,s,t,"JWT_KEY_TYPE");super(r,{...t,...s});d(this,"audience");d(this,"persistAccessToken",!1);d(this,"keyStorage");d(this,"jwtSecretKeyFile","");d(this,"jwtPublicKeyFile","");if(this.audience=r,h("authServerBaseUrl",u.String,this,t,"AUTH_SERVER_BASE_URL",!0),h("jwtSecretKeyFile",u.String,this,t,"JWT_SECRET_KEY_FILE"),h("jwtPublicKeyFile",u.String,this,t,"JWT_PUBLIC_KEY_FILE"),h("jwtSecretKey",u.String,this,t,"JWT_SECRET_KEY"),h("jwtPublicKey",u.String,this,t,"JWT_PUBLIC_KEY"),h("clockTolerance",u.Number,this,t,"OAUTH_CLOCK_TOLERANCE"),h("persistAccessToken",u.Boolean,this,t,"OAUTH_PERSIST_ACCESS_TOKEN"),this.keyStorage=t.keyStorage,this.jwtSecretKey||this.jwtSecretKeyFile){if(this.jwtPublicKey||this.jwtPublicKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric and public/private JWT keys");if(this.jwtSecretKey&&this.jwtSecretKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric key and file");this.jwtSecretKeyFile&&(this.jwtSecretKey=G.readFileSync(this.jwtSecretKeyFile,"utf8"))}else if(this.jwtPublicKey||this.jwtPublicKeyFile){if(this.jwtPublicKeyFile&&this.jwtPublicKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both public key and public key file");this.jwtPublicKeyFile&&(this.jwtPublicKey=G.readFileSync(this.jwtPublicKeyFile,"utf8"))}}async hash(r){return p.hash(r)}async tokenAuthorized(r,t){var i;const s=await super.tokenAuthorized(r,t);if(s&&t=="access"&&this.persistAccessToken&&this.keyStorage)try{const o=e.KeyPrefix.accessToken+p.hash(s.jti?s.jti:s.sid?s.sid:""),n=await this.keyStorage.getKey(o),l=new Date;if(n.expires&&((i=n.expires)==null?void 0:i.getTime())<l.getTime()){e.CrossauthLogger.logger.error(e.j({msg:"Access token expired in storage but not in JWT"}));return}}catch(o){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't get token from database - is it valid?",hashedAccessToken:p.hash(s.jti?s.jti:s.sid?s.sid:"")})),e.CrossauthLogger.logger.debug(e.j({err:o}));return}return s}}class cr extends e.OAuthClientBase{constructor(r,t){const s={client_id:""};h("client_id",u.String,s,t,"OAUTH_CLIENT_ID",!0);super({authServerBaseUrl:r,tokenConsumer:new Ee(s.client_id,{audience:s.client_id,authServerBaseUrl:r,...t}),...t});d(this,"deviceAuthorizationUrl","device_authorization");d(this,"userCreationType","idToken");d(this,"userMatchField","username");d(this,"idTokenMatchField","sub");d(this,"userCreationFn");d(this,"userStorage");this.client_id=s.client_id;let i={};if(h("stateLength",u.String,this,t,"OAUTH_STATE_LENGTH"),h("verifierLength",u.String,this,t,"OAUTH_VERIFIER_LENGTH"),h("client_secret",u.String,i,t,"OAUTH_CLIENT_SECRET"),h("codeChallengeMethod",u.String,this,t,"OAUTH_CODE_CHALLENGE_METHOD"),h("deviceAuthorizationUrl",u.String,this,t,"OAUTH_DEVICE_AUTHORIZATION_URL"),h("oauthLogFetch",u.Boolean,this,t,"OAUTH_LOG_FETCH"),this.deviceAuthorizationUrl.startsWith("/")&&(this.deviceAuthorizationUrl=this.deviceAuthorizationUrl.substring(1)),i.client_secret&&(this.client_secret=i.client_secret),h("userCreationType",u.String,this,t,"OAUTH_USER_CREATION_TYPE"),h("userMatchField",u.String,this,t,"OAUTH_USER_MATCH_FIELD"),h("idTokenMatchField",u.String,this,t,"OAUTH_IDTOKEN_MaTCH_FIELD"),this.userCreationType=="merge"?this.userCreationFn=dr:this.userCreationType=="embed"?this.userCreationFn=ur:t.userCreationFn&&this.userCreationType=="custom"?this.userCreationFn=t.userCreationFn:this.userCreationFn=hr,t.userStorage&&(this.userStorage=t.userStorage),h("oauthPostType",u.String,this,t,"OAUTH_POST_TYPE"),h("oauthUseUserInfoEndpoint",u.Boolean,this,t,"OAUTH_USE_USER_INFO_ENDPOINT"),h("oauthAuthorizeRedirect",u.String,this,t,"OAUTH_AUTHORIZE_REDIRECT"),this.oauthPostType!="json"&&this.oauthPostType!="form")throw new e.CrossauthError(e.ErrorCode.Configuration,"oauthPostType must be json or form")}randomValue(r){return p.randomValue(r)}async sha256(r){return p.sha256(r)}}async function dr(C,a,r,t){if(!a)throw new e.CrossauthError(e.ErrorCode.Configuration,"userCreationType set to merge but no user storage set");try{let s;return r=="username"?s=await a.getUserByUsername(C[t]):r=="username"?s=await a.getUserByEmail(C[t]):s=await a.getUserBy(r,C[t]),{...C,...s.user}}catch(s){const i=e.CrossauthError.asCrossauthError(s);if(i.code==e.ErrorCode.UserNotExist||i.code==e.ErrorCode.UserNotActive)return;throw e.CrossauthLogger.logger.error(e.j({err:s})),s}}async function ur(C,a,r,t){if(!a)throw new e.CrossauthError(e.ErrorCode.Configuration,"userCreationType set to embed but no user storage set");try{let s;return r=="username"?s=await a.getUserByUsername(C[t]):r=="username"?s=await a.getUserByEmail(C[t]):s=await a.getUserBy(r,C[t]),{...s.user,idToken:C}}catch(s){const i=e.CrossauthError.asCrossauthError(s);if(i.code==e.ErrorCode.UserNotExist||i.code==e.ErrorCode.UserNotActive)return;throw e.CrossauthLogger.logger.error({err:s}),s}}async function hr(C,a,r,t){return{id:C.userid??C.sub,username:C.sub,state:C.state??"active"}}class gr{constructor(a,r={}){d(this,"tokenConsumers");this.tokenConsumers=[...a]}async accessTokenAuthorized(a){try{const r=Ae.decodeJwt(a);for(let t of this.tokenConsumers)if(r.iss==t.authServerBaseUrl&&(r.aud==t.audience||r.aud==null&&t.audience==""))return await t.tokenAuthorized(a,"access");throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid issuer in access token")}catch(r){e.CrossauthLogger.logger.warn(e.j({err:r}));return}}}exports.ApiKeyManager=re;exports.Authenticator=Y;exports.Crypto=p;exports.DoubleSubmitCsrfToken=ye;exports.DummyFactor2Authenticator=er;exports.EmailAuthenticator=W;exports.InMemoryKeyStorage=xe;exports.InMemoryOAuthAuthorizationStorage=De;exports.InMemoryOAuthClientStorage=Re;exports.InMemoryUserStorage=Ne;exports.KeyStorage=K;exports.LdapAuthenticator=rr;exports.LdapUserStorage=Z;exports.LocalPasswordAuthenticator=ne;exports.OAuthAuthorizationServer=lr;exports.OAuthAuthorizationStorage=ie;exports.OAuthClientBackend=cr;exports.OAuthClientManager=M;exports.OAuthClientStorage=se;exports.OAuthResourceServer=gr;exports.OAuthTokenConsumer=Ee;exports.ParamType=u;exports.PasswordAuthenticator=le;exports.PostgresKeyStorage=Ye;exports.PostgresOAuthAuthorizationStorage=Ze;exports.PostgresOAuthClientStorage=Ge;exports.PostgresUserStorage=Je;exports.PrismaKeyStorage=Ke;exports.PrismaOAuthAuthorizationStorage=Le;exports.PrismaOAuthClientStorage=Fe;exports.PrismaUserStorage=$;exports.SessionCookie=O;exports.SessionManager=ir;exports.SmsAuthenticator=J;exports.TokenEmailer=P;exports.TotpAuthenticator=tr;exports.TwilioAuthenticator=ce;exports.UserStorage=F;exports.setParameter=h;exports.toCookieSerializeOptions=sr;
|
|
1
|
+
"use strict";var Se=Object.defineProperty;var Te=(w,a,r)=>a in w?Se(w,a,{enumerable:!0,configurable:!0,writable:!0,value:r}):w[a]=r;var d=(w,a,r)=>Te(w,typeof a!="symbol"?a+"":a,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@crossauth/common"),z=require("@prisma/client"),de=require("@mbakereth/ldapjs"),I=require("node:crypto"),ve=require("node:util"),V=require("nunjucks"),we=require("nodemailer"),_e=require("twilio"),ke=require("qrcode"),oe=require("otplib"),Y=require("jsonwebtoken"),Z=require("node:fs"),be=require("crypto"),Ue=require("jose");function Ae(w){const a=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(w){for(const r in w)if(r!=="default"){const t=Object.getOwnPropertyDescriptor(w,r);Object.defineProperty(a,r,t.get?t:{enumerable:!0,get:()=>w[r]})}}return a.default=w,Object.freeze(a)}const Pe=Ae(Ue);var u=(w=>(w[w.String=0]="String",w[w.Number=1]="Number",w[w.Boolean=2]="Boolean",w[w.Json=3]="Json",w[w.JsonArray=4]="JsonArray",w))(u||{});function Ie(w,a){let r=w.split("."),t=a;for(let s in r){const i=r[s];if(!(i in t)||t[i]==null)return;t=t[i]}return t}function ue(w,a){let r=w.split("."),t=a;for(let s in r){const i=r[s];if(!(i in t)||t[i]==null)return!1;t=t[i]}return!0}function Ke(w,a,r){const t=Ie(a,r);w[a.replace(".","_")]=t}function Oe(w,a,r,t){var i;const s=a.replace(".","_");switch(r){case 0:w[s]=process.env[t]=="null"?null:process.env[t];break;case 1:w[s]=process.env[t]=="null"?null:Number(process.env[t]);break;case 2:w[s]=["1","true"].includes(((i=process.env[t])==null?void 0:i.toLowerCase())??"");break;case 3:w[s]=JSON.parse(process.env[t]??"{}");break;case 4:w[s]=JSON.parse(process.env[t]??"[]");break}}function h(w,a,r,t,s,i=!1){const o="CROSSAUTH_"+s;if(i&&!ue(w,t)&&!(o&&o in process.env))throw new e.CrossauthError(e.ErrorCode.Configuration,w+" is required");ue(w,t)?Ke(r,w,t):s&&o in process.env&&process.env[o]!=null&&Oe(r,w,a,o)}class x{constructor(a={}){d(this,"userEditableFields",[]);d(this,"adminEditableFields",[]);d(this,"normalizeUsername",!0);d(this,"normalizeEmail",!0);h("userEditableFields",u.JsonArray,this,a,"USER_EDITABLE_FIELDS"),h("adminEditableFields",u.JsonArray,this,a,"ADMIN_EDITABLE_FIELDS"),h("normalizeUsername",u.JsonArray,this,a,"NORMALIZE_USERNAME"),h("normalizeEmail",u.JsonArray,this,a,"NORMALIZE_EMAIL")}createUser(a,r){throw new e.CrossauthError(e.ErrorCode.Configuration)}static normalize(a){return a.normalize("NFD").replace(new RegExp("\\p{Diacritic}","gu"),"").toLowerCase()}}class L{static decodeData(a){return a==null||a==""?{}:JSON.parse(a)}static encodeData(a){return a?JSON.stringify(a):"{}"}updateDataInternal(a,r,t){if(r.indexOf(".")>0){let s=r.split("."),i=a;for(let o=0;o<s.length-1&&a;o++)i=i[s[o]];if(i)return i[s[s.length-1]]=t,a}else return a[r]=t,a}deleteDataInternal(a,r){if(r.indexOf(".")>0){let t=r.split("."),s=a;for(let i=0;i<t.length-1&&a;i++)s=s[t[i]];return s&&s[t[t.length-1]]?(delete s[t[t.length-1]],!0):!1}else return r in a?(delete a[r],!0):!1}}class se{constructor(a={}){}}class ie{constructor(a={}){}}class $ extends x{constructor(r={}){super(r);d(this,"userTable","user");d(this,"userSecretsTable","userSecrets");d(this,"idColumn","id");d(this,"useridForeignKeyColumn","userid");d(this,"prismaClient");d(this,"includes",["secrets"]);d(this,"includesObject",{});d(this,"forceIdToNumber",!0);h("userTable",u.String,this,r,"USER_TABLE"),h("userSecretsTable",u.String,this,r,"USER_SECRETS_TABLE"),h("idColumn",u.String,this,r,"USER_ID_COLUMN"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),h("includes",u.String,this,r,"USER_INCLUDES"),h("forceIdToNumber",u.String,this,r,"USER_FORCE_ID_TO_NUMBER"),this.includes.forEach(t=>{this.includesObject[t]=!0}),r&&r.prismaClient?this.prismaClient=r.prismaClient:this.prismaClient=new z.PrismaClient}async getUser(r,t,s){let i,o;try{o=await this.prismaClient[this.userTable].findUniqueOrThrow({where:{[r]:t},include:this.includesObject})}catch{i=new e.CrossauthError(e.ErrorCode.UserNotExist)}if(this.prismaClient||(i=new e.CrossauthError(e.ErrorCode.Connection)),i)throw i;if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);if((s==null?void 0:s.skipEmailVerifiedCheck)!=!0&&o.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User has not verified email"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"User must change password"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&(o.state==e.UserState.passwordResetNeeded||o.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"User must reset password"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);const n=o.secrets||{};return o.secrets&&(delete n[this.useridForeignKeyColumn],delete o.secrets),{user:{...o,id:o[this.idColumn]},secrets:{userid:o[this.idColumn],...n}}}async getUserByUsername(r,t){const s=$.normalize(r);return this.getUser("username_normalized",s,t)}async getUserBy(r,t,s){return this.getUser(r,t,s)}async getUserByEmail(r,t){const s=$.normalize(r);return this.getUser("email_normalized",s,t)}async getUserById(r,t){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.getUser(this.idColumn,Number(r),t)}catch(s){if(e.CrossauthError.asCrossauthError(s).code==e.ErrorCode.UserNotExist)return await this.getUser(this.idColumn,r,t);throw e.CrossauthLogger.logger.debug(e.j({err:s})),s}return await this.getUser(this.idColumn,r,t)}async updateUser(r,t){if(!r.id)throw new e.CrossauthError(e.ErrorCode.InvalidKey);t&&!t.userid&&(t={...t,userid:r[this.idColumn]});try{let{id:s,...i}=r,{userid:o,...n}=t??{};"email"in i&&i.email&&(i={email_normalized:$.normalize(i.email),...i}),"username"in i&&i.username&&(i={username_normalized:$.normalize(i.username),...i}),t?await this.prismaClient.$transaction(async l=>{let c={};try{c=await l[this.userSecretsTable].findUniqueOrThrow({where:{[this.useridForeignKeyColumn]:r.id}})}catch{}let{userid:g,...f}=c??{};n={...f,...n},await l[this.userTable].update({where:{[this.idColumn]:r.id},data:{...i}}),await l[this.userSecretsTable].upsert({where:{[this.useridForeignKeyColumn]:r.id},update:n,create:{[this.useridForeignKeyColumn]:r.id,...n}})}):await this.prismaClient[this.userTable].update({where:{[this.idColumn]:r.id},data:i})}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating user")}}async createUser(r,t){let s;if(t&&!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordFormat,"Password required when creating user");let i,o="",n="";try{"email"in r&&r.email&&(n=$.normalize(r.email)),"username"in r&&r.username&&(o=$.normalize(r.username)),t?i=await this.prismaClient[this.userTable].create({data:{...r,email_normalized:n,username_normalized:o,secrets:{create:t}},include:{secrets:!0}}):i=await this.prismaClient[this.userTable].create({data:{...r,email_normalized:n,username_normalized:o}})}catch(l){e.CrossauthLogger.logger.debug(e.j({err:l})),s=new e.CrossauthError(e.ErrorCode.Connection,"Error creating user"),(l instanceof z.Prisma.PrismaClientKnownRequestError||l instanceof Object&&"code"in l)&&l.code==="P2002"&&(s=new e.CrossauthError(e.ErrorCode.UserExists))}if(s)throw s;return i}async deleteUserByUsername(r){let t;try{return this.prismaClient[this.userTable].deleteMany({where:{username:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting user")}if(t)throw t}async deleteUserById(r){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.deleteUserById_internal(Number(r))}catch{e.CrossauthLogger.logger.debug(e.j({msg:"Failed forcing id to number when deleting user"}))}return await this.deleteUserById_internal(r)}async deleteUserById_internal(r){let t;try{return this.prismaClient[this.userTable].delete({where:{id:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting user")}if(t)throw t}async getUsers(r,t){let s={};r&&(s.skip=r),t&&(s.take=t);try{return await this.prismaClient[this.userTable].findMany({...s,orderBy:[{username_normalized:"asc"}],include:this.includesObject})}catch(i){throw e.CrossauthLogger.logger.error(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Couldn't select from user table")}}}class Fe extends L{constructor(r={}){super();d(this,"keyTable","key");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"useridForeignKeyColumn","userid");h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.Number,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.keyTable&&(this.keyTable=r.keyTable),r.prismaClient==null?this.prismaClient=new z.PrismaClient:this.prismaClient=r.prismaClient}async getKey(r){return await this.getKeyWithTransaction(r,this.prismaClient)}async getKeyWithTransaction(r,t){let s={userid:0,value:"",created:new Date,expires:void 0},i;try{let o=await t[this.keyTable].findUniqueOrThrow({where:{value:r}});s={...o,userid:o[this.useridForeignKeyColumn]},this.useridForeignKeyColumn!="userid"&&delete s[this.useridForeignKeyColumn]}catch(o){e.CrossauthLogger.logger.debug(e.j({err:o})),i=new e.CrossauthError(e.ErrorCode.InvalidKey)}if(i)throw i;return s}async saveKey(r,t,s,i,o,n={}){let l;try{let c={[this.useridForeignKeyColumn]:r,value:t,created:s,expires:i??null,data:o,...n};await this.prismaClient[this.keyTable].create({data:c})}catch(c){c instanceof z.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.warn(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.KeyExists)):(e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key")):(e.CrossauthLogger.logger.debug(e.j({err:c})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key"))}if(l)throw l}async deleteKey(r){let t;try{return this.prismaClient[this.keyTable].deleteMany({where:{value:r}})}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s})),t=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(t)throw t}async deleteAllForUser(r,t,s){let i;try{return s?this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}},{value:{not:s}}]}}):this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}}]}})}catch(o){e.CrossauthLogger.logger.debug(e.j({err:o})),i=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(i)throw i}async deleteMatching(r){try{let t=[];for(let s in r)s=="userid"?t.push({[this.useridForeignKeyColumn]:r[s]}):t.push({[s]:r[s]});return this.prismaClient[this.keyTable].deleteMany({where:{AND:t}})}catch(t){throw e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Error deleting keys")}}async deleteWithPrefix(r,t){let s;try{return this.prismaClient[this.keyTable].deleteMany({where:{AND:[{[this.useridForeignKeyColumn]:r??null},{value:{startsWith:t}}]}})}catch(i){e.CrossauthLogger.logger.debug(e.j({err:i})),s=new e.CrossauthError(e.ErrorCode.Connection,"Error deleting key")}if(s)throw s}async getAllForUser(r){let t=[],s;try{t=(await this.prismaClient[this.keyTable].findMany({where:{[this.useridForeignKeyColumn]:r??null}})).map(o=>{let n={...o,userid:o[this.useridForeignKeyColumn]};return this.useridForeignKeyColumn!="userid"&&delete n[this.useridForeignKeyColumn],n})}catch{s=new e.CrossauthError(e.ErrorCode.InvalidKey)}if(s)throw s;return t}async updateKey(r){await this.updateKeyWithTransaction(r,this.prismaClient)}async updateKeyWithTransaction(r,t){let s;if(!r.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey);try{let i={...r};delete i.value,await t[this.keyTable].update({where:{value:r.value},data:i})}catch(i){s=new e.CrossauthError(e.ErrorCode.Connection,String(i))}if(s)throw e.CrossauthLogger.logger.debug(e.j({err:s})),s}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){try{await this.prismaClient.$transaction(async s=>{const i=await this.getKeyWithTransaction(r,s);let o;if(!i.data||i.data=="")o={};else try{o=JSON.parse(i.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let n of t){let l=this.updateDataInternal(o,n.dataName,n.value);if(!l)throw new e.CrossauthError(e.ErrorCode.BadRequest,`Parents of ${n.dataName} not found in key data`);o=l}await this.updateKeyWithTransaction({value:i.value,data:JSON.stringify(o)},s)},{timeout:this.transactionTimeout})}catch(s){throw s&&typeof s=="object"&&!("isCrossauthError"in s)?(e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):s}}async deleteData(r,t){try{let s=!1;await this.prismaClient.$transaction(async i=>{let o={};const n=await this.getKeyWithTransaction(r,i);if(n.data&&n.data!=""){try{o=JSON.parse(n.data)}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),new e.CrossauthError(e.ErrorCode.DataFormat)}s=this.deleteDataInternal(o,t)}s&&await this.updateKeyWithTransaction({value:n.value,data:JSON.stringify(o)},i)},{timeout:this.transactionTimeout})}catch(s){throw s&&typeof s=="object"&&!("isCrossauthError"in s)?(e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):s}}}class Le extends se{constructor(r={}){super();d(this,"clientTable","oAuthClient");d(this,"redirectUriTable","OAuthClientRedirectUri");d(this,"validFlowTable","OAuthClientValidFlow");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"updateMode","DeleteAndInsert");d(this,"useridForeignKeyColumn","userid");h("clientTable",u.String,this,r,"OAUTH_CLIENT_TABLE"),h("redirectUriTable",u.String,this,r,"OAUTH_REDIRECTURI_TABLE"),h("validFlowTable",u.String,this,r,"OAUTH_VALID_FLOW_TABLE"),h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("updateMode",u.String,this,r,"OAUTHCLIENT_UPDATE_MODE"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.prismaClient==null?this.prismaClient=new z.PrismaClient:this.prismaClient=r.prismaClient}async getClientById(r){return(await this.getClientWithTransaction("client_id",r,this.prismaClient,!0,void 0))[0]}async getClientByName(r,t){return await this.getClientWithTransaction("client_name",r,this.prismaClient,!1,t)}async getClientWithTransaction(r,t,s,i,o){const n=o==null&&o!==null?{}:{[this.useridForeignKeyColumn]:o};try{if(i){const l=await s[this.clientTable].findUniqueOrThrow({where:{[r]:t,...n},include:{redirect_uri:!0,valid_flow:!0}}),c=l.redirect_uri,g=l.valid_flow;let f=l[this.useridForeignKeyColumn];return f===null&&(f=void 0),this.useridForeignKeyColumn!="userid"&&delete l[this.useridForeignKeyColumn],[{...l,userid:f,client_secret:l.client_secret??void 0,redirect_uri:c.map(C=>C.uri),valid_flow:g.map(C=>C.flow)}]}else{const l=await s[this.clientTable].findMany({where:{[r]:t,...n},include:{redirect_uri:!0,valid_flow:!0}});for(let c of l){const g=c.redirect_uri,f=c.valid_flow;let C=c[this.useridForeignKeyColumn];C==null&&(C=void 0),c.userid=C,this.useridForeignKeyColumn!="userid"&&delete c[this.useridForeignKeyColumn],c.client_secret=c.client_secret??void 0,c.redirect_uri=g.map(p=>p.uri),c.valid_flow=f.map(p=>p.flow)}return l}}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid OAuth client",[r]:t,cerr:l})),new e.CrossauthError(e.ErrorCode.InvalidClientId)}}async createClient(r){try{return this.prismaClient.$transaction(async t=>{try{throw await this.getClientWithTransaction("client_id",r.client_id,t,!0,r.userid),new e.CrossauthError(e.ErrorCode.ClientExists)}catch{}return await this.createClientWithTransaction(r,t)},{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed creating client")):t}}async createClientWithTransaction(r,t){const{redirect_uri:s,valid_flow:i,userid:o,...n}=r;let l;if(o&&(n[this.useridForeignKeyColumn]=o),this.useridForeignKeyColumn!="userid"&&delete r[this.useridForeignKeyColumn],s)for(let c=0;c<s.length;++c){if(s[c].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[c])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[c]} is not valid`)}}if(i){for(let c=0;c<i.length;++c)if(!e.OAuthFlows.isValidFlow(i[c]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Invalid flow "+i[c])}try{l=await t[this.clientTable].create({data:n})}catch(c){throw c instanceof z.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.ClientExists,"Attempt to create an OAuth client with a client_id that already exists. Maximum attempts failed")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}if(!l)throw e.CrossauthLogger.logger.error(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),new e.CrossauthError(e.ErrorCode.KeyExists);if(s)try{for(let c=0;c<s.length;++c)await t[this.redirectUriTable].create({data:{client_id:l.client_id,uri:s[c]}})}catch(c){throw c instanceof z.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?c.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Attempt to create an OAuth client with a redirect uri that already belongs to another client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}if(i)try{for(let c=0;c<i.length;++c)await t[this.validFlowTable].create({data:{client_id:l.client_id,flow:i[c]}})}catch(c){throw c instanceof z.Prisma.PrismaClientKnownRequestError||c instanceof Object&&"code"in c?(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client")):(e.CrossauthLogger.logger.debug(e.j({err:c})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}return{...l,redirect_uri:s,valid_flow:i}}async deleteClient(r){try{return this.prismaClient.$transaction(async t=>await this.deleteClientWithTransaction(r,t),{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed deleting client")):t}}async deleteClientWithTransaction(r,t){try{await t[this.clientTable].deleteMany({where:{client_id:r}})}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection,"Error deleting OAuth client")}}async updateClient(r){try{return this.prismaClient.$transaction(async t=>this.updateMode=="Update"?await this.updateClientWithTransaction_update(r,t):await this.updateClientWithTransaction_deleteAndInsert(r,t),{timeout:this.transactionTimeout})}catch(t){throw t&&typeof t=="object"&&!("isCrossauthError"in t)?(e.CrossauthLogger.logger.debug(e.j({err:t})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating client")):t}}async updateClientWithTransaction_update(r,t){if(!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const s=r.redirect_uri,i=r.valid_flow;if(s)for(let o=0;o<s.length;++o){if(s[o].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[o])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[o]} is not valid`)}}if(i){for(let o=0;o<i.length;++o)if(!e.OAuthFlows.isValidFlow(i[o]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Redirect Uri's may not contain page fragments")}try{let o={...r};delete o.client_id,delete o.redirect_uri,delete o.valid_flow,"userid"in o&&this.useridForeignKeyColumn!="userid"&&(o[this.useridForeignKeyColumn]=o.userid,delete o.userid),Object.keys(o).length>0&&await t[this.clientTable].update({where:{client_id:r.client_id},data:o})}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")}if(s!=null)try{await this.prismaClient[this.redirectUriTable].deleteMany({where:{client_id:r.client_id}});for(let o=0;o<s.length;++o)await t[this.redirectUriTable].create({data:{client_id:r.client_id,uri:s[o]}})}catch(o){throw o instanceof z.Prisma.PrismaClientKnownRequestError||o instanceof Object&&"code"in o?o.code=="P2002"?(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.KeyExists,"Attempt to update an OAuth client with a redirect Uri that already belongs to another client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client"))}if(i!=null)try{await this.prismaClient[this.validFlowTable].deleteMany({where:{client_id:r.client_id}});for(let o=0;o<i.length;++o)await t[this.validFlowTable].create({data:{client_id:r.client_id,flow:i[o]}})}catch(o){throw o instanceof z.Prisma.PrismaClientKnownRequestError||o instanceof Object&&"code"in o?(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client")):(e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating client"))}}async updateClientWithTransaction_deleteAndInsert(r,t){if(!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const i={...(await this.getClientWithTransaction("client_id",r.client_id,this.prismaClient,!0,void 0))[0],...r};"userid"in i&&this.useridForeignKeyColumn!="userid"&&(i[this.useridForeignKeyColumn]=i.userid,delete i.userid),await this.deleteClientWithTransaction(r.client_id,t),await this.createClientWithTransaction(i,t)}async getClients(r,t,s){let i={};r&&(i.skip=r),t&&(i.take=t);try{let o=[];return s||s===null?o=await this.prismaClient[this.clientTable].findMany({...i,where:{[this.useridForeignKeyColumn]:s},orderBy:[{client_name:"asc"}]}):o=await this.prismaClient[this.clientTable].findMany({...i,orderBy:[{client_name:"asc"}]}),o.forEach(n=>(this.useridForeignKeyColumn!="userid"&&(n.userid=n[this.useridForeignKeyColumn],delete n[this.useridForeignKeyColumn]),n.userid=n.userid===null?void 0:n.userid,n)),o}catch(o){throw e.CrossauthLogger.logger.error(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Couldn't select from client table")}}}class xe extends ie{constructor(r={}){super();d(this,"authorizationTable","oAuthAuthorization");d(this,"prismaClient");d(this,"transactionTimeout",5e3);d(this,"useridForeignKeyColumn","userid");h("authorizationTable",u.String,this,r,"OAUTH_CLIENT_TABLE"),h("transactionTimeout",u.Number,this,r,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.String,this,r,"USER_ID_FOREIGN_KEY_COLUMN"),r.prismaClient==null?this.prismaClient=new z.PrismaClient:this.prismaClient=r.prismaClient}async getAuthorizations(r,t){try{return(await this.prismaClient[this.authorizationTable].findMany({where:{client_id:r,[this.useridForeignKeyColumn]:t??null},select:{scope:!0}})).map(i=>i.scope)}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),new e.CrossauthError(e.ErrorCode.Connection)}}async updateAuthorizations(r,t,s){return this.prismaClient.$transaction(async i=>await this.updateAuthorizationsWithTransaction(r,t,s,i),{timeout:this.transactionTimeout})}async updateAuthorizationsWithTransaction(r,t,s,i){try{await i[this.authorizationTable].deleteMany({where:{client_id:r,[this.useridForeignKeyColumn]:t??null}});const o=[];s.forEach(n=>{o.push(i[this.authorizationTable].create({data:{client_id:r,[this.useridForeignKeyColumn]:t??null,scope:n}}))}),await Promise.all(o)}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.Connection,"Error updating OAuth authorizations")}}}class Ne extends x{constructor(r={}){super(r);d(this,"usersByUsername",{});d(this,"usersByEmail",{});d(this,"secretsByUsername",{});d(this,"secretsByEmail",{})}async createUser(r,t){if(r.username_normalized=x.normalize(r.username),r.username_normalized in this.usersByUsername)throw new e.CrossauthError(e.ErrorCode.UserExists);if("email"in r&&r.email&&(r.email_normalized=x.normalize(r.email),r.email_normalized in this.getUserByEmail))throw new e.CrossauthError(e.ErrorCode.UserExists);const s={id:r.username,...r};return this.usersByUsername[r.username_normalized]=s,this.secretsByUsername[r.username_normalized]=t??{},"email"in r&&r.email&&(this.usersByEmail[r.email_normalized]=s),"email"in r&&r.email&&(this.secretsByEmail[r.email_normalized]=t??{}),{id:r.username,...r}}async getUserByUsername(r,t){const s=x.normalize(r);if(s in this.usersByUsername){const i=this.usersByUsername[s];if(!i)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"Password change required"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&(i.state==e.UserState.passwordResetNeeded||i.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"Password reset required"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((t==null?void 0:t.skipEmailVerifiedCheck)!=!0&&i.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User email not verified"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);const o=this.secretsByUsername[s];return{user:{...i},secrets:{userid:i.id,...o}}}throw e.CrossauthLogger.logger.debug(e.j({msg:"User does not exist"})),new e.CrossauthError(e.ErrorCode.UserNotExist)}async getUserByEmail(r,t){const s=x.normalize(r);if(s in this.usersByEmail){const i=this.usersByEmail[s];if(!i)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if((t==null?void 0:t.skipEmailVerifiedCheck)!=!0&&i.state=="awaitingemailverification")throw e.CrossauthLogger.logger.debug(e.j({msg:"User email not verified"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((t==null?void 0:t.skipActiveCheck)!=!0&&i.state!="active")throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);const o=this.secretsByEmail[s];return{user:{...i},secrets:{userid:i.id,...o}}}throw e.CrossauthLogger.logger.debug(e.j({msg:"User does not exist"})),new e.CrossauthError(e.ErrorCode.UserNotExist)}async getUserById(r,t){return this.getUserByUsername(r,t)}async getUserBy(r,t,s){throw new e.CrossauthError(e.ErrorCode.NotImplemented)}async updateUser(r,t){let s={...r};if("username"in s&&s.username?s.username_normalized=x.normalize(s.username):"id"in s&&s.id&&(s.username_normalized=x.normalize(String(s.id))),"email"in s&&s.email&&(s.email_normalized=x.normalize(s.email)),s.username_normalized&&s.username_normalized in this.usersByUsername){for(let i in s)this.usersByUsername[s.username_normalized][i]=s[i];t&&(this.secretsByUsername[s.username_normalized]={...this.secretsByUsername[s.username_normalized],...t})}}async deleteUserByUsername(r){const t=x.normalize(String(r));if(t in this.usersByUsername){const s=this.usersByUsername[t];delete this.usersByUsername[t],delete this.secretsByUsername[t];const i=x.normalize(String(s.email));i in this.usersByEmail&&(delete this.usersByEmail[i],delete this.secretsByEmail[i])}}async deleteUserById(r){return await this.deleteUserByUsername(String(r))}async getUsers(r,t){const s=Object.keys(this.usersByUsername).sort();let i=[];r||(r=0);let o=t||s.length;o>=s.length-r&&(o=s.length-r);for(let n=r;n<o;++n)i.push(this.usersByUsername[s[n]]);return i}}class De extends L{constructor(){super();d(this,"keys",{});d(this,"keysByUserId",{});d(this,"nonUserKeys",[])}async getKey(r){if(this.keys&&r in this.keys)return this.keys[r];e.CrossauthLogger.logger.debug(e.j({msg:"Key does not exist in key storage"}));let t=new e.CrossauthError(e.ErrorCode.InvalidKey);throw e.CrossauthLogger.logger.debug(e.j({err:t})),t}async saveKey(r,t,s,i,o,n){const l={value:t,userid:r,created:s,expires:i,data:o,...n};this.keys[t]=l,r?r in this.keysByUserId?this.keysByUserId[r].push(l):this.keysByUserId[r]=[l]:this.nonUserKeys.push(l)}async deleteKey(r){if(r in this.keys){const t=this.keys[r];t.userid?delete this.keysByUserId[t.userid]:this.nonUserKeys=this.nonUserKeys.filter(s=>s.value!=r),delete this.keys[r]}}async deleteAllForUser(r,t,s=void 0){for(const i in this.keys)this.keys[i].userid==r&&(!s||i!=s)&&i.startsWith(t)&&delete this.keys[i];r?r in this.keysByUserId&&delete this.keysByUserId[r]:this.nonUserKeys=[]}async getAllForUser(r){return r?r in this.keysByUserId?this.keysByUserId[r]:[]:this.nonUserKeys}async deleteMatching(r){for(let t in this.keys){let s=!0;const i=this.keys[t];for(let o in r)if(o in i&&i[o]!=r[o]){s=!1;break}s&&delete this.keys[t]}for(let t in this.keysByUserId){const s=this.keysByUserId[t];for(let i=0;i<s.length;++i){let o=!0,n=0;const l=s[i];for(let c in r)if(c in l&&l[c]!=r[c]){o=!1,n=i;break}o&&(this.keysByUserId[t]=this.keysByUserId[t].splice(n,1))}}for(let t=0;t<this.nonUserKeys.length;++t){let s=!0,i=0;const o=this.nonUserKeys[t];for(let n in r)if(n in o&&o[n]!=r[n]){s=!1,i=t;break}s&&(this.nonUserKeys=this.nonUserKeys.splice(i,1))}}async updateKey(r){if(r.value&&r.value in this.keys){let t=r.value??"";for(let s in r)this.keys[t][s]=r[s]}}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){const s=await this.getKey(r);let i;if(!s.data||s.data=="")i={};else try{i=JSON.parse(s.data)}catch(o){throw e.CrossauthLogger.logger.debug(e.j({err:o})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let o of t)if(this.updateDataInternal(i,o.dataName,o.value))s.data=JSON.stringify(i);else throw new e.CrossauthError(e.ErrorCode.BadRequest,`parents of ${o.dataName} not found in key data`)}async deleteData(r,t){const s=await this.getKey(r);let i;if(!s.data||s.data=="")return;try{i=JSON.parse(s.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}this.deleteDataInternal(i,t)&&(s.data=JSON.stringify(i))}}class je extends se{constructor(r={}){super();d(this,"clients",{});d(this,"clientsByName",{})}async getClientById(r){if(this.clients&&r in this.clients)return this.clients[r];e.CrossauthLogger.logger.debug(e.j({msg:"Client does not exist in client storage"}));let t=new e.CrossauthError(e.ErrorCode.InvalidClientId);throw e.CrossauthLogger.logger.debug(e.j({err:t})),t}async getClientByName(r,t){if(this.clientsByName&&r in this.clientsByName){const s=this.clientsByName[r];if(t==null&&t!==null)return s;const i=[];for(let o of s)o.userid===t&&i.push(o);return i}return[]}async createClient(r){return"userid"in r||(r.userid=null),r.client_name in this.clientsByName||(this.clientsByName[r.client_name]=[]),this.clientsByName[r.client_name].push(r),this.clients[r.client_id]=r}async deleteClient(r){if(r in this.clients){const t=this.clients[r].client_name;if(t in this.clientsByName){let s=this.clientsByName[t];for(let i=0;i<s.length;++i)if(s[i].client_id==r){s.splice(i,1);break}}delete this.clients[r]}}async updateClient(r){if(r.client_id&&r.client_id in this.clients){const t=this.clients[r.client_id];this.clients[r.client_id]={...t,...r}}}async getClients(r,t,s){const i=Object.keys(this.clients).sort();let o=[];r||(r=0);let n=t||i.length;n>=i.length-r&&(n=i.length-r);for(let l=r;l<n;++l)s===null?this.clients[i[l]].userid==null&&o.push(this.clients[i[l]]):s!=null&&s!==null?this.clients[i[l]].userid==s&&o.push(this.clients[i[l]]):o.push(this.clients[i[l]]);return o}}class Re extends ie{constructor(r={}){super();d(this,"byClientAndUser",{});d(this,"byClient",{})}async getAuthorizations(r,t){if(t){if(r in this.byClientAndUser){const s=this.byClientAndUser[r];if(t in s)return s[t]}}else if(r in this.byClient)return this.byClient[r];return[]}async updateAuthorizations(r,t,s){if(t){r in this.byClientAndUser||(this.byClientAndUser[r]={});const i=this.byClientAndUser[r];i[t]=[...s]}else this.byClient[r]=[...s]}}function Be(w,a){return{username:Array.isArray(a.uid)?a.uid[0]:a.uid,state:"active",...w}}class X extends x{constructor(r,t={}){super(t);d(this,"localStorage");d(this,"ldapUrls",[]);d(this,"ldapUserSearchBase","");d(this,"ldapUsernameAttribute","cn");d(this,"createUserFn",Be);this.localStorage=r,h("ldapUrls",u.JsonArray,this,t,"LDAP_URL",!0),h("ldapUserSearchBase",u.String,this,t,"LDAP_USER_SEARCH_BASE"),h("ldapUsernameAttribute",u.String,this,t,"LDAP_USENAME_ATTRIBUTE"),t.createUserFn&&(this.createUserFn=t.createUserFn)}async createUser(r,t){if(!(t!=null&&t.password))throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);const s=await this.getLdapUser(r.username,t.password);return r=this.createUserFn(r,s),await this.localStorage.createUser(r,{password:"pbkdf2:sha256:32:600000:0:DISABLED:DISABLED"})}async getUserByUsername(r,t){return await this.localStorage.getUserByUsername(r,t)}async getUserById(r,t){return await this.localStorage.getUserById(r,t)}async getUserByEmail(r,t){return await this.localStorage.getUserByEmail(r,t)}async getUserBy(r,t,s){return await this.localStorage.getUserBy(r,t,s)}async getUsers(r,t){return await this.localStorage.getUsers(r,t)}async updateUser(r,t){return await this.localStorage.updateUser(r,void 0)}async deleteUserByUsername(r){await this.localStorage.deleteUserByUsername(r)}async deleteUserById(r){await this.localStorage.deleteUserById(r)}async getLdapUser(r,t){let s;try{const i=X.sanitizeLdapDnForSerach(r),o=[this.ldapUsernameAttribute+"="+i,this.ldapUserSearchBase].join(",");if(!t)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);return e.CrossauthLogger.logger.debug(e.j({msg:"LDAP search "+o})),s=await this.ldapBind(o,t),await this.searchUser(s,o)}catch(i){e.CrossauthLogger.logger.debug(e.j({err:i}));const o=e.CrossauthError.asCrossauthError(i);throw i instanceof de.InvalidCredentialsError?new e.CrossauthError(e.ErrorCode.UsernameOrPasswordInvalid):o.code!=e.ErrorCode.UnknownError?o:new e.CrossauthError(e.ErrorCode.Connection,"LDAP error getting user")}}ldapBind(r,t){return new Promise((s,i)=>{let o=de.createClient({url:this.ldapUrls});o.on("connect",function(){o.bind(r,t,function(n){if(n){i(n),o.unbind();return}s(o)})}),o.on("timeout",n=>{i(n)}),o.on("connectTimeout",n=>{i(n)}),o.on("error",n=>{i(n)}),o.on("connectError",function(n){if(n){i(n);return}})})}async searchUser(r,t,s){return new Promise(function(i,o){let n={scope:"base"};s&&(n.attributes=s),r.search(t,n,function(l,c){let g;if(l){o(l),r.unbind();return}c.on("searchEntry",function(f){g=X.searchResultToUser(f.pojo)}),c.on("error",function(f){o(f),r.unbind()}),c.on("end",function(f){f.status!=0?o(new e.CrossauthError(e.ErrorCode.Connection,"LDAP onnection failed")):g?i(g):o(new e.CrossauthError(e.ErrorCode.UsernameOrPasswordInvalid)),r.unbind()})})})}static searchResultToUser(r){let t={dn:r.objectName,state:"active"};return r.attributes.forEach(s=>{t[s.type]=s.values.length==1?s.values[0]:s.values}),t}static sanitizeLdapDn(r){return r.replace("\\","\\\\").replace(",",",").replace("+","+").replace('"','"').replace("<","<").replace(">",">").replace("#","#").trim()}static sanitizeLdapDnForSerach(r){return X.sanitizeLdapDn(r).replace("*","*").replace("(","(").replace(")",")")}}class q extends x{constructor(r,t={}){super(t);d(this,"userTable","users");d(this,"userSecretsTable","usersecrets");d(this,"idColumn","id");d(this,"useridForeignKeyColumn","userid");d(this,"forceIdToNumber",!0);d(this,"dbPool");this.dbPool=r,h("userTable",u.String,this,t,"USER_TABLE"),h("userSecretsTable",u.String,this,t,"USER_SECRETS_TABLE"),h("idColumn",u.String,this,t,"USER_ID_COLUMN"),h("forceIdToNumber",u.String,this,t,"USER_FORCE_ID_TO_NUMBER"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN")}async getUserById(r,t){return await this.getUser(this.idColumn,r,t)}async getUserByUsername(r,t){const s=this.normalizeUsername?q.normalize(r):r;return await this.getUser("username_normalized",s,t)}async getUserByEmail(r,t){const s=this.normalizeEmail?q.normalize(r):r;return this.getUser("email_normalized",s,t)}async getUserBy(r,t,s){return await this.getUser(r,t,s)}async getUser(r,t,s){let i=await this.dbPool.connect(),o,n,l=this.dbPool.parameters();try{await i.startTransaction();let c=`select * from ${this.userTable} where ${r} = `+l.nextParameter(),g=await i.execute(c,[t]);if(g.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);let f,C,p;if(this.idColumn in g[0])f=g[0][this.idColumn];else throw new e.CrossauthError(e.ErrorCode.Configuration,"ID column "+this.idColumn+" not present in user table");if("username"in g[0])C=g[0].username;else throw new e.CrossauthError(e.ErrorCode.Configuration,"username column "+this.idColumn+" not present in user table");if("state"in g[0])p=g[0].state;else throw new e.CrossauthError(e.ErrorCode.Configuration,"state column "+this.idColumn+" not present in user table");if(o={...g[0],id:f,username:C,state:p},!o)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(l=this.dbPool.parameters(),c=`select * from ${this.userSecretsTable} where ${this.useridForeignKeyColumn} = `+l.nextParameter(),g=await i.execute(c,[o.id]),g.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(g.length>0?n={userid:o.id,...g[0]}:n={userid:o.id},!n)throw new e.CrossauthError(e.ErrorCode.UserNotExist);if(this.useridForeignKeyColumn!="userid"&&this.useridForeignKeyColumn in n&&delete n[this.useridForeignKeyColumn],await i.commit(),(s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.awaitingTwoFactorSetup)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA setup is not complete"})),new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.disabled)throw e.CrossauthLogger.logger.debug(e.j({msg:"User is deactivated"})),new e.CrossauthError(e.ErrorCode.UserNotActive);if((s==null?void 0:s.skipEmailVerifiedCheck)!=!0&&o.state==e.UserState.awaitingEmailVerification)throw e.CrossauthLogger.logger.debug(e.j({msg:"User has not verified email"})),new e.CrossauthError(e.ErrorCode.EmailNotVerified);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.passwordChangeNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"User must change password"})),new e.CrossauthError(e.ErrorCode.PasswordChangeNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&(o.state==e.UserState.passwordResetNeeded||o.state==e.UserState.passwordAndFactor2ResetNeeded))throw e.CrossauthLogger.logger.debug(e.j({msg:"User must reset password"})),new e.CrossauthError(e.ErrorCode.PasswordResetNeeded);if((s==null?void 0:s.skipActiveCheck)!=!0&&o.state==e.UserState.factor2ResetNeeded)throw e.CrossauthLogger.logger.debug(e.j({msg:"2FA reset required"})),new e.CrossauthError(e.ErrorCode.Factor2ResetNeeded);return{user:o,secrets:n}}catch(c){throw await i.rollback(),c}finally{i.release()}}async getUsers(r,t){const s=await this.dbPool.connect();let i=[],o=[],n="",l="",c=this.dbPool.parameters();r&&(l="OFFSET "+c.nextParameter()),t&&(o.push(t),n="LIMIT "+c.nextParameter());try{let g=`select * from ${this.userTable} ${n} ${l} order by username_normalized asc`,f=await s.execute(g,o);if(f.length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);for(let C of f){let p,m,y;if(this.idColumn in C)p=C[this.idColumn];else throw new e.CrossauthError(e.ErrorCode.Configuration,"ID column "+this.idColumn+" not present in user table");if("username"in C)m=C.username;else throw new e.CrossauthError(e.ErrorCode.Configuration,"username column "+this.idColumn+" not present in user table");if("state"in C)y=C.state;else throw new e.CrossauthError(e.ErrorCode.Configuration,"state column "+this.idColumn+" not present in user table");let S={...C,id:p,username:m,state:y};i.push(S)}return i}catch(g){throw g}finally{s.release()}}async updateUser(r,t){if(!(this.idColumn in r))throw new e.CrossauthError(e.ErrorCode.InvalidKey);t&&!t.userid&&(t={...t,userid:r[this.idColumn]}),t&&this.useridForeignKeyColumn!="userid"&&this.useridForeignKeyColumn in t&&delete t[this.useridForeignKeyColumn];const s=await this.dbPool.connect();try{await s.startTransaction();let i=this.dbPool.parameters(),o=`select * from ${this.userTable} where ${this.idColumn} = `+i.nextParameter();if((await s.execute(o,[r.id])).length==0)throw new e.CrossauthError(e.ErrorCode.UserNotExist);let l={...r},c=t?{...t}:void 0;"email"in l&&l.email&&(l={email_normalized:this.normalizeEmail?q.normalize(l.email):l.email,...l}),"username"in l&&l.username&&(l={username_normalized:this.normalizeUsername?q.normalize(l.username):l.username,...l}),i=this.dbPool.parameters();let g=[],f=[];for(let C in l)l[C]!=null&&C!="id"&&(g.push(C+"= "+i.nextParameter()),f.push(l[C]));if(g.length>0){let C=g.join(", ");f.push(r.id);let p=`update ${this.userTable} set ${C} where ${this.idColumn} = `+i.nextParameter();await s.execute(p,f)}if(t){g=[],f=[],i=this.dbPool.parameters();for(let C in c)c[C]!=null&&C!="userid"&&(g.push(C+"= "+i.nextParameter()),f.push(c[C]));if(g.length>0){let C=g.join(", ");f.push(r.id);let p=`update ${this.userSecretsTable} set ${C} where userid = `+i.nextParameter();await s.execute(p,f)}}await s.commit()}catch(i){throw await s.rollback(),i}finally{s.release()}}async createUser(r,t){if(t&&(t={...t}),t&&!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordFormat,"Password required when creating user");t&&this.useridForeignKeyColumn in t&&delete t[this.useridForeignKeyColumn],t&&"userid"in t&&delete t.userid;const s=await this.dbPool.connect();let i;try{await s.startTransaction();let o={...r},n=t?{...t}:void 0;"email"in o&&o.email&&(o={email_normalized:this.normalizeEmail?q.normalize(o.email):o.email,...o}),"username"in o&&o.username&&(o={username_normalized:this.normalizeUsername?q.normalize(o.username):o.username,...o});let l=[],c=[],g=[];const f=this.dbPool.parameters();for(let p in o)o[p]!=null&&p!="id"&&(l.push(p),c.push(f.nextParameter()),g.push(o[p]));if(l.length>0){let p=l.join(", "),m=c.join(", ");const y=`insert into ${this.userTable} (${p}) values (${m}) returning ${this.idColumn}`,S=await s.execute(y,g);if(S.length==0||!S[0][this.idColumn])throw new e.CrossauthError(e.ErrorCode.Connection,"Couldn't create user");i=S[0][this.idColumn]}if(!i)throw new e.CrossauthError(e.ErrorCode.Connection,"Couldn't create user");if(t){l=[],c=[],g=[];const p=this.dbPool.parameters();l.push("userid"),c.push(p.nextParameter()),g.push(i);for(let m in n)n[m]!=null&&m!="userid"&&(l.push(m),c.push(p.nextParameter()),g.push(n[m]));if(l.length>0){let m=l.join(", "),y=c.join(", ");const S=`insert into ${this.userSecretsTable} (${m}) values (${y})`;e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:S})),await s.execute(S,g)}}return await s.commit(),(await this.getUserById(i)).user}catch(o){await s.rollback();const n=e.CrossauthError.asCrossauthError(o);throw e.CrossauthLogger.logger.debug(e.j({err:n})),n.code==e.ErrorCode.ConstraintViolation?new e.CrossauthError(e.ErrorCode.UserExists,"User already exists"):n}finally{s.release()}}async deleteUserByUsername(r){const t=await this.dbPool.connect();let{user:s}=await this.getUserByUsername(r),i=s.id;try{await t.startTransaction();let o=this.dbPool.parameters(),n=`delete from ${this.userSecretsTable} where ${this.useridForeignKeyColumn}=`+o.nextParameter();await t.execute(n,[i]),o=this.dbPool.parameters(),n=`delete from ${this.userTable} where username=`+o.nextParameter(),await t.execute(n,[r]),await t.commit()}catch(o){throw await t.rollback(),o}finally{t.release()}}async deleteUserById(r){if(this.forceIdToNumber&&typeof r=="string"&&r.match(/^[+-]?[0-9]+$/))try{return await this.deleteUserById_internal(Number(r))}catch{e.CrossauthLogger.logger.debug(e.j({msg:"Failed forcing id to number when deleting user"}))}return await this.deleteUserById_internal(r)}async deleteUserById_internal(r){const t=await this.dbPool.connect();try{await t.startTransaction();let s=this.dbPool.parameters(),i=`delete from ${this.userSecretsTable} where ${this.useridForeignKeyColumn}=`+s.nextParameter();await t.execute(i,[r]),s=this.dbPool.parameters(),i=`delete from ${this.userTable} where ${this.idColumn}=`+s.nextParameter(),await t.execute(i,[r]),await t.commit()}catch(s){throw await t.rollback(),s}finally{t.release()}}}class ze extends L{constructor(r,t={}){super();d(this,"keyTable","keys");d(this,"dbPool");d(this,"useridForeignKeyColumn","userid");h("transactionTimeout",u.Number,this,t,"TRANSACTION_TIMEOUT"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),t.keyTable&&(this.keyTable=t.keyTable),this.dbPool=r}async getKey(r){const t=await this.dbPool.connect();try{await t.startTransaction();const s=await this.getKeyInTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async getKeyInTransaction(r,t){const s=this.dbPool.parameters();let i=`select * from ${this.keyTable} where value = `+s.nextParameter(),o=await r.execute(i,[t]);if(o.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidKey);return this.makeKey(o[0])}makeKey(r){r={...r};let t,s=null,i,o;if(this.useridForeignKeyColumn in r&&(s=r[this.useridForeignKeyColumn],this.useridForeignKeyColumn!="userid"&&delete r[this.useridForeignKeyColumn]),r.value)t=r.value;else throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No value in key");if(r.created)i=r.created;else throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No creation date in key");return r.expires&&(o=r.expires),r.userid||r.userid,{value:t,userid:s,created:i,expires:o,...r}}async saveKey(r,t,s,i,o,n={}){let l,c=[this.useridForeignKeyColumn,"value","created","expires","data"],g=this.dbPool.parameters(),f=[];for(let S=0;S<5;++S)f.push(g.nextParameter());let C=[r??null,t,s,i??null,o??""];for(let S in n)c.push(S),f.push(g.nextParameter()),C.push(n[S]);let p=c.join(", "),m=f.join(", ");const y=await this.dbPool.connect();try{const S=`insert into ${this.keyTable} (${p}) values (${m})`;await y.execute(S,C)}catch(S){e.CrossauthError.asCrossauthError(S).code==e.ErrorCode.ConstraintViolation?(e.CrossauthLogger.logger.warn(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),e.CrossauthLogger.logger.debug(e.j({err:S})),l=new e.CrossauthError(e.ErrorCode.KeyExists)):(e.CrossauthLogger.logger.debug(e.j({err:S})),l=new e.CrossauthError(e.ErrorCode.Connection,"Error saving key"))}finally{y.release()}if(l)throw l}async deleteKey(r){const t=await this.dbPool.connect();try{let s=this.dbPool.parameters(),i=`delete from ${this.keyTable} where value=`;i+=s.nextParameter(),e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:i})),await t.execute(i,[r])}finally{t.release()}}async deleteAllForUser(r,t,s){const i=await this.dbPool.connect();try{let o,n=[],l="",c=this.dbPool.parameters();if(r){const g=c.nextParameter(),f=c.nextParameter();o=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} = ${g} and value like ${f} `,n=[r]}else{const g=c.nextParameter();o=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} is null and value like ${g}`}n.push(t+"%"),s&&(l="and value != "+c.nextParameter(),n.push(s)),o+=" "+l,e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:o})),await i.execute(o,n)}catch(o){throw o}finally{i.release()}}async deleteMatching(r){const t=await this.dbPool.connect();try{let s=[],i=[];const o=this.dbPool.parameters();for(let c in r){let g=c=="userid"?this.useridForeignKeyColumn:c;r[c]==null?s.push(g+" is null"):(s.push(g+" = "+o.nextParameter()),i.push(r[c]))}let n=s.join(" and "),l=`delete from ${this.keyTable} where ${n}`;await t.execute(l,i)}catch(s){throw s}finally{t.release()}}async deleteWithPrefix(r,t){const s=await this.dbPool.connect();try{let i,o=[];const n=this.dbPool.parameters();if(r){let l=n.nextParameter(),c=n.nextParameter();i=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} = ${l} and value like ${c}`,o.push(r)}else{let l=n.nextParameter();i=`delete from ${this.keyTable} where ${this.useridForeignKeyColumn} is null and value like ${l}`}o.push(t+"%"),await s.execute(i,o)}catch(i){throw i}finally{s.release()}}async getAllForUser(r){const t=await this.dbPool.connect();try{let s=[],i,o=[];const n=this.dbPool.parameters();r?(i=`select * from ${this.keyTable} where ${this.useridForeignKeyColumn} = `+n.nextParameter(),o=[r]):i=`select * from ${this.keyTable} where ${this.useridForeignKeyColumn} is null`,e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:i}));let l=await t.execute(i,o);if(l.length==0)return[];for(let c of l){let g=this.makeKey(c);this.useridForeignKeyColumn!="userid"&&(g.userid=g[this.useridForeignKeyColumn],delete g[this.useridForeignKeyColumn]),s.push(g)}return s}catch(s){throw s}finally{t.release()}}async updateKey(r){const t=await this.dbPool.connect();try{await t.startTransaction(),await this.updateKeyInTransaction(t,r),await t.commit()}catch(s){throw await t.rollback(),s}finally{t.release()}}async updateKeyInTransaction(r,t){let s={...t};if(!t.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey);delete s.value;let i=[],o=[],n=this.dbPool.parameters();for(let l in s){let c=l;s[l]!=null&&l=="userid"&&this.useridForeignKeyColumn!="userid"&&(c=this.useridForeignKeyColumn),i.push(l+"= "+n.nextParameter()),o.push(s[c])}if(o.push(t.value),i.length>0){let l=i.join(", "),c=`update ${this.keyTable} set ${l} where value = `+n.nextParameter();e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:c})),await r.execute(c,o)}}async updateData(r,t,s){return await this.updateManyData(r,[{dataName:t,value:s}])}async updateManyData(r,t){const s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getKeyInTransaction(s,r);let o;if(!i.data||i.data=="")o={};else try{o=JSON.parse(i.data)}catch(n){throw e.CrossauthLogger.logger.debug(e.j({err:n})),new e.CrossauthError(e.ErrorCode.DataFormat)}for(let n of t){let l=this.updateDataInternal(o,n.dataName,n.value);if(!l)throw new e.CrossauthError(e.ErrorCode.BadRequest,`Parents of ${n.dataName} not found in key data`);o=l}await this.updateKeyInTransaction(s,{value:i.value,data:JSON.stringify(o)}),await s.commit()}catch(i){throw await s.rollback(),i&&typeof i=="object"&&!("isCrossauthError"in i)?(e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):i}finally{s.release()}}async deleteData(r,t){const s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getKeyInTransaction(s,r);let o={},n=!1;if(i.data&&i.data!=""){try{o=JSON.parse(i.data)}catch(l){throw e.CrossauthLogger.logger.debug(e.j({err:l})),new e.CrossauthError(e.ErrorCode.DataFormat)}n=this.deleteDataInternal(o,t)}n&&await this.updateKeyInTransaction(s,{value:i.value,data:JSON.stringify(o)}),await s.commit()}catch(i){throw await s.rollback(),i&&typeof i=="object"&&!("isCrossauthError"in i)?(e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.Connection,"Failed updating session data")):i}finally{s.release()}}}class He extends se{constructor(r,t={}){super();d(this,"clientTable","oauthclient");d(this,"redirectUriTable","oauthclientredirecturi");d(this,"validFlowTable","oauthclientvalidflow");d(this,"dbPool");d(this,"useridForeignKeyColumn","userid");h("clientTable",u.String,this,t,"OAUTH_CLIENT_TABLE"),h("redirectUriTable",u.String,this,t,"OAUTH_REDIRECTURI_TABLE"),h("validFlowTable",u.String,this,t,"OAUTH_VALID_FLOW_TABLE"),h("updateMode",u.String,this,t,"OAUTHCLIENT_UPDATE_MODE"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),this.dbPool=r}async getClientById(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=await this.getClientWithTransaction(t,"client_id",r,void 0);if(await t.commit(),s.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return s[0]}catch(s){throw await t.rollback(),s}finally{t.release()}}async getClientByName(r,t){let s=await this.dbPool.connect();try{await s.startTransaction();const i=await this.getClientWithTransaction(s,"client_name",r,t);if(await s.commit(),i.length==0)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return i}catch(i){throw await s.rollback(),i}finally{s.release()}}makeClient(r){let t,s,i,o=!1,n=[],l=[];if("client_id"in r&&(t=r.client_id),!t)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);if("client_name"in r&&(s=r.client_name),!s)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);return"client_secret"in r&&(i=r.client_secret),"confidential"in r&&(o=r.confidential),"redirect_uri"in r&&r.redirect_uri&&(n=r.redirect_uri),"valid_flow"in r&&r.valid_flow&&(l=r.valid_flow),{client_id:t,client_name:s,client_secret:i,confidential:o,redirect_uri:n,valid_flow:l,...r}}async getClientWithTransaction(r,t,s,i,o,n){let l=[],c=this.dbPool.parameters(),g=[],f=`select c.*, r.uri as uri, null as flow from ${this.clientTable} as c left join ${this.redirectUriTable} r on c.client_id = r.client_id `,C="";t&&s&&(C=`where c.${t} = `+c.nextParameter(),g.push(s)),i!==null&&i==null||(C==""?C="where ":C+=" and ",i==null?C+="userid is null":(C+=`${this.useridForeignKeyColumn} = `+c.nextParameter(),g.push(i)));let p=`select c.*, null as uri, f.flow as flow from ${this.clientTable} as c left join ${this.validFlowTable} f on c.client_id = f.client_id `,m="";t&&s&&(m=`where c.${t} = `+c.nextParameter(),g.push(s)),i!==null&&i==null||(m==""?m="where ":m+=" and ",i==null?m+="userid is null":(m+=`${this.useridForeignKeyColumn} = `+c.nextParameter(),g.push(i))),n&&(o||(o=0),o=Number(o),n=Number(n),C==""?C="where ":C+=" and ",C+=` c.client_id in (select client_id from ${this.clientTable} limit ${n} offset ${o})`,m==""?m="where ":m+=" and ",m+=` c.client_id in (select client_id from ${this.clientTable} limit ${n} offset ${o})`),f+=C,p+=m;let y=f+" union "+p+" order by client_id";const S=await r.execute(y,g);let _;for(let N of S)(!_||N.client_id!=_.client_id)&&(_&&l.push(_),_=this.makeClient(N),_.valid_flow=[],_.redirect_uri=[]),N.uri&&_.redirect_uri.push(N.uri),N.flow&&_.valid_flow.push(N.flow);return _&&l.push(_),l}async createClient(r){let t=await this.dbPool.connect();try{if(await t.startTransaction(),!r.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);if((await this.getClientWithTransaction(t,"client_id",r.client_id,r.userid)).length!=0)throw new e.CrossauthError(e.ErrorCode.ClientExists);let i=await this.createClientWithTransaction(t,r);return await t.commit(),i}catch(s){throw await t.rollback(),s}finally{t.release()}}async createClientWithTransaction(r,t){const{redirect_uri:s,valid_flow:i,userid:o,...n}=t;if(o&&(n[this.useridForeignKeyColumn]=o),s)for(let m=0;m<s.length;++m){if(s[m].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[m])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[m]} is not valid`)}}if(i){for(let m=0;m<i.length;++m)if(!e.OAuthFlows.isValidFlow(i[m]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Invalid flow "+i[m])}let l=[],c=[],g=[],f=this.dbPool.parameters();try{for(let m in n)l.push(m),c.push(f.nextParameter()),g.push(n[m]);if(l.length>0){let m=l.join(", "),y=c.join(", ");const S=`insert into ${this.clientTable} (${m}) values (${y})`;await r.execute(S,g)}}catch(m){throw typeof m=="object"&&m!=null&&"code"in m&&typeof m.code=="string"&&(m.code.startsWith("22")||m.code.startsWith("23"))?(e.CrossauthLogger.logger.debug(e.j({err:m})),new e.CrossauthError(e.ErrorCode.InvalidClientId,"Attempt to create an OAuth client with a client_id that already exists. Maximum attempts failed")):(e.CrossauthLogger.logger.debug(e.j({err:m})),new e.CrossauthError(e.ErrorCode.Connection,"Error saving OAuth client"))}let C=await this.getClientWithTransaction(r,"client_id",t.client_id,t.userid);if(C.length==0)throw e.CrossauthLogger.logger.error(e.j({msg:"Attempt to create key that already exists. Stack trace follows"})),new e.CrossauthError(e.ErrorCode.KeyExists);let p=C[0];if(s)for(let m=0;m<s.length;++m){g=[],f=this.dbPool.parameters();let y=`insert into ${this.redirectUriTable} (client_id, uri) values (`+f.nextParameter()+", "+f.nextParameter()+")";g.push(p.client_id),g.push(s[m]),await r.execute(y,g)}if(i)for(let m=0;m<i.length;++m){g=[],f=this.dbPool.parameters();let y=`insert into ${this.validFlowTable} (client_id, flow) values (`+f.nextParameter()+", "+f.nextParameter()+")";g.push(p.client_id),g.push(i[m]),await r.execute(y,g)}return{...p,redirect_uri:s,valid_flow:i}}async deleteClient(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=this.deleteClientWithTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async deleteClientWithTransaction(r,t){let s=[],o=this.dbPool.parameters().nextParameter(),n=`delete from ${this.redirectUriTable} where client_id = ${o}`;s.push(t),await r.execute(n,s),n=`delete from ${this.validFlowTable} where client_id = ${o}`,await r.execute(n,s),n=`delete from ${this.clientTable} where client_id = ${o}`,await r.execute(n,s)}async updateClient(r){let t=await this.dbPool.connect();try{await t.startTransaction();const s=this.updateClientWithTransaction(t,r);return await t.commit(),s}catch(s){throw await t.rollback(),s}finally{t.release()}}async updateClientWithTransaction(r,t){if(!t.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId);const s=t.redirect_uri,i=t.valid_flow;if(s)for(let y=0;y<s.length;++y){if(s[y].includes("#"))throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,"Redirect Uri's may not contain page fragments");try{new URL(s[y])}catch{throw new e.CrossauthError(e.ErrorCode.InvalidRedirectUri,`Redriect uri ${s[y]} is not valid`)}}if(i){for(let y=0;y<i.length;++y)if(!e.OAuthFlows.isValidFlow(i[y]))throw new e.CrossauthError(e.ErrorCode.InvalidOAuthFlow,"Redirect Uri's may not contain page fragments")}if(!t.client_id)throw new e.CrossauthError(e.ErrorCode.InvalidClientId,"No client ig given");let{client_id:o,redirect_uri:n,valid_flow:l,...c}=t;n||(n=[]),l||(l=[]);let g=this.dbPool.parameters(),f=`delete from ${this.redirectUriTable} where client_id = `+g.nextParameter();await r.execute(f,[t.client_id]),g=this.dbPool.parameters(),f=`delete from ${this.validFlowTable} where client_id = `+g.nextParameter(),await r.execute(f,[t.client_id]);let C=[],p=[],m=[];g=this.dbPool.parameters(),f=`delete from ${this.validFlowTable} where client_id = `+g.nextParameter();for(let y in c)C.push(y),p.push(g.nextParameter()),m.push(c[y]);if(C.length>0){let y=C.join(", "),S=p.join(", ");f=`update ${this.clientTable} set (${y}) values (${S})`,await r.execute(f,m)}if(n)for(let y=0;y<n.length;++y){m=[],g=this.dbPool.parameters();let S=`insert into ${this.redirectUriTable} (client_id, uri) values (`+g.nextParameter()+", "+g.nextParameter()+")";m.push(t.client_id),m.push(n[y]),await r.execute(S,m)}if(l)for(let y=0;y<l.length;++y){m=[],g=this.dbPool.parameters();let S=`insert into ${this.validFlowTable} (client_id, flow) values (`+g.nextParameter()+", "+g.nextParameter()+")";m.push(t.client_id),m.push(l[y]),await r.execute(S,m)}}async getClients(r,t,s){let i=await this.dbPool.connect();try{await i.startTransaction();const o=this.getClientWithTransaction(i,void 0,void 0,s,r,t);return await i.commit(),o}catch(o){throw await i.rollback(),o}finally{i.release()}}}class Me extends ie{constructor(r,t={}){super();d(this,"authorizationTable","oauthauthorization");d(this,"useridForeignKeyColumn","userid");d(this,"dbPool");h("authorizationTable",u.String,this,t,"OAUTH_CLIENT_TABLE"),h("useridForeignKeyColumn",u.String,this,t,"USER_ID_FOREIGN_KEY_COLUMN"),this.dbPool=r}async getAuthorizations(r,t){let s=await this.dbPool.connect();try{const i=this.dbPool.parameters(),o=[];let n=`select scope from ${this.authorizationTable} where client_id = `+i.nextParameter();return o.push(r),t===null?n+=` and ${this.useridForeignKeyColumn} is null`:t&&(n+=` and ${this.useridForeignKeyColumn} = `+i.nextParameter(),o.push(t)),(await s.execute(n,o)).map(g=>g.scope)}catch(i){throw i}finally{s.release()}}async updateAuthorizations(r,t,s){let i=await this.dbPool.connect();try{await i.startTransaction();let o=this.dbPool.parameters(),n=[],l=`delete from ${this.authorizationTable} where client_id = `+o.nextParameter();n.push(r),t?(l+=` and ${this.useridForeignKeyColumn} = `+o.nextParameter(),n.push(t)):l+=` and ${this.useridForeignKeyColumn} is null`,await i.execute(l,n);for(let c of s)o=this.dbPool.parameters(),n=[],l=`insert into ${this.authorizationTable} (client_id, userid, scope) values (`+o.nextParameter()+", "+o.nextParameter()+", "+o.nextParameter()+")",n.push(r),n.push(t),n.push(c),await i.execute(l,n);await i.commit()}catch(o){throw await i.rollback(),o}finally{i.release()}}}class Ve{constructor(){}}class $e{constructor(){}}class qe{}class ae extends Ve{constructor(r){super();d(this,"pgPool");this.pgPool=r}async connect(){const r=await this.pgPool.connect();return e.CrossauthLogger.logger.debug(e.j({msg:"DB connect"})),new We(r)}parameters(){return new Je}}class We extends qe{constructor(r){super();d(this,"pgClient");this.pgClient=r}crossauthErrorFromPostgresError(r){let t,s;if(r&&typeof r=="object"&&"code"in r&&typeof r.code=="string"&&(t=r.code),r&&typeof r=="object"&&"detail"in r&&typeof r.detail=="string"&&(s=r.detail),t!=null&&t.startsWith("23")){const o=t+" : "+(s??"Constraint violation during database insert/update");return new e.CrossauthError(e.ErrorCode.ConstraintViolation,o)}const i=t?t+" : "+(s??"Constraint violation during database insert/update"):"Couldn't execute database query";return new e.CrossauthError(e.ErrorCode.Connection,i)}async execute(r,t=[]){try{return e.CrossauthLogger.logger.debug(e.j({msg:"Executing query",query:r})),(await this.pgClient.query({text:r,values:t})).rows}catch(s){throw e.CrossauthLogger.logger.debug(e.j({err:s})),this.crossauthErrorFromPostgresError(s)}}release(){e.CrossauthLogger.logger.debug(e.j({msg:"DB release"})),this.pgClient.release()}async startTransaction(){e.CrossauthLogger.logger.debug(e.j({msg:"DB start transaction"})),await this.pgClient.query("BEGIN")}async commit(){e.CrossauthLogger.logger.debug(e.j({msg:"DB commit"})),await this.pgClient.query("COMMIT")}async rollback(){e.CrossauthLogger.logger.debug(e.j({msg:"DB rollback"})),await this.pgClient.query("ROLLBACK")}}class Je extends $e{constructor(){super();d(this,"nextParam",1)}nextParameter(){return"$"+this.nextParam++}}class Ye extends q{constructor(a,r={}){super(new ae(a),r)}}class Ge extends ze{constructor(a,r={}){super(new ae(a),r)}}class Ze extends He{constructor(a,r={}){super(new ae(a),r)}}class Xe extends Me{constructor(a,r={}){super(new ae(a),r)}}class G{constructor(a){d(this,"friendlyName");d(this,"factorName","");if(!(a!=null&&a.friendlyName))throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator must have a friendly name");this.friendlyName=a==null?void 0:a.friendlyName}capabilities(){return{canCreateUser:this.canCreateUser(),canUpdateUser:this.canUpdateUser(),canUpdateSecrets:this.canUpdateSecrets()}}requireUserEntry(){return!0}}class le extends G{secretNames(){return["password"]}transientSecretNames(){return[]}mfaType(){return"none"}mfaChannel(){return"none"}}const he=process.env.PBKDF2_DIGEST||"sha256",ge=Number(process.env.PBKDF2_ITERATIONS||6e5),fe=Number(process.env.PBKDF2_KEYLENGTH||32),Qe=Number(process.env.PBKDF2_KEYLENGTH||16),Q="sha256",H=class H{static async passwordsEqual(a,r,t){let s=H.decodePasswordHash(r),i=await H.passwordHash(a,{salt:s.salt,encode:!1,secret:s.useSecret?t:void 0,iterations:s.iterations,keyLen:s.keyLen,digest:s.digest});if(i.length!=s.hashedPassword.length)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);return I.timingSafeEqual(Buffer.from(i),Buffer.from(s.hashedPassword))}static base64Decode(a){return Buffer.from(a,"base64url").toString("utf-8")}static base64Encode(a){return Buffer.from(a,"utf-8").toString("base64url")}static decodePasswordHash(a){const r=a.split(":");if(r.length!=7)throw new e.CrossauthError(e.ErrorCode.InvalidHash);try{return{hashedPassword:r[6],salt:r[5],useSecret:r[4]!="0",iterations:Number(r[3]),keyLen:Number(r[2]),digest:r[1]}}catch{throw new e.CrossauthError(e.ErrorCode.InvalidHash)}}static encodePasswordHash(a,r,t,s,i,o){return"pbkdf2:"+o+":"+String(i)+":"+String(s)+":"+(t?1:0)+":"+r+":"+a}static randomSalt(){return H.randomValue(Qe)}static randomValue(a){return I.randomBytes(a).toString("base64url")}static randomBase32(a,r){var i;const s=[...I.randomBytes(a)].map(o=>H.Base32[o%32]).join("");return r?((i=s.match(/(.{1,4})/g))==null?void 0:i.join("-"))??s:s}static uuid(){return I.randomUUID()}static hash(a){return this.sha256(a)}static sha256(a){return I.createHash("sha256").update(a).digest("base64url")}static async passwordHash(a,r={}){let{salt:t,secret:s,encode:i}={...r};t||(t=H.randomSalt());let o=s!=null,n=o?t+"!"+s:t;i==null&&(i=!1);let g=(await ve.promisify(I.pbkdf2)(a,n,r.iterations??ge,r.keyLen??fe,r.digest??he)).toString("base64url");return i&&(g=this.encodePasswordHash(g,t,o,r.iterations??ge,r.keyLen??fe,r.digest??he)),g}static signableToken(a,r,t){return r==null&&(r=H.randomSalt()),t||(t=new Date().getTime()),Buffer.from(JSON.stringify({...a,t,s:r})).toString("base64url")}static sign(a,r,t,s){const i=H.signableToken(a,t,s),o=I.createHmac(Q,r);return i+"."+o.update(i).digest("base64url")}static signSecureToken(a,r){const t=I.createHmac(Q,r);return a+"."+t.update(a).digest("base64url")}static unsign(a,r,t){const s=a.split(".");if(s.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const i=s[0],o=s[1],n=JSON.parse(Buffer.from(i,"base64url").toString());if(t&&n.t+t*1e3>new Date().getTime())throw new e.CrossauthError(e.ErrorCode.Expired);const c=I.createHmac(Q,r).update(i).digest("base64url");if(c.length!=o.length)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");if(!I.timingSafeEqual(Buffer.from(c),Buffer.from(o)))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");return n}static unsignSecureToken(a,r){const t=a.split(".");if(t.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const s=t[0],i=t[1],o=s,l=I.createHmac(Q,r).update(s).digest("base64url");if(l.length!=i.length)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");if(!I.timingSafeEqual(Buffer.from(l),Buffer.from(i)))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Signature does not match payload");return o}static xor(a,r){const t=Buffer.from(a,"base64url"),s=Buffer.from(r,"base64url"),i=t.map((o,n)=>o^s[n]);return Buffer.from(i).toString("base64url")}static symmetricEncrypt(a,r,t=void 0){t||(t=I.randomBytes(16));let s=Buffer.from(r,"base64url");var i=I.createCipheriv("aes-256-cbc",s,t);let o=i.update(a);return o=Buffer.concat([o,i.final()]),t.toString("base64url")+"."+o.toString("base64url")}static symmetricDecrypt(a,r){let t=Buffer.from(r,"base64url");const s=a.split(".");if(s.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidHash,"Not AES-256-CBC ciphertext");let i=Buffer.from(s[0],"base64url"),o=Buffer.from(s[1],"base64url");var n=I.createDecipheriv("aes-256-cbc",t,i);let l=n.update(o);return l=Buffer.concat([l,n.final()]),l.toString()}};d(H,"Base32","ABCDEFGHJKLMNPQRSTUVWXYZ23456789".split(""));let E=H;function er(w){let a=[];if(!w.password)a.push("Password not provided");else{const r=w.password;r.length<8&&a.push("Password must be at least 8 characters"),r.match(/[a-z]/)==null&&a.push("Password must contain at least one lowercase character"),r.match(/[A-Z]/)==null&&a.push("Password must contain at least one uppercase character"),r.match(/[0-9]/)==null&&a.push("Password must contain at least one digit")}return a}const te=class te extends le{constructor(r,t={}){super({friendlyName:"Local password",...t});d(this,"secret");d(this,"enableSecretForPasswords",!1);d(this,"pbkdf2Digest","sha256");d(this,"pbkdf2Iterations",6e5);d(this,"pbkdf2SaltLength",16);d(this,"pbkdf2KeyLength",32);d(this,"validatePasswordFn",er);h("secret",u.String,this,t,"HASHER_SECRET"),h("enableSecretForPasswordHash",u.Boolean,this,t,"ENABLE_SECRET_FOR_PASSWORDS"),h("pbkdf2Digest",u.String,this,t,"PASSWORD_PBKDF2_DIGEST"),h("pbkdf2Iterations",u.String,this,t,"PASSWORD_PBKDF2_ITERATIONS"),h("pbkdf2SaltLength",u.String,this,t,"PASSWORD_PBKDF2_SALTLENGTH"),h("pbkdf2KeyLength",u.String,this,t,"PASSWORD_PBKDF2_KEYLENGTH"),t.validatePasswordFn&&(this.validatePasswordFn=t.validatePasswordFn)}async authenticateUser(r,t,s){if(!s.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid,"Password not provided");if(!t.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid);if(!await E.passwordsEqual(s.password,t.password,this.secret))throw e.CrossauthLogger.logger.debug(e.j({msg:"Invalid password hash",user:r.username})),new e.CrossauthError(e.ErrorCode.PasswordInvalid);if(r.state=="awaitingtwofactorsetup")throw new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if(r.state=="awaitingemailverification")throw new e.CrossauthError(e.ErrorCode.EmailNotVerified);if(r.state=="deactivated")throw new e.CrossauthError(e.ErrorCode.UserNotActive)}validateSecrets(r){return this.validatePasswordFn(r)}async createPasswordHash(r,t){return await E.passwordHash(r,{salt:t,encode:!0,secret:this.enableSecretForPasswords?this.secret:void 0,iterations:this.pbkdf2Iterations,keyLen:this.pbkdf2KeyLength,digest:this.pbkdf2Digest})}async createPasswordForStorage(r){return this.createPasswordHash(r)}async passwordMatchesHash(r,t,s){return t==te.NoPassword?!1:await E.passwordsEqual(r,t,s)}async createPersistentSecrets(r,t,s){if(!t.password)throw new e.CrossauthError(e.ErrorCode.Unauthorized,"No password provided");if(s&&s.password!=t.password)throw new e.CrossauthError(e.ErrorCode.PasswordMatch);return{password:await this.createPasswordHash(t.password)}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!0}skipEmailVerificationOnSignup(){return!1}async prepareConfiguration(r){}async reprepareConfiguration(r,t){}};d(te,"NoPassword","********");let ne=te;class W extends G{constructor(r={}){super({friendlyName:"Email otp",...r});d(this,"views","views");d(this,"emailAuthenticatorTextBody","emailauthenticationtextbody.njk");d(this,"emailAuthenticatorHtmlBody");d(this,"emailAuthenticatorSubject","Login code");d(this,"emailFrom","");d(this,"smtpHost","");d(this,"smtpPort",587);d(this,"smtpUseTls",!0);d(this,"smtpUsername");d(this,"smtpPassword");d(this,"emailAuthenticatorTokenExpires",60*5);d(this,"render");h("views",u.String,this,r,"VIEWS"),h("emailAuthenticatorTextBody",u.String,this,r,"EMAIL_AUTHENTICATOR_TEXT_BODY"),h("emailAuthenticatorHtmlBody",u.String,this,r,"EMAIL_AUTHENTICATOR_HTML_BODY"),h("emailAuthenticatorSubject",u.String,this,r,"EMAIL_AUTHENTICATOR_SUBJECT"),h("emailFrom",u.String,this,r,"EMAIL_FROM",!0),h("smtpHost",u.String,this,r,"SMTP_HOST",!0),h("smtpPort",u.Number,this,r,"SMTP_PORT"),h("smtpUsername",u.String,this,r,"SMTP_USERNAME"),h("smtpPassword",u.String,this,r,"SMTP_PASSWORD"),h("smtpUseTls",u.Boolean,this,r,"SMTP_USE_TLS"),h("emailAuthenticatorTokenExpires",u.Number,this,r,"EMAIL_AUTHENTICATOR_TOKEN_EXPIRES"),r.render?this.render=r.render:V.configure(this.views,{autoescape:!0})}mfaType(){return"oob"}mfaChannel(){return"email"}createEmailer(){let r={};return this.smtpUsername&&(r.user=this.smtpUsername),this.smtpPassword&&(r.pass=this.smtpPassword),we.createTransport({host:this.smtpHost,port:this.smtpPort,secure:this.smtpUseTls,auth:r})}async sendToken(r,t){W.validateEmail(r),this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.emailAuthenticatorSubject},i={otp:t};return this.emailAuthenticatorTextBody&&(s.text=this.render?this.render(this.emailAuthenticatorTextBody,i):V.render(this.emailAuthenticatorTextBody,i)),this.emailAuthenticatorHtmlBody&&(s.html=this.render?this.render(this.emailAuthenticatorHtmlBody,i):V.render(this.emailAuthenticatorHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on EmailAuthenticator before using");const t=W.zeroPad(I.randomInt(999999),6),s=r.email?r.email:r.username;W.validateEmail(s);const i=new Date,o=new Date(i.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),n={username:r.username,email:s,factor2:this.factorName},l={username:r.username,factor2:this.factorName,expiry:o,otp:t},c=this.sendToken(s,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:c,email:s})),{userData:n,sessionData:l}}async reprepareConfiguration(r,t){const s=L.decodeData(t.data)["2fa"],i=W.zeroPad(I.randomInt(999999),6),o=new Date,n=new Date(o.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),l=this.sendToken(s.email,i);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:l,email:s.email})),{userData:{email:s.email,factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=W.zeroPad(I.randomInt(999999),6),s=new Date,i=new Date(s.getTime()+1e3*this.emailAuthenticatorTokenExpires).getTime(),o=r.email||r.username,n=this.sendToken(o,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp email",emailMessageId:n,email:o})),{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!0}static isEmailValid(r){return String(r).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static validateEmail(r){if(r==null||!W.isEmailValid(r))throw new e.CrossauthError(e.ErrorCode.InvalidEmail)}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class J extends G{constructor(r={}){super({friendlyName:"SMS otp",...r});d(this,"views","views");d(this,"smsAuthenticatorBody","smsauthenticationbody.njk");d(this,"smsAuthenticatorFrom","");d(this,"smsAuthenticatorTokenExpires",60*5);d(this,"render");h("views",u.String,this,r,"VIEWS"),h("smsAuthenticatorBody",u.String,this,r,"SMS_AUTHENTICATOR_BODY"),h("smsAuthenticatorFrom",u.String,this,r,"SMS_AUTHENTICATOR_FROM",!0),h("smsAuthenticatorTokenExpires",u.Number,this,r,"SMS_AUTHENTICATOR_TOKEN_EXPIRES"),r.render?this.render=r.render:V.configure(this.views,{autoescape:!0})}mfaType(){return"oob"}mfaChannel(){return"sms"}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on SmsAuthenticator before using");const t=J.zeroPad(I.randomInt(999999),6),s=r.phone;J.validatePhone(s);const i=new Date,o=new Date(i.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),n={username:r.username,phone:s,factor2:this.factorName},l={username:r.username,factor2:this.factorName,expiry:o,otp:t};let c={otp:t};const g=this.render?this.render(this.smsAuthenticatorBody,c):V.render(this.smsAuthenticatorBody,c),f=this.sendSms(s,g);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:f,phone:s})),{userData:n,sessionData:l}}async reprepareConfiguration(r,t){const s=L.decodeData(t.data)["2fa"],i=J.zeroPad(I.randomInt(999999),6),o=new Date,n=new Date(o.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),l=this.sendSms(s.phone,i);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:l,phone:s.phone})),{userData:{phone:s.phone,factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=J.zeroPad(I.randomInt(999999),6),s=new Date,i=new Date(s.getTime()+1e3*this.smsAuthenticatorTokenExpires).getTime(),o=r.phone,n=this.sendSms(o,t);return e.CrossauthLogger.logger.info(e.j({msg:"Sent factor otp sms",smsMessageId:n,phone:o})),{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}static isPhoneValid(r){return String(r).match(/^\+[1-9][0-9]{7,14}$/)!=null}static validatePhone(r){if(r==null||!J.isPhoneValid(r))throw new e.CrossauthError(e.ErrorCode.InvalidPhoneNumber)}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class ce extends J{constructor(r={}){super(r);d(this,"accountSid");d(this,"authToken");if(!process.env.TWILIO_ACCOUNT_SID||!process.env.TWILIO_AUTH_TOKEN)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in environment to use Twilio");this.accountSid=process.env.TWILIO_ACCOUNT_SID,this.authToken=process.env.TWILIO_AUTH_TOKEN}async sendSms(r,t){ce.validatePhone(r);let s={from:this.smsAuthenticatorFrom,to:r,body:t};return(await _e(this.accountSid,this.authToken).messages.create(s)).sid}}class rr extends G{constructor(r,t={}){super({friendlyName:"Dummy factor2",...t});d(this,"code");this.code=r}mfaType(){return"oob"}mfaChannel(){return"email"}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on DummyFactor2AuthenticatorOptions before using");const t=new Date,s=new Date(t.getTime()+1e3*60).getTime(),i={username:r.username,factor2:this.factorName},o={username:r.username,factor2:this.factorName,expiry:s,otp:this.code};return{userData:i,sessionData:o}}async reprepareConfiguration(r,t){const s=L.decodeData(t.data)["2fa"],i=this.code,o=new Date,n=new Date(o.getTime()+1e3*60).getTime();return{userData:{factor2:s.factor2,otp:i},secrets:{},newSessionData:{...s,otp:i,expiry:n}}}async authenticateUser(r,t,s){if(s.otp!=(t==null?void 0:t.otp))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid code");const i=new Date().getTime();if(!t.expiry||i>t.expiry)throw new e.CrossauthError(e.ErrorCode.Expired,"Token has expired")}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){const t=this.code,s=new Date,i=new Date(s.getTime()+1e3*60).getTime();return{otp:t,expiry:i}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return[]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}static isEmailValid(r){return String(r).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static zeroPad(r,t){var s=t-r.toString().length+1;return Array(+(s>0&&s)).join("0")+r}}class tr extends le{constructor(r,t={}){super({friendlyName:"LDAP",...t});d(this,"ldapAutoCreateAccount",!1);d(this,"ldapStorage");d(this,"ldapAutoCreateFactor1","ldap");h("ldapAutoCreateAccount",u.Boolean,this,t,"LDAP_AUTO_CREATE_ACCOUNT"),h("ldapAutoCreateFactor1",u.Boolean,this,t,"LDAP_AUTO_CREATE_FACTOR1"),this.ldapStorage=r}async authenticateUser(r,t,s){if(!s.password)throw new e.CrossauthError(e.ErrorCode.PasswordInvalid,"Password not provided");await this.ldapStorage.getLdapUser(r.username,s.password);let i;try{if(this.ldapAutoCreateAccount)try{i=(await this.ldapStorage.getUserByUsername(r.username)).user,i.factor1=this.ldapAutoCreateFactor1}catch{i=await this.ldapStorage.createUser({factor1:this.ldapAutoCreateFactor1,...r},s)}else i=(await this.ldapStorage.getUserByUsername(r.username)).user;if(i.state=="awaitingtwofactorsetup")throw new e.CrossauthError(e.ErrorCode.TwoFactorIncomplete);if(i.state=="awaitingemailverification")throw new e.CrossauthError(e.ErrorCode.EmailNotVerified);if(i.state=="deactivated")throw new e.CrossauthError(e.ErrorCode.UserNotActive)}catch(o){throw console.log(o),e.CrossauthLogger.logger.debug(e.j({err:o})),o}}validateSecrets(r){return[]}requireUserEntry(){return!1}async createPersistentSecrets(r,t,s){return{}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}skipEmailVerificationOnSignup(){return!1}async prepareConfiguration(r){}async reprepareConfiguration(r,t){}}class sr extends G{constructor(r,t){super({friendlyName:"Google Authenticator",...t});d(this,"appName");this.appName=r}mfaType(){return"otp"}mfaChannel(){return"none"}async createSecret(r,t){t||(t=oe.authenticator.generateSecret());let s="";return await ke.toDataURL(oe.authenticator.keyuri(r,this.appName,t)).then(i=>{s=i}).catch(i=>{throw e.CrossauthLogger.logger.debug(e.j({err:i})),new e.CrossauthError(e.ErrorCode.UnknownError,"Couldn't generate 2FA URL")}),{qrUrl:s,secret:t}}async getSecretFromSession(r,t){let s=L.decodeData(t.data);if(s&&s["2fa"]&&(s=s["2fa"]),!("totpsecret"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"TOTP data not in session");if(!("factor2"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"TOTP factor name not in session");const i=s.totpsecret,{qrUrl:o,secret:n}=await this.createSecret(r,i);return{qrUrl:o,secret:n,factor2:s.factor2}}async prepareConfiguration(r){if(!this.factorName)throw new e.CrossauthError(e.ErrorCode.Configuration,"Please set factorName on TotpAuthenticator before using");const{qrUrl:t,secret:s}=await this.createSecret(r.username),i={username:r.username,qr:t,totpsecret:s,factor2:this.factorName},o={username:r.username,totpsecret:s,factor2:this.factorName};return{userData:i,sessionData:o}}async reprepareConfiguration(r,t){const{qrUrl:s,secret:i,factor2:o}=await this.getSecretFromSession(r,t);return{userData:{qr:s,totpsecret:i,factor2:o},secrets:{totpsecret:i},newSessionData:void 0}}async authenticateUser(r,t,s){if(!t.totpsecret||!s.otp)throw new e.CrossauthError(e.ErrorCode.InvalidToken,"TOTP secret or code not given");const i=s.otp,o=t.totpsecret;if(!oe.authenticator.check(i,o))throw new e.CrossauthError(e.ErrorCode.InvalidToken,"Invalid TOTP code")}async createPersistentSecrets(r,t,s){const{secret:i}=await this.createSecret(r);return{totpsecret:i}}async createOneTimeSecrets(r){return{}}canCreateUser(){return!0}canUpdateUser(){return!0}canUpdateSecrets(){return!1}secretNames(){return["totpsecret"]}transientSecretNames(){return["otp"]}validateSecrets(r){return[]}skipEmailVerificationOnSignup(){return!1}}const ee=16;class P{constructor(a,r,t={}){d(this,"userStorage");d(this,"keyStorage");d(this,"views","views");d(this,"siteUrl");d(this,"prefix","/");d(this,"emailVerificationTextBody","emailverificationtextbody.njk");d(this,"emailVerificationHtmlBody");d(this,"emailVerificationSubject","Please verify your email");d(this,"passwordResetTextBody","passwordresettextbody.njk");d(this,"passwordResetHtmlBody");d(this,"passwordResetSubject","Password reset");d(this,"emailFrom","");d(this,"smtpHost","");d(this,"smtpPort",587);d(this,"smtpUseTls",!0);d(this,"smtpUsername");d(this,"smtpPassword");d(this,"verifyEmailExpires",60*60*24);d(this,"passwordResetExpires",60*60*24);d(this,"render");this.userStorage=a,this.keyStorage=r,h("siteUrl",u.String,this,t,"SITE_URL",!0),h("prefix",u.String,this,t,"PREFIX"),h("views",u.String,this,t,"VIEWS"),h("emailVerificationTextBody",u.String,this,t,"EMAIL_VERIFICATION_TEXT_BODY"),h("emailVerificationHtmlBody",u.String,this,t,"EMAIL_VERIFICATION_HTML_BODY"),h("emailVerificationSubject",u.String,this,t,"EMAIL_VERIFICATION_SUBJECT"),h("passwordResetTextBody",u.String,this,t,"PASSWORD_RESET_TEXT_BODY"),h("passwordResetHtmlBody",u.String,this,t,"PASSWORD_RESET_HTML_BODY"),h("passwordResetSubject",u.String,this,t,"PASSWORD_RESET_SUBJECT"),h("emailFrom",u.String,this,t,"EMAIL_FROM",!0),h("smtpHost",u.String,this,t,"SMTP_HOST",!0),h("smtpPort",u.Number,this,t,"SMTP_PORT"),h("smtpUsername",u.String,this,t,"SMTP_USERNAME"),h("smtpPassword",u.String,this,t,"SMTP_PASSWORD"),h("smtpUseTls",u.Boolean,this,t,"SMTP_USE_TLS"),h("verifyEmailExpires",u.Boolean,this,t,"VERIFY_EMAIL_EXPIRES"),h("passwordResetExpires",u.String,this,t,"PASSWORD_RESET_EXPIRES"),t.render?this.render=t.render:V.configure(this.views,{autoescape:!0})}createEmailer(){let a={};return this.smtpUsername&&(a.user=this.smtpUsername),this.smtpPassword&&(a.pass=this.smtpPassword),we.createTransport({host:this.smtpHost,port:this.smtpPort,secure:this.smtpUseTls,auth:a})}static hashEmailVerificationToken(a){return e.KeyPrefix.emailVerificationToken+E.hash(a)}static hashPasswordResetToken(a){return e.KeyPrefix.passwordResetToken+E.hash(a)}async createAndSaveEmailVerificationToken(a,r=""){let s=0;const i=new Date,o=new Date(i.getTime()+1e3*this.verifyEmailExpires);for(;s<10;){let n=E.randomValue(ee),l=P.hashEmailVerificationToken(n);try{return await this.keyStorage.saveKey(a,l,i,o,r),n}catch{n=E.randomValue(ee),l=P.hashEmailVerificationToken(n),s++}}throw new e.CrossauthError(e.ErrorCode.Connection,"failed creating a unique key")}async _sendEmailVerificationToken(a,r,t){this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.emailVerificationSubject},i={token:a,siteUrl:this.siteUrl,prefix:this.prefix};return t&&(i={...i,...t}),this.emailVerificationTextBody&&(s.text=this.render?this.render(this.emailVerificationTextBody,i):V.render(this.emailVerificationTextBody,i)),this.emailVerificationHtmlBody&&(s.html=this.render?this.render(this.emailVerificationHtmlBody,i):V.render(this.emailVerificationHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async sendEmailVerificationToken(a,r="",t={}){if(!this.emailVerificationTextBody&&!this.emailVerificationHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either emailVerificationTextBody or emailVerificationHtmlBody must be set to send email verification emails");let{user:s}=await this.userStorage.getUserById(a,{skipEmailVerifiedCheck:!0}),i=r;i!=""?P.validateEmail(i):(i=s.email??s.username,i||(i=s.username),P.validateEmail(i)),P.validateEmail(i);const o=await this.createAndSaveEmailVerificationToken(a,r),n=await this._sendEmailVerificationToken(o,i,t);e.CrossauthLogger.logger.info(e.j({msg:"Sent email verification email",emailMessageId:n,email:i}))}async verifyEmailVerificationToken(a){const r=P.hashEmailVerificationToken(a);let t=await this.keyStorage.getKey(r);try{if(!t.userid||!t.expires)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const{user:s}=await this.userStorage.getUserById(t.userid,{skipEmailVerifiedCheck:!0});let i=(s.email??s.username).toLowerCase();if(i||(i=s.username.toLowerCase()),P.validateEmail(i),new Date().getTime()>t.expires.getTime())throw new e.CrossauthError(e.ErrorCode.Expired);return{userid:t.userid,newEmail:t.data??""}}finally{}}async deleteEmailVerificationToken(a){try{const r=P.hashEmailVerificationToken(a);await this.keyStorage.deleteKey(r)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t}))}}async createAndSavePasswordResetToken(a){let t=0;const s=new Date,i=new Date(s.getTime()+1e3*this.passwordResetExpires);for(;t<10;){let o=E.randomValue(ee),n=P.hashPasswordResetToken(o);try{return await this.keyStorage.saveKey(a,n,s,i),o}catch{o=E.randomValue(ee),n=P.hashPasswordResetToken(o),t++}}throw new e.CrossauthError(e.ErrorCode.Connection,"failed creating a unique key")}async verifyPasswordResetToken(a){const r=P.hashPasswordResetToken(a);e.CrossauthLogger.logger.debug("verifyPasswordResetToken "+a+" "+r);let t=await this.keyStorage.getKey(r);if(!t.userid)throw new e.CrossauthError(e.ErrorCode.InvalidKey);if(!t.userid||!t.expires)throw new e.CrossauthError(e.ErrorCode.InvalidKey);const{user:s}=await this.userStorage.getUserById(t.userid,{skipActiveCheck:!0});if(s.state!=e.UserState.active&&s.state!=e.UserState.passwordResetNeeded&&s.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);if(new Date().getTime()>t.expires.getTime())throw new e.CrossauthError(e.ErrorCode.Expired);return s}async _sendPasswordResetToken(a,r,t){if(!this.emailVerificationTextBody&&!this.emailVerificationHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either emailVerificationTextBody or emailVerificationHtmlBody must be set to send email verification emails");this.smtpUsername&&this.smtpUsername,this.smtpPassword&&this.smtpPassword;let s={from:this.emailFrom,to:r,subject:this.passwordResetSubject},i={token:a,siteUrl:this.siteUrl,prefix:this.prefix};return t&&(i={...i,...t}),this.passwordResetTextBody&&(s.text=this.render?this.render(this.passwordResetTextBody,i):V.render(this.passwordResetTextBody,i)),this.passwordResetHtmlBody&&(s.html=this.render?this.render(this.passwordResetHtmlBody,i):V.render(this.passwordResetHtmlBody,i)),(await this.createEmailer().sendMail(s)).messageId}async sendPasswordResetToken(a,r={},t=!1){if(!this.passwordResetTextBody&&!this.passwordResetHtmlBody)throw new e.CrossauthError(e.ErrorCode.Configuration,"Either passwordResetTextBody or passwordResetTextBody must be set to send email verification emails");let{user:s}=await this.userStorage.getUserById(a,{skipActiveCheck:!0});if(!t&&s.state!=e.UserState.active&&s.state!=e.UserState.passwordResetNeeded&&s.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);let i=(s.email??s.username).toLowerCase();i||(i=s.username.toLowerCase()),P.validateEmail(i);const o=await this.createAndSavePasswordResetToken(a),n=await this._sendPasswordResetToken(o,i,r);e.CrossauthLogger.logger.info(e.j({msg:"Sent password reset email",emailMessageId:n,email:i}))}static isEmailValid(a){return String(a).toLowerCase().match(/^(([^<>()[\]\.,;:\s@"]+(\.[^<>()[\]\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)!=null}static validateEmail(a){if(a==null||!P.isEmailValid(a))throw new e.CrossauthError(e.ErrorCode.InvalidEmail)}}const me=16,Ce=16;function ir(w){return{...w,path:w.path??"/"}}class ye{constructor(a={}){d(this,"headerName","X-CROSSAUTH-CSRF");d(this,"cookieName","CSRFTOKEN");d(this,"domain");d(this,"httpOnly",!1);d(this,"path","/");d(this,"secure",!0);d(this,"sameSite","lax");d(this,"secret","");h("headerName",u.String,this,a,"CSRF_HEADER_NAME"),h("cookieName",u.String,this,a,"CSRF_COOKIE_NAME"),h("domain",u.String,this,a,"CSRF_COOKIE_DOMAIN"),h("httpOnly",u.Boolean,this,a,"CSRF_COOKIE_HTTPONLY"),h("path",u.String,this,a,"CSRF_COOKIE_PATH"),h("secure",u.Boolean,this,a,"CSRF_COOKIE_SECURE"),h("sameSite",u.String,this,a,"CSRF_COOKIE_SAMESITE"),h("secret",u.String,this,a,"SECRET",!0)}createCsrfToken(){return E.randomValue(me)}makeCsrfCookie(a){const r=E.signSecureToken(a,this.secret);let t={};return this.domain&&(t.domain=this.domain),this.path&&(t.path=this.path),t.sameSite=this.sameSite,this.httpOnly&&(t.httpOnly=this.httpOnly),this.secure&&(t.secure=this.secure),{name:this.cookieName,value:r,options:t}}makeCsrfFormOrHeaderToken(a){return this.maskCsrfToken(a)}unsignCookie(a){return E.unsignSecureToken(a,this.secret)}makeCsrfCookieString(a){let r=this.cookieName+"="+a+"; SameSite="+this.sameSite;return this.domain&&(r+="; "+this.domain),this.path&&(r+="; "+this.path),this.httpOnly&&(r+="; httpOnly"),this.secure&&(r+="; secure"),r}maskCsrfToken(a){const r=E.randomValue(me),t=E.xor(a,r);return r+"."+t}unmaskCsrfToken(a){const r=a.split(".");if(r.length!=2)throw new e.CrossauthError(e.ErrorCode.InvalidCsrf,"CSRF token in header or form not in correct format");const t=r[0],s=r[1];return E.xor(s,t)}validateDoubleSubmitCsrfToken(a,r){const t=this.unmaskCsrfToken(r);let s;try{s=E.unsignSecureToken(a,this.secret)}catch(i){throw e.CrossauthLogger.logger.error(e.j({err:i})),new e.CrossauthError(e.ErrorCode.InvalidCsrf,"Invalid CSRF cookie")}if(s!=t)throw e.CrossauthLogger.logger.warn(e.j({msg:"Invalid CSRF token received - form/header value does not match",csrfCookieHash:E.hash(a)})),new e.CrossauthError(e.ErrorCode.InvalidCsrf)}validateCsrfCookie(a){try{return E.unsignSecureToken(a,this.secret)}catch(r){throw e.CrossauthLogger.logger.error(e.j({err:r})),new e.CrossauthError(e.ErrorCode.InvalidCsrf,"Invalid CSRF cookie")}}}class K{constructor(a,r={}){d(this,"userStorage");d(this,"keyStorage");d(this,"idleTimeout",0);d(this,"persist",!0);d(this,"filterFunction");d(this,"cookieName","SESSIONID");d(this,"maxAge",60*60*24*30);d(this,"domain");d(this,"httpOnly",!1);d(this,"path","/");d(this,"secure",!0);d(this,"sameSite","lax");d(this,"secret","");r.userStorage&&(this.userStorage=r.userStorage),this.keyStorage=a,h("idleTimeout",u.Number,this,r,"SESSION_IDLE_TIMEOUT"),h("persist",u.Boolean,this,r,"PERSIST_SESSION_ID"),this.filterFunction=r.filterFunction,h("cookieName",u.String,this,r,"SESSION_COOKIE_NAME"),h("maxAge",u.String,this,r,"SESSION_COOKIE_MAX_AGE"),h("domain",u.String,this,r,"SESSION_COOKIE_DOMAIN"),h("httpOnly",u.Boolean,this,r,"SESSIONCOOKIE_HTTPONLY"),h("path",u.String,this,r,"SESSION_COOKIE_PATH"),h("secure",u.Boolean,this,r,"SESSION_COOKIE_SECURE"),h("sameSite",u.String,this,r,"SESSION_COOKIE_SAMESITE"),h("secret",u.String,this,r,"SECRET",!0)}expiry(a){let r;return this.maxAge>0&&(r=new Date,r.setTime(a.getTime()+this.maxAge*1e3)),r}static hashSessionId(a){return e.KeyPrefix.session+E.hash(a)}async createSessionKey(a,r={}){let s=0,i=E.randomValue(Ce);const o=new Date;let n=this.expiry(o),l=!1;for(;s<10&&!l;){const c=K.hashSessionId(i);try{this.idleTimeout>0&&a&&(r={...r,lastActivity:new Date}),await this.keyStorage.saveKey(a,c,o,n,void 0,r),l=!0}catch(g){let f=e.CrossauthError.asCrossauthError(g);if(f.code==e.ErrorCode.KeyExists||f.code==e.ErrorCode.InvalidKey){if(s++,i=E.randomValue(Ce),s>10)throw e.CrossauthLogger.logger.error(e.j({msg:"Max attempts exceeded trying to create session ID"})),new e.CrossauthError(e.ErrorCode.KeyExists)}else throw e.CrossauthLogger.logger.debug(e.j({err:g})),g}}return{userid:a,value:i,created:o,expires:n}}makeCookie(a,r){let t=E.signSecureToken(a.value,this.secret),s={};return r==null&&(r=this.persist),this.domain&&(s.domain=this.domain),a.expires&&r&&(s.expires=a.expires),this.path&&(s.path=this.path),s.sameSite=this.sameSite,this.httpOnly&&(s.httpOnly=this.httpOnly),this.secure&&(s.secure=this.secure),{name:this.cookieName,value:t,options:s}}makeCookieString(a){let r=a.name+"="+a.value;return this.sameSite&&(r+="; SameSite="+this.sameSite),a.options.expires&&(r+="; expires="+new Date(a.options.expires).toUTCString()),this.domain&&(r+="; domain="+this.domain),this.path&&(r+="; path="+this.path),this.httpOnly&&(r+="; httpOnly"),this.secure&&(r+="; secure"),r}async updateSessionKey(a){if(!a.value)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"No session when updating activity");a.value=K.hashSessionId(a.value),await this.keyStorage.updateKey(a)}unsignCookie(a){return E.unsignSecureToken(a,this.secret)}async getUserForSessionId(a,r){const t=await this.getSessionKey(a);if(!this.userStorage)return{key:t,user:void 0};if(t.userid){let{user:s}=await this.userStorage.getUserById(t.userid,r);return{user:s,key:t}}else return{user:void 0,key:t}}async getSessionKey(a){const r=Date.now(),t=K.hashSessionId(a),s=await this.keyStorage.getKey(t);if(s.value=a,s.expires&&r>s.expires.getTime())throw e.CrossauthLogger.logger.warn(e.j({msg:"Session id in cookie expired in key storage",hashedSessionCookie:E.hash(a)})),new e.CrossauthError(e.ErrorCode.Expired);if(s.userid&&this.idleTimeout>0&&s.lastactive&&r>s.lastactive.getTime()+this.idleTimeout*1e3)throw e.CrossauthLogger.logger.warn(e.j({msg:"Session cookie with expired idle time received",hashedSessionCookie:E.hash(a)})),new e.CrossauthError(e.ErrorCode.Expired);if(this.filterFunction&&!this.filterFunction(s))throw e.CrossauthLogger.logger.warn(e.j({msg:"Filter function on session id in cookie failed",hashedSessionCookie:E.hash(a)})),new e.CrossauthError(e.ErrorCode.InvalidKey);return s}async deleteAllForUser(a,r){r&&(r=K.hashSessionId(r)),await this.keyStorage.deleteAllForUser(a,e.KeyPrefix.session,r)}}class ar{constructor(a,r,t={}){d(this,"userStorage");d(this,"keyStorage");d(this,"emailTokenStorage");d(this,"csrfTokens");d(this,"session");d(this,"authenticators");d(this,"enableEmailVerification",!1);d(this,"enablePasswordReset",!1);d(this,"tokenEmailer");d(this,"allowedFactor2",[]);t.userStorage&&(this.userStorage=t.userStorage),this.keyStorage=a,this.authenticators=r;for(let s in this.authenticators)this.authenticators[s].factorName=s;if(this.session=new K(this.keyStorage,{...t==null?void 0:t.sessionCookieOptions,...t??{}}),this.csrfTokens=new ye({...t==null?void 0:t.doubleSubmitCookieOptions,...t??{}}),h("allowedFactor2",u.JsonArray,this,t,"ALLOWED_FACTOR2"),h("enableEmailVerification",u.Boolean,this,t,"ENABLE_EMAIL_VERIFICATION"),h("enablePasswordReset",u.Boolean,this,t,"ENABLE_PASSWORD_RESET"),this.emailTokenStorage=this.keyStorage,this.userStorage&&(this.enableEmailVerification||this.enablePasswordReset)){let s=this.keyStorage;t.emailTokenStorage&&(this.emailTokenStorage=t.emailTokenStorage),this.tokenEmailer=new P(this.userStorage,s,t)}}get sessionCookieName(){return this.session.cookieName}get sessionCookiePath(){return this.session.path}get csrfCookieName(){return this.csrfTokens.cookieName}get csrfCookiePath(){return this.csrfTokens.path}get csrfHeaderName(){return this.csrfTokens.headerName}async login(a,r,t={},s,i,o=!1){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call login if no user storage provided");let n={userid:""},l="";if(i)n=(await this.userStorage.getUserByUsername(i.username,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0})).secrets;else{let p={username:"",state:"active"};try{let y=await this.userStorage.getUserByUsername(a,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});n=y.secrets,i=y.user,p=y.user}catch{for(let S in this.authenticators)this.authenticators[S].requireUserEntry()||(p={username:r.username,state:"active"},l=S)}if(p.username=="")throw new e.CrossauthError(e.ErrorCode.UserNotExist);await this.authenticators[(i==null?void 0:i.factor1)??l].authenticateUser(p,n,r);let m=await this.userStorage.getUserByUsername(a,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});n=m.secrets,i=m.user}let c;if(i.state==e.UserState.passwordChangeNeeded)c=(await this.createAnonymousSession({data:JSON.stringify({passwordchange:{username:i.username}})})).sessionCookie;else if(i.state==e.UserState.factor2ResetNeeded)c=(await this.createAnonymousSession({data:JSON.stringify({factor2change:{username:i.username}})})).sessionCookie;else if(!o&&i.factor2&&i.factor2!=""){const{sessionCookie:p}=await this.initiateTwoFactorLogin(i);c=p}else{const p=await this.session.createSessionKey(i.id,t);c=this.session.makeCookie(p,s)}const g=this.csrfTokens.createCsrfToken(),f=this.csrfTokens.makeCsrfCookie(g),C=this.csrfTokens.makeCsrfFormOrHeaderToken(g);try{this.emailTokenStorage.deleteAllForUser(i.id,e.KeyPrefix.passwordResetToken)}catch(p){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:a})),e.CrossauthLogger.logger.debug(e.j({err:p}))}return{sessionCookie:c,csrfCookie:f,csrfFormOrHeaderValue:C,user:i,secrets:n}}async createAnonymousSession(a={}){const r=await this.session.createSessionKey(void 0,a),t=this.session.makeCookie(r,!1);let{csrfCookie:s,csrfFormOrHeaderValue:i}=await this.createCsrfToken();return{sessionCookie:t,csrfCookie:s,csrfFormOrHeaderValue:i}}async logout(a){const r=await this.session.getSessionKey(a);return await this.keyStorage.deleteKey(K.hashSessionId(r.value))}async logoutFromAll(a,r){return this.session.deleteAllForUser(a,r)}async userForSessionId(a){return await this.session.getUserForSessionId(a)}async dataStringForSessionId(a){try{let{key:r}=await this.session.getUserForSessionId(a);return r.data}catch(r){let t=e.CrossauthError.asCrossauthError(r);switch(t.code){case e.ErrorCode.Expired:return;default:throw t}}}async dataForSessionId(a){const r=await this.dataStringForSessionId(a);return!r||r.length==0?{}:JSON.parse(r)}async createCsrfToken(){this.csrfTokens.makeCsrfCookie(await this.csrfTokens.createCsrfToken());const a=this.csrfTokens.createCsrfToken(),r=this.csrfTokens.makeCsrfFormOrHeaderToken(a);return{csrfCookie:this.csrfTokens.makeCsrfCookie(a),csrfFormOrHeaderValue:r}}async createCsrfFormOrHeaderValue(a){const r=this.csrfTokens.unsignCookie(a);return this.csrfTokens.makeCsrfFormOrHeaderToken(r)}getSessionId(a){return this.session.unsignCookie(a)}validateDoubleSubmitCsrfToken(a,r){if(!a||!r)throw new e.CrossauthError(e.ErrorCode.InvalidCsrf,"CSRF missing from either cookie or form/header value");this.csrfTokens.validateDoubleSubmitCsrfToken(a,r)}validateCsrfCookie(a){this.csrfTokens.validateCsrfCookie(a)}async updateSessionActivity(a){const{key:r}=await this.session.getSessionKey(a);this.session.idleTimeout>0&&this.session.updateSessionKey({value:r.value,lastactive:new Date})}async updateSessionData(a,r,t){const s=K.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:`Updating session data value ${r}`,hashedSessionCookie:E.hash(a)})),await this.keyStorage.updateData(s,r,t)}async updateManySessionData(a,r){const t=K.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:"Updating session data",hashedSessionCookie:E.hash(a)})),await this.keyStorage.updateManyData(t,r)}async deleteSessionData(a,r){const t=K.hashSessionId(a);e.CrossauthLogger.logger.debug(e.j({msg:`Updating session data value ${r}`,hashedSessionCookie:E.hash(a)})),await this.keyStorage.deleteData(t,r)}async deleteSession(a){return await this.keyStorage.deleteKey(K.hashSessionId(a))}async createUser(a,r,t,s=!1,i=!1){var l;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call createUser if no user storage provided");if(!this.authenticators[a.factor1])throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator cannot create users");this.authenticators[a.factor1].skipEmailVerificationOnSignup()==!0&&(s=!0);let o=i?void 0:await this.authenticators[a.factor1].createPersistentSecrets(a.username,r,t);const n=i?await this.userStorage.createUser(a):await this.userStorage.createUser(a,o);return!s&&this.enableEmailVerification&&this.tokenEmailer&&await((l=this.tokenEmailer)==null?void 0:l.sendEmailVerificationToken(n.id,void 0)),n}async deleteUserByUsername(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call deleteUser if no user storage provided");this.userStorage.deleteUserByUsername(a)}async initiateTwoFactorSignup(a,r,t,s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call initiateTwoFactorSignup if no user storage provided");if(!this.authenticators[a.factor1])throw new e.CrossauthError(e.ErrorCode.Configuration,"Authenticator cannot create users");if(!this.authenticators[a.factor2])throw new e.CrossauthError(e.ErrorCode.Configuration,"Two factor authentication not enabled for user");const o=await this.authenticators[a.factor2].prepareConfiguration(a),n=o==null?{}:o.userData,l=o==null?{}:o.sessionData,c=await this.authenticators[a.factor1].createPersistentSecrets(a.username,r,s);return a.state="awaitingtwofactorsetup",await this.keyStorage.updateData(K.hashSessionId(t),"2fa",l),{userid:(await this.userStorage.createUser(a,c)).id,userData:n}}async initiateTwoFactorSetup(a,r,t){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call initiateTwOFactorSetup if no user storage provided");if(r&&r!="none"){if(!this.authenticators[r])throw new e.CrossauthError(e.ErrorCode.Configuration,"Two factor authentication not enabled for user");const i=await this.authenticators[r].prepareConfiguration(a),o=i==null?{}:i.userData,n=i==null?{}:i.sessionData;return n&&(n.userData=o),await this.keyStorage.updateData(K.hashSessionId(t),"2fa",n),o}return await this.userStorage.updateUser({id:a.id,factor2:r??""}),await this.keyStorage.updateData(K.hashSessionId(t),"2fa",void 0),{}}async repeatTwoFactorSignup(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call repeatTwoFactorSignup if no user storage provided");const r=(await this.dataForSessionId(a))["2fa"],t=r.username,s=r.factor2,i=K.hashSessionId(a),o=await this.keyStorage.getKey(i),l=await this.authenticators[s].reprepareConfiguration(t,o),c=l==null?{}:l.userData,g=l==null?{}:l.secrets,f=l==null?{}:l.newSessionData;f&&await this.keyStorage.updateData(i,"2fa",f);const{user:C}=await this.userStorage.getUserByUsername(t,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0});return{userid:C.id,userData:c,secrets:g}}async completeTwoFactorSetup(a,r){var p;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorSetup if no user storage provided");let t=!1,{user:s,key:i}=await this.session.getUserForSessionId(r,{skipActiveCheck:!0});if(s&&s.state!=e.UserState.active&&s.state!=e.UserState.factor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);if(!i)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let o=L.decodeData(i.data)["2fa"];if(!(o!=null&&o.factor2)||!(o!=null&&o.username))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");let n=o.username;const l=this.authenticators[o.factor2];if(!l)throw new e.CrossauthError(e.ErrorCode.Configuration,"Unrecognised second factor authentication");const c={},g=l.secretNames();for(let m in o)g.includes(m)&&(c[m]=o[m]);await l.authenticateUser(void 0,o,a),s||(t=!0,s=(await this.userStorage.getUserByUsername(n,{skipActiveCheck:!0,skipEmailVerifiedCheck:!0})).user);const f=l.skipEmailVerificationOnSignup()==!0;if(!s)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Couldn't fetch user");const C={id:s.id,state:!f&&this.enableEmailVerification?"awaitingemailverification":"active",factor2:o.factor2};return l.secretNames().length>0?await this.userStorage.updateUser(C,c):await this.userStorage.updateUser(C),!f&&t&&this.enableEmailVerification&&this.tokenEmailer&&await((p=this.tokenEmailer)==null?void 0:p.sendEmailVerificationToken(s.id,void 0)),await this.keyStorage.updateData(K.hashSessionId(i.value),"2fa",void 0),{...s,...C}}async initiateTwoFactorLogin(a){const t=await this.authenticators[a.factor2].createOneTimeSecrets(a),{sessionCookie:s}=await this.createAnonymousSession({data:JSON.stringify({"2fa":{username:a.username,twoFactorInitiated:!0,factor2:a.factor2,...t}})}),i=this.csrfTokens.createCsrfToken(),o=this.csrfTokens.makeCsrfCookie(i),n=this.csrfTokens.makeCsrfFormOrHeaderToken(i);return{sessionCookie:s,csrfCookie:o,csrfFormOrHeaderValue:n}}async initiateTwoFactorPageVisit(a,r,t,s,i){const n=await this.authenticators[a.factor2].createOneTimeSecrets(a);let l,c,g;const f=K.hashSessionId(r);e.CrossauthLogger.logger.debug("initiateTwoFactorPageVisit "+a.username+" "+r+" "+f);let C={username:a.username,factor2:a.factor2,secrets:n,body:t,url:s};return i&&(C["content-type"]=i),await this.keyStorage.updateData(f,"pre2fa",C),{sessionCookie:l,csrfCookie:c,csrfFormOrHeaderValue:g}}async completeTwoFactorPageVisit(a,r){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorPageVisit if no user storage provided");let{key:t}=await this.session.getUserForSessionId(r);if(!t)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let s=L.decodeData(t.data);if(!("pre2fa"in s))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");const{secrets:i}=await this.userStorage.getUserByUsername(s.pre2fa.username),o=this.authenticators[s.pre2fa.factor2];if(!o)throw new e.CrossauthError(e.ErrorCode.Configuration,"Unrecognised second factor authentication");const n={},l=o.secretNames();for(let c in i)l.includes(c)&&c in i&&(n[c]=i[c]);await o.authenticateUser(void 0,{...n,...s.pre2fa.secrets},a),await this.keyStorage.updateData(K.hashSessionId(t.value),"pre2fa",void 0)}async cancelTwoFactorPageVisit(a){let{key:r}=await this.session.getUserForSessionId(a);if(!r)throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Session key not found");let t=L.decodeData(r.data);if(!("pre2fa"in t))throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Two factor authentication not initiated");return await this.keyStorage.updateData(K.hashSessionId(r.value),"pre2fa",void 0),t.pre2fa}async completeTwoFactorLogin(a,r,t={},s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call completeTwoFactorLogin if no user storage provided");let{key:i}=await this.session.getUserForSessionId(r);if(!i||!i.data||i.data=="")throw new e.CrossauthError(e.ErrorCode.Unauthorized);let o=L.decodeData(i.data)["2fa"],n=o.username,l=o.factor2;const{user:c,secrets:g}=await this.userStorage.getUserByUsername(n),f=this.authenticators[l];if(!f)throw new e.CrossauthError(e.ErrorCode.Configuration,"Second factor "+l+" not enabled");await f.authenticateUser(c,{...g,...o},a);const C=await this.session.createSessionKey(c.id,t);await this.keyStorage.deleteKey(K.hashSessionId(i.value));const p=this.session.makeCookie(C,s),m=this.csrfTokens.createCsrfToken(),y=this.csrfTokens.makeCsrfCookie(m),S=this.csrfTokens.makeCsrfFormOrHeaderToken(m);try{this.emailTokenStorage.deleteAllForUser(c.id,e.KeyPrefix.passwordResetToken)}catch(_){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:n})),e.CrossauthLogger.logger.debug(e.j({err:_}))}return{sessionCookie:p,csrfCookie:y,csrfFormOrHeaderValue:S,user:c}}async requestPasswordReset(a){var t;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call requestPasswordReset if no user storage provided");const{user:r}=await this.userStorage.getUserByEmail(a,{skipActiveCheck:!0});if(r.state!=e.UserState.active&&r.state!=e.UserState.passwordResetNeeded&&r.state!=e.UserState.passwordAndFactor2ResetNeeded)throw new e.CrossauthError(e.ErrorCode.UserNotActive);await((t=this.tokenEmailer)==null?void 0:t.sendPasswordResetToken(r.id))}async applyEmailVerificationToken(a){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call applyEmailVerificationToken if no user storage provided");if(e.CrossauthLogger.logger.debug(e.j({msg:"applyEmailVerificationToken"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Email verification not enabled");try{let{userid:r,newEmail:t}=await this.tokenEmailer.verifyEmailVerificationToken(a),{user:s}=await this.userStorage.getUserById(r,{skipEmailVerifiedCheck:!0}),i;"email"in s&&s.email!=null?i=s.email:i=s.username;let o={id:s.id};return(s.state="awaitingemailverification")&&(o.state="active"),t!=""?o.email=t:i=void 0,await this.userStorage.updateUser(o),await this.tokenEmailer.deleteEmailVerificationToken(a),{...s,...o,oldEmail:i}}finally{}}async userForPasswordResetToken(a){if(e.CrossauthLogger.logger.debug(e.j({msg:"userForPasswordResetToken"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Password reset not enabled");return await this.tokenEmailer.verifyPasswordResetToken(a)}async changeSecrets(a,r,t,s,i){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call changeSecrets if no user storage provided");let{user:o,secrets:n}=await this.userStorage.getUserByUsername(a);const l=r==1?o.factor1:o.factor2;i!=null&&await this.authenticators[l].authenticateUser(o,n,i);const c=await this.authenticators[o.factor1].createPersistentSecrets(o.username,t,s);await this.userStorage.updateUser({id:o.id},c);try{this.emailTokenStorage.deleteAllForUser(o.id,e.KeyPrefix.passwordResetToken)}catch(g){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:a})),e.CrossauthLogger.logger.debug(e.j({err:g}))}return o}async updateUser(a,r,t=!1,s=!1){var f,C;let i;if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call updateUser if no user storage provided");if(!("id"in a)||a.id==null)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Please specify a user id");if(!("username"in a)||a.username==null)throw new e.CrossauthError(e.ErrorCode.UserNotExist,"Please specify a userername");let{email:o,username:n,password:l,...c}=r;c.userid=a.userid;let g=!1;if(o)i=o,P.validateEmail(i),g=!0;else if(n){i=n;try{P.validateEmail(a.username),g=!0}catch{}g&&P.validateEmail(i)}return!t&&this.enableEmailVerification&&g?await((f=this.tokenEmailer)==null?void 0:f.sendEmailVerificationToken(a.id,i)):(o&&(c.email=o),n&&(c.username=n)),(r.state==e.UserState.passwordResetNeeded||r.state==e.UserState.passwordAndFactor2ResetNeeded)&&await((C=this.tokenEmailer)==null?void 0:C.sendPasswordResetToken(a.id,{},s)),await this.userStorage.updateUser(c),{emailVerificationTokenSent:!t&&this.enableEmailVerification&&g,passwordResetTokenSent:r.state==e.UserState.passwordResetNeeded||r.state==e.UserState.passwordAndFactor2ResetNeeded}}async resetSecret(a,r,t,s){if(!this.userStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot call resetSecret if no user storage provided");if(e.CrossauthLogger.logger.debug(e.j({msg:"resetSecret"})),!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration,"Password reset not enabled");const i=await this.userForPasswordResetToken(a),o=r==1?i.factor1:i.factor2;if(!this.tokenEmailer)throw new e.CrossauthError(e.ErrorCode.Configuration);let n=i.state==e.UserState.passwordAndFactor2ResetNeeded?e.UserState.factor2ResetNeeded:e.UserState.active;await this.userStorage.updateUser({id:i.id,state:n},await this.authenticators[o].createPersistentSecrets(i.username,t,s));try{await this.emailTokenStorage.deleteAllForUser(i.id,e.KeyPrefix.passwordResetToken)}catch(l){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't delete password reset tokens while logging in",user:i.username})),e.CrossauthLogger.logger.debug(e.j({err:l}))}return{...i,state:n}}}class re{constructor(a,r={}){d(this,"apiKeyStorage");d(this,"keyLength",16);d(this,"secret","");d(this,"prefix",e.KeyPrefix.apiKey);d(this,"authScheme","ApiKey");this.apiKeyStorage=a,h("secret",u.String,this,r,"SECRET",!0),h("keyLength",u.String,this,r,"APIKEY_LENGTH"),h("prefix",u.String,this,r,"APIKEY_PREFIX"),h("authScheme",u.String,this,r,"APIKEY_AUTHSCHEME")}async createKey(a,r,t,s,i){const o=E.randomValue(this.keyLength),n=new Date,l=s?new Date(n.getTime()+s*1e3):void 0,c=re.hashApiKeyValue(o),g={name:a,value:o,userid:r,data:L.encodeData(t),expires:l,created:n,...i};await this.apiKeyStorage.saveKey(r,this.prefix+c,n,l,g.data,{name:a,...i});const f=this.signApiKeyValue(o);return{key:g,token:f}}static hashApiKeyValue(a){return E.hash(a)}static hashSignedApiKeyValue(a){return E.hash(a.split(".")[0])}unsignApiKeyValue(a){return E.unsign(a,this.secret).v}signApiKeyValue(a){return E.sign({v:a},this.secret)}async getKey(a){if(this.authScheme!=""&&a.startsWith(this.authScheme+" ")){const i=new RegExp(`^${this.authScheme} `);a=a.replace(i,"")}const r=this.unsignApiKeyValue(a),t=re.hashApiKeyValue(r),s=await this.apiKeyStorage.getKey(this.prefix+t);if(!("name"in s))throw new e.CrossauthError(e.ErrorCode.InvalidKey,"Not a valid API key");return{...s,name:s.name}}async validateToken(a){const r=a.split(" ");if(r.length!=2||r[0]!=this.authScheme)throw new e.CrossauthError(e.ErrorCode.InvalidKey,`Not a ${this.authScheme} token`);return await this.getKey(r[1])}}const or=16,nr=32;class M{constructor(a={}){d(this,"oauthPbkdf2Digest","sha256");d(this,"oauthPbkdf2Iterations",4e4);d(this,"oauthPbkdf2KeyLength",32);d(this,"clientStorage");if(!a.clientStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must specify clientStorage when adding a client manager");this.clientStorage=a.clientStorage,h("oauthPbkdf2Digest",u.String,this,a,"OAUTH_PBKDF2_DIGEST"),h("oauthPbkdf2KeyLength",u.String,this,a,"OAUTH_PBKDF2_KEYLENGTH"),h("requireRedirectUriRegistration",u.Boolean,this,a,"OAUTH_REQUIRE_REDIRECT_URI_REGISTRATION")}async createClient(a,r,t,s=!0,i){const o=M.randomClientId();let n,l;s&&(l=M.randomClientSecret(),n=await E.passwordHash(l,{encode:!0,iterations:this.oauthPbkdf2Iterations,keyLen:this.oauthPbkdf2KeyLength,digest:this.oauthPbkdf2Digest})),r.forEach(f=>{M.validateUri(f)}),t||(t=e.OAuthFlows.allFlows());const c={client_id:o,client_secret:n,client_name:a,redirect_uri:r,confidential:s,valid_flow:t,userid:i};let g;for(let f=0;f<5;++f)try{g=await this.clientStorage.createClient(c);break}catch(C){if(f==4){if(e.CrossauthError.asCrossauthError(C).code!=e.ErrorCode.ClientExists)throw C}else c.client_id=M.randomClientId()}if(!g)throw new e.CrossauthError(e.ErrorCode.ClientExists);return g.client_secret&&l&&(g.client_secret=l),g}async updateClient(a,r,t=!1){const s=await this.clientStorage.getClientById(a);let i=!1,o;r.confidential===!0&&!s.confidential||r.confidential===!0&&t?(o=M.randomClientSecret(),r.client_secret=await E.passwordHash(o,{encode:!0,iterations:this.oauthPbkdf2Iterations,keyLen:this.oauthPbkdf2KeyLength,digest:this.oauthPbkdf2Digest}),i=!0):r.confidential===!1&&(r.client_secret=null),r.redirect_uri&&r.redirect_uri.forEach(l=>{M.validateUri(l)}),r.client_id=a,await this.clientStorage.updateClient(r);const n=await this.clientStorage.getClientById(a);return o&&(n.client_secret=o),{client:n,newSecret:i}}static randomClientId(){return E.randomValue(or)}static randomClientSecret(){return E.randomValue(nr)}static validateUri(a){let r=!1;try{r=new URL(a).hash.length==0}catch(t){try{r=new URL(a).hash.length==0}catch{e.CrossauthLogger.logger.debug(e.j({err:t}))}}if(!r)throw e.CrossauthError.fromOAuthError("invalid_request",`Invalid redirect Uri ${a}`)}}class Ee extends e.OAuthTokenConsumerBase{constructor(r,t={}){const s={};h("jwtKeyType",u.String,s,t,"JWT_KEY_TYPE");super(r,{...t,...s});d(this,"audience");d(this,"persistAccessToken",!1);d(this,"keyStorage");d(this,"jwtSecretKeyFile","");d(this,"jwtPublicKeyFile","");if(this.audience=r,h("authServerBaseUrl",u.String,this,t,"AUTH_SERVER_BASE_URL",!0),h("jwtSecretKeyFile",u.String,this,t,"JWT_SECRET_KEY_FILE"),h("jwtPublicKeyFile",u.String,this,t,"JWT_PUBLIC_KEY_FILE"),h("jwtSecretKey",u.String,this,t,"JWT_SECRET_KEY"),h("jwtPublicKey",u.String,this,t,"JWT_PUBLIC_KEY"),h("clockTolerance",u.Number,this,t,"OAUTH_CLOCK_TOLERANCE"),h("persistAccessToken",u.Boolean,this,t,"OAUTH_PERSIST_ACCESS_TOKEN"),this.keyStorage=t.keyStorage,this.jwtSecretKey||this.jwtSecretKeyFile){if(this.jwtPublicKey||this.jwtPublicKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric and public/private JWT keys");if(this.jwtSecretKey&&this.jwtSecretKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric key and file");this.jwtSecretKeyFile&&(this.jwtSecretKey=Z.readFileSync(this.jwtSecretKeyFile,"utf8"))}else if(this.jwtPublicKey||this.jwtPublicKeyFile){if(this.jwtPublicKeyFile&&this.jwtPublicKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both public key and public key file");this.jwtPublicKeyFile&&(this.jwtPublicKey=Z.readFileSync(this.jwtPublicKeyFile,"utf8"))}}async hash(r){return E.hash(r)}async tokenAuthorized(r,t,s){var o;const i=await super.tokenAuthorized(r,t,s);if(i&&t=="access"&&this.persistAccessToken&&this.keyStorage)try{const n=e.KeyPrefix.accessToken+E.hash(i.jti?i.jti:i.sid?i.sid:""),l=await this.keyStorage.getKey(n),c=new Date;if(l.expires&&((o=l.expires)==null?void 0:o.getTime())<c.getTime()){e.CrossauthLogger.logger.error(e.j({msg:"Access token expired in storage but not in JWT"}));return}}catch(n){e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't get token from database - is it valid?",hashedAccessToken:E.hash(i.jti?i.jti:i.sid?i.sid:"")})),e.CrossauthLogger.logger.debug(e.j({err:n}));return}return i}}class pe extends e.OAuthClientBase{constructor(r,t){const s={client_id:""};h("client_id",u.String,s,t,"OAUTH_CLIENT_ID",!0);super({authServerBaseUrl:r,tokenConsumer:new Ee(s.client_id,{audience:s.client_id,authServerBaseUrl:r,...t}),...t});d(this,"deviceAuthorizationUrl","device_authorization");d(this,"userCreationType","idToken");d(this,"userMatchField","username");d(this,"idTokenMatchField","sub");d(this,"userCreationFn");d(this,"userStorage");this.client_id=s.client_id;let i={};if(h("stateLength",u.String,this,t,"OAUTH_STATE_LENGTH"),h("verifierLength",u.String,this,t,"OAUTH_VERIFIER_LENGTH"),h("client_secret",u.String,i,t,"OAUTH_CLIENT_SECRET"),h("codeChallengeMethod",u.String,this,t,"OAUTH_CODE_CHALLENGE_METHOD"),h("deviceAuthorizationUrl",u.String,this,t,"OAUTH_DEVICE_AUTHORIZATION_URL"),h("oauthLogFetch",u.Boolean,this,t,"OAUTH_LOG_FETCH"),this.deviceAuthorizationUrl.startsWith("/")&&(this.deviceAuthorizationUrl=this.deviceAuthorizationUrl.substring(1)),i.client_secret&&(this.client_secret=i.client_secret),h("userCreationType",u.String,this,t,"OAUTH_USER_CREATION_TYPE"),h("userMatchField",u.String,this,t,"OAUTH_USER_MATCH_FIELD"),h("idTokenMatchField",u.String,this,t,"OAUTH_IDTOKEN_MaTCH_FIELD"),this.userCreationType=="merge"?this.userCreationFn=lr:this.userCreationType=="embed"?this.userCreationFn=cr:t.userCreationFn&&this.userCreationType=="custom"?this.userCreationFn=t.userCreationFn:this.userCreationFn=dr,t.userStorage&&(this.userStorage=t.userStorage),h("oauthPostType",u.String,this,t,"OAUTH_POST_TYPE"),h("oauthUseUserInfoEndpoint",u.Boolean,this,t,"OAUTH_USE_USER_INFO_ENDPOINT"),h("oauthAuthorizeRedirect",u.String,this,t,"OAUTH_AUTHORIZE_REDIRECT"),this.oauthPostType!="json"&&this.oauthPostType!="form")throw new e.CrossauthError(e.ErrorCode.Configuration,"oauthPostType must be json or form")}randomValue(r){return E.randomValue(r)}async sha256(r){return E.sha256(r)}}async function lr(w,a,r,t){if(!a)throw new e.CrossauthError(e.ErrorCode.Configuration,"userCreationType set to merge but no user storage set");try{let s;return r=="username"?s=await a.getUserByUsername(w[t]):r=="username"?s=await a.getUserByEmail(w[t]):s=await a.getUserBy(r,w[t]),{...w,...s.user}}catch(s){const i=e.CrossauthError.asCrossauthError(s);if(i.code==e.ErrorCode.UserNotExist||i.code==e.ErrorCode.UserNotActive)return;throw e.CrossauthLogger.logger.error(e.j({err:s})),s}}async function cr(w,a,r,t){if(!a)throw new e.CrossauthError(e.ErrorCode.Configuration,"userCreationType set to embed but no user storage set");try{let s;return r=="username"?s=await a.getUserByUsername(w[t]):r=="username"?s=await a.getUserByEmail(w[t]):s=await a.getUserBy(r,w[t]),{...s.user,idToken:w}}catch(s){const i=e.CrossauthError.asCrossauthError(s);if(i.code==e.ErrorCode.UserNotExist||i.code==e.ErrorCode.UserNotActive)return;throw e.CrossauthLogger.logger.error({err:s}),s}}async function dr(w,a,r,t){return{id:w.userid??w.sub,username:w.sub,state:w.state??"active"}}function ur(w){switch(w){case"HS256":case"HS384":case"HS512":case"RS256":case"RS384":case"RS512":case"ES256":case"ES384":case"ES512":case"PS256":case"PS384":case"PS512":case"none":return w}throw new e.CrossauthError(e.ErrorCode.Configuration,"Invalid JWT signing algorithm "+w)}class hr{constructor(a,r,t,s={}){d(this,"clientStorage");d(this,"keyStorage");d(this,"userStorage");d(this,"authenticators",{});d(this,"authStorage");d(this,"clientManager");d(this,"oauthIssuer","");d(this,"audience",null);d(this,"requireRedirectUriRegistration",!0);d(this,"requireClientSecretOrChallenge",!0);d(this,"jwtAlgorithm","RS256");d(this,"jwtAlgorithmChecked","RS256");d(this,"codeLength",32);d(this,"jwtKeyType","");d(this,"jwtSecretKey","");d(this,"jwtPublicKey","");d(this,"jwtPrivateKey","");d(this,"jwtSecretKeyFile","");d(this,"jwtPublicKeyFile","");d(this,"jwtPrivateKeyFile","");d(this,"jwtKid","1");d(this,"secretOrPrivateKey","");d(this,"secretOrPublicKey","");d(this,"persistAccessToken",!1);d(this,"issueRefreshToken",!1);d(this,"opaqueAccessToken",!1);d(this,"accessTokenExpiry",60*60);d(this,"refreshTokenExpiry",60*60);d(this,"rollingRefreshToken",!0);d(this,"authorizationCodeExpiry",60*5);d(this,"mfaTokenExpiry",60*5);d(this,"clockTolerance",10);d(this,"emptyScopeIsValid",!0);d(this,"validateScopes",!1);d(this,"validScopes",[]);d(this,"idTokenClaims",{});d(this,"accessTokenClaims",{});d(this,"upstreamClient");d(this,"upstreamClientOptions");d(this,"userCodeExpiry",60*5);d(this,"userCodeThrottle",1500);d(this,"deviceCodePollInterval",5);d(this,"userCodeLength",8);d(this,"deviceCodeLength",16);d(this,"userCodeDashEvery",4);d(this,"deviceCodeVerificationUri","");d(this,"authServerBaseUrl","");d(this,"validFlows",["all"]);d(this,"allowedFactor2",[]);this.clientStorage=a,this.keyStorage=r,this.userStorage=s.userStorage,this.authStorage=s.authStorage,t&&(this.authenticators=t),this.clientManager=new M({clientStorage:a,...s}),h("authServerBaseUrl",u.String,this,s,"AUTH_SERVER_BASE_URL",!0),h("oauthIssuer",u.String,this,s,"OAUTH_ISSUER"),this.oauthIssuer||(this.oauthIssuer=this.authServerBaseUrl),h("audience",u.String,this,s,"OAUTH_AUDIENCE"),h("oauthPbkdf2Iterations",u.String,this,s,"OAUTH_PBKDF2_ITERATIONS"),h("requireClientSecretOrChallenge",u.Boolean,this,s,"OAUTH_REQUIRE_CLIENT_SECRET_OR_CHALLENGE"),h("jwtAlgorithm",u.String,this,s,"JWT_ALGORITHM"),h("codeLength",u.Number,this,s,"OAUTH_CODE_LENGTH"),h("jwtKeyType",u.String,this,s,"JWT_KEY_TYPE"),h("jwtSecretKeyFile",u.String,this,s,"JWT_SECRET_KEY_FILE"),h("jwtPublicKeyFile",u.String,this,s,"JWT_PUBLIC_KEY_FILE"),h("jwtPrivateKeyFile",u.String,this,s,"JWT_PRIVATE_KEY_FILE"),h("jwtSecretKey",u.String,this,s,"JWT_SECRET_KEY"),h("jwtPublicKey",u.String,this,s,"JWT_PUBLIC_KEY"),h("jwtPrivateKey",u.String,this,s,"JWT_PRIVATE_KEY"),h("jwtKid",u.String,this,s,"JWT_KID"),h("persistAccessToken",u.String,this,s,"OAUTH_PERSIST_ACCESS_TOKEN"),h("issueRefreshToken",u.String,this,s,"OAUTH_ISSUE_REFRESH_TOKEN"),h("opaqueAccessToken",u.String,this,s,"OAUTH_OPAQUE_ACCESS_TOKEN"),h("accessTokenExpiry",u.Number,this,s,"OAUTH_ACCESS_TOKEN_EXPIRY"),h("refreshTokenExpiry",u.Number,this,s,"OAUTH_REFRESH_TOKEN_EXPIRY"),h("rollingRefreshToken",u.Boolean,this,s,"OAUTH_ROLLING_REFRESH_TOKEN"),h("authorizationCodeExpiry",u.Number,this,s,"OAUTH_AUTHORIZATION_CODE_EXPIRY"),h("mfaTokenExpiry",u.Number,this,s,"OAUTH_MFA_TOKEN_EXPIRY"),h("clockTolerance",u.Number,this,s,"OAUTH_CLOCK_TOLERANCE"),h("validateScopes",u.Boolean,this,s,"OAUTH_VALIDATE_SCOPES"),h("emptyScopeIsValid",u.Boolean,this,s,"OAUTH_EMPTY_SCOPE_VALID"),h("validScopes",u.JsonArray,this,s,"OAUTH_VALID_SCOPES"),h("validFlows",u.JsonArray,this,s,"OAUTH_validFlows"),h("idTokenClaims",u.Json,this,s,"OAUTH_ID_TOKEN_CLAIMS"),h("accessTokenClaims",u.Json,this,s,"OAUTH_ACCESS_TOKEN_CLAIMS"),h("allowedFactor2",u.JsonArray,this,s,"ALLOWED_FACTOR2"),h("userCodeExpiry",u.Number,this,s,"DEVICECODE_USERCODE_EXPIRY"),h("userCodeThrottle",u.Number,this,s,"DEVICECODE_USERCODE_THROTTLE"),h("deviceCodePollInterval",u.Number,this,s,"DEVICECODE_POLL_INTERVAL"),h("deviceCodeLength",u.Number,this,s,"DEVICECODE_LENGTH"),h("userCodeLength",u.Number,this,s,"DEVICECODE_USERCODE_LENGTH");let i={};if(h("userCodeDashEvery",u.String,i,s,"DEVICECODE_USERCODE_DASH_EVERY"),i.userCodeDashEvery)if(i.userCodeDashEvery==""||i.userCodeDashEvery.toLowerCase()=="null")this.userCodeDashEvery=null;else try{this.userCodeDashEvery=Number(i.userCodeDashEvery)}catch{throw new e.CrossauthError(e.ErrorCode.Configuration,"userCodeDashEvery must be a number or null")}if(h("deviceCodeVerificationUri",u.String,this,s,"DEVICECODE_VERIFICATION_URI"),s.upstreamClient&&(this.upstreamClientOptions=s.upstreamClient,this.upstreamClient=new pe(s.upstreamClient.authServerBaseUrl,s.upstreamClient.options),!s.upstreamClient.options.redirect_uri))throw new e.CrossauthError(e.ErrorCode.Configuration,"Must define redirect_uri in upstreamClient options");if(this.validFlows.length==1&&this.validFlows[0]==e.OAuthFlows.All&&(this.validFlows=e.OAuthFlows.allFlows()),this.jwtAlgorithmChecked=ur(this.jwtAlgorithm),this.jwtSecretKey||this.jwtSecretKeyFile){if(this.jwtPublicKey||this.jwtPublicKeyFile||this.jwtPrivateKey||this.jwtPrivateKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric and public/private JWT keys");if(this.jwtSecretKey&&this.jwtSecretKeyFile)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify symmetric key and file");this.jwtSecretKeyFile&&(this.jwtSecretKey=Z.readFileSync(this.jwtSecretKeyFile,"utf8"))}else if((this.jwtPrivateKey||this.jwtPrivateKeyFile)&&(this.jwtPublicKey||this.jwtPublicKeyFile)){if(this.jwtPrivateKeyFile&&this.jwtPrivateKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both private key and private key file");if(this.jwtPrivateKeyFile&&(this.jwtPrivateKey=Z.readFileSync(this.jwtPrivateKeyFile,"utf8")),this.jwtPublicKeyFile&&this.jwtPublicKey)throw new e.CrossauthError(e.ErrorCode.Configuration,"Cannot specify both public key and public key file");this.jwtPublicKeyFile&&(this.jwtPublicKey=Z.readFileSync(this.jwtPublicKeyFile,"utf8"))}else throw new e.CrossauthError(e.ErrorCode.Configuration,"Must specify either a JWT secret key or a public and private key pair");if(this.jwtSecretKey?this.secretOrPrivateKey=this.secretOrPublicKey=this.jwtSecretKey:(this.secretOrPrivateKey=this.jwtPrivateKey,this.secretOrPublicKey=this.jwtPublicKey),(this.jwtPublicKey||this.jwtPrivateKey)&&!this.jwtKeyType)throw new e.CrossauthError(e.ErrorCode.Configuration,"If setting jwtPublicKey or jwtPrivate key, must also set jwtKeyType");if(this.opaqueAccessToken&&(this.persistAccessToken=!0),(this.validFlows.includes(e.OAuthFlows.Password)||this.validFlows.includes(e.OAuthFlows.PasswordMfa))&&(!this.userStorage||Object.keys(this.authenticators).length==0))throw new e.CrossauthError(e.ErrorCode.Configuration,"If password flow or password MFA flow is enabled, userStorage and authenticators must be provided");if((this.issueRefreshToken||this.persistAccessToken)&&!this.keyStorage)throw new e.CrossauthError(e.ErrorCode.Configuration,"Must have key storage if persisting access tokens or issuing refresh tokens")}async authorizeGetEndpoint({responseType:a,client_id:r,redirect_uri:t,scope:s,state:i,codeChallenge:o,codeChallengeMethod:n,user:l}){if(!this.responseTypesSupported().includes(a))return{error:"unsupported_response_type",error_description:"Unsupported response type "+a};let g;try{g=await this.clientStorage.getClientById(r)}catch(y){return e.CrossauthLogger.logger.debug(e.j({err:y})),{error:"unauthorized_client",error_description:"Client is not authorized"}}const{scopes:f,error:C,error_description:p}=await this.validateAndPersistScope(r,s,l);if(C)return{error:C,error_description:p};const m=this.inferFlowFromGet(a,f||[],o);if(!m||!this.validFlows.includes(m))return{error:"access_denied",error_description:"Unsupported flow type "+m};if(!g.valid_flow.includes(m))return{error:"unauthorized_client",error_description:"Client does not support "+m};try{this.validateState(i)}catch{return{error:"invalid_request",error_description:"Invalid state"}}return a=="code"?await this.getAuthorizationCode(g,t,f,i,o,n,l):{error:"unsupported_response_type",error_description:`Invalid response_type ${a}`}}async hasAllScopes(a,r,t){if(!this.authStorage)return!1;const s=await this.authStorage.getAuthorizations(a,r==null?void 0:r.id);return t.filter(o=>s.includes(o)).length==t.length}async validateAndPersistScope(a,r,t){let s,i;if(!r&&!this.emptyScopeIsValid)return{error:"invalid_scope",error_description:"Must provide at least one scope"};if(r){const{error:o,errorDescription:n,scopes:l}=this.validateScope(r);if(s=l,i=l,o)return{error:o,error_description:n??"Unknown error"}}else i=[null];if(this.authStorage)try{const o=i??[],n=await this.authStorage.getAuthorizations(a,t==null?void 0:t.id),l=[...new Set([...n,...o])];e.CrossauthLogger.logger.debug(e.j({msg:"Updating authorizations for "+a+" to "+l})),await this.authStorage.updateAuthorizations(a,(t==null?void 0:t.id)??null,l)}catch(o){return e.CrossauthLogger.logger.debug(e.j({err:o})),{error:"server_error",error_description:"Couldn't save scope"}}else if(r)return{error:"server_error",error_description:"Must provide auth storage in order to use scopes"};return{scopes:s}}async authenticateClient(a,r,t){let s=!1;switch(a){case e.OAuthFlows.AuthorizationCode:case e.OAuthFlows.AuthorizationCodeWithPKCE:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.ClientCredentials:s=!0;break;case e.OAuthFlows.Password:case e.OAuthFlows.PasswordMfa:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.RefreshToken:s=r.confidential==!0||r.client_secret!=null||t!=null;break;case e.OAuthFlows.DeviceCode:s=r.confidential==!0||r.client_secret!=null||t!=null;break}return s&&(r.client_secret==null||t==null)?{error:"access_denied",error_description:"Client secret is required for this client"}:s&&(!t||!r.client_secret)?{error:"access_denied",error_description:"Client is confidential but either secret not passed or is missing in database"}:s&&!await E.passwordsEqual(t??"",r.client_secret??"")?{error:"access_denied",error_description:"Incorrect client secret"}:{}}async getClientById(a){let r;try{return r=await this.clientStorage.getClientById(a),{client:r}}catch{return{error:"access_denied",error_description:"client id does not exist"}}}async tokenEndpoint({grantType:a,client_id:r,scope:t,code:s,client_secret:i,codeVerifier:o,refreshToken:n,username:l,password:c,mfaToken:g,oobCode:f,bindingCode:C,otp:p,deviceCode:m}){var U,B,j;const y=this.inferFlowFromPost(a,o);if(!y)return{error:"server_error",error_description:"Unable to determine OAuth flow type"};const S=await this.getClientById(r);if(!S.client)return S;const _=S.client,N=await this.authenticateClient(y,_,i);if(N.error)return N;if(y==e.OAuthFlows.Password&&!this.validFlows.includes(y)&&!this.validFlows.includes(e.OAuthFlows.PasswordMfa))return{error:"access_denied",error_description:"Unsupported flow type "+y};if(!y||!this.validFlows.includes(y))return{error:"access_denied",error_description:"Unsupported flow type "+y};if(_&&!_.valid_flow.includes(y))return{error:"unauthorized_client",error_description:"Client does not support "+y};let D=!1;this.issueRefreshToken&&y!=e.OAuthFlows.RefreshToken&&(D=!0),this.issueRefreshToken&&y==e.OAuthFlows.RefreshToken&&this.rollingRefreshToken&&(D=!0);let b;if(a=="authorization_code")return this.requireClientSecretOrChallenge&&_&&_.client_secret&&!i&&!o?{error:"access_denied",error_description:"Must provide either a client secret or use PKCE"}:_&&_.client_secret&&!i?{error:"access_denied",error_description:"No client secret or code verifier provided for authorization coode flow"}:s?await this.makeAccessToken({client:_,code:s,client_secret:i,codeVerifier:o,issueRefreshToken:D}):{error:"access_denied",error_description:"No authorization code provided for authorization code flow"};if(a=="refresh_token"){if(this.upstreamClient&&this.upstreamClientOptions){if(!n)return{error:"invalid_request",error_description:"If executing the refresh token flow, must provide a refresh token"};let T=await this.upstreamClient.refreshTokenFlow(n);if(!T.access_token)return{error:"access_denied",error_description:"Didn't receive an access token"};let v=T.access_token;if(this.upstreamClientOptions.accessTokenIsJwt&&(v=await this.upstreamClient.validateAccessToken(T.access_token,!1),!v))return{error:"access_denied",error_description:"Couldn't decode access token"};const O=await this.upstreamClientOptions.tokenMergeFn(v,T.id_payload,this.userStorage);if(O.authorized){const F=await this.createTokensFromPayload(r,O.access_payload,O.id_payload);return T.access_token=F.access_token,T.id_token=F.id_token,T.id_payload=F.id_payload,T}else return e.CrossauthLogger.logger.warn(e.j({msg:O.error_description})),{error:O.error,error_description:O.error_description}}const A=await this.getRefreshTokenData(n);if(!n||!A||!this.userStorage)return{error:"access_denied",error_description:"Refresh token is invalid"};let k;if(A.username)try{const{user:T}=await((U=this.userStorage)==null?void 0:U.getUserByUsername(A.username));k=T}catch(T){return e.CrossauthLogger.logger.error(e.j({err:T,msg:"Couldn't get user for refresh token. Doesn't exist?",username:A.username})),{error:"access_denied",error_description:"Refresh token is invalid"}}try{const T=e.KeyPrefix.refreshToken+E.hash(n);await this.keyStorage.deleteKey(T)}catch(T){const v=e.CrossauthError.asCrossauthError(T);e.CrossauthLogger.logger.debug(e.j({err:T})),e.CrossauthLogger.logger.warn(e.j({msg:"Cannot delete refresh token",cerr:v}))}return await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,issueRefreshToken:D,scopes:A.scope,user:k})}else if(a=="client_credentials"){const{scopes:A,error:k,error_description:T}=await this.validateAndPersistScope(r,t,void 0);return k?{error:k,error_description:T}:await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:D})}else if(a=="password"){if(!l||!c)return{error:"access_denied",error_description:"Username and/or password not provided for password flow"};try{if(!this.userStorage)return{error:"server_error",error_description:"Password authentication not configured"};const{user:v,secrets:O}=await this.userStorage.getUserByUsername(l),F=this.authenticators[v.factor1];if(!F||!F.secretNames().includes("password"))return{error:"access_denied",error_description:"Password flow used but factor 1 authenticator does not accept passwords"};await F.authenticateUser(v,O,{password:c}),b=v}catch(v){return e.CrossauthLogger.logger.debug(e.j({err:v})),{error:"access_denied",error_description:"Username and/or password do not match"}}const{scopes:A,error:k,error_description:T}=await this.validateAndPersistScope(r,t,b);return k?{error:k,error_description:T}:b.factor2?this.allowedFactor2.length>0&&(b.state==e.UserState.factor2ResetNeeded||!this.allowedFactor2.includes(b.factor2?b.factor2:"none"))?{error:"access_denied",error_description:"2FA method not allowed or needs to be reconfigured"}:await this.createMfaRequest(b):await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:D,user:b})}else if(a=="http://auth0.com/oauth/grant-type/mfa-otp"){const{scopes:A,error:k,error_description:T}=await this.validateAndPersistScope(r,t,void 0);if(k)return{error:k,error_description:T};if(!p)return{error:"access_denied",error_description:"OTP not provided"};if(!g)return{error:"access_denied",error_description:"MFA token not provided"};const v=await this.validateMfaToken(g),O=e.KeyPrefix.mfaToken+E.hash(g);if(!v.user||!v.key)return{error:"access_denied",error_description:"Invalid MFA token"};const F=this.authenticators[v.user.factor2];if(!F||!this.userStorage)return{error:"access_denied",error_description:"MFA type is not supported for OAuth"};try{const{secrets:R}=await this.userStorage.getUserById(v.user.id);await F.authenticateUser(v.user,R,{otp:p})}catch(R){return e.CrossauthLogger.logger.debug(e.j({err:R})),{error:"access_denied",error_description:"Invalid OTP"}}try{await this.keyStorage.deleteKey(O)}catch(R){e.CrossauthLogger.logger.debug(e.j({err:R})),e.CrossauthLogger.logger.warn(e.j({cerr:R,msg:"Couldn't delete mfa token",hashedMfaToken:v.key.value}))}return await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:D,user:v.user})}else if(a=="http://auth0.com/oauth/grant-type/mfa-oob"){const{scopes:A,error:k,error_description:T}=await this.validateAndPersistScope(r,t,void 0);if(k)return{error:k,error_description:T};if(!f||!C)return{error:"access_denied",error_description:"OOB code or binding code not provided"};if(!g)return{error:"access_denied",error_description:"MFA token not provided"};const v=await this.validateMfaToken(g);if(!v.user||!v.key)return{error:"access_denied",error_description:"Invalid MFA token"};const O=this.authenticators[v.user.factor2];if(!O||!this.userStorage)return{error:"access_denied",error_description:"MFA type is not supported for OAuth"};try{const{secrets:F}=await this.userStorage.getUserById(v.user.id),R=L.decodeData(v.key.data).omfa;if(!R||!R.otp||!R.oobCode)return{error:"server_error",error_description:"Cannot retrieve email OTP"};if(R.oobCode!=f)return{error:"access_denied",error_description:"Invalid OOB code"};await O.authenticateUser(v.user,{...F,otp:R.otp,expiry:(B=v.key.expires)==null?void 0:B.getTime()},{otp:C})}catch(F){return e.CrossauthLogger.logger.debug(e.j({err:F})),{error:"access_denied",error_description:"Invalid OTP"}}try{await this.keyStorage.deleteKey(v.key.value)}catch(F){e.CrossauthLogger.logger.debug(e.j({err:F})),e.CrossauthLogger.logger.warn(e.j({cerr:F,msg:"Couldn't delete mfa token",hashedMfaToken:v.key.value}))}return await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,scopes:A,issueRefreshToken:D,user:v.user})}else if(a=="urn:ietf:params:oauth:grant-type:device_code"){if(!m)return{error:"invalid_request",error_description:"No device code given"};let A;try{A=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+m)}catch(k){const T=e.CrossauthError.asCrossauthError(k);return e.CrossauthLogger.logger.debug(e.j({err:T})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't get device code",cerr:T})),{error:"accerss_denied",error_description:"Invalid device code"}}try{const k=JSON.parse(A.data??"{}"),T=new Date().getTime();if(A.expires&&T>A.expires.getTime())return await this.deleteDeviceCode(m),{error:"expired_token",error_description:"Code has expired"};if(k.ok!=!0)return{error:"authorization_pending",error_description:"Waiting for user code to be entered"};{let v=k.scope?k.scope.split(" "):void 0,O=k.userid?await((j=this.userStorage)==null?void 0:j.getUserById(k.userid)):void 0;return await this.deleteDeviceCode(m),await this.makeAccessToken({client:_,client_secret:i,codeVerifier:o,scopes:v,issueRefreshToken:D,user:O==null?void 0:O.user})}}catch(k){const T=e.CrossauthError.asCrossauthError(k);return e.CrossauthLogger.logger.debug(e.j({err:T})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't get device code",cerr:T})),await this.deleteDeviceCode(m),{error:"accerss_denied",error_description:"Invalid device code"}}}else return{error:"invalid_request",error_description:`Invalid grant_type ${a}`}}async deleteDeviceCode(a){try{await this.keyStorage.deleteKey(e.KeyPrefix.deviceCode+a)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't delete device code",cerr:t}))}}async deleteUserCode(a){try{await this.keyStorage.deleteKey(e.KeyPrefix.userCode+a)}catch(r){const t=e.CrossauthError.asCrossauthError(r);e.CrossauthLogger.logger.debug(e.j({err:t})),e.CrossauthLogger.logger.error(e.j({msg:"Couldn't delete user code",cerr:t}))}}async deviceAuthorizationEndpoint({client_id:a,scope:r,client_secret:t}){var m;if(this.deviceCodeVerificationUri=="")return{error:"invalid_request",error_description:"Must provide deviceCodeVerificationUri if using the device code flow"};try{new URL(this.deviceCodeVerificationUri)}catch{return{error:"invalid_request",error_description:"Invalid deviceCodeVerificationUri"}}const s=e.OAuthFlows.DeviceCode,i=await this.getClientById(a);if(!i.client)return i;const o=i.client,n=await this.authenticateClient(s,o,t);if(n.error)return n;if(!this.validFlows.includes(s))return{error:"access_denied",error_description:"Unsupported flow type "+s};if(r){const{error:y,errorDescription:S}=this.validateScope(r);if(y)return{error:y,error_description:S}}let l,c=!1;const g=new Date,f=this.userCodeExpiry,C=new Date(g.getTime()+this.userCodeExpiry*1e3+this.clockTolerance*1e3);for(let y=0;y<10&&!c;++y)try{l=E.randomValue(this.deviceCodeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.deviceCode+l,g,C,JSON.stringify({scope:r,client_id:a})),c=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!c||!l)return{error:"server_error",error_description:"Couldn't create device code"};let p;c=!1;for(let y=0;y<10&&!c;++y)try{p=E.randomBase32(this.userCodeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.userCode+p,g,C,JSON.stringify({deviceCode:l})),c=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!c||!p)return await this.deleteDeviceCode(l),{error:"server_error",error_description:"Couldn't create device code"};if(p&&this.userCodeDashEvery){const y=new RegExp(String.raw`(.{1,${this.userCodeDashEvery}})`,"g");p=(m=p.match(y))==null?void 0:m.join("-")}return{device_code:l,user_code:p,verification_uri:this.deviceCodeVerificationUri,verification_uri_complete:this.deviceCodeVerificationUri+"?user_code="+p,expires_in:f,interval:this.deviceCodePollInterval}}async deviceEndpoint({userCode:a,user:r}){var g;a=a.replace(/[ -]*/g,"");let t,s={};try{t=await this.keyStorage.getKey(e.KeyPrefix.userCode+a),s=JSON.parse((t==null?void 0:t.data)??"{}")}catch{return{ok:!1,error:"access_denied",error_description:"Invalid user code"}}if(!s.deviceCode)return e.CrossauthLogger.logger.error(e.j({msg:"No device code for user code",userCodeHash:E.hash(a)})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"No device code for user code"};let i;try{i=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+s.deviceCode)}catch(f){const C=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:C})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid device code for user code",userCodeHash:E.hash(a),deviceCodeHash:E.hash(s.deviceCode),cerr:C})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"Invalid device code user code"}}let o,n;try{if(!i.data)throw new e.CrossauthError(e.ErrorCode.UnknownError);const f=JSON.parse(i.data);if(o=f.scope,n=f.client_id,!n)throw new e.CrossauthError(e.ErrorCode.UnknownError)}catch{return await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"server_error",error_description:"Unexpected or incomplete data in device code key"}}if(new Date().getTime()>((g=s.expires)==null?void 0:g.getTime()))return await this.deleteUserCode(a),{ok:!1,error:"expired_token",error_description:"User code has expired",client_id:n};if(s.ok==!0)return{ok:!1,error:"access_denied",error_description:"User code has already been used",client_id:n};let c=!1;if(e.CrossauthLogger.logger.debug(e.j({msg:"Checking scopes have been authorized",scope:o})),o?c=await this.hasAllScopes(n,r,o.split(" ")):c=await this.hasAllScopes(n,r,[null]),!c){try{r!=null&&r.id&&await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"userid",r.id)}catch(f){const C=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:C})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update user id on user code entry - deleting",cerr:C})),await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:n}}return{ok:!0,scope:o,client_id:n,scopeAuthorizationNeeded:!0}}try{r!=null&&r.id&&await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"userid",r.id),await this.keyStorage.updateData(e.KeyPrefix.deviceCode+s.deviceCode,"ok",!0)}catch(f){const C=e.CrossauthError.asCrossauthError(f);return e.CrossauthLogger.logger.debug(e.j({err:C})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update status on user code entry - deleting",cerr:C})),await this.deleteUserCode(a),await this.deleteDeviceCode(s.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:n}}return await this.deleteUserCode(a),{ok:!0,scope:o,client_id:n}}async authorizeDeviceFlowScopes(a){a=a.replace(/[ -]*/g,"");let r,t={};try{r=await this.keyStorage.getKey(e.KeyPrefix.userCode+a),t=JSON.parse((r==null?void 0:r.data)??"{}")}catch{return{ok:!1,error:"access_denied",error_description:"Invalid user code"}}if(!t.deviceCode)return e.CrossauthLogger.logger.error(e.j({msg:"No device code for user code",userCodeHash:E.hash(a)})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"No device code for user code"};let s;try{s=await this.keyStorage.getKey(e.KeyPrefix.deviceCode+t.deviceCode)}catch(n){const l=e.CrossauthError.asCrossauthError(n);return e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.error(e.j({msg:"Invalid device code for user code",userCodeHash:E.hash(a),deviceCodeHash:E.hash(t.deviceCode),cerr:l})),await this.deleteUserCode(a),{ok:!1,error:"server_error",error_description:"Invalid device code user code"}}let i,o;try{if(!s.data)throw new e.CrossauthError(e.ErrorCode.UnknownError);const n=JSON.parse(s.data);if(i=n.scope,o=n.client_id,!o)throw new e.CrossauthError(e.ErrorCode.UnknownError)}catch{return await this.deleteUserCode(a),await this.deleteDeviceCode(t.deviceCode),{ok:!1,error:"server_error",error_description:"Unexpected or incomplete data in device code key"}}try{await this.keyStorage.updateData(e.KeyPrefix.deviceCode+t.deviceCode,"ok",!0)}catch(n){const l=e.CrossauthError.asCrossauthError(n);return e.CrossauthLogger.logger.debug(e.j({err:l})),e.CrossauthLogger.logger.warn(e.j({msg:"Couldn't update status on user code entry - deleting",cerr:l})),await this.deleteUserCode(a),await this.deleteDeviceCode(t.deviceCode),{ok:!1,error:"access_denied",error_description:"Invalid user code",client_id:o}}return await this.deleteUserCode(a),{ok:!0,scope:i,client_id:o}}async createMfaRequest(a){const r=E.randomValue(this.codeLength),t=e.KeyPrefix.mfaToken+E.hash(r),s=new Date;try{await this.keyStorage.saveKey(a.id,t,s,this.mfaTokenExpiry?new Date(s.getTime()+(this.mfaTokenExpiry+this.clockTolerance)*1e3):void 0,JSON.stringify({omfaaid:a.factor2}))}catch(i){const o=e.CrossauthError.asCrossauthError(i);e.CrossauthLogger.logger.debug(e.j({err:o})),e.CrossauthLogger.logger.error(e.j({cerr:o,msg:"Couldn't save MFA token"}))}return{mfa_token:r,error:"mfa_required",error_description:"Multifactor authentication required"}}async validateMfaToken(a){var s;let r,t;try{const i=e.KeyPrefix.mfaToken+E.hash(a);if(t=await this.keyStorage.getKey(i),!t.userid)return{error:"access_denied",error_description:"Invalid MFA token"};if(!this.userStorage)return{error:"server_error",error_description:"No user storage defined"};const{user:o}=await((s=this.userStorage)==null?void 0:s.getUserById(t.userid));r=o}catch(i){return e.CrossauthLogger.logger.debug(e.j({err:i})),e.CrossauthLogger.logger.error(e.j({cerr:i,msg:"Invalid MFA token"})),{error:"access_denied",error_description:"Invalid MFA token"}}if(!r)return{error:"access_denied",error_description:"Invalid MFA token"};try{if(L.decodeData(t.data).omfaaid!=r.factor2)return{error:"access_denied",error_description:"authenticatorId not valid for user"}}catch{return{error:"server_error",error_description:"Error getting data for MFA token"}}return{user:r,key:t}}async mfaAuthenticatorsEndpoint(a){const r=await this.validateMfaToken(a);if(!r.user)return r;const t=r.user;if(!t.factor2)return{authenticators:[]};const s=this.authenticators[t.factor2];if(!s)return{error:"server_error",error_description:"User has an unsupported MFA authenticator"};let i;if(s.mfaType()=="otp")i={id:t.factor2,authenticator_type:"otp",active:!0};else if(s.mfaType()=="oob")i={id:t.factor2,authenticator_type:"oob",active:!0,name:t.email??t.username,oob_channel:s.mfaChannel()};else return{error:"server_error",error_description:"User has an unsupported MFA authenticator"};return{authenticators:[i]}}async mfaChallengeEndpoint(a,r,t,s,i){const o=e.OAuthFlows.PasswordMfa,n=await this.getClientById(r);if(!n.client)return n;const l=n.client,c=await this.authenticateClient(o,l,t);if(c.error)return c;const g=await this.validateMfaToken(a);if(!g.user||!g.key)return g;if(g.user.factor2!=i)return{error:"access_denied",error_description:"Invalid MFA authenticator"};if(s!="otp"&&s!="oob")return{error:"invalid_request",error_description:"Invalid MFA challenge type"};let f={};s=="oob"&&(f={oobCode:E.randomValue(this.codeLength)});try{const C=this.authenticators[g.user.factor2];if(!C)throw new e.CrossauthError(e.ErrorCode.Configuration,"User's authenticator has not been loaded");const p=await C.createOneTimeSecrets(g.user);await this.keyStorage.updateData(g.key.value,"omfa",{...f,...p})}catch(C){return e.CrossauthLogger.logger.debug(e.j({err:C})),{error:"server_error",error_description:"Unable to initiate OOB authentication"}}return s=="otp"?{challenge_type:"otp"}:{challenge_type:"oob",oob_code:f==null?void 0:f.oobCode,binding_method:"prompt"}}inferFlowFromGet(a,r,t){if(a=="code"&&!r.includes("openid"))return t?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode;if(r.includes("openid")&&a=="code")return t?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode}inferFlowFromPost(a,r){if(a=="authorization_code")return r?e.OAuthFlows.AuthorizationCodeWithPKCE:e.OAuthFlows.AuthorizationCode;if(a=="client_credentials")return e.OAuthFlows.ClientCredentials;if(a=="refresh_token")return e.OAuthFlows.RefreshToken;if(a=="urn:ietf:params:oauth:grant-type:device_code")return e.OAuthFlows.DeviceCode;if(a=="password")return e.OAuthFlows.Password;if(a=="http://auth0.com/oauth/grant-type/mfa-otp")return e.OAuthFlows.PasswordMfa;if(a=="http://auth0.com/oauth/grant-type/mfa-oob")return e.OAuthFlows.PasswordMfa}async getAuthorizationCode(a,r,t,s,i,o,n){if(i&&(o||(o="S256"),o!="S256"&&o!="plain"))return{error:"invalid_request",error_description:"Code challenge method must be S256 or plain"};const l=r;if(M.validateUri(l),this.requireRedirectUriRegistration&&!a.redirect_uri.includes(l))return{error:"invalid_request",error_description:`The redirect uri ${r} is invalid`};const c=new Date,g=this.authorizationCodeExpiry?new Date(c.getTime()+this.authorizationCodeExpiry*1e3+this.clockTolerance*1e3):void 0,f={client_id:a.client_id,redirect_uri:r};t&&(f.scope=t),i&&(f.challengeMethod=o,f.challenge=E.hash(i)),n&&(f.username=n.username,f.id=n.id);const C=JSON.stringify(f);let p=!1,m="";for(let y=0;y<10&&!p;++y)try{m=E.randomValue(this.codeLength),await this.keyStorage.saveKey(void 0,e.KeyPrefix.authorizationCode+E.hash(m),c,g,C),p=!0}catch{e.CrossauthLogger.logger.debug(e.j({msg:`Attempt number${y} at creating a unique authozation code failed`}))}if(!p)throw new e.CrossauthError(e.ErrorCode.KeyExists,"Couldn't create a authorization code");return{code:m,state:s}}async getAuthorizationCodeData(a){let r,t={};try{r=await this.keyStorage.getKey(e.KeyPrefix.authorizationCode+E.hash(a)),t=L.decodeData(r.data)}catch(s){e.CrossauthLogger.logger.debug(e.j({err:s}));return}return t}async deleteAuthorizationCodeData(a){try{await this.keyStorage.deleteKey(e.KeyPrefix.authorizationCode+E.hash(a))}catch(r){e.CrossauthLogger.logger.warn(e.j({err:r,msg:"Couldn't delete authorization code from storage"}))}}async setAuthorizationCodeData(a,r){const t=await this.keyStorage.getKey(e.KeyPrefix.authorizationCode+E.hash(a));t.data=JSON.stringify(r),this.keyStorage.updateKey(t)}async makeAccessToken({client:a,code:r,client_secret:t,codeVerifier:s,scopes:i,issueRefreshToken:o=!1,user:n}){var N,D;let l=!0;try{a.client_secret!=null&&(l=await E.passwordsEqual(t??"",a.client_secret??""))}catch(b){return e.CrossauthLogger.logger.error(e.j({err:b})),{error:"server_error",error_description:"Couldn't validate client"}}if(!l)return{error:"access_denied",error_description:"Invalid client secret"};let c={};if(r){let b;try{b=await this.keyStorage.getKey(e.KeyPrefix.authorizationCode+E.hash(r)),c=L.decodeData(b.data)}catch(U){return e.CrossauthLogger.logger.debug(e.j({err:U})),{error:"access_denied",error_description:"Invalid or expired authorization code"}}try{await this.keyStorage.deleteKey(b.value)}catch(U){e.CrossauthLogger.logger.warn(e.j({err:U,msg:"Couldn't delete authorization code from storatge",client_id:a==null?void 0:a.client_id}))}i=c.scope}if(n&&(c.username=n.username),c.challengeMethod&&!c.challenge&&c.challengeMethod!="plain"&&c.challengeMethod!="S256")return{error:"access_denied",error_description:"Invalid code challenge/code challenge method method for authorization code"};if(c.challenge){const b=c.challengeMethod=="plain"?s??"":E.sha256(s??"");if(E.hash(b)!=c.challenge)return{error:"access_denied",error_description:"Code verifier is incorrect"}}const g=new Date,f=Math.ceil(g.getTime()/1e3);let C;if((i&&i.includes("openid")||Object.keys(this.accessTokenClaims).length>0)&&this.userStorage&&c.username)try{const{user:b}=await this.userStorage.getUserByUsername(c.username);n=b}catch(b){return e.CrossauthLogger.logger.error(e.j({err:b})),{error:"server_error",error_description:"Couldn't load user data"}}const p=E.uuid();let m={jti:p,iat:f,iss:this.oauthIssuer,sub:c.username,type:"access"};m=this.addClaims(m,this.accessTokenClaims,i,n),i&&(m.scope=i),this.accessTokenExpiry!=null&&(m.exp=f+this.accessTokenExpiry,C=new Date(g.getTime()+this.accessTokenExpiry*1e3+this.clockTolerance*1e3)),this.audience&&(m.aud=this.audience);const y=await new Promise((b,U)=>{Y.sign(m,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:"1"},(B,j)=>{j?b(j):U(B||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})});this.persistAccessToken&&this.keyStorage&&await((N=this.keyStorage)==null?void 0:N.saveKey(void 0,e.KeyPrefix.accessToken+E.hash(p),g,C));let S;if(i&&i.includes("openid")){const b=E.uuid();let U={aud:a.client_id,jti:b,iat:f,iss:this.oauthIssuer,sub:c.username,type:"id"};if(i.includes("email")&&(n!=null&&n.email)&&(U.email=n.email),i.includes("address")&&n&&"address"in n&&(U.address=n.address),i.includes("phone")&&n&&"phone"in n&&(U.phone=n.phone),i.includes("profile")&&n)for(let B of["name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"])U[B]=n[B];U=this.addClaims(U,this.idTokenClaims,i,n),U.scope=i,this.accessTokenExpiry!=null&&(U.exp=f+this.accessTokenExpiry),S=await new Promise((B,j)=>{Y.sign(U,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:this.jwtKid},(A,k)=>{k?B(k):j(A||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})})}let _;if(o){const b={username:c.username,client_id:a.client_id};i&&(b.scope=i);let U;const j={jti:E.uuid(),iat:f,iss:this.oauthIssuer,sub:c.username,type:"refresh"};this.refreshTokenExpiry!=null&&(j.exp=f+this.refreshTokenExpiry,U=this.refreshTokenExpiry?new Date(f+this.refreshTokenExpiry*1e3+this.clockTolerance*1e3):void 0),this.oauthIssuer&&(j.aud=this.oauthIssuer),_=await new Promise((A,k)=>{Y.sign(j,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:"1"},(T,v)=>{v?A(v):k(T||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})}),_&&await((D=this.keyStorage)==null?void 0:D.saveKey(void 0,e.KeyPrefix.refreshToken+E.hash(_),g,U,JSON.stringify(b)))}return{access_token:y,id_token:S,refresh_token:_,expires_in:this.accessTokenExpiry==null?void 0:this.accessTokenExpiry,token_type:"Bearer",scope:i?i.join(" "):void 0}}async createTokensFromPayload(a,r,t){var g;const s=new Date,i=Math.ceil(s.getTime()/1e3);let o,n,l,c;if(r){const f=E.uuid();let C={...r,jti:f,iat:i,iss:this.oauthIssuer,type:"access"};this.accessTokenExpiry!=null&&(C.exp=i+this.accessTokenExpiry,o=new Date(s.getTime()+this.accessTokenExpiry*1e3+this.clockTolerance*1e3)),this.audience&&(C.aud=this.audience),n=await new Promise((p,m)=>{Y.sign(C,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:"1"},(y,S)=>{S?p(S):m(y||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})}),c=C,this.persistAccessToken&&this.keyStorage&&await((g=this.keyStorage)==null?void 0:g.saveKey(void 0,e.KeyPrefix.accessToken+E.hash(f),s,o))}if(t!=null){const f=E.uuid();if(t={...t,aud:a,jti:f,iat:i,iss:this.oauthIssuer,type:"id"},t){const C=t;l=await new Promise((p,m)=>{Y.sign(C,this.secretOrPrivateKey,{algorithm:this.jwtAlgorithmChecked,keyid:this.jwtKid},(y,S)=>{S?p(S):m(y||new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})})}}return{access_token:n,id_token:l,access_payload:c,id_payload:t,expires_in:this.accessTokenExpiry==null?void 0:this.accessTokenExpiry,token_type:"Bearer"}}addClaims(a,r,t,s){if(s){if(t){for(let i of t)if(i in r)if(r[i]=="all")a={...a,...s};else{let o=r[i];typeof o=="string"&&(o=[o]);for(let n in o)a[n]=s[o[n]]}}if("all"in r){let i=r.all;if(typeof i=="string"&&(i=[i]),i=="all")a={...a,...s};else for(let o in i)a[o]=s[i[o]]}}return a}async validAuthorizationCode(a){try{const r=e.KeyPrefix.authorizationCode+E.hash(a);return await this.keyStorage.getKey(r),!0}catch(r){return e.CrossauthLogger.logger.debug(e.j({err:r})),!1}}async validRefreshToken(a){try{const r=e.KeyPrefix.refreshToken+E.hash(a);return await this.keyStorage.getKey(r),!0}catch(r){return e.CrossauthLogger.logger.debug(e.j({err:r})),!1}}async getRefreshTokenData(a){if(a)try{const r=e.KeyPrefix.refreshToken+E.hash(a),t=await this.keyStorage.getKey(r);return JSON.parse(t.data||"{}")}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validIdToken(a){try{return await this.validateJwt(a,"id")}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validAccessToken(a){try{const r=await this.validateJwt(a,"access");if(this.persistAccessToken){const t=e.KeyPrefix.accessToken+E.hash(r.payload.jti);await this.keyStorage.getKey(t)}return r}catch(r){e.CrossauthLogger.logger.debug(e.j({err:r}));return}}async validateJwt(a,r){return new Promise((t,s)=>{Y.verify(a,this.secretOrPublicKey,{clockTolerance:this.clockTolerance,complete:!0},(i,o)=>{o?!r||o.payload.type==r?t(o):s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid JWT type")):i?(e.CrossauthLogger.logger.debug(e.j({err:i})),s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid JWT signature"))):s(new e.CrossauthError(e.ErrorCode.Unauthorized,"Couldn't create jwt"))})})}validateScope(a){let r=[];try{r=a.split(" ")}catch{const s="invalid_scope",i=`Invalid scope ${a}`;return e.CrossauthLogger.logger.debug(e.j({err:e.CrossauthError.fromOAuthError(s,i)})),{error:s,errorDescription:i}}if(this.validateScopes){let t;if(r.forEach(s=>{if(!this.validScopes.includes(s)){const i="invalid_scope",o=`Illegal scope ${s}`;e.CrossauthLogger.logger.debug(e.j({err:e.CrossauthError.fromOAuthError(i,o)})),t={error:i,errorDescription:o}}}),t)return t}return{scopes:r}}redirect_uri(a,r,t){const s=a.includes("?")?"&":"?";return`${a}${s}code=${r}&state=${t}`}responseTypesSupported(){let a=[];return(this.validFlows.includes(e.OAuthFlows.AuthorizationCode)||this.validFlows.includes(e.OAuthFlows.AuthorizationCodeWithPKCE)||this.validFlows.includes(e.OAuthFlows.OidcAuthorizationCode))&&a.push("code"),a}oidcConfiguration({authorizeEndpoint:a,tokenEndpoint:r,jwksUri:t,additionalClaims:s}){let i=[];this.validFlows.forEach(n=>{const l=e.OAuthFlows.grantType(n);l&&(i=[...i,...l])});const o=["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512","PS256","PS384","PS512"];return s||(s=[]),{issuer:this.oauthIssuer,authorization_endpoint:new URL(a??"authorize",this.oauthIssuer).toString(),token_endpoint:new URL(r??"token",this.oauthIssuer).toString(),token_endpoint_auth_methods_supported:["client_secret_post"],jwks_uri:new URL(t??"jwks",this.oauthIssuer).toString(),response_types_supported:this.responseTypesSupported(),response_modes_supported:["query"],grant_types_supported:i,token_endpoint_auth_signing_alg_values_supported:o,subject_types_supported:["public"],id_token_signing_alg_values_supported:o,claims_supported:["iss","sub","aud","jti","iat","type",...s],request_uri_parameter_supported:!0,require_request_uri_registration:this.requireRedirectUriRegistration}}jwks(){let a=[];if(this.jwtPublicKey){const r=be.createPublicKey(this.jwtPublicKey).export({format:"jwk"});r.kid="1",r.alg=this.jwtKeyType,a.push(r)}return{keys:a}}validateState(a){if(!/^[A-Za-z0-9_-]+$/.test(a))throw e.CrossauthError.fromOAuthError("invalid_request")}validateAuthorizeParameters({response_type:a,client_id:r,redirect_uri:t,scope:s,state:i,code_challenge:o,code_challenge_method:n}){let l;/^[A-Za-z0-9_-]+$/.test(a)?/^[A-Za-z0-9_-]+$/.test(r)?s&&!/^[A-Za-z0-9_+ -]+$/.test(s)?l="scope is invalid":/^[A-Za-z0-9_-]+$/.test(i)?o&&!/^[A-Za-z0-9_-]+$/.test(o)?l="code_challenge is invalid":n&&!/^[A-Za-z0-9_-]+$/.test(n)&&(l="code_challenge_method is invalid"):l="state is invalid":l="client_id is invalid":l="response_type is invalid";try{new URL(t)}catch{l="redirect_uri is invalid"}return(!t||t.includes("#"))&&(l="redirect_uri is invalid"),l?{error:"invalid_request",error_description:l}:{}}}class gr{constructor(a,r={}){d(this,"tokenConsumers");this.tokenConsumers=[...a]}async accessTokenAuthorized(a){try{const r=Pe.decodeJwt(a);for(let t of this.tokenConsumers)if(r.iss==t.authServerBaseUrl&&(r.aud==t.audience||r.aud==null&&t.audience==""))return await t.tokenAuthorized(a,"access");throw new e.CrossauthError(e.ErrorCode.Unauthorized,"Invalid issuer in access token")}catch(r){e.CrossauthLogger.logger.warn(e.j({err:r}));return}}}exports.ApiKeyManager=re;exports.Authenticator=G;exports.Crypto=E;exports.DoubleSubmitCsrfToken=ye;exports.DummyFactor2Authenticator=rr;exports.EmailAuthenticator=W;exports.InMemoryKeyStorage=De;exports.InMemoryOAuthAuthorizationStorage=Re;exports.InMemoryOAuthClientStorage=je;exports.InMemoryUserStorage=Ne;exports.KeyStorage=L;exports.LdapAuthenticator=tr;exports.LdapUserStorage=X;exports.LocalPasswordAuthenticator=ne;exports.OAuthAuthorizationServer=hr;exports.OAuthAuthorizationStorage=ie;exports.OAuthClientBackend=pe;exports.OAuthClientManager=M;exports.OAuthClientStorage=se;exports.OAuthResourceServer=gr;exports.OAuthTokenConsumer=Ee;exports.ParamType=u;exports.PasswordAuthenticator=le;exports.PostgresKeyStorage=Ge;exports.PostgresOAuthAuthorizationStorage=Xe;exports.PostgresOAuthClientStorage=Ze;exports.PostgresUserStorage=Ye;exports.PrismaKeyStorage=Fe;exports.PrismaOAuthAuthorizationStorage=xe;exports.PrismaOAuthClientStorage=Le;exports.PrismaUserStorage=$;exports.SessionCookie=K;exports.SessionManager=ar;exports.SmsAuthenticator=J;exports.TokenEmailer=P;exports.TotpAuthenticator=sr;exports.TwilioAuthenticator=ce;exports.UserStorage=x;exports.setParameter=h;exports.toCookieSerializeOptions=ir;
|