@authrim/core 0.1.0 → 0.1.1
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 +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/config.ts","../src/types/errors.ts","../src/client/discovery.ts","../src/events/emitter.ts","../src/auth/pkce.ts","../src/utils/base64url.ts","../src/auth/state.ts","../src/utils/jwt.ts","../src/auth/authorization-code.ts","../src/types/token.ts","../src/token/manager.ts","../src/token/introspection.ts","../src/token/revocation.ts","../src/session/logout.ts","../src/session/token-api.ts","../src/session/manager.ts","../src/client/index.ts","../src/auth/silent-auth.ts","../src/utils/hash.ts"],"names":["resolveConfig","config","AuthrimError","_AuthrimError","code","message","options","error","getErrorMeta","ERROR_META_MAP","normalizeIssuer","issuer","_DiscoveryClient","normalizedIssuer","cached","discoveryUrl","doc","response","docIssuer","DiscoveryClient","EventEmitter","event","handler","onceHandler","data","handlers","PKCEHelper","crypto","codeVerifier","codeChallenge","verifier","base64urlEncode","base64","len","i","byte1","byte2","byte3","triplet","BASE64_CHARS","base64urlDecode","str","paddingLen","outputLen","output","outputIndex","BASE64_LOOKUP","byte4","stringToBase64url","encoder","base64urlToString","base64url","STORAGE_KEYS","issuerHash","clientIdHash","state","_StateManager","storage","ttlSeconds","stateBytes","nonceBytes","nonce","now","authState","key","stored","prefix","all","value","StateManager","decodeJwt","jwt","parts","headerB64","payloadB64","signature","header","payload","decodeIdToken","idToken","isJwtExpired","skewSeconds","getIdTokenNonce","AuthorizationCodeFlow","http","clientId","discovery","pkce","endpoint","params","scope","protectedParams","result","callbackUrl","searchParams","errorDescription","tokenEndpoint","body","errorData","tokenResponse","expiresAt","TOKEN_TYPE_URIS","_TokenManager","tokens","refreshToken","attemptedRetry","authrimError","newTokens","request","subjectTokenType","type","TokenManager","TokenIntrospector","token","TokenRevoker","LogoutHandler","storedIdToken","storedTokens","revocationResult","endSessionEndpoint","tokenKey","idTokenKey","TokenApiClient","accessToken","userinfoEndpoint","SessionManager","hashForKey","length","hash","AuthrimClient","hashLength","tokenApiClient","pkcePair","createAuthrimClient","client","INTERACTIVE_LOGIN_REQUIRED_ERRORS","SilentAuthHandler","responseUrl","errorCode","calculateDsHash","deviceSecret","leftHalf"],"mappings":"aAqJO,SAASA,CAAAA,CAAcC,CAAAA,CAA6C,CACzE,OAAO,CACL,GAAGA,CAAAA,CACH,MAAA,CAAQA,CAAAA,CAAO,QAAU,CAAC,QAAA,CAAU,SAAS,CAAA,CAC7C,WAAYA,CAAAA,CAAO,UAAA,EAAc,KAAA,CACjC,mBAAA,CAAqBA,EAAO,mBAAA,EAAuB,IAAA,CAAO,GAAA,CAC1D,kBAAA,CAAoBA,EAAO,kBAAA,EAAsB,EAAA,CACjD,eAAA,CAAiBA,CAAAA,CAAO,iBAAmB,GAAA,CAC3C,WAAA,CAAa,CACX,UAAA,CAAYA,EAAO,WAAA,EAAa,UAAA,EAAc,EAChD,CACF,CACF,CCzDO,IAAMC,CAAAA,CAAN,MAAMC,CAAAA,SAAqB,KAAM,CAatC,WAAA,CAAYC,EAAwBC,CAAAA,CAAiBC,CAAAA,CAA+B,CAClF,KAAA,CAAMD,CAAO,CAAA,CACb,IAAA,CAAK,IAAA,CAAO,cAAA,CACZ,KAAK,IAAA,CAAOD,CAAAA,CACZ,IAAA,CAAK,OAAA,CAAUE,GAAS,OAAA,CACxB,IAAA,CAAK,QAAA,CAAWA,CAAAA,EAAS,SACzB,IAAA,CAAK,KAAA,CAAQA,CAAAA,EAAS,MACxB,CAKA,OAAO,cAAA,CAAeC,CAAAA,CAIL,CAef,IAAMH,CAAAA,CAda,CACjB,iBAAA,CACA,sBACA,eAAA,CACA,2BAAA,CACA,eAAA,CACA,cAAA,CACA,0BACA,eAAA,CACA,eACF,CAAA,CAI0C,QAAA,CAASG,EAAM,KAAuB,CAAA,CAC3EA,CAAAA,CAAM,KAAA,CACP,kBAEJ,OAAO,IAAIJ,CAAAA,CAAaC,CAAAA,CAAMG,EAAM,iBAAA,EAAqBA,CAAAA,CAAM,KAAA,CAAO,CACpE,SAAUA,CAAAA,CAAM,SAAA,CAChB,OAAA,CAAS,CAAE,cAAeA,CAAAA,CAAM,KAAM,CACxC,CAAC,CACH,CAKA,WAAA,EAAuB,CACrB,OAAO,IAAA,CAAK,IAAA,GAAS,eAAA,EAAmB,IAAA,CAAK,OAAS,eACxD,CAKA,IAAI,IAAA,EAAyB,CAC3B,OAAOC,CAAAA,CAAa,IAAA,CAAK,IAAI,CAC/B,CACF,CAAA,CAKMC,CAAAA,CAA6D,CAEjE,gBAAiB,CACf,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,iBAAA,CACZ,QAAA,CAAU,OACZ,EACA,mBAAA,CAAqB,CACnB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,iBAAA,CACZ,SAAU,OACZ,CAAA,CACA,aAAA,CAAe,CACb,UAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,iBACZ,QAAA,CAAU,OACZ,CAAA,CACA,yBAAA,CAA2B,CACzB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,iBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,cAAe,CACb,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,iBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,YAAA,CAAc,CACZ,SAAA,CAAW,KACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,WAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,uBAAA,CAAyB,CACvB,SAAA,CAAW,KACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,WAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,MACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CAGA,cAAe,CACb,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,EACA,aAAA,CAAe,CACb,SAAA,CAAW,KAAA,CACX,UAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,iBACZ,QAAA,CAAU,OACZ,CAAA,CACA,cAAA,CAAgB,CACd,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,gBAAiB,CACf,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,EACA,oBAAA,CAAsB,CACpB,SAAA,CAAW,IAAA,CACX,UAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,KACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,WAAY,CAAA,CACZ,UAAA,CAAY,eAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,KACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,WAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,eAAA,CAAiB,CACf,SAAA,CAAW,IAAA,CACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,IACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,SAAU,OACZ,CAAA,CACA,kBAAA,CAAoB,CAClB,UAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,kBACZ,QAAA,CAAU,OACZ,CAAA,CACA,mBAAA,CAAqB,CACnB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,iBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,cAAe,CACb,SAAA,CAAW,IAAA,CACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,UAAA,CAAY,EACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,OACZ,EACA,iBAAA,CAAmB,CACjB,SAAA,CAAW,KAAA,CACX,UAAW,KAAA,CACX,UAAA,CAAY,iBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CAGA,SAAA,CAAW,CACT,SAAA,CAAW,MACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,SAAU,SACZ,CAAA,CACA,aAAA,CAAe,CACb,UAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,YAAa,CACX,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,EACA,aAAA,CAAe,CACb,SAAA,CAAW,IAAA,CACX,UAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,UAAA,CAAY,EACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,OACZ,EACA,oBAAA,CAAsB,CACpB,SAAA,CAAW,IAAA,CACX,UAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CAGA,WAAA,CAAa,CACX,SAAA,CAAW,MACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,SAAU,OACZ,CAAA,CACA,YAAA,CAAc,CACZ,UAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,iBACZ,QAAA,CAAU,OACZ,CAAA,CACA,aAAA,CAAe,CACb,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,gBAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CAGA,eAAA,CAAiB,CACf,SAAA,CAAW,KAAA,CACX,UAAW,KAAA,CACX,UAAA,CAAY,MAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CACA,YAAA,CAAc,CACZ,SAAA,CAAW,KACX,SAAA,CAAW,IAAA,CACX,YAAA,CAAc,GAAA,CACd,WAAY,CAAA,CACZ,UAAA,CAAY,OAAA,CACZ,QAAA,CAAU,OACZ,CAAA,CAGA,oBAAA,CAAsB,CACpB,SAAA,CAAW,MACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,MAAA,CACZ,SAAU,SACZ,CAAA,CACA,cAAA,CAAgB,CACd,SAAA,CAAW,IAAA,CACX,SAAA,CAAW,IAAA,CACX,aAAc,GAAA,CACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,QACZ,QAAA,CAAU,OACZ,CAAA,CAGA,mBAAA,CAAqB,CACnB,SAAA,CAAW,IAAA,CACX,SAAA,CAAW,IAAA,CACX,aAAc,GAAA,CACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,QACZ,QAAA,CAAU,OACZ,CAAA,CACA,gBAAA,CAAkB,CAChB,SAAA,CAAW,IAAA,CACX,SAAA,CAAW,IAAA,CACX,aAAc,GAAA,CACd,UAAA,CAAY,CAAA,CACZ,UAAA,CAAY,QACZ,QAAA,CAAU,OACZ,CAAA,CACA,yBAAA,CAA2B,CACzB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,MAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,uBAAwB,CACtB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,MAAA,CACZ,QAAA,CAAU,SACZ,EAGA,cAAA,CAAgB,CACd,SAAA,CAAW,KAAA,CACX,UAAW,KAAA,CACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,oBAAA,CAAsB,CACpB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,UAAA,CAAY,iBACZ,QAAA,CAAU,SACZ,CAAA,CACA,gBAAA,CAAkB,CAChB,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,KAAA,CACX,WAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,CAAA,CACA,2BAA4B,CAC1B,SAAA,CAAW,KAAA,CACX,SAAA,CAAW,MACX,UAAA,CAAY,gBAAA,CACZ,QAAA,CAAU,SACZ,CACF,EAKO,SAASD,CAAAA,CAAaJ,CAAAA,CAA0C,CACrE,OAAOK,CAAAA,CAAeL,CAAI,CAC5B,CCxbO,SAASM,CAAAA,CAAgBC,CAAAA,CAAwB,CACtD,OAAOA,CAAAA,CAAO,OAAA,CAAQ,MAAA,CAAQ,EAAE,CAClC,CAKO,IAAMC,CAAAA,CAAN,MAAMA,CAAgB,CAQ3B,WAAA,CAAYN,CAAAA,CAAiC,CAL7C,IAAA,CAAiB,KAAA,CAAsC,IAAI,GAAA,CAMzD,KAAK,IAAA,CAAOA,CAAAA,CAAQ,IAAA,CACpB,IAAA,CAAK,WAAaA,CAAAA,CAAQ,UAAA,EAAcM,CAAAA,CAAgB,qBAC1D,CASA,MAAM,QAAA,CAASD,CAAAA,CAAgD,CAC7D,IAAME,CAAAA,CAAmBH,CAAAA,CAAgBC,CAAM,EACzCG,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAID,CAAgB,CAAA,CAG9C,GAAIC,CAAAA,EAAU,CAAC,KAAK,SAAA,CAAUA,CAAM,CAAA,CAClC,OAAOA,EAAO,GAAA,CAIhB,IAAMC,CAAAA,CAAe,CAAA,EAAGF,CAAgB,CAAA,iCAAA,CAAA,CAEpCG,CAAAA,CACJ,GAAI,CACF,IAAMC,CAAAA,CAAW,MAAM,IAAA,CAAK,IAAA,CAAK,MAA6BF,CAAY,CAAA,CAC1E,GAAI,CAACE,CAAAA,CAAS,EAAA,CACZ,MAAM,IAAIf,EAAa,iBAAA,CAAmB,CAAA,0BAAA,EAA6Be,CAAAA,CAAS,MAAM,GAAI,CACxF,OAAA,CAAS,CAAE,MAAA,CAAQA,EAAS,MAAA,CAAQ,UAAA,CAAYA,CAAAA,CAAS,UAAW,CACtE,CAAC,CAAA,CAEHD,CAAAA,CAAMC,CAAAA,CAAS,KACjB,CAAA,MAASV,CAAAA,CAAO,CACd,MAAIA,aAAiBL,CAAAA,CACbK,CAAAA,CAEF,IAAIL,CAAAA,CAAa,kBAAmB,oCAAA,CAAsC,CAC9E,KAAA,CAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAAA,CACxC,OAAA,CAAS,CAAE,GAAA,CAAKQ,CAAa,CAC/B,CAAC,CACH,CAGA,IAAMG,CAAAA,CAAYR,CAAAA,CAAgBM,EAAI,MAAM,CAAA,CAC5C,GAAIE,CAAAA,GAAcL,EAChB,MAAM,IAAIX,CAAAA,CACR,oBAAA,CACA,oDAAoDW,CAAgB,CAAA,QAAA,EAAWK,CAAS,CAAA,CAAA,CAAA,CACxF,CACE,OAAA,CAAS,CACP,QAAA,CAAUL,CAAAA,CACV,OAAQK,CACV,CACF,CACF,CAAA,CAIF,OAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAIL,CAAAA,CAAkB,CAC/B,GAAA,CAAAG,CAAAA,CACA,SAAA,CAAW,IAAA,CAAK,KAClB,CAAC,CAAA,CAEMA,CACT,CAKQ,SAAA,CAAUF,CAAAA,CAAkC,CAClD,OAAO,KAAK,GAAA,EAAI,CAAIA,CAAAA,CAAO,SAAA,CAAY,KAAK,UAC9C,CAKA,UAAA,EAAmB,CACjB,KAAK,KAAA,CAAM,KAAA,GACb,CAOA,YAAYH,CAAAA,CAAsB,CAChC,IAAA,CAAK,KAAA,CAAM,MAAA,CAAOD,CAAAA,CAAgBC,CAAM,CAAC,EAC3C,CACF,CAAA,CAjGaC,CAAAA,CAMa,oBAAA,CAAuB,KAAO,GAAA,CANjD,IAAMO,CAAAA,CAANP,MCjCMQ,CAAAA,CAAN,KAAmB,CAAnB,WAAA,EAAA,CACL,KAAQ,SAAA,CAA+E,IAAI,IAAA,CAS3F,EAAA,CAA+BC,EAAUC,CAAAA,CAA6C,CACpF,OAAK,IAAA,CAAK,UAAU,GAAA,CAAID,CAAK,CAAA,EAC3B,IAAA,CAAK,UAAU,GAAA,CAAIA,CAAAA,CAAO,IAAI,GAAK,EAErC,IAAA,CAAK,SAAA,CAAU,GAAA,CAAIA,CAAK,EAAG,GAAA,CAAIC,CAAgD,CAAA,CAGxE,IAAM,CACX,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAOC,CAAO,EACzB,CACF,CASA,IAAA,CAAiCD,CAAAA,CAAUC,EAA6C,CACtF,IAAMC,CAAAA,EAAgBC,CAAAA,EAA2B,CAC/C,IAAA,CAAK,GAAA,CAAIH,CAAAA,CAAOE,CAAW,EAC3BD,CAAAA,CAAQE,CAAI,EACd,CAAA,CAAA,CAEA,OAAO,IAAA,CAAK,EAAA,CAAGH,CAAAA,CAAOE,CAAW,CACnC,CAQA,GAAA,CAAgCF,CAAAA,CAAUC,EAAuC,CAC/E,IAAMG,CAAAA,CAAW,IAAA,CAAK,UAAU,GAAA,CAAIJ,CAAK,CAAA,CACrCI,CAAAA,GACFA,EAAS,MAAA,CAAOH,CAAgD,CAAA,CAC5DG,CAAAA,CAAS,OAAS,CAAA,EACpB,IAAA,CAAK,SAAA,CAAU,MAAA,CAAOJ,CAAK,CAAA,EAGjC,CAQA,IAAA,CAAiCA,CAAAA,CAAUG,EAA8B,CACvE,IAAMC,CAAAA,CAAW,IAAA,CAAK,UAAU,GAAA,CAAIJ,CAAK,CAAA,CACzC,GAAII,CAAAA,CACF,IAAA,IAAWH,CAAAA,IAAWG,CAAAA,CACpB,GAAI,CACFH,CAAAA,CAAQE,CAAI,EACd,OAASjB,CAAAA,CAAO,CAEd,OAAA,CAAQ,KAAA,CAAM,+BAA+Bc,CAAK,CAAA,EAAA,CAAA,CAAMd,CAAK,EAC/D,CAGN,CAOA,kBAAA,CAAmBc,CAAAA,CAAgC,CAC7CA,EACF,IAAA,CAAK,SAAA,CAAU,MAAA,CAAOA,CAAK,EAE3B,IAAA,CAAK,SAAA,CAAU,KAAA,GAEnB,CAQA,aAAA,CAAcA,CAAAA,CAAiC,CAC7C,OAAO,IAAA,CAAK,SAAA,CAAU,GAAA,CAAIA,CAAK,GAAG,IAAA,EAAQ,CAC5C,CACF,MC9EaK,CAAAA,CAAN,KAAiB,CACtB,WAAA,CAA6BC,EAAwB,CAAxB,IAAA,CAAA,MAAA,CAAAA,EAAyB,CAStD,MAAM,YAAA,EAAkC,CACtC,IAAMC,CAAAA,CAAe,MAAM,IAAA,CAAK,MAAA,CAAO,oBAAA,EAAqB,CACtDC,EAAgB,MAAM,IAAA,CAAK,MAAA,CAAO,qBAAA,CAAsBD,CAAY,CAAA,CAE1E,OAAO,CACL,YAAA,CAAAA,CAAAA,CACA,aAAA,CAAAC,CAAAA,CACA,mBAAA,CAAqB,MACvB,CACF,CAOA,MAAM,oBAAA,EAAwC,CAC5C,OAAO,IAAA,CAAK,MAAA,CAAO,oBAAA,EACrB,CAQA,MAAM,qBAAA,CAAsBC,CAAAA,CAAmC,CAC7D,OAAO,IAAA,CAAK,MAAA,CAAO,qBAAA,CAAsBA,CAAQ,CACnD,CACF,ECxDO,SAASC,EAAgBP,CAAAA,CAA0B,CAExD,IAAIQ,CAAAA,CAAS,GAGPC,CAAAA,CAAMT,CAAAA,CAAK,MAAA,CACjB,IAAA,IAASU,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAID,CAAAA,CAAKC,GAAK,CAAA,CAAG,CAC/B,IAAMC,CAAAA,CAAQX,EAAKU,CAAC,CAAA,CACdE,CAAAA,CAAQF,CAAAA,CAAI,EAAID,CAAAA,CAAMT,CAAAA,CAAKU,CAAAA,CAAI,CAAC,EAAI,CAAA,CACpCG,CAAAA,CAAQH,CAAAA,CAAI,CAAA,CAAID,EAAMT,CAAAA,CAAKU,CAAAA,CAAI,CAAC,CAAA,CAAI,EAEpCI,CAAAA,CAAWH,CAAAA,EAAS,EAAA,CAAOC,CAAAA,EAAS,EAAKC,CAAAA,CAE/CL,CAAAA,EAAUO,CAAAA,CAAcD,CAAAA,EAAW,EAAA,CAAM,EAAI,CAAA,CAC7CN,CAAAA,EAAUO,EAAcD,CAAAA,EAAW,EAAA,CAAM,EAAI,CAAA,CAC7CN,GAAUE,CAAAA,CAAI,CAAA,CAAID,CAAAA,CAAMM,CAAAA,CAAcD,GAAW,CAAA,CAAK,EAAI,CAAA,CAAI,EAAA,CAC9DN,GAAUE,CAAAA,CAAI,CAAA,CAAID,CAAAA,CAAMM,CAAAA,CAAaD,EAAU,EAAI,CAAA,CAAI,GACzD,CAIA,OAAON,CAAAA,CAAO,OAAA,CAAQ,KAAA,CAAO,GAAG,EAAE,OAAA,CAAQ,KAAA,CAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,CAAO,EAAE,CACzE,CAQO,SAASQ,CAAAA,CAAgBC,CAAAA,CAAyB,CAEvD,IAAIT,CAAAA,CAASS,CAAAA,CAAI,OAAA,CAAQ,IAAA,CAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,IAAA,CAAM,GAAG,EAGrD,KAAOT,CAAAA,CAAO,MAAA,CAAS,CAAA,EACrBA,GAAU,GAAA,CAIZ,IAAMC,CAAAA,CAAMD,CAAAA,CAAO,OACbU,CAAAA,CAAaV,CAAAA,CAAO,QAAA,CAAS,IAAI,EAAI,CAAA,CAAIA,CAAAA,CAAO,QAAA,CAAS,GAAG,EAAI,CAAA,CAAI,CAAA,CACpEW,CAAAA,CAAaV,CAAAA,CAAM,EAAK,CAAA,CAAIS,CAAAA,CAC5BE,CAAAA,CAAS,IAAI,WAAWD,CAAS,CAAA,CAEnCE,CAAAA,CAAc,CAAA,CAClB,QAASX,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAID,CAAAA,CAAKC,GAAK,CAAA,CAAG,CAC/B,IAAMC,CAAAA,CAAQW,EAAcd,CAAAA,CAAO,UAAA,CAAWE,CAAC,CAAC,EAC1CE,CAAAA,CAAQU,CAAAA,CAAcd,CAAAA,CAAO,UAAA,CAAWE,EAAI,CAAC,CAAC,CAAA,CAC9CG,CAAAA,CAAQS,CAAAA,CAAcd,CAAAA,CAAO,UAAA,CAAWE,CAAAA,CAAI,CAAC,CAAC,CAAA,CAC9Ca,CAAAA,CAAQD,CAAAA,CAAcd,EAAO,UAAA,CAAWE,CAAAA,CAAI,CAAC,CAAC,EAE9CI,CAAAA,CAAWH,CAAAA,EAAS,EAAA,CAAOC,CAAAA,EAAS,GAAOC,CAAAA,EAAS,CAAA,CAAKU,CAAAA,CAE3DF,CAAAA,CAAcF,IAAWC,CAAAA,CAAOC,CAAAA,EAAa,CAAA,CAAKP,CAAAA,EAAW,GAAM,GAAA,CAAA,CACnEO,CAAAA,CAAcF,CAAAA,GAAWC,CAAAA,CAAOC,GAAa,CAAA,CAAKP,CAAAA,EAAW,CAAA,CAAK,GAAA,CAAA,CAClEO,CAAAA,CAAcF,CAAAA,GAAWC,CAAAA,CAAOC,CAAAA,EAAa,EAAIP,CAAAA,CAAU,GAAA,EACjE,CAEA,OAAOM,CACT,CAQO,SAASI,CAAAA,CAAkBP,CAAAA,CAAqB,CACrD,IAAMQ,CAAAA,CAAU,IAAI,WAAA,CACpB,OAAOlB,CAAAA,CAAgBkB,CAAAA,CAAQ,MAAA,CAAOR,CAAG,CAAC,CAC5C,CAQO,SAASS,CAAAA,CAAkBC,EAA2B,CAE3D,OADgB,IAAI,WAAA,GACL,MAAA,CAAOX,CAAAA,CAAgBW,CAAS,CAAC,CAClD,CAGA,IAAMZ,CAAAA,CAAe,mEAGfO,CAAAA,CAAgB,IAAI,UAAA,CAAW,GAAG,EACxC,IAAA,IAASZ,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAIK,EAAa,MAAA,CAAQL,CAAAA,EAAAA,CACvCY,CAAAA,CAAcP,CAAAA,CAAa,WAAWL,CAAC,CAAC,CAAA,CAAIA,CAAAA,KCzDjCkB,CAAAA,CAAe,CAI1B,SAAA,CAAW,CAACC,EAAoBC,CAAAA,CAAsBC,CAAAA,GACpD,CAAA,QAAA,EAAWF,CAAU,IAAIC,CAAY,CAAA,MAAA,EAASC,CAAK,CAAA,CAAA,CAKrD,MAAA,CAAQ,CAACF,CAAAA,CAAoBC,CAAAA,GAC3B,WAAWD,CAAU,CAAA,CAAA,EAAIC,CAAY,CAAA,OAAA,CAAA,CAKvC,QAAS,CAACD,CAAAA,CAAoBC,CAAAA,GAC5B,CAAA,QAAA,EAAWD,CAAU,CAAA,CAAA,EAAIC,CAAY,CAAA,SAAA,CAAA,CAKvC,eAAA,CAAiB,CAACD,CAAAA,CAAoBC,CAAAA,GACpC,CAAA,QAAA,EAAWD,CAAU,IAAIC,CAAY,CAAA,MAAA,CACzC,CAAA,CAKaE,CAAAA,CAAN,MAAMA,CAAa,CAIxB,WAAA,CACmB7B,CAAAA,CACA8B,EACAJ,CAAAA,CACAC,CAAAA,CACjB,CAJiB,IAAA,CAAA,MAAA,CAAA3B,CAAAA,CACA,IAAA,CAAA,OAAA,CAAA8B,CAAAA,CACA,IAAA,CAAA,UAAA,CAAAJ,EACA,IAAA,CAAA,YAAA,CAAAC,EAChB,CAUH,MAAM,kBAAkBhD,CAAAA,CAAuD,CAC7E,IAAMoD,CAAAA,CAAapD,EAAQ,UAAA,EAAckD,CAAAA,CAAa,mBAAA,CAGhDG,CAAAA,CAAa,MAAM,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,EAAE,EAC7CC,CAAAA,CAAa,MAAM,IAAA,CAAK,MAAA,CAAO,YAAY,EAAE,CAAA,CAE7CL,CAAAA,CAAQxB,CAAAA,CAAgB4B,CAAU,CAAA,CAClCE,CAAAA,CAAQ9B,CAAAA,CAAgB6B,CAAU,CAAA,CAElCE,CAAAA,CAAM,IAAA,CAAK,GAAA,GACXC,CAAAA,CAAuB,CAC3B,KAAA,CAAAR,CAAAA,CACA,MAAAM,CAAAA,CACA,YAAA,CAAcvD,CAAAA,CAAQ,YAAA,CACtB,YAAaA,CAAAA,CAAQ,WAAA,CACrB,SAAA,CAAWwD,CAAAA,CACX,UAAWA,CAAAA,CAAMJ,CAAAA,CAAa,GAChC,CAAA,CAGMM,EAAMZ,CAAAA,CAAa,SAAA,CAAU,IAAA,CAAK,UAAA,CAAY,KAAK,YAAA,CAAcG,CAAK,CAAA,CAC5E,OAAA,MAAM,KAAK,OAAA,CAAQ,GAAA,CAAIS,CAAAA,CAAK,IAAA,CAAK,SAAA,CAAUD,CAAS,CAAC,CAAA,CAE9CA,CACT,CAYA,MAAM,uBAAA,CAAwBR,CAAAA,CAAmC,CAC/D,IAAMS,CAAAA,CAAMZ,CAAAA,CAAa,SAAA,CAAU,KAAK,UAAA,CAAY,IAAA,CAAK,YAAA,CAAcG,CAAK,EAE5E,GAAI,CACF,IAAMU,CAAAA,CAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAID,CAAG,EAEzC,GAAI,CAACC,CAAAA,CACH,MAAM,IAAI/D,CAAAA,CAAa,eAAA,CAAiB,iCAAiC,CAAA,CAG3E,IAAI6D,CAAAA,CACJ,GAAI,CACFA,EAAY,IAAA,CAAK,KAAA,CAAME,CAAM,EAC/B,MAAQ,CACN,MAAM,IAAI/D,CAAAA,CAAa,gBAAiB,sBAAsB,CAChE,CAGA,GAAI,KAAK,GAAA,EAAI,CAAI6D,CAAAA,CAAU,SAAA,CACzB,MAAM,IAAI7D,CAAAA,CAAa,eAAA,CAAiB,mBAAmB,EAG7D,OAAO6D,CACT,CAAA,OAAE,CAKA,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAOC,CAAG,EAC/B,CACF,CAUA,MAAM,sBAAsC,CAE1C,GAAI,CAAC,IAAA,CAAK,QAAQ,MAAA,CAChB,OAGF,IAAME,CAAAA,CAASd,EAAa,eAAA,CAAgB,IAAA,CAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAAA,CACxEe,CAAAA,CAAM,MAAM,IAAA,CAAK,QAAQ,MAAA,EAAO,CAChCL,CAAAA,CAAM,IAAA,CAAK,KAAI,CAErB,IAAA,GAAW,CAACE,CAAAA,CAAKI,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQD,CAAG,CAAA,CAC3C,GAAKH,CAAAA,CAAI,UAAA,CAAWE,CAAM,CAAA,CAI1B,GAAI,CACF,IAAMH,EAAuB,IAAA,CAAK,KAAA,CAAMK,CAAK,CAAA,CACzCN,EAAMC,CAAAA,CAAU,SAAA,EAClB,MAAM,IAAA,CAAK,QAAQ,MAAA,CAAOC,CAAG,EAEjC,CAAA,KAAQ,CAEN,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAOA,CAAG,EAC/B,CAEJ,CACF,EA1HaR,EAEa,mBAAA,CAAsB,GAAA,CAFzC,IAAMa,CAAAA,CAANb,ECjCA,SAASc,CAAAA,CAAuCC,CAAAA,CAA4B,CACjF,IAAMC,CAAAA,CAAQD,CAAAA,CAAI,KAAA,CAAM,GAAG,CAAA,CAC3B,GAAIC,CAAAA,CAAM,MAAA,GAAW,EACnB,MAAM,IAAI,KAAA,CAAM,sCAAsC,EAGxD,GAAM,CAACC,CAAAA,CAAWC,CAAAA,CAAYC,CAAS,CAAA,CAAIH,CAAAA,CAE3C,GAAI,CACF,IAAMI,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAM1B,CAAAA,CAAkBuB,CAAS,CAAC,CAAA,CAChDI,CAAAA,CAAU,IAAA,CAAK,KAAA,CAAM3B,CAAAA,CAAkBwB,CAAU,CAAC,EAExD,OAAO,CACL,MAAA,CAAAE,CAAAA,CACA,QAAAC,CAAAA,CACA,SAAA,CAAAF,CACF,CACF,MAAQ,CACN,MAAM,IAAI,KAAA,CAAM,sCAAsC,CACxD,CACF,CAWO,SAASG,EAAcC,CAAAA,CAAgC,CAE5D,OADgBT,CAAAA,CAAyBS,CAAO,CAAA,CACjC,OACjB,CASO,SAASC,EAAaH,CAAAA,CAA2BI,CAAAA,CAAsB,CAAA,CAAY,CACxF,GAAIJ,CAAAA,CAAQ,GAAA,GAAQ,MAAA,CAClB,OAAO,MAAA,CAGT,IAAMf,CAAAA,CAAM,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,EAAI,CAAI,GAAI,EACxC,OAAOe,CAAAA,CAAQ,GAAA,CAAMI,CAAAA,CAAcnB,CACrC,CAQO,SAASoB,CAAAA,CAAgBH,CAAAA,CAAqC,CACnE,GAAI,CAEF,OADeD,CAAAA,CAAcC,CAAO,CAAA,CACtB,KAChB,CAAA,KAAQ,CACN,MACF,CACF,CCxBO,IAAMI,CAAAA,CAAN,KAA4B,CACjC,WAAA,CACmBC,CAAAA,CACAC,EACjB,CAFiB,IAAA,CAAA,IAAA,CAAAD,CAAAA,CACA,IAAA,CAAA,QAAA,CAAAC,EAChB,CAWH,qBAAA,CACEC,CAAAA,CACAvB,CAAAA,CACAwB,EACAjF,CAAAA,CACwB,CACxB,IAAMkF,CAAAA,CAAWF,EAAU,sBAAA,CACrBG,CAAAA,CAAS,IAAI,eAAA,CAGnBA,EAAO,GAAA,CAAI,WAAA,CAAa,IAAA,CAAK,QAAQ,EACrCA,CAAAA,CAAO,GAAA,CAAI,eAAA,CAAiB,MAAM,EAClCA,CAAAA,CAAO,GAAA,CAAI,cAAA,CAAgBnF,CAAAA,CAAQ,WAAW,CAAA,CAC9CmF,CAAAA,CAAO,GAAA,CAAI,QAAS1B,CAAAA,CAAU,KAAK,CAAA,CACnC0B,CAAAA,CAAO,IAAI,OAAA,CAAS1B,CAAAA,CAAU,KAAK,CAAA,CAGnC0B,EAAO,GAAA,CAAI,gBAAA,CAAkBF,CAAAA,CAAK,aAAa,EAC/CE,CAAAA,CAAO,GAAA,CAAI,uBAAA,CAAyBF,CAAAA,CAAK,mBAAmB,CAAA,CAG5D,IAAMG,CAAAA,CAAQpF,CAAAA,CAAQ,OAAS,gBAAA,CAe/B,GAdAmF,CAAAA,CAAO,GAAA,CAAI,QAASC,CAAK,CAAA,CAGrBpF,CAAAA,CAAQ,MAAA,EACVmF,EAAO,GAAA,CAAI,QAAA,CAAUnF,CAAAA,CAAQ,MAAM,EAEjCA,CAAAA,CAAQ,SAAA,EACVmF,CAAAA,CAAO,GAAA,CAAI,aAAcnF,CAAAA,CAAQ,SAAS,CAAA,CAExCA,CAAAA,CAAQ,WACVmF,CAAAA,CAAO,GAAA,CAAI,YAAA,CAAcnF,CAAAA,CAAQ,SAAS,CAAA,CAIxCA,CAAAA,CAAQ,WAAA,CAAa,CAEvB,IAAMqF,CAAAA,CAAkB,IAAI,GAAA,CAAI,CAC9B,YACA,eAAA,CACA,cAAA,CACA,OAAA,CACA,OAAA,CACA,iBACA,uBAAA,CACA,OACF,CAAC,CAAA,CAED,IAAA,GAAW,CAAC3B,CAAAA,CAAKI,CAAK,IAAK,MAAA,CAAO,OAAA,CAAQ9D,CAAAA,CAAQ,WAAW,EACvDqF,CAAAA,CAAgB,GAAA,CAAI3B,CAAAA,CAAI,WAAA,EAAa,CAAA,EAKzCyB,CAAAA,CAAO,GAAA,CAAIzB,CAAAA,CAAKI,CAAK,EAEzB,CAKA,IAAMwB,CAAAA,CAAiC,CAAE,GAAA,CAH7B,CAAA,EAAGJ,CAAQ,CAAA,CAAA,EAAIC,EAAO,QAAA,EAAU,CAAA,CAGC,CAAA,CAE7C,OAAInF,CAAAA,CAAQ,WAAA,GACVsF,CAAAA,CAAO,KAAA,CAAQ7B,CAAAA,CAAU,KAAA,CACzB6B,CAAAA,CAAO,KAAA,CAAQ7B,EAAU,KAAA,CAAA,CAGpB6B,CACT,CASA,aAAA,CAAcC,EAAsD,CAClE,IAAIC,CAAAA,CAGAD,CAAAA,CAAY,SAAS,GAAG,CAAA,CAI1BC,CAAAA,CAAAA,CAHYD,CAAAA,CAAY,WAAW,MAAM,CAAA,CACrC,IAAI,GAAA,CAAIA,CAAW,CAAA,CACnB,IAAI,GAAA,CAAIA,CAAAA,CAAa,qBAAqB,CAAA,EAC3B,YAAA,CAEnBC,CAAAA,CAAe,IAAI,gBAAgBD,CAAW,CAAA,CAIhD,IAAMtF,CAAAA,CAAQuF,CAAAA,CAAa,GAAA,CAAI,OAAO,CAAA,CACtC,GAAIvF,CAAAA,CAAO,CACT,IAAMwF,CAAAA,CAAmBD,EAAa,GAAA,CAAI,mBAAmB,CAAA,EAAK,sBAAA,CAClE,MAAM,IAAI5F,CAAAA,CAAa,aAAA,CAAe6F,CAAAA,CAAkB,CACtD,OAAA,CAAS,CACP,KAAA,CAAAxF,CAAAA,CACA,kBAAmBwF,CAAAA,CACnB,SAAA,CAAWD,CAAAA,CAAa,GAAA,CAAI,WAAW,CACzC,CACF,CAAC,CACH,CAGA,IAAM1F,CAAAA,CAAO0F,CAAAA,CAAa,GAAA,CAAI,MAAM,CAAA,CAC9BvC,CAAAA,CAAQuC,CAAAA,CAAa,IAAI,OAAO,CAAA,CAEtC,GAAI,CAAC1F,EACH,MAAM,IAAIF,CAAAA,CAAa,cAAA,CAAgB,0CAA0C,CAAA,CAEnF,GAAI,CAACqD,CAAAA,CACH,MAAM,IAAIrD,CAAAA,CAAa,eAAA,CAAiB,uCAAuC,EAGjF,OAAO,CAAE,IAAA,CAAAE,CAAAA,CAAM,MAAAmD,CAAM,CACvB,CAUA,MAAM,aACJ+B,CAAAA,CACAhF,CAAAA,CACmB,CACnB,IAAM0F,CAAAA,CAAgBV,CAAAA,CAAU,cAAA,CAG1BW,CAAAA,CAAO,IAAI,eAAA,CAAgB,CAC/B,UAAA,CAAY,oBAAA,CACZ,UAAW,IAAA,CAAK,QAAA,CAChB,IAAA,CAAM3F,CAAAA,CAAQ,KACd,YAAA,CAAcA,CAAAA,CAAQ,WAAA,CACtB,aAAA,CAAeA,EAAQ,YACzB,CAAC,CAAA,CAGGW,CAAAA,CACJ,GAAI,CACFA,CAAAA,CAAW,MAAM,IAAA,CAAK,KAAK,KAAA,CAAqB+E,CAAAA,CAAe,CAC7D,MAAA,CAAQ,OACR,OAAA,CAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,IAAA,CAAMC,CAAAA,CAAK,QAAA,EACb,CAAC,EACH,CAAA,MAAS1F,CAAAA,CAAO,CACd,MAAM,IAAIL,CAAAA,CAAa,eAAA,CAAiB,uBAAwB,CAC9D,KAAA,CAAOK,CAAAA,YAAiB,KAAA,CAAQA,EAAQ,MAC1C,CAAC,CACH,CAEA,GAAI,CAACU,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMiF,CAAAA,CAAYjF,CAAAA,CAAS,IAAA,CAC3B,MAAM,IAAIf,CAAAA,CAAa,aAAA,CAAe,uBAAA,CAAyB,CAC7D,OAAA,CAAS,CACP,MAAA,CAAQe,CAAAA,CAAS,OACjB,KAAA,CAAOiF,CAAAA,EAAW,KAAA,CAClB,iBAAA,CAAmBA,GAAW,iBAChC,CACF,CAAC,CACH,CAEA,IAAMC,CAAAA,CAAgBlF,CAAAA,CAAS,IAAA,CAG/B,GAAIkF,CAAAA,CAAc,QAAA,EACKjB,CAAAA,CAAgBiB,CAAAA,CAAc,QAAQ,CAAA,GACtC7F,CAAAA,CAAQ,KAAA,CAE3B,MAAM,IAAIJ,CAAAA,CAAa,gBAAA,CAAkB,8CAA8C,CAAA,CAK3F,IAAM4D,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,KAAI,CAAI,GAAI,CAAA,CAClCsC,CAAAA,CAAYD,EAAc,UAAA,CAAarC,CAAAA,CAAMqC,CAAAA,CAAc,UAAA,CAAarC,EAAM,IAAA,CAYpF,OAT2B,CACzB,WAAA,CAAaqC,EAAc,YAAA,CAC3B,SAAA,CAAYA,CAAAA,CAAc,UAAA,EAA2B,SACrD,SAAA,CAAAC,CAAAA,CACA,YAAA,CAAcD,CAAAA,CAAc,cAC5B,OAAA,CAASA,CAAAA,CAAc,QAAA,CACvB,KAAA,CAAOA,EAAc,KACvB,CAGF,CACF,MC/MaE,CAAAA,CAAkB,CAC7B,YAAA,CAAc,+CAAA,CACd,aAAA,CAAe,gDAAA,CACf,QAAA,CAAU,2CACZ,EC1CO,IAAMC,CAAAA,CAAN,MAAMA,CAAa,CAkBxB,WAAA,CAAYhG,CAAAA,CAA8B,CAR1C,IAAA,CAAQ,eAA2C,IAAA,CAGnD,IAAA,CAAQ,SAAA,CAA0C,IAAA,CAMhD,KAAK,IAAA,CAAOA,CAAAA,CAAQ,IAAA,CACpB,IAAA,CAAK,QAAUA,CAAAA,CAAQ,OAAA,CACvB,IAAA,CAAK,QAAA,CAAWA,EAAQ,QAAA,CACxB,IAAA,CAAK,UAAA,CAAaA,CAAAA,CAAQ,WAC1B,IAAA,CAAK,YAAA,CAAeA,CAAAA,CAAQ,YAAA,CAC5B,IAAA,CAAK,kBAAA,CACHA,CAAAA,CAAQ,kBAAA,EAAsBgG,EAAa,4BAAA,CAC7C,IAAA,CAAK,YAAA,CAAehG,CAAAA,CAAQ,aAC9B,CAKA,YAAA,CAAagF,CAAAA,CAAwC,CACnD,KAAK,SAAA,CAAYA,EACnB,CAKA,IAAY,UAAmB,CAC7B,OAAOlC,CAAAA,CAAa,MAAA,CAAO,KAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAC/D,CAKA,IAAY,UAAA,EAAqB,CAC/B,OAAOA,EAAa,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAChE,CAOA,MAAM,WAAsC,CAC1C,IAAMa,CAAAA,CAAS,MAAM,KAAK,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,QAAQ,EACnD,GAAI,CAACA,CAAAA,CACH,OAAO,KAGT,GAAI,CACF,OAAO,IAAA,CAAK,MAAMA,CAAM,CAC1B,CAAA,KAAQ,CAEN,aAAM,IAAA,CAAK,WAAA,EAAY,CAChB,IACT,CACF,CAOA,MAAM,UAAA,CAAWsC,CAAAA,CAAiC,CAChD,MAAM,IAAA,CAAK,OAAA,CAAQ,IAAI,IAAA,CAAK,QAAA,CAAU,IAAA,CAAK,SAAA,CAAUA,CAAM,CAAC,CAAA,CAGxDA,CAAAA,CAAO,OAAA,EACT,MAAM,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,WAAYA,CAAAA,CAAO,OAAO,EAE1D,CAKA,MAAM,WAAA,EAA6B,CACjC,MAAM,IAAA,CAAK,QAAQ,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CACvC,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,UAAU,EAC3C,CAYA,MAAM,gBAAkC,CACtC,IAAMA,CAAAA,CAAS,MAAM,KAAK,SAAA,EAAU,CAEpC,GAAI,CAACA,EACH,MAAM,IAAIrG,CAAAA,CAAa,WAAA,CAAa,iDAAiD,CAAA,CAIvF,GAAI,IAAA,CAAK,aAAA,CAAcqG,CAAM,CAAA,CAAG,CAC9B,GAAI,CAACA,EAAO,YAAA,CACV,MAAM,IAAIrG,CAAAA,CACR,gBACA,qDACF,CAAA,CAEF,OAAO,IAAA,CAAK,eAAA,CAAgBqG,CAAAA,CAAO,YAAY,CACjD,CAEA,OAAOA,CAAAA,CAAO,WAChB,CAOA,MAAM,UAAA,EAAqC,CAEzC,OAAA,CADe,MAAM,KAAK,SAAA,EAAU,GACrB,OAAA,EAAW,IAC5B,CAQQ,aAAA,CAAcA,CAAAA,CAA2B,CAC/C,IAAMzC,EAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,GAAQ,GAAI,CAAA,CACxC,OAAOyC,CAAAA,CAAO,UAAY,IAAA,CAAK,kBAAA,EAAsBzC,CACvD,CAWA,MAAc,eAAA,CAAgB0C,CAAAA,CAAuC,CAEnE,GAAI,IAAA,CAAK,cAAA,CAEP,OAAA,CADe,MAAM,KAAK,cAAA,EACZ,WAAA,CAIhB,IAAA,CAAK,cAAA,CAAiB,KAAK,kBAAA,CAAmBA,CAAY,CAAA,CAE1D,GAAI,CAEF,OAAA,CADe,MAAM,IAAA,CAAK,cAAA,EACZ,WAChB,CAAA,OAAE,CAEA,IAAA,CAAK,cAAA,CAAiB,KACxB,CACF,CASA,MAAc,kBAAA,CACZA,EACAC,CAAAA,CAAiB,KAAA,CACE,CACnB,GAAI,CACF,OAAO,MAAM,IAAA,CAAK,SAAA,CAAUD,CAAY,CAC1C,CAAA,MAASjG,CAAAA,CAAO,CAEd,GAAI,IAAA,CAAK,gBAAA,CAAiBA,CAAK,CAAA,EAAK,CAACkG,CAAAA,CACnC,OAAO,IAAA,CAAK,kBAAA,CAAmBD,EAAc,IAAI,CAAA,CAEnD,MAAMjG,CACR,CACF,CAQA,MAAc,SAAA,CAAUiG,CAAAA,CAAyC,CAC/D,GAAI,CAAC,IAAA,CAAK,SAAA,CACR,MAAM,IAAItG,CAAAA,CAAa,cAAA,CAAgB,4BAA4B,CAAA,CAGrE,IAAM8F,CAAAA,CAAgB,IAAA,CAAK,UAAU,cAAA,CAG/BC,CAAAA,CAAO,IAAI,eAAA,CAAgB,CAC/B,UAAA,CAAY,eAAA,CACZ,SAAA,CAAW,IAAA,CAAK,SAChB,aAAA,CAAeO,CACjB,CAAC,CAAA,CAEGvF,EACJ,GAAI,CACFA,CAAAA,CAAW,MAAM,KAAK,IAAA,CAAK,KAAA,CAAqB+E,CAAAA,CAAe,CAC7D,OAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,IAAA,CAAMC,CAAAA,CAAK,QAAA,EACb,CAAC,EACH,CAAA,MAAS1F,EAAO,CACd,IAAMmG,CAAAA,CAAe,IAAIxG,EAAa,eAAA,CAAiB,8BAAA,CAAgC,CACrF,KAAA,CAAOK,aAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,EACD,MAAA,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,aAAA,CAAe,CAAE,KAAA,CAAOmG,CAAa,CAAC,CAAA,CACxDA,CACR,CAEA,GAAI,CAACzF,CAAAA,CAAS,GAAI,CAChB,IAAMiF,CAAAA,CAAYjF,CAAAA,CAAS,IAAA,CACrByF,CAAAA,CAAe,IAAIxG,CAAAA,CAAa,gBAAiB,sBAAA,CAAwB,CAC7E,OAAA,CAAS,CACP,OAAQe,CAAAA,CAAS,MAAA,CACjB,KAAA,CAAOiF,CAAAA,EAAW,MAClB,iBAAA,CAAmBA,CAAAA,EAAW,iBAChC,CACF,CAAC,CAAA,CACD,MAAA,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,cAAe,CAAE,KAAA,CAAOQ,CAAa,CAAC,EACxDA,CACR,CAEA,IAAMP,CAAAA,CAAgBlF,EAAS,IAAA,CAGzB6C,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,EAAI,CAAI,GAAI,EAClCsC,CAAAA,CAAYD,CAAAA,CAAc,UAAA,CAAarC,CAAAA,CAAMqC,EAAc,UAAA,CAAarC,CAAAA,CAAM,IAAA,CAG9E6C,CAAAA,CAAsB,CAC1B,WAAA,CAAaR,CAAAA,CAAc,YAAA,CAC3B,SAAA,CAAYA,EAAc,UAAA,EAA2B,QAAA,CACrD,SAAA,CAAAC,CAAAA,CACA,aAAcD,CAAAA,CAAc,aAAA,EAAiBK,CAAAA,CAC7C,OAAA,CAASL,EAAc,QAAA,CACvB,KAAA,CAAOA,CAAAA,CAAc,KACvB,EAGA,OAAA,MAAM,IAAA,CAAK,UAAA,CAAWQ,CAAS,CAAA,CAG/B,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,kBAAmB,CAAE,MAAA,CAAQA,CAAU,CAAC,EAEzDA,CACT,CAKQ,gBAAA,CAAiBpG,CAAAA,CAAyB,CAChD,OAAIA,CAAAA,YAAiBL,CAAAA,CACZK,CAAAA,CAAM,OAAS,eAAA,CAEjB,KACT,CAOA,MAAM,iBAAoC,CACxC,IAAMgG,CAAAA,CAAS,MAAM,KAAK,SAAA,EAAU,CACpC,GAAI,CAACA,EACH,OAAO,MAAA,CAIT,IAAMzC,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,GAAQ,GAAI,CAAA,CACxC,OAAIyC,CAAAA,CAAO,WAAazC,CAAAA,CAEf,CAAC,CAACyC,CAAAA,CAAO,aAGX,IACT,CAYA,MAAM,aAAA,CAAcK,EAA6D,CAC/E,GAAI,CAAC,IAAA,CAAK,UACR,MAAM,IAAI1G,CAAAA,CAAa,cAAA,CAAgB,4BAA4B,CAAA,CAGrE,IAAM8F,CAAAA,CAAgB,IAAA,CAAK,UAAU,cAAA,CAG/Ba,CAAAA,CAAmB,IAAA,CAAK,iBAAA,CAAkBD,CAAAA,CAAQ,gBAAA,EAAoB,cAAc,CAAA,CAGpFX,EAAO,IAAI,eAAA,CAAgB,CAC/B,UAAA,CAAY,kDACZ,SAAA,CAAW,IAAA,CAAK,QAAA,CAChB,aAAA,CAAeW,EAAQ,YAAA,CACvB,kBAAA,CAAoBC,CACtB,CAAC,EAGGD,CAAAA,CAAQ,QAAA,EACVX,CAAAA,CAAK,GAAA,CAAI,WAAYW,CAAAA,CAAQ,QAAQ,CAAA,CAEnCA,CAAAA,CAAQ,OACVX,CAAAA,CAAK,GAAA,CAAI,OAAA,CAASW,CAAAA,CAAQ,KAAK,CAAA,CAE7BA,CAAAA,CAAQ,kBAAA,EACVX,CAAAA,CAAK,GAAA,CAAI,sBAAA,CAAwB,IAAA,CAAK,iBAAA,CAAkBW,EAAQ,kBAAkB,CAAC,CAAA,CAEjFA,CAAAA,CAAQ,aACVX,CAAAA,CAAK,GAAA,CAAI,aAAA,CAAeW,CAAAA,CAAQ,UAAU,CAAA,CACtCA,CAAAA,CAAQ,cAAA,EACVX,CAAAA,CAAK,IAAI,kBAAA,CAAoB,IAAA,CAAK,iBAAA,CAAkBW,CAAAA,CAAQ,cAAc,CAAC,CAAA,CAAA,CAI/E,IAAI3F,CAAAA,CACJ,GAAI,CACFA,CAAAA,CAAW,MAAM,IAAA,CAAK,KAAK,KAAA,CAA6B+E,CAAAA,CAAe,CACrE,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,IAAA,CAAMC,CAAAA,CAAK,QAAA,EACb,CAAC,EACH,CAAA,MAAS1F,CAAAA,CAAO,CACd,IAAMmG,CAAAA,CAAe,IAAIxG,CAAAA,CAAa,gBAAiB,+BAAA,CAAiC,CACtF,KAAA,CAAOK,CAAAA,YAAiB,MAAQA,CAAAA,CAAQ,MAC1C,CAAC,CAAA,CACD,WAAK,YAAA,EAAc,IAAA,CAAK,aAAA,CAAe,CAAE,MAAOmG,CAAa,CAAC,CAAA,CACxDA,CACR,CAEA,GAAI,CAACzF,CAAAA,CAAS,GAAI,CAChB,IAAMiF,CAAAA,CAAYjF,CAAAA,CAAS,KACrByF,CAAAA,CAAe,IAAIxG,CAAAA,CAAa,sBAAA,CAAwB,wBAAyB,CACrF,OAAA,CAAS,CACP,MAAA,CAAQe,EAAS,MAAA,CACjB,KAAA,CAAOiF,CAAAA,EAAW,KAAA,CAClB,kBAAmBA,CAAAA,EAAW,iBAChC,CACF,CAAC,EACD,MAAA,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,aAAA,CAAe,CAAE,KAAA,CAAOQ,CAAa,CAAC,CAAA,CACxDA,CACR,CAEA,IAAMP,CAAAA,CAAgBlF,EAAS,IAAA,CAGzB6C,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,CAAI,GAAI,CAAA,CAClCsC,EAAYD,CAAAA,CAAc,UAAA,CAAarC,CAAAA,CAAMqC,CAAAA,CAAc,WAAarC,CAAAA,CAAM,IAAA,CAE9EyC,CAAAA,CAAmB,CACvB,YAAaJ,CAAAA,CAAc,YAAA,CAC3B,SAAA,CAAYA,CAAAA,CAAc,YAA2B,QAAA,CACrD,SAAA,CAAAC,CAAAA,CACA,YAAA,CAAcD,EAAc,aAAA,CAC5B,OAAA,CAASA,CAAAA,CAAc,QAAA,CACvB,KAAA,CAAOA,CAAAA,CAAc,KACvB,CAAA,CAEMP,EAA8B,CAClC,MAAA,CAAAW,CAAAA,CACA,eAAA,CAAiBJ,EAAc,iBACjC,CAAA,CAGA,OAAA,IAAA,CAAK,YAAA,EAAc,KAAK,iBAAA,CAAmB,CACzC,MAAA,CAAAI,CAAAA,CACA,gBAAiBJ,CAAAA,CAAc,iBACjC,CAAC,CAAA,CAEMP,CACT,CAKQ,iBAAA,CAAkBkB,CAAAA,CAA6D,CACrF,OAAOT,CAAAA,CAAgBS,CAAI,CAC7B,CACF,EA1ZaR,CAAAA,CAgBa,4BAAA,CAA+B,EAAA,CAhBlD,IAAMS,CAAAA,CAANT,ECyBA,IAAMU,CAAAA,CAAN,KAAwB,CAI7B,WAAA,CAAY1G,CAAAA,CAAmC,CAC7C,KAAK,IAAA,CAAOA,CAAAA,CAAQ,IAAA,CACpB,IAAA,CAAK,SAAWA,CAAAA,CAAQ,SAC1B,CAcA,MAAM,WACJgF,CAAAA,CACAhF,CAAAA,CACgC,CAChC,IAAMkF,EAAWF,CAAAA,CAAU,sBAAA,CAE3B,GAAI,CAACE,EACH,MAAM,IAAItF,CAAAA,CACR,2BAAA,CACA,2DACF,CAAA,CAGF,OAAO,IAAA,CAAK,sBAAA,CAAuBsF,CAAAA,CAAUlF,CAAO,CACtD,CAYA,MAAM,sBAAA,CACJkF,CAAAA,CACAlF,CAAAA,CACgC,CAEhC,IAAM2F,CAAAA,CAAO,IAAI,eAAA,CAAgB,CAC/B,UAAW,IAAA,CAAK,QAAA,CAChB,KAAA,CAAO3F,CAAAA,CAAQ,KACjB,CAAC,CAAA,CAEGA,CAAAA,CAAQ,aAAA,EACV2F,EAAK,GAAA,CAAI,iBAAA,CAAmB3F,CAAAA,CAAQ,aAAa,EAGnD,IAAIW,CAAAA,CACJ,GAAI,CACFA,EAAW,MAAM,IAAA,CAAK,IAAA,CAAK,KAAA,CAAkDuE,CAAAA,CAAU,CACrF,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,KAAMS,CAAAA,CAAK,QAAA,EACb,CAAC,EACH,CAAA,MAAS1F,CAAAA,CAAO,CACd,MAAM,IAAIL,CAAAA,CAAa,eAAA,CAAiB,oCAAA,CAAsC,CAC5E,MAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CAEA,GAAI,CAACU,EAAS,EAAA,CAAI,CAChB,IAAMiF,CAAAA,CAAYjF,EAAS,IAAA,CAC3B,MAAM,IAAIf,CAAAA,CAAa,sBAAuB,4BAAA,CAA8B,CAC1E,OAAA,CAAS,CACP,OAAQe,CAAAA,CAAS,MAAA,CACjB,KAAA,CAAOiF,CAAAA,EAAW,MAClB,iBAAA,CAAmBA,CAAAA,EAAW,iBAChC,CACF,CAAC,CACH,CAEA,OAAOjF,CAAAA,CAAS,IAClB,CAWA,MAAM,QAAA,CAASqE,CAAAA,CAAkC2B,EAAiC,CAEhF,OAAA,CADe,MAAM,IAAA,CAAK,WAAW3B,CAAAA,CAAW,CAAE,KAAA,CAAA2B,CAAM,CAAC,CAAA,EAC3C,MAChB,CACF,ECvIO,IAAMC,CAAAA,CAAN,KAAmB,CAIxB,YAAY5G,CAAAA,CAA8B,CACxC,IAAA,CAAK,IAAA,CAAOA,EAAQ,IAAA,CACpB,IAAA,CAAK,QAAA,CAAWA,CAAAA,CAAQ,SAC1B,CAcA,MAAM,MAAA,CAAOgF,CAAAA,CAAkChF,EAA4C,CACzF,IAAMkF,CAAAA,CAAWF,CAAAA,CAAU,oBAE3B,GAAI,CAACE,CAAAA,CACH,MAAM,IAAItF,CAAAA,CACR,wBAAA,CACA,wDACF,CAAA,CAIF,IAAM+F,CAAAA,CAAO,IAAI,eAAA,CAAgB,CAC/B,SAAA,CAAW,IAAA,CAAK,QAAA,CAChB,KAAA,CAAO3F,EAAQ,KACjB,CAAC,CAAA,CAEGA,CAAAA,CAAQ,eACV2F,CAAAA,CAAK,GAAA,CAAI,iBAAA,CAAmB3F,CAAAA,CAAQ,aAAa,CAAA,CAGnD,IAAIW,CAAAA,CACJ,GAAI,CACFA,CAAAA,CAAW,MAAM,IAAA,CAAK,IAAA,CAAK,MAAiCuE,CAAAA,CAAU,CACpE,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,IAAA,CAAMS,CAAAA,CAAK,QAAA,EACb,CAAC,EACH,CAAA,MAAS1F,CAAAA,CAAO,CACd,MAAM,IAAIL,CAAAA,CAAa,eAAA,CAAiB,iCAAA,CAAmC,CACzE,KAAA,CAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CAGA,GAAIU,EAAS,EAAA,CACX,OAIF,IAAMiF,CAAAA,CAAYjF,EAAS,IAAA,CAC3B,MAAM,IAAIf,CAAAA,CAAa,mBAAoB,yBAAA,CAA2B,CACpE,OAAA,CAAS,CACP,MAAA,CAAQe,CAAAA,CAAS,MAAA,CACjB,KAAA,CAAOiF,GAAW,KAAA,CAClB,iBAAA,CAAmBA,CAAAA,EAAW,iBAChC,CACF,CAAC,CACH,CAWA,MAAM,mBAAmBV,CAAAA,CAAkBlF,CAAAA,CAA4C,CAErF,IAAM2F,EAAO,IAAI,eAAA,CAAgB,CAC/B,SAAA,CAAW,KAAK,QAAA,CAChB,KAAA,CAAO3F,CAAAA,CAAQ,KACjB,CAAC,CAAA,CAEGA,CAAAA,CAAQ,aAAA,EACV2F,CAAAA,CAAK,IAAI,iBAAA,CAAmB3F,CAAAA,CAAQ,aAAa,CAAA,CAGnD,IAAIW,CAAAA,CACJ,GAAI,CACFA,EAAW,MAAM,IAAA,CAAK,IAAA,CAAK,KAAA,CAAiCuE,EAAU,CACpE,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,mCAClB,CAAA,CACA,IAAA,CAAMS,EAAK,QAAA,EACb,CAAC,EACH,OAAS1F,CAAAA,CAAO,CACd,MAAM,IAAIL,EAAa,eAAA,CAAiB,iCAAA,CAAmC,CACzE,KAAA,CAAOK,aAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CAEA,GAAIU,CAAAA,CAAS,GACX,OAGF,IAAMiF,CAAAA,CAAYjF,CAAAA,CAAS,KAC3B,MAAM,IAAIf,CAAAA,CAAa,kBAAA,CAAoB,0BAA2B,CACpE,OAAA,CAAS,CACP,MAAA,CAAQe,EAAS,MAAA,CACjB,KAAA,CAAOiF,CAAAA,EAAW,KAAA,CAClB,kBAAmBA,CAAAA,EAAW,iBAChC,CACF,CAAC,CACH,CACF,ECvFO,IAAMiB,CAAAA,CAAN,KAAoB,CASzB,WAAA,CAAY7G,CAAAA,CAA+B,CACzC,IAAA,CAAK,OAAA,CAAUA,CAAAA,CAAQ,OAAA,CACvB,KAAK,QAAA,CAAWA,CAAAA,CAAQ,QAAA,CACxB,IAAA,CAAK,WAAaA,CAAAA,CAAQ,UAAA,CAC1B,IAAA,CAAK,YAAA,CAAeA,EAAQ,YAAA,CAC5B,IAAA,CAAK,YAAA,CAAeA,CAAAA,CAAQ,aAC5B,IAAA,CAAK,SAAA,CAAYA,CAAAA,CAAQ,SAAA,CACzB,KAAK,YAAA,CAAe,IAAI4G,CAAAA,CAAa,CACnC,KAAM5G,CAAAA,CAAQ,IAAA,CACd,QAAA,CAAUA,CAAAA,CAAQ,QACpB,CAAC,EACH,CAcA,MAAM,OACJgF,CAAAA,CACAhF,CAAAA,CACuB,CAEvB,IAAM8G,EAAgB,MAAM,IAAA,CAAK,gBAAA,EAAiB,CAC5CC,EAAe,MAAM,IAAA,CAAK,eAAA,EAAgB,CAG5CC,EACAhH,CAAAA,EAAS,YAAA,EAAgBgF,CAAAA,EAAW,mBAAA,EAAuB+B,IAC7DC,CAAAA,CAAmB,MAAM,IAAA,CAAK,YAAA,CAAahC,EAAW+B,CAAY,CAAA,CAAA,CAIpE,MAAM,IAAA,CAAK,aAAY,CAGvB,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,gBAAiB,CAAE,MAAA,CAAQ,QAAS,CAAC,CAAA,CAI7D,IAAIE,CAAAA,CAUJ,GARI,KAAK,SAAA,EAAW,UAAA,GAAe,MAAA,CAEjCA,CAAAA,CAAqB,KAAK,SAAA,CAAU,UAAA,CAC3BjC,CAAAA,GACTiC,CAAAA,CAAqBjC,EAAU,oBAAA,CAAA,CAI7B,CAACiC,CAAAA,CACH,OAAO,CAAE,SAAA,CAAW,IAAA,CAAM,UAAA,CAAYD,CAAiB,EAIzD,IAAMvC,CAAAA,CAAUzE,CAAAA,EAAS,WAAA,EAAe8G,EAElC3B,CAAAA,CAAS,IAAI,eAAA,CAAgB,CACjC,UAAW,IAAA,CAAK,QAClB,CAAC,CAAA,CAED,OAAIV,CAAAA,EACFU,CAAAA,CAAO,GAAA,CAAI,gBAAiBV,CAAO,CAAA,CAEjCzE,CAAAA,EAAS,qBAAA,EACXmF,EAAO,GAAA,CAAI,0BAAA,CAA4BnF,CAAAA,CAAQ,qBAAqB,EAElEA,CAAAA,EAAS,KAAA,EACXmF,CAAAA,CAAO,GAAA,CAAI,QAASnF,CAAAA,CAAQ,KAAK,CAAA,CAG5B,CACL,UAAW,CAAA,EAAGiH,CAAkB,CAAA,CAAA,EAAI9B,CAAAA,CAAO,UAAU,CAAA,CAAA,CACrD,SAAA,CAAW,KAAA,CACX,WAAY6B,CACd,CACF,CAWA,MAAc,YAAA,CACZhC,CAAAA,CACAiB,CAAAA,CACkD,CAClD,IAAMX,CAAAA,CAAkD,CACtD,SAAA,CAAW,IACb,EAEA,GAAI,CAEEW,CAAAA,CAAO,YAAA,GACT,MAAM,IAAA,CAAK,YAAA,CAAa,MAAA,CAAOjB,CAAAA,CAAW,CACxC,KAAA,CAAOiB,CAAAA,CAAO,YAAA,CACd,aAAA,CAAe,eACjB,CAAC,CAAA,CACDX,CAAAA,CAAO,mBAAA,CAAsB,IAI/B,MAAM,IAAA,CAAK,YAAA,CAAa,MAAA,CAAON,EAAW,CACxC,KAAA,CAAOiB,CAAAA,CAAO,WAAA,CACd,aAAA,CAAe,cACjB,CAAC,CAAA,CACDX,EAAO,kBAAA,CAAqB,CAAA,EAC9B,CAAA,MAASrF,CAAAA,CAAO,CACdqF,CAAAA,CAAO,KAAA,CAAQrF,CAAAA,YAAiB,KAAA,CAAQA,EAAQ,IAAI,KAAA,CAAM,MAAA,CAAOA,CAAK,CAAC,EACzE,CAEA,OAAOqF,CACT,CAKA,MAAc,WAAA,EAA6B,CACzC,IAAM4B,EAAWpE,CAAAA,CAAa,MAAA,CAAO,IAAA,CAAK,UAAA,CAAY,KAAK,YAAY,CAAA,CACjEqE,CAAAA,CAAarE,CAAAA,CAAa,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAAA,CAE1E,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAOoE,CAAQ,CAAA,CAClC,MAAM,IAAA,CAAK,QAAQ,MAAA,CAAOC,CAAU,EACtC,CAKA,MAAc,gBAAA,EAA2C,CACvD,IAAMA,CAAAA,CAAarE,EAAa,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAAA,CAC1E,OAAO,IAAA,CAAK,OAAA,CAAQ,IAAIqE,CAAU,CACpC,CAKA,MAAc,eAAA,EAA4C,CACxD,IAAMD,CAAAA,CAAWpE,EAAa,MAAA,CAAO,IAAA,CAAK,UAAA,CAAY,IAAA,CAAK,YAAY,CAAA,CACjEa,CAAAA,CAAS,MAAM,IAAA,CAAK,QAAQ,GAAA,CAAIuD,CAAQ,CAAA,CAC9C,GAAI,CAACvD,CAAAA,CACH,OAAO,IAAA,CAET,GAAI,CACF,OAAO,IAAA,CAAK,KAAA,CAAMA,CAAM,CAC1B,CAAA,KAAQ,CACN,OAAO,IACT,CACF,CACF,EC9MO,IAAMyD,CAAAA,CAAN,KAAqB,CAG1B,WAAA,CAAYpH,CAAAA,CAAgC,CAC1C,IAAA,CAAK,IAAA,CAAOA,CAAAA,CAAQ,KACtB,CASA,MAAM,YAAA,CACJgF,CAAAA,CACAqC,CAAAA,CAC6B,CAC7B,IAAMC,CAAAA,CAAmBtC,CAAAA,CAAU,iBAAA,CAEnC,GAAI,CAACsC,CAAAA,CACH,OAAO,CACL,MAAO,KAAA,CACP,KAAA,CAAO,IAAI1H,CAAAA,CAAa,uBAAwB,iCAAiC,CACnF,CAAA,CAGF,GAAI,CACF,IAAMe,CAAAA,CAAW,MAAM,IAAA,CAAK,KAAK,KAAA,CAAgB2G,CAAAA,CAAkB,CACjE,MAAA,CAAQ,MACR,OAAA,CAAS,CACP,aAAA,CAAe,CAAA,OAAA,EAAUD,CAAW,CAAA,CACtC,CACF,CAAC,CAAA,CAED,OAAK1G,CAAAA,CAAS,EAAA,CAiBP,CACL,KAAA,CAAO,GACP,IAAA,CAAMA,CAAAA,CAAS,IACjB,CAAA,CAlBMA,EAAS,MAAA,GAAW,GAAA,CACf,CACL,KAAA,CAAO,GACP,KAAA,CAAO,IAAIf,CAAAA,CAAa,iBAAA,CAAmB,qBAAqB,CAClE,CAAA,CAGK,CACL,KAAA,CAAO,CAAA,CAAA,CACP,KAAA,CAAO,IAAIA,CAAAA,CAAa,uBAAwB,sBAAA,CAAwB,CACtE,OAAA,CAAS,CAAE,OAAQe,CAAAA,CAAS,MAAO,CACrC,CAAC,CACH,CAOJ,CAAA,MAASV,CAAAA,CAAO,CACd,OAAO,CACL,KAAA,CAAO,KAAA,CACP,KAAA,CAAO,IAAIL,CAAAA,CAAa,eAAA,CAAiB,yBAAA,CAA2B,CAClE,MAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CACF,CACF,CAUA,MAAM,WAAA,CAAY+E,CAAAA,CAAkCqC,CAAAA,CAAwC,CAC1F,IAAMC,CAAAA,CAAmBtC,CAAAA,CAAU,iBAAA,CAEnC,GAAI,CAACsC,CAAAA,CACH,MAAM,IAAI1H,EAAa,sBAAA,CAAwB,iCAAiC,CAAA,CAGlF,IAAIe,EACJ,GAAI,CACFA,CAAAA,CAAW,MAAM,KAAK,IAAA,CAAK,KAAA,CAAgB2G,CAAAA,CAAkB,CAC3D,OAAQ,KAAA,CACR,OAAA,CAAS,CACP,aAAA,CAAe,UAAUD,CAAW,CAAA,CACtC,CACF,CAAC,EACH,CAAA,MAASpH,CAAAA,CAAO,CACd,MAAM,IAAIL,CAAAA,CAAa,eAAA,CAAiB,2BAAA,CAA6B,CACnE,KAAA,CAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CAEA,GAAI,CAACU,CAAAA,CAAS,EAAA,CACZ,MAAM,IAAIf,EAAa,gBAAA,CAAkB,yBAAA,CAA2B,CAClE,OAAA,CAAS,CAAE,MAAA,CAAQe,CAAAA,CAAS,MAAO,CACrC,CAAC,CAAA,CAGH,OAAOA,CAAAA,CAAS,IAClB,CACF,ECjHO,IAAM4G,CAAAA,CAAN,KAAqB,CAO1B,WAAA,CAAYvH,CAAAA,CAAgC,CAF5C,KAAQ,SAAA,CAA0C,IAAA,CAGhD,IAAA,CAAK,YAAA,CAAeA,EAAQ,YAAA,CAC5B,IAAA,CAAK,cAAA,CAAiBA,CAAAA,CAAQ,eAChC,CAKA,YAAA,CAAagF,CAAAA,CAAwC,CACnD,KAAK,SAAA,CAAYA,EACnB,CAUA,MAAM,iBAAoC,CACxC,OAAO,IAAA,CAAK,YAAA,CAAa,iBAC3B,CASA,MAAM,YAAA,EAA4C,CAChD,GAAI,CAAC,IAAA,CAAK,UACR,OAAO,CACL,KAAA,CAAO,KAAA,CACP,MAAO,IAAIpF,CAAAA,CAAa,cAAA,CAAgB,kCAAkC,CAC5E,CAAA,CAGF,GAAI,CACF,IAAMyH,EAAc,MAAM,IAAA,CAAK,YAAA,CAAa,cAAA,GAC5C,OAAO,IAAA,CAAK,cAAA,CAAe,YAAA,CAAa,KAAK,SAAA,CAAWA,CAAW,CACrE,CAAA,MAASpH,EAAO,CACd,OAAIA,CAAAA,YAAiBL,CAAAA,CACZ,CAAE,KAAA,CAAO,KAAA,CAAO,KAAA,CAAAK,CAAM,CAAA,CAExB,CACL,KAAA,CAAO,KAAA,CACP,MAAO,IAAIL,CAAAA,CAAa,sBAAA,CAAwB,yBAAA,CAA2B,CACzE,KAAA,CAAOK,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAQ,MAC1C,CAAC,CACH,CACF,CACF,CAUA,MAAM,OAAA,EAA6B,CACjC,GAAI,CAAC,IAAA,CAAK,SAAA,CACR,MAAM,IAAIL,EAAa,cAAA,CAAgB,kCAAkC,CAAA,CAG3E,IAAMyH,CAAAA,CAAc,MAAM,IAAA,CAAK,YAAA,CAAa,gBAAe,CAC3D,OAAO,IAAA,CAAK,cAAA,CAAe,YAAY,IAAA,CAAK,SAAA,CAAWA,CAAW,CACpE,CACF,EC/DA,eAAeG,CAAAA,CACbnG,CAAAA,CACAyC,EACA2D,CAAAA,CAAS,EAAA,CACQ,CACjB,IAAMC,EAAO,MAAMrG,CAAAA,CAAO,MAAA,CAAOyC,CAAK,EACtC,OAAOrC,CAAAA,CAAgBiG,CAAI,CAAA,CAAE,MAAM,CAAA,CAAGD,CAAM,CAC9C,KAKaE,CAAAA,CAAN,KAAoB,CAmDzB,WAAA,CAAYhI,EAA6B,CAPzC,IAAA,CAAQ,WAAA,CAAc,KAAA,CAQpB,KAAK,MAAA,CAASD,CAAAA,CAAcC,CAAM,CAAA,CAClC,KAAK,gBAAA,CAAmBS,CAAAA,CAAgBT,CAAAA,CAAO,MAAM,EAGrD,IAAA,CAAK,MAAA,CAAS,IAAImB,CAAAA,CAElB,KAAK,eAAA,CAAkB,IAAID,CAAAA,CAAgB,CACzC,KAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAClB,UAAA,CAAY,KAAK,MAAA,CAAO,mBAC1B,CAAC,CAAA,CAED,IAAA,CAAK,IAAA,CAAO,IAAIO,CAAAA,CAAW,KAAK,MAAA,CAAO,MAAM,CAAA,CAE7C,IAAA,CAAK,aAAe,IAAIyD,CAAAA,CAAsB,IAAA,CAAK,MAAA,CAAO,KAAM,IAAA,CAAK,MAAA,CAAO,QAAQ,EACtF,CAOA,MAAM,UAAA,EAA4B,CAChC,GAAI,KAAK,WAAA,CACP,OAIF,IAAM+C,CAAAA,CAAa,KAAK,MAAA,CAAO,WAAA,CAAY,UAAA,CAC3C,IAAA,CAAK,WAAa,MAAMJ,CAAAA,CAAW,IAAA,CAAK,MAAA,CAAO,MAAA,CAAQ,IAAA,CAAK,gBAAA,CAAkBI,CAAU,EACxF,IAAA,CAAK,YAAA,CAAe,MAAMJ,CAAAA,CAAW,KAAK,MAAA,CAAO,MAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAUI,CAAU,CAAA,CAGzF,IAAA,CAAK,YAAA,CAAe,IAAI7D,CAAAA,CACtB,IAAA,CAAK,MAAA,CAAO,MAAA,CACZ,KAAK,MAAA,CAAO,OAAA,CACZ,IAAA,CAAK,UAAA,CACL,KAAK,YACP,CAAA,CAGA,IAAA,CAAK,YAAA,CAAe,IAAI0C,CAAAA,CAAa,CACnC,IAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAClB,OAAA,CAAS,IAAA,CAAK,OAAO,OAAA,CACrB,QAAA,CAAU,IAAA,CAAK,MAAA,CAAO,SACtB,UAAA,CAAY,IAAA,CAAK,UAAA,CACjB,YAAA,CAAc,KAAK,YAAA,CACnB,kBAAA,CAAoB,IAAA,CAAK,MAAA,CAAO,mBAChC,YAAA,CAAc,IAAA,CAAK,MACrB,CAAC,EAGD,IAAA,CAAK,aAAA,CAAgB,IAAII,CAAAA,CAAc,CACrC,OAAA,CAAS,IAAA,CAAK,MAAA,CAAO,OAAA,CACrB,KAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAClB,QAAA,CAAU,IAAA,CAAK,MAAA,CAAO,QAAA,CACtB,UAAA,CAAY,KAAK,UAAA,CACjB,YAAA,CAAc,IAAA,CAAK,YAAA,CACnB,aAAc,IAAA,CAAK,MAAA,CACnB,SAAA,CAAW,IAAA,CAAK,OAAO,SACzB,CAAC,CAAA,CAGD,IAAA,CAAK,kBAAoB,IAAIH,CAAAA,CAAkB,CAC7C,IAAA,CAAM,KAAK,MAAA,CAAO,IAAA,CAClB,QAAA,CAAU,IAAA,CAAK,OAAO,QACxB,CAAC,CAAA,CAGD,IAAA,CAAK,aAAe,IAAIE,CAAAA,CAAa,CACnC,IAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAClB,QAAA,CAAU,KAAK,MAAA,CAAO,QACxB,CAAC,CAAA,CAGD,IAAMiB,CAAAA,CAAiB,IAAIT,CAAAA,CAAe,CACxC,KAAM,IAAA,CAAK,MAAA,CAAO,IACpB,CAAC,EAGD,IAAA,CAAK,cAAA,CAAiB,IAAIG,CAAAA,CAAe,CACvC,YAAA,CAAc,IAAA,CAAK,YAAA,CACnB,cAAA,CAAAM,CACF,CAAC,CAAA,CAGD,MAAM,IAAA,CAAK,aAAa,oBAAA,EAAqB,CAE7C,IAAA,CAAK,WAAA,CAAc,KACrB,CAKQ,iBAAA,EAA0B,CAChC,GAAI,CAAC,IAAA,CAAK,WAAA,CACR,MAAM,IAAIjI,CAAAA,CACR,iBAAA,CACA,oDACF,CAEJ,CAOA,MAAM,QAAA,EAA2C,CAC/C,IAAMoF,EAAY,MAAM,IAAA,CAAK,eAAA,CAAgB,QAAA,CAAS,KAAK,gBAAgB,CAAA,CAG3E,OAAA,IAAA,CAAK,YAAA,CAAa,aAAaA,CAAS,CAAA,CACxC,IAAA,CAAK,cAAA,CAAe,aAAaA,CAAS,CAAA,CAEnCA,CACT,CAeA,MAAM,qBAAA,CACJhF,CAAAA,CACiC,CACjC,KAAK,iBAAA,EAAkB,CAEvB,IAAMgF,CAAAA,CAAY,MAAM,IAAA,CAAK,QAAA,EAAS,CAGhC8C,CAAAA,CAAW,MAAM,IAAA,CAAK,IAAA,CAAK,YAAA,EAAa,CAGxCrE,EAAY,MAAM,IAAA,CAAK,YAAA,CAAa,iBAAA,CAAkB,CAC1D,WAAA,CAAazD,CAAAA,CAAQ,WAAA,CACrB,YAAA,CAAc8H,EAAS,YAAA,CACvB,UAAA,CAAY,IAAA,CAAK,MAAA,CAAO,eAC1B,CAAC,CAAA,CAGKxC,CAAAA,CAAS,IAAA,CAAK,aAAa,qBAAA,CAAsBN,CAAAA,CAAWvB,CAAAA,CAAWqE,CAAAA,CAAU9H,CAAO,CAAA,CAG9F,OAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,mBAAoB,CAAE,GAAA,CAAKsF,CAAAA,CAAO,GAAI,CAAC,CAAA,CAEjDA,CACT,CAWA,MAAM,eAAeC,CAAAA,CAAwC,CAC3D,IAAA,CAAK,iBAAA,GAGL,GAAM,CAAE,IAAA,CAAAzF,CAAAA,CAAM,MAAAmD,CAAM,CAAA,CAAI,IAAA,CAAK,YAAA,CAAa,cAAcsC,CAAW,CAAA,CAGnE,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,eAAA,CAAiB,CAAE,IAAA,CAAAzF,EAAM,KAAA,CAAAmD,CAAM,CAAC,CAAA,CAGjD,IAAMQ,CAAAA,CAAY,MAAM,IAAA,CAAK,YAAA,CAAa,wBAAwBR,CAAK,CAAA,CAGjE+B,CAAAA,CAAY,MAAM,KAAK,QAAA,EAAS,CAGhCiB,CAAAA,CAAS,MAAM,KAAK,YAAA,CAAa,YAAA,CAAajB,CAAAA,CAAW,CAC7D,KAAAlF,CAAAA,CACA,KAAA,CAAAmD,CAAAA,CACA,WAAA,CAAaQ,EAAU,WAAA,CACvB,YAAA,CAAcA,CAAAA,CAAU,YAAA,CACxB,KAAA,CAAOA,CAAAA,CAAU,KACnB,CAAC,EAGD,OAAA,MAAM,IAAA,CAAK,YAAA,CAAa,UAAA,CAAWwC,CAAM,CAAA,CAElCA,CACT,CASA,IAAI,OAAQ,CACV,OAAA,IAAA,CAAK,iBAAA,EAAkB,CAChB,CAIL,cAAA,CAAgB,IAAM,IAAA,CAAK,YAAA,CAAa,gBAAe,CAKvD,SAAA,CAAW,IAAM,IAAA,CAAK,aAAa,SAAA,EAAU,CAK7C,UAAA,CAAY,IAAM,KAAK,YAAA,CAAa,UAAA,EAAW,CAK/C,eAAA,CAAiB,IAAM,IAAA,CAAK,YAAA,CAAa,eAAA,GAczC,QAAA,CAAU,MAAOK,CAAAA,GAEf,MAAM,KAAK,QAAA,EAAS,CACb,IAAA,CAAK,YAAA,CAAa,cAAcA,CAAO,CAAA,CAAA,CAahD,UAAA,CAAY,MAAOtG,GAAoE,CACrF,IAAMgF,CAAAA,CAAY,MAAM,KAAK,QAAA,EAAS,CACtC,OAAO,IAAA,CAAK,kBAAkB,UAAA,CAAWA,CAAAA,CAAWhF,CAAO,CAC7D,EAWA,MAAA,CAAQ,MAAOA,CAAAA,EAA+C,CAC5D,IAAMgF,CAAAA,CAAY,MAAM,IAAA,CAAK,UAAS,CACtC,OAAO,IAAA,CAAK,YAAA,CAAa,OAAOA,CAAAA,CAAWhF,CAAO,CACpD,CACF,CACF,CASA,IAAI,OAAA,EAAU,CACZ,YAAK,iBAAA,EAAkB,CAChB,CAIL,eAAA,CAAiB,IAAM,IAAA,CAAK,cAAA,CAAe,eAAA,EAAgB,CAK3D,MAAO,IAAM,IAAA,CAAK,cAAA,CAAe,YAAA,EACnC,CACF,CAOA,MAAM,eAAA,EAAoC,CACxC,OAAA,IAAA,CAAK,iBAAA,EAAkB,CAChB,KAAK,YAAA,CAAa,eAAA,EAC3B,CAOA,MAAM,OAAA,EAA6B,CACjC,OAAA,IAAA,CAAK,iBAAA,GACE,IAAA,CAAK,cAAA,CAAe,OAAA,EAC7B,CAcA,MAAM,MAAA,CAAOA,CAAAA,CAAgD,CAC3D,KAAK,iBAAA,EAAkB,CAEvB,IAAIgF,CAAAA,CAA0C,KAC9C,GAAI,CACFA,CAAAA,CAAY,MAAM,KAAK,QAAA,GACzB,CAAA,KAAQ,CAER,CAEA,OAAO,IAAA,CAAK,aAAA,CAAc,OAAOA,CAAAA,CAAWhF,CAAO,CACrD,CAaA,GAA+Be,CAAAA,CAAUC,CAAAA,CAA6C,CACpF,OAAO,KAAK,MAAA,CAAO,EAAA,CAAGD,CAAAA,CAAOC,CAAO,CACtC,CASA,IAAA,CAAiCD,CAAAA,CAAUC,CAAAA,CAA6C,CACtF,OAAO,IAAA,CAAK,MAAA,CAAO,IAAA,CAAKD,EAAOC,CAAO,CACxC,CAQA,GAAA,CAAgCD,EAAUC,CAAAA,CAAuC,CAC/E,IAAA,CAAK,MAAA,CAAO,GAAA,CAAID,CAAAA,CAAOC,CAAO,EAChC,CACF,EAWA,eAAsB+G,CAAAA,CAAoBpI,CAAAA,CAAqD,CAC7F,IAAMqI,CAAAA,CAAS,IAAIL,CAAAA,CAAchI,CAAM,CAAA,CACvC,OAAA,MAAMqI,CAAAA,CAAO,UAAA,GACNA,CACT,CC7aA,IAAMC,CAAAA,CAAoC,IAAI,GAAA,CAAI,CAChD,gBAAA,CACA,sBAAA,CACA,mBACA,4BACF,CAAC,CAAA,CAQYC,CAAAA,CAAN,KAAwB,CAC7B,WAAA,CAA6BnD,CAAAA,CAAkB,CAAlB,cAAAA,EAAmB,CAahD,kBAAA,CACEC,CAAAA,CACAvB,EACAwB,CAAAA,CACAjF,CAAAA,CACqB,CACrB,IAAMkF,EAAWF,CAAAA,CAAU,sBAAA,CACrBG,CAAAA,CAAS,IAAI,gBAGnBA,CAAAA,CAAO,GAAA,CAAI,WAAA,CAAa,IAAA,CAAK,QAAQ,CAAA,CACrCA,CAAAA,CAAO,GAAA,CAAI,eAAA,CAAiB,MAAM,CAAA,CAClCA,CAAAA,CAAO,GAAA,CAAI,cAAA,CAAgBnF,EAAQ,WAAW,CAAA,CAC9CmF,CAAAA,CAAO,GAAA,CAAI,QAAS1B,CAAAA,CAAU,KAAK,CAAA,CACnC0B,CAAAA,CAAO,GAAA,CAAI,OAAA,CAAS1B,CAAAA,CAAU,KAAK,EAGnC0B,CAAAA,CAAO,GAAA,CAAI,QAAA,CAAU,MAAM,EAG3BA,CAAAA,CAAO,GAAA,CAAI,gBAAA,CAAkBF,CAAAA,CAAK,aAAa,CAAA,CAC/CE,CAAAA,CAAO,GAAA,CAAI,uBAAA,CAAyBF,EAAK,mBAAmB,CAAA,CAG5D,IAAMG,CAAAA,CAAQpF,EAAQ,KAAA,EAAS,QAAA,CAY/B,GAXAmF,CAAAA,CAAO,IAAI,OAAA,CAASC,CAAK,CAAA,CAGrBpF,CAAAA,CAAQ,WACVmF,CAAAA,CAAO,GAAA,CAAI,YAAA,CAAcnF,CAAAA,CAAQ,SAAS,CAAA,CAExCA,CAAAA,CAAQ,WAAA,EACVmF,EAAO,GAAA,CAAI,eAAA,CAAiBnF,CAAAA,CAAQ,WAAW,EAI7CA,CAAAA,CAAQ,WAAA,CAAa,CACvB,IAAMqF,EAAkB,IAAI,GAAA,CAAI,CAC9B,WAAA,CACA,gBACA,cAAA,CACA,OAAA,CACA,OAAA,CACA,gBAAA,CACA,wBACA,OAAA,CACA,QACF,CAAC,CAAA,CAED,OAAW,CAAC3B,CAAAA,CAAKI,CAAK,CAAA,GAAK,OAAO,OAAA,CAAQ9D,CAAAA,CAAQ,WAAW,CAAA,CACvDqF,CAAAA,CAAgB,GAAA,CAAI3B,CAAAA,CAAI,WAAA,EAAa,CAAA,EAGzCyB,CAAAA,CAAO,GAAA,CAAIzB,CAAAA,CAAKI,CAAK,EAEzB,CAIA,IAAMwB,CAAAA,CAA8B,CAAE,GAAA,CAF1B,CAAA,EAAGJ,CAAQ,CAAA,CAAA,EAAIC,EAAO,QAAA,EAAU,CAAA,CAEF,CAAA,CAE1C,OAAInF,CAAAA,CAAQ,WAAA,GACVsF,CAAAA,CAAO,KAAA,CAAQ7B,EAAU,KAAA,CACzB6B,CAAAA,CAAO,KAAA,CAAQ7B,CAAAA,CAAU,OAGpB6B,CACT,CAWA,uBAAA,CAAwB6C,CAAAA,CAOtB,CACA,IAAI3C,CAAAA,CAGA2C,CAAAA,CAAY,SAAS,GAAG,CAAA,CAI1B3C,CAAAA,CAAAA,CAHY2C,CAAAA,CAAY,WAAW,MAAM,CAAA,CACrC,IAAI,GAAA,CAAIA,CAAW,CAAA,CACnB,IAAI,GAAA,CAAIA,CAAAA,CAAa,qBAAqB,CAAA,EAC3B,YAAA,CAEnB3C,CAAAA,CAAe,IAAI,gBAAgB2C,CAAW,CAAA,CAIhD,IAAMlI,CAAAA,CAAQuF,EAAa,GAAA,CAAI,OAAO,CAAA,CACtC,GAAIvF,EAAO,CACT,IAAMwF,CAAAA,CAAmBD,CAAAA,CAAa,GAAA,CAAI,mBAAmB,CAAA,CACvD4C,CAAAA,CAAY,KAAK,kBAAA,CAAmBnI,CAAK,CAAA,CAE/C,OAAO,CACL,OAAA,CAAS,KAAA,CACT,KAAA,CAAO,IAAIL,EAAawI,CAAAA,CAAW3C,CAAAA,EAAoB,IAAA,CAAK,sBAAA,CAAuBxF,CAAK,CAAA,CAAG,CACzF,OAAA,CAAS,CACP,MAAAA,CAAAA,CACA,iBAAA,CAAmBwF,CAAAA,CACnB,SAAA,CAAWD,EAAa,GAAA,CAAI,WAAW,CACzC,CACF,CAAC,CACH,CACF,CAGA,IAAM1F,CAAAA,CAAO0F,CAAAA,CAAa,GAAA,CAAI,MAAM,EAC9BvC,CAAAA,CAAQuC,CAAAA,CAAa,GAAA,CAAI,OAAO,EAEtC,OAAK1F,CAAAA,CAMAmD,CAAAA,CAOE,CAAE,QAAS,IAAA,CAAM,IAAA,CAAAnD,CAAAA,CAAM,KAAA,CAAAmD,CAAM,CAAA,CAN3B,CACL,OAAA,CAAS,KAAA,CACT,MAAO,IAAIrD,CAAAA,CAAa,eAAA,CAAiB,mDAAmD,CAC9F,CAAA,CATO,CACL,OAAA,CAAS,KAAA,CACT,MAAO,IAAIA,CAAAA,CAAa,cAAA,CAAgB,sDAAsD,CAChG,CAUJ,CAQA,0BAAA,CAA2BK,EAA8B,CACvD,OAAOgI,CAAAA,CAAkC,GAAA,CAAIhI,EAAM,IAAI,CACzD,CAKQ,kBAAA,CACNA,EAC+G,CAC/G,OAAIgI,CAAAA,CAAkC,GAAA,CAAIhI,CAAK,CAAA,CACtCA,CAAAA,CAEF,aACT,CAKQ,uBAAuBA,CAAAA,CAAuB,CACpD,OAAQA,CAAAA,EACN,KAAK,gBAAA,CACH,OAAO,4CAAA,CACT,KAAK,sBAAA,CACH,OAAO,2BAAA,CACT,KAAK,mBACH,OAAO,uBAAA,CACT,KAAK,4BAAA,CACH,OAAO,6BAAA,CACT,QACE,OAAO,8BACX,CACF,CACF,EC9OA,eAAsBoI,CAAAA,CACpBC,EACAjH,CAAAA,CACiB,CAEjB,IAAMqG,CAAAA,CAAO,MAAMrG,CAAAA,CAAO,MAAA,CAAOiH,CAAY,CAAA,CAGvCC,EAAWb,CAAAA,CAAK,KAAA,CAAM,CAAA,CAAGA,CAAAA,CAAK,OAAS,CAAC,CAAA,CAG9C,OAAOjG,CAAAA,CAAgB8G,CAAQ,CACjC","file":"index.cjs","sourcesContent":["/**\r\n * Client Configuration\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\n\r\n/**\r\n * Endpoint overrides\r\n */\r\nexport interface EndpointOverrides {\r\n /** Authorization endpoint */\r\n authorization?: string;\r\n /** Token endpoint */\r\n token?: string;\r\n /** UserInfo endpoint */\r\n userinfo?: string;\r\n /** Revocation endpoint */\r\n revocation?: string;\r\n /** End session endpoint (null to disable) */\r\n endSession?: string | null;\r\n}\r\n\r\n/**\r\n * Hash options for storage key generation\r\n */\r\nexport interface HashOptions {\r\n /**\r\n * Hash length for storage keys\r\n *\r\n * Default: 16 characters (96 bits)\r\n * Alternative: 22 characters (132 bits) for lower collision risk\r\n */\r\n hashLength?: number;\r\n}\r\n\r\n/**\r\n * Authrim Client Configuration\r\n */\r\nexport interface AuthrimClientConfig {\r\n /**\r\n * OpenID Connect issuer URL\r\n *\r\n * This is used to fetch the discovery document and validate tokens.\r\n * Trailing slashes are automatically normalized.\r\n */\r\n issuer: string;\r\n\r\n /**\r\n * OAuth 2.0 client ID\r\n */\r\n clientId: string;\r\n\r\n /**\r\n * HTTP client implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides a default browser implementation.\r\n */\r\n http: HttpClient;\r\n\r\n /**\r\n * Crypto provider implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides a default browser implementation.\r\n */\r\n crypto: CryptoProvider;\r\n\r\n /**\r\n * Storage provider implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides localStorage/sessionStorage implementations.\r\n */\r\n storage: AuthrimStorage;\r\n\r\n /**\r\n * Default redirect URI for authorization requests\r\n */\r\n redirectUri?: string;\r\n\r\n /**\r\n * Default scopes to request\r\n *\r\n * Default: ['openid', 'profile']\r\n */\r\n scopes?: string[];\r\n\r\n /**\r\n * Manual endpoint overrides\r\n *\r\n * Use these to override discovery document endpoints.\r\n * Set endSession to null to disable logout redirect.\r\n */\r\n endpoints?: EndpointOverrides;\r\n\r\n /**\r\n * Enable Flow Engine (server-driven UI)\r\n *\r\n * Default: false\r\n * Set to true to enable server-driven UI flows.\r\n * Note: Both SDK and server must have Flow Engine enabled for it to work.\r\n */\r\n flowEngine?: boolean;\r\n\r\n /**\r\n * Discovery cache TTL in milliseconds\r\n *\r\n * Default: 3600000 (1 hour)\r\n */\r\n discoveryCacheTtlMs?: number;\r\n\r\n /**\r\n * Token refresh skew in seconds\r\n *\r\n * Refresh tokens this many seconds before expiration.\r\n * Default: 30\r\n */\r\n refreshSkewSeconds?: number;\r\n\r\n /**\r\n * State/nonce TTL in seconds\r\n *\r\n * Default: 600 (10 minutes)\r\n */\r\n stateTtlSeconds?: number;\r\n\r\n /**\r\n * Hash options for storage key generation\r\n */\r\n hashOptions?: HashOptions;\r\n}\r\n\r\n/**\r\n * Resolved configuration with defaults applied\r\n */\r\nexport interface ResolvedConfig extends Required<\r\n Omit<AuthrimClientConfig, 'endpoints' | 'redirectUri' | 'hashOptions'>\r\n> {\r\n endpoints?: EndpointOverrides;\r\n redirectUri?: string;\r\n hashOptions: Required<HashOptions>;\r\n}\r\n\r\n/**\r\n * Apply defaults to configuration\r\n */\r\nexport function resolveConfig(config: AuthrimClientConfig): ResolvedConfig {\r\n return {\r\n ...config,\r\n scopes: config.scopes ?? ['openid', 'profile'],\r\n flowEngine: config.flowEngine ?? false,\r\n discoveryCacheTtlMs: config.discoveryCacheTtlMs ?? 3600 * 1000,\r\n refreshSkewSeconds: config.refreshSkewSeconds ?? 30,\r\n stateTtlSeconds: config.stateTtlSeconds ?? 600,\r\n hashOptions: {\r\n hashLength: config.hashOptions?.hashLength ?? 16,\r\n },\r\n };\r\n}\r\n","/**\r\n * Authrim SDK Error Types\r\n */\r\n\r\n/**\r\n * User action recommended for error recovery\r\n */\r\nexport type AuthrimErrorUserAction =\r\n | 'retry'\r\n | 'reauthenticate'\r\n | 'contact_support'\r\n | 'check_network'\r\n | 'none';\r\n\r\n/**\r\n * Error severity level\r\n */\r\nexport type AuthrimErrorSeverity = 'fatal' | 'error' | 'warning';\r\n\r\n/**\r\n * Error metadata for recovery information\r\n */\r\nexport interface AuthrimErrorMeta {\r\n /** Whether this is a transient error */\r\n transient: boolean;\r\n /** Whether automatic retry is possible */\r\n retryable: boolean;\r\n /** Suggested retry wait time in milliseconds */\r\n retryAfterMs?: number;\r\n /** Maximum number of retry attempts */\r\n maxRetries?: number;\r\n /** Recommended user action */\r\n userAction: AuthrimErrorUserAction;\r\n /** Error severity level */\r\n severity: AuthrimErrorSeverity;\r\n}\r\n\r\n/**\r\n * Error codes used by the SDK\r\n */\r\nexport type AuthrimErrorCode =\r\n // OAuth 2.0 / OIDC standard errors\r\n | 'invalid_request'\r\n | 'unauthorized_client'\r\n | 'access_denied'\r\n | 'unsupported_response_type'\r\n | 'invalid_scope'\r\n | 'server_error'\r\n | 'temporarily_unavailable'\r\n | 'invalid_grant'\r\n | 'invalid_token'\r\n // SDK-specific errors\r\n | 'invalid_state'\r\n | 'expired_state'\r\n | 'invalid_nonce'\r\n | 'nonce_mismatch'\r\n | 'session_expired'\r\n | 'session_check_failed'\r\n | 'network_error'\r\n | 'timeout_error'\r\n | 'discovery_error'\r\n | 'discovery_mismatch'\r\n | 'configuration_error'\r\n | 'storage_error'\r\n | 'flow_engine_error'\r\n // Token errors\r\n | 'no_tokens'\r\n | 'token_expired'\r\n | 'token_error'\r\n | 'refresh_error'\r\n | 'token_exchange_error'\r\n // Callback errors\r\n | 'oauth_error'\r\n | 'missing_code'\r\n | 'missing_state'\r\n // Initialization errors\r\n | 'not_initialized'\r\n | 'no_discovery'\r\n // Session errors\r\n | 'no_userinfo_endpoint'\r\n | 'userinfo_error'\r\n // Token introspection/revocation errors\r\n | 'introspection_error'\r\n | 'revocation_error'\r\n | 'no_introspection_endpoint'\r\n | 'no_revocation_endpoint'\r\n // Silent auth errors (OIDC prompt=none)\r\n | 'login_required'\r\n | 'interaction_required'\r\n | 'consent_required'\r\n | 'account_selection_required';\r\n\r\n/**\r\n * Options for creating an AuthrimError\r\n */\r\nexport interface AuthrimErrorOptions {\r\n details?: Record<string, unknown>;\r\n errorUri?: string;\r\n cause?: Error;\r\n}\r\n\r\n/**\r\n * Authrim SDK Error class\r\n */\r\nexport class AuthrimError extends Error {\r\n /** Error code for programmatic handling */\r\n readonly code: AuthrimErrorCode;\r\n\r\n /** Additional error details */\r\n readonly details?: Record<string, unknown>;\r\n\r\n /** OAuth error_uri if provided */\r\n readonly errorUri?: string;\r\n\r\n /** Underlying cause */\r\n readonly cause?: Error;\r\n\r\n constructor(code: AuthrimErrorCode, message: string, options?: AuthrimErrorOptions) {\r\n super(message);\r\n this.name = 'AuthrimError';\r\n this.code = code;\r\n this.details = options?.details;\r\n this.errorUri = options?.errorUri;\r\n this.cause = options?.cause;\r\n }\r\n\r\n /**\r\n * Create an AuthrimError from an OAuth error response\r\n */\r\n static fromOAuthError(error: {\r\n error: string;\r\n error_description?: string;\r\n error_uri?: string;\r\n }): AuthrimError {\r\n const oauthCodes = [\r\n 'invalid_request',\r\n 'unauthorized_client',\r\n 'access_denied',\r\n 'unsupported_response_type',\r\n 'invalid_scope',\r\n 'server_error',\r\n 'temporarily_unavailable',\r\n 'invalid_grant',\r\n 'invalid_token',\r\n ] as const;\r\n\r\n type OAuthErrorCode = (typeof oauthCodes)[number];\r\n\r\n const code: AuthrimErrorCode = oauthCodes.includes(error.error as OAuthErrorCode)\r\n ? (error.error as OAuthErrorCode)\r\n : 'invalid_request';\r\n\r\n return new AuthrimError(code, error.error_description ?? error.error, {\r\n errorUri: error.error_uri,\r\n details: { originalError: error.error },\r\n });\r\n }\r\n\r\n /**\r\n * Check if an error is retryable (e.g., network errors)\r\n */\r\n isRetryable(): boolean {\r\n return this.code === 'network_error' || this.code === 'timeout_error';\r\n }\r\n\r\n /**\r\n * Get error metadata for recovery guidance\r\n */\r\n get meta(): AuthrimErrorMeta {\r\n return getErrorMeta(this.code);\r\n }\r\n}\r\n\r\n/**\r\n * Error metadata mapping for each error code\r\n */\r\nconst ERROR_META_MAP: Record<AuthrimErrorCode, AuthrimErrorMeta> = {\r\n // OAuth 2.0 / OIDC standard errors\r\n invalid_request: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n unauthorized_client: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n access_denied: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n unsupported_response_type: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n invalid_scope: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n server_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 5000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n temporarily_unavailable: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 10000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n invalid_grant: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n invalid_token: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n\r\n // SDK-specific errors\r\n invalid_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n expired_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n invalid_nonce: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n nonce_mismatch: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n session_expired: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n session_check_failed: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n network_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 3,\r\n userAction: 'check_network',\r\n severity: 'error',\r\n },\r\n timeout_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n discovery_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 5000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n discovery_mismatch: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n configuration_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n storage_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 1000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n flow_engine_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n\r\n // Token errors\r\n no_tokens: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n token_expired: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n token_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n refresh_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n token_exchange_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Callback errors\r\n oauth_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n missing_code: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n missing_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n\r\n // Initialization errors\r\n not_initialized: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'fatal',\r\n },\r\n no_discovery: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Session errors\r\n no_userinfo_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n userinfo_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Token introspection/revocation errors\r\n introspection_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n revocation_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n no_introspection_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n no_revocation_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n\r\n // Silent auth errors\r\n login_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n interaction_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n consent_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n account_selection_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n};\r\n\r\n/**\r\n * Get error metadata for a given error code\r\n */\r\nexport function getErrorMeta(code: AuthrimErrorCode): AuthrimErrorMeta {\r\n return ERROR_META_MAP[code];\r\n}\r\n","/**\r\n * OIDC Discovery Client\r\n *\r\n * Fetches and caches OIDC Discovery documents from the authorization server.\r\n * https://openid.net/specs/openid-connect-discovery-1_0.html\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Cached discovery document with timestamp\r\n */\r\ninterface CachedDiscovery {\r\n doc: OIDCDiscoveryDocument;\r\n fetchedAt: number;\r\n}\r\n\r\n/**\r\n * Discovery client options\r\n */\r\nexport interface DiscoveryClientOptions {\r\n /** HTTP client for making requests */\r\n http: HttpClient;\r\n /** Cache TTL in milliseconds (default: 1 hour) */\r\n cacheTtlMs?: number;\r\n}\r\n\r\n/**\r\n * Normalize issuer URL\r\n *\r\n * Removes trailing slashes to ensure consistent comparison.\r\n *\r\n * @param issuer - Issuer URL\r\n * @returns Normalized issuer URL\r\n */\r\nexport function normalizeIssuer(issuer: string): string {\r\n return issuer.replace(/\\/+$/, '');\r\n}\r\n\r\n/**\r\n * OIDC Discovery Client\r\n */\r\nexport class DiscoveryClient {\r\n private readonly http: HttpClient;\r\n private readonly cacheTtlMs: number;\r\n private readonly cache: Map<string, CachedDiscovery> = new Map();\r\n\r\n /** Default cache TTL: 1 hour */\r\n private static readonly DEFAULT_CACHE_TTL_MS = 3600 * 1000;\r\n\r\n constructor(options: DiscoveryClientOptions) {\r\n this.http = options.http;\r\n this.cacheTtlMs = options.cacheTtlMs ?? DiscoveryClient.DEFAULT_CACHE_TTL_MS;\r\n }\r\n\r\n /**\r\n * Fetch the OIDC Discovery document for an issuer\r\n *\r\n * @param issuer - Issuer URL\r\n * @returns Discovery document\r\n * @throws AuthrimError if discovery fails or issuer mismatch\r\n */\r\n async discover(issuer: string): Promise<OIDCDiscoveryDocument> {\r\n const normalizedIssuer = normalizeIssuer(issuer);\r\n const cached = this.cache.get(normalizedIssuer);\r\n\r\n // Return cached document if still valid\r\n if (cached && !this.isExpired(cached)) {\r\n return cached.doc;\r\n }\r\n\r\n // Fetch discovery document\r\n const discoveryUrl = `${normalizedIssuer}/.well-known/openid-configuration`;\r\n\r\n let doc: OIDCDiscoveryDocument;\r\n try {\r\n const response = await this.http.fetch<OIDCDiscoveryDocument>(discoveryUrl);\r\n if (!response.ok) {\r\n throw new AuthrimError('discovery_error', `Discovery request failed: ${response.status}`, {\r\n details: { status: response.status, statusText: response.statusText },\r\n });\r\n }\r\n doc = response.data;\r\n } catch (error) {\r\n if (error instanceof AuthrimError) {\r\n throw error;\r\n }\r\n throw new AuthrimError('discovery_error', 'Failed to fetch discovery document', {\r\n cause: error instanceof Error ? error : undefined,\r\n details: { url: discoveryUrl },\r\n });\r\n }\r\n\r\n // Validate issuer matches (security check)\r\n const docIssuer = normalizeIssuer(doc.issuer);\r\n if (docIssuer !== normalizedIssuer) {\r\n throw new AuthrimError(\r\n 'discovery_mismatch',\r\n `Issuer mismatch in discovery document: expected \"${normalizedIssuer}\", got \"${docIssuer}\"`,\r\n {\r\n details: {\r\n expected: normalizedIssuer,\r\n actual: docIssuer,\r\n },\r\n }\r\n );\r\n }\r\n\r\n // Cache the document\r\n this.cache.set(normalizedIssuer, {\r\n doc,\r\n fetchedAt: Date.now(),\r\n });\r\n\r\n return doc;\r\n }\r\n\r\n /**\r\n * Check if a cached document has expired\r\n */\r\n private isExpired(cached: CachedDiscovery): boolean {\r\n return Date.now() - cached.fetchedAt > this.cacheTtlMs;\r\n }\r\n\r\n /**\r\n * Clear the discovery cache (useful for testing)\r\n */\r\n clearCache(): void {\r\n this.cache.clear();\r\n }\r\n\r\n /**\r\n * Clear a specific issuer from the cache\r\n *\r\n * @param issuer - Issuer URL to clear\r\n */\r\n clearIssuer(issuer: string): void {\r\n this.cache.delete(normalizeIssuer(issuer));\r\n }\r\n}\r\n","/**\r\n * Event Emitter\r\n *\r\n * Simple typed event emitter for SDK events.\r\n */\r\n\r\nimport type { AuthrimEvents, AuthrimEventName, AuthrimEventHandler } from './types.js';\r\n\r\n/**\r\n * Typed Event Emitter\r\n */\r\nexport class EventEmitter {\r\n private listeners: Map<AuthrimEventName, Set<AuthrimEventHandler<AuthrimEventName>>> = new Map();\r\n\r\n /**\r\n * Subscribe to an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n on<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(handler as AuthrimEventHandler<AuthrimEventName>);\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.off(event, handler);\r\n };\r\n }\r\n\r\n /**\r\n * Subscribe to an event (one-time)\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n once<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n const onceHandler = ((data: AuthrimEvents[T]) => {\r\n this.off(event, onceHandler);\r\n handler(data);\r\n }) as AuthrimEventHandler<T>;\r\n\r\n return this.on(event, onceHandler);\r\n }\r\n\r\n /**\r\n * Unsubscribe from an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler to remove\r\n */\r\n off<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): void {\r\n const handlers = this.listeners.get(event);\r\n if (handlers) {\r\n handlers.delete(handler as AuthrimEventHandler<AuthrimEventName>);\r\n if (handlers.size === 0) {\r\n this.listeners.delete(event);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Emit an event\r\n *\r\n * @param event - Event name\r\n * @param data - Event data\r\n */\r\n emit<T extends AuthrimEventName>(event: T, data: AuthrimEvents[T]): void {\r\n const handlers = this.listeners.get(event);\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try {\r\n handler(data);\r\n } catch (error) {\r\n // Log but don't throw - one handler failure shouldn't affect others\r\n console.error(`Error in event handler for '${event}':`, error);\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Remove all listeners for an event (or all events)\r\n *\r\n * @param event - Event name (optional, removes all if not specified)\r\n */\r\n removeAllListeners(event?: AuthrimEventName): void {\r\n if (event) {\r\n this.listeners.delete(event);\r\n } else {\r\n this.listeners.clear();\r\n }\r\n }\r\n\r\n /**\r\n * Get the number of listeners for an event\r\n *\r\n * @param event - Event name\r\n * @returns Number of listeners\r\n */\r\n listenerCount(event: AuthrimEventName): number {\r\n return this.listeners.get(event)?.size ?? 0;\r\n }\r\n}\r\n","/**\r\n * PKCE (Proof Key for Code Exchange) Helper\r\n *\r\n * Implements RFC 7636 for Authorization Code Flow security.\r\n * https://tools.ietf.org/html/rfc7636\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\n\r\n/**\r\n * PKCE challenge method\r\n */\r\nexport type CodeChallengeMethod = 'S256' | 'plain';\r\n\r\n/**\r\n * PKCE pair (verifier and challenge)\r\n */\r\nexport interface PKCEPair {\r\n /** Code verifier (high-entropy random string) */\r\n codeVerifier: string;\r\n /** Code challenge (derived from verifier) */\r\n codeChallenge: string;\r\n /** Challenge method used */\r\n codeChallengeMethod: CodeChallengeMethod;\r\n}\r\n\r\n/**\r\n * PKCE Helper class\r\n */\r\nexport class PKCEHelper {\r\n constructor(private readonly crypto: CryptoProvider) {}\r\n\r\n /**\r\n * Generate a PKCE pair (verifier + challenge)\r\n *\r\n * Uses S256 method (SHA-256 hash, base64url-encoded).\r\n *\r\n * @returns PKCE pair\r\n */\r\n async generatePKCE(): Promise<PKCEPair> {\r\n const codeVerifier = await this.crypto.generateCodeVerifier();\r\n const codeChallenge = await this.crypto.generateCodeChallenge(codeVerifier);\r\n\r\n return {\r\n codeVerifier,\r\n codeChallenge,\r\n codeChallengeMethod: 'S256',\r\n };\r\n }\r\n\r\n /**\r\n * Generate only the code verifier\r\n *\r\n * @returns Code verifier string\r\n */\r\n async generateCodeVerifier(): Promise<string> {\r\n return this.crypto.generateCodeVerifier();\r\n }\r\n\r\n /**\r\n * Generate a code challenge from a verifier\r\n *\r\n * @param verifier - Code verifier\r\n * @returns Code challenge (base64url-encoded SHA-256 hash)\r\n */\r\n async generateCodeChallenge(verifier: string): Promise<string> {\r\n return this.crypto.generateCodeChallenge(verifier);\r\n }\r\n}\r\n","/**\r\n * Base64URL Encoding/Decoding Utilities\r\n *\r\n * Implements RFC 4648 Section 5 (Base64 URL and Filename Safe Alphabet)\r\n */\r\n\r\n/**\r\n * Encode a Uint8Array to base64url string\r\n *\r\n * @param data - Bytes to encode\r\n * @returns Base64URL encoded string (no padding)\r\n */\r\nexport function base64urlEncode(data: Uint8Array): string {\r\n // Convert to base64\r\n let base64 = '';\r\n\r\n // Use platform-agnostic conversion\r\n const len = data.length;\r\n for (let i = 0; i < len; i += 3) {\r\n const byte1 = data[i];\r\n const byte2 = i + 1 < len ? data[i + 1] : 0;\r\n const byte3 = i + 2 < len ? data[i + 2] : 0;\r\n\r\n const triplet = (byte1 << 16) | (byte2 << 8) | byte3;\r\n\r\n base64 += BASE64_CHARS[(triplet >> 18) & 0x3f];\r\n base64 += BASE64_CHARS[(triplet >> 12) & 0x3f];\r\n base64 += i + 1 < len ? BASE64_CHARS[(triplet >> 6) & 0x3f] : '';\r\n base64 += i + 2 < len ? BASE64_CHARS[triplet & 0x3f] : '';\r\n }\r\n\r\n // Convert to base64url (replace + with -, / with _)\r\n // Remove padding (=)\r\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\n\r\n/**\r\n * Decode a base64url string to Uint8Array\r\n *\r\n * @param str - Base64URL encoded string\r\n * @returns Decoded bytes\r\n */\r\nexport function base64urlDecode(str: string): Uint8Array {\r\n // Convert base64url to base64\r\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\r\n\r\n // Add padding if needed\r\n while (base64.length % 4) {\r\n base64 += '=';\r\n }\r\n\r\n // Decode base64\r\n const len = base64.length;\r\n const paddingLen = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\r\n const outputLen = (len * 3) / 4 - paddingLen;\r\n const output = new Uint8Array(outputLen);\r\n\r\n let outputIndex = 0;\r\n for (let i = 0; i < len; i += 4) {\r\n const byte1 = BASE64_LOOKUP[base64.charCodeAt(i)];\r\n const byte2 = BASE64_LOOKUP[base64.charCodeAt(i + 1)];\r\n const byte3 = BASE64_LOOKUP[base64.charCodeAt(i + 2)];\r\n const byte4 = BASE64_LOOKUP[base64.charCodeAt(i + 3)];\r\n\r\n const triplet = (byte1 << 18) | (byte2 << 12) | (byte3 << 6) | byte4;\r\n\r\n if (outputIndex < outputLen) output[outputIndex++] = (triplet >> 16) & 0xff;\r\n if (outputIndex < outputLen) output[outputIndex++] = (triplet >> 8) & 0xff;\r\n if (outputIndex < outputLen) output[outputIndex++] = triplet & 0xff;\r\n }\r\n\r\n return output;\r\n}\r\n\r\n/**\r\n * Encode a string to base64url\r\n *\r\n * @param str - String to encode (UTF-8)\r\n * @returns Base64URL encoded string\r\n */\r\nexport function stringToBase64url(str: string): string {\r\n const encoder = new TextEncoder();\r\n return base64urlEncode(encoder.encode(str));\r\n}\r\n\r\n/**\r\n * Decode a base64url string to string\r\n *\r\n * @param base64url - Base64URL encoded string\r\n * @returns Decoded string (UTF-8)\r\n */\r\nexport function base64urlToString(base64url: string): string {\r\n const decoder = new TextDecoder();\r\n return decoder.decode(base64urlDecode(base64url));\r\n}\r\n\r\n// Base64 character set\r\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\r\n\r\n// Lookup table for decoding\r\nconst BASE64_LOOKUP = new Uint8Array(256);\r\nfor (let i = 0; i < BASE64_CHARS.length; i++) {\r\n BASE64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;\r\n}\r\n","/**\r\n * State/Nonce Manager\r\n *\r\n * Manages CSRF protection (state) and replay attack prevention (nonce)\r\n * for Authorization Code Flow.\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { base64urlEncode } from '../utils/base64url.js';\r\n\r\n/**\r\n * Auth state stored in storage\r\n */\r\nexport interface AuthState {\r\n /** State parameter (CSRF protection) */\r\n state: string;\r\n /** Nonce parameter (replay attack prevention) */\r\n nonce: string;\r\n /** PKCE code verifier */\r\n codeVerifier: string;\r\n /** Redirect URI used for this auth request */\r\n redirectUri: string;\r\n /** Timestamp when state was created */\r\n createdAt: number;\r\n /** Timestamp when state expires */\r\n expiresAt: number;\r\n}\r\n\r\n/**\r\n * Options for generating auth state\r\n */\r\nexport interface GenerateAuthStateOptions {\r\n /** Redirect URI for this auth request */\r\n redirectUri: string;\r\n /** Code verifier for PKCE */\r\n codeVerifier: string;\r\n /** TTL in seconds (default: 600 = 10 minutes) */\r\n ttlSeconds?: number;\r\n}\r\n\r\n/**\r\n * Storage keys factory\r\n */\r\nexport const STORAGE_KEYS = {\r\n /**\r\n * Auth state key (state-specific)\r\n */\r\n authState: (issuerHash: string, clientIdHash: string, state: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:auth:${state}`,\r\n\r\n /**\r\n * Token storage key\r\n */\r\n tokens: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:tokens`,\r\n\r\n /**\r\n * ID token storage key\r\n */\r\n idToken: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:id_token`,\r\n\r\n /**\r\n * Auth state prefix for cleanup\r\n */\r\n authStatePrefix: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:auth:`,\r\n} as const;\r\n\r\n/**\r\n * State Manager\r\n */\r\nexport class StateManager {\r\n /** Default TTL: 10 minutes */\r\n private static readonly DEFAULT_TTL_SECONDS = 600;\r\n\r\n constructor(\r\n private readonly crypto: CryptoProvider,\r\n private readonly storage: AuthrimStorage,\r\n private readonly issuerHash: string,\r\n private readonly clientIdHash: string\r\n ) {}\r\n\r\n /**\r\n * Generate and store auth state\r\n *\r\n * Creates state, nonce, stores them with the code verifier.\r\n *\r\n * @param options - Generation options\r\n * @returns Generated state string\r\n */\r\n async generateAuthState(options: GenerateAuthStateOptions): Promise<AuthState> {\r\n const ttlSeconds = options.ttlSeconds ?? StateManager.DEFAULT_TTL_SECONDS;\r\n\r\n // Generate random state and nonce (32 bytes each)\r\n const stateBytes = await this.crypto.randomBytes(32);\r\n const nonceBytes = await this.crypto.randomBytes(32);\r\n\r\n const state = base64urlEncode(stateBytes);\r\n const nonce = base64urlEncode(nonceBytes);\r\n\r\n const now = Date.now();\r\n const authState: AuthState = {\r\n state,\r\n nonce,\r\n codeVerifier: options.codeVerifier,\r\n redirectUri: options.redirectUri,\r\n createdAt: now,\r\n expiresAt: now + ttlSeconds * 1000,\r\n };\r\n\r\n // Store in storage\r\n const key = STORAGE_KEYS.authState(this.issuerHash, this.clientIdHash, state);\r\n await this.storage.set(key, JSON.stringify(authState));\r\n\r\n return authState;\r\n }\r\n\r\n /**\r\n * Validate and consume state\r\n *\r\n * Retrieves, validates, and ALWAYS deletes the state (success or failure).\r\n * This ensures replay attack prevention and GC.\r\n *\r\n * @param state - State parameter from callback\r\n * @returns Auth state if valid\r\n * @throws AuthrimError if state is invalid or expired\r\n */\r\n async validateAndConsumeState(state: string): Promise<AuthState> {\r\n const key = STORAGE_KEYS.authState(this.issuerHash, this.clientIdHash, state);\r\n\r\n try {\r\n const stored = await this.storage.get(key);\r\n\r\n if (!stored) {\r\n throw new AuthrimError('invalid_state', 'State not found or already used');\r\n }\r\n\r\n let authState: AuthState;\r\n try {\r\n authState = JSON.parse(stored);\r\n } catch {\r\n throw new AuthrimError('invalid_state', 'Malformed state data');\r\n }\r\n\r\n // Check expiration (no skew - strict)\r\n if (Date.now() > authState.expiresAt) {\r\n throw new AuthrimError('expired_state', 'State has expired');\r\n }\r\n\r\n return authState;\r\n } finally {\r\n // ALWAYS delete (success, failure, or exception)\r\n // This is critical for:\r\n // 1. Replay attack prevention\r\n // 2. GC of used/expired states\r\n await this.storage.remove(key);\r\n }\r\n }\r\n\r\n /**\r\n * Clean up expired states\r\n *\r\n * This is a best-effort cleanup. Only works if storage.getAll() is available.\r\n * The primary GC mechanism is validateAndConsumeState()'s finally delete.\r\n *\r\n * Safe to call at startup or periodically.\r\n */\r\n async cleanupExpiredStates(): Promise<void> {\r\n // Only works if storage supports getAll()\r\n if (!this.storage.getAll) {\r\n return;\r\n }\r\n\r\n const prefix = STORAGE_KEYS.authStatePrefix(this.issuerHash, this.clientIdHash);\r\n const all = await this.storage.getAll();\r\n const now = Date.now();\r\n\r\n for (const [key, value] of Object.entries(all)) {\r\n if (!key.startsWith(prefix)) {\r\n continue;\r\n }\r\n\r\n try {\r\n const authState: AuthState = JSON.parse(value);\r\n if (now > authState.expiresAt) {\r\n await this.storage.remove(key);\r\n }\r\n } catch {\r\n // Parse failure - delete corrupted entry\r\n await this.storage.remove(key);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * JWT Utilities\r\n *\r\n * Note: This module only provides decoding (parsing) functionality.\r\n * JWT signature verification MUST be performed by the server.\r\n * Never trust decoded JWT claims without server-side verification.\r\n */\r\n\r\nimport { base64urlToString } from './base64url.js';\r\nimport type { IdTokenClaims } from '../types/oidc.js';\r\n\r\n/**\r\n * JWT Header\r\n */\r\nexport interface JwtHeader {\r\n alg: string;\r\n typ?: string;\r\n kid?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Decoded JWT structure\r\n */\r\nexport interface DecodedJwt<T = Record<string, unknown>> {\r\n header: JwtHeader;\r\n payload: T;\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Decode a JWT without verifying the signature\r\n *\r\n * WARNING: This function does NOT verify the JWT signature.\r\n * Use this only for reading claims after the token has been\r\n * validated by the authorization server.\r\n *\r\n * @param jwt - JWT string to decode\r\n * @returns Decoded JWT parts\r\n * @throws Error if the JWT format is invalid\r\n */\r\nexport function decodeJwt<T = Record<string, unknown>>(jwt: string): DecodedJwt<T> {\r\n const parts = jwt.split('.');\r\n if (parts.length !== 3) {\r\n throw new Error('Invalid JWT format: expected 3 parts');\r\n }\r\n\r\n const [headerB64, payloadB64, signature] = parts;\r\n\r\n try {\r\n const header = JSON.parse(base64urlToString(headerB64)) as JwtHeader;\r\n const payload = JSON.parse(base64urlToString(payloadB64)) as T;\r\n\r\n return {\r\n header,\r\n payload,\r\n signature,\r\n };\r\n } catch {\r\n throw new Error('Invalid JWT format: failed to decode');\r\n }\r\n}\r\n\r\n/**\r\n * Decode an ID token and extract claims\r\n *\r\n * WARNING: This function does NOT verify the ID token.\r\n * The token MUST be verified by the authorization server before use.\r\n *\r\n * @param idToken - ID token string\r\n * @returns ID token claims\r\n */\r\nexport function decodeIdToken(idToken: string): IdTokenClaims {\r\n const decoded = decodeJwt<IdTokenClaims>(idToken);\r\n return decoded.payload;\r\n}\r\n\r\n/**\r\n * Check if a JWT is expired\r\n *\r\n * @param jwt - Decoded JWT payload with exp claim\r\n * @param skewSeconds - Clock skew tolerance in seconds (default: 0)\r\n * @returns true if expired\r\n */\r\nexport function isJwtExpired(payload: { exp?: number }, skewSeconds: number = 0): boolean {\r\n if (payload.exp === undefined) {\r\n return false; // No expiration\r\n }\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n return payload.exp + skewSeconds < now;\r\n}\r\n\r\n/**\r\n * Get the nonce claim from an ID token\r\n *\r\n * @param idToken - ID token string\r\n * @returns nonce value or undefined\r\n */\r\nexport function getIdTokenNonce(idToken: string): string | undefined {\r\n try {\r\n const claims = decodeIdToken(idToken);\r\n return claims.nonce;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n","/**\r\n * Authorization Code Flow\r\n *\r\n * Implements OAuth 2.0 Authorization Code Flow with PKCE.\r\n * Uses 2-step pattern: buildAuthorizationUrl() + handleCallback()\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet, TokenResponse } from '../types/token.js';\r\nimport type { AuthState } from './state.js';\r\nimport type { PKCEPair } from './pkce.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { getIdTokenNonce } from '../utils/jwt.js';\r\n\r\n/**\r\n * Options for building authorization URL\r\n */\r\nexport interface BuildAuthorizationUrlOptions {\r\n /** Redirect URI (required) */\r\n redirectUri: string;\r\n /** Scopes to request (default: 'openid profile') */\r\n scope?: string;\r\n /** Prompt behavior */\r\n prompt?: 'none' | 'login' | 'consent' | 'select_account';\r\n /** Hint about the login identifier */\r\n loginHint?: string;\r\n /** Requested Authentication Context Class Reference values */\r\n acrValues?: string;\r\n /** Additional custom parameters */\r\n extraParams?: Record<string, string>;\r\n /**\r\n * Expose state/nonce in result (for SSR/external storage)\r\n *\r\n * Default: false (security: state/nonce stay internal)\r\n * Set to true only when you need to store state externally\r\n * (e.g., cookie, server-side session)\r\n */\r\n exposeState?: boolean;\r\n}\r\n\r\n/**\r\n * Result of buildAuthorizationUrl\r\n */\r\nexport interface AuthorizationUrlResult {\r\n /** Authorization URL to redirect to */\r\n url: string;\r\n /** State parameter (only if exposeState: true) */\r\n state?: string;\r\n /** Nonce parameter (only if exposeState: true) */\r\n nonce?: string;\r\n}\r\n\r\n/**\r\n * Internal authorization context (stored by StateManager)\r\n */\r\nexport interface AuthorizationContext {\r\n /** Auth state object */\r\n authState: AuthState;\r\n /** PKCE pair */\r\n pkce: PKCEPair;\r\n}\r\n\r\n/**\r\n * Options for exchanging authorization code\r\n */\r\nexport interface ExchangeCodeOptions {\r\n /** Authorization code */\r\n code: string;\r\n /** State parameter (for validation) */\r\n state: string;\r\n /** Redirect URI used in authorization request */\r\n redirectUri: string;\r\n /** Code verifier for PKCE */\r\n codeVerifier: string;\r\n /** Nonce to validate in ID token */\r\n nonce: string;\r\n}\r\n\r\n/**\r\n * Authorization Code Flow helper\r\n */\r\nexport class AuthorizationCodeFlow {\r\n constructor(\r\n private readonly http: HttpClient,\r\n private readonly clientId: string\r\n ) {}\r\n\r\n /**\r\n * Build authorization URL\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param authState - Auth state from StateManager\r\n * @param pkce - PKCE pair\r\n * @param options - Authorization options\r\n * @returns Authorization URL result\r\n */\r\n buildAuthorizationUrl(\r\n discovery: OIDCDiscoveryDocument,\r\n authState: AuthState,\r\n pkce: PKCEPair,\r\n options: BuildAuthorizationUrlOptions\r\n ): AuthorizationUrlResult {\r\n const endpoint = discovery.authorization_endpoint;\r\n const params = new URLSearchParams();\r\n\r\n // Required parameters\r\n params.set('client_id', this.clientId);\r\n params.set('response_type', 'code');\r\n params.set('redirect_uri', options.redirectUri);\r\n params.set('state', authState.state);\r\n params.set('nonce', authState.nonce);\r\n\r\n // PKCE parameters\r\n params.set('code_challenge', pkce.codeChallenge);\r\n params.set('code_challenge_method', pkce.codeChallengeMethod);\r\n\r\n // Scopes\r\n const scope = options.scope ?? 'openid profile';\r\n params.set('scope', scope);\r\n\r\n // Optional parameters\r\n if (options.prompt) {\r\n params.set('prompt', options.prompt);\r\n }\r\n if (options.loginHint) {\r\n params.set('login_hint', options.loginHint);\r\n }\r\n if (options.acrValues) {\r\n params.set('acr_values', options.acrValues);\r\n }\r\n\r\n // Extra custom parameters (with security parameter protection)\r\n if (options.extraParams) {\r\n // Security parameters that MUST NOT be overwritten\r\n const protectedParams = new Set([\r\n 'client_id',\r\n 'response_type',\r\n 'redirect_uri',\r\n 'state',\r\n 'nonce',\r\n 'code_challenge',\r\n 'code_challenge_method',\r\n 'scope',\r\n ]);\r\n\r\n for (const [key, value] of Object.entries(options.extraParams)) {\r\n if (protectedParams.has(key.toLowerCase())) {\r\n // Silently ignore attempts to override security parameters\r\n // This prevents CSRF, PKCE bypass, and other attacks\r\n continue;\r\n }\r\n params.set(key, value);\r\n }\r\n }\r\n\r\n const url = `${endpoint}?${params.toString()}`;\r\n\r\n // Build result\r\n const result: AuthorizationUrlResult = { url };\r\n\r\n if (options.exposeState) {\r\n result.state = authState.state;\r\n result.nonce = authState.nonce;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Parse callback URL and extract code/state\r\n *\r\n * @param callbackUrl - Callback URL or query string\r\n * @returns Parsed code and state\r\n * @throws AuthrimError if code or state is missing, or if error is present\r\n */\r\n parseCallback(callbackUrl: string): { code: string; state: string } {\r\n let searchParams: URLSearchParams;\r\n\r\n // Support both full URL and query string\r\n if (callbackUrl.includes('?')) {\r\n const url = callbackUrl.startsWith('http')\r\n ? new URL(callbackUrl)\r\n : new URL(callbackUrl, 'https://dummy.local');\r\n searchParams = url.searchParams;\r\n } else {\r\n searchParams = new URLSearchParams(callbackUrl);\r\n }\r\n\r\n // Check for OAuth error response\r\n const error = searchParams.get('error');\r\n if (error) {\r\n const errorDescription = searchParams.get('error_description') ?? 'Authorization failed';\r\n throw new AuthrimError('oauth_error', errorDescription, {\r\n details: {\r\n error,\r\n error_description: errorDescription,\r\n error_uri: searchParams.get('error_uri'),\r\n },\r\n });\r\n }\r\n\r\n // Extract code and state\r\n const code = searchParams.get('code');\r\n const state = searchParams.get('state');\r\n\r\n if (!code) {\r\n throw new AuthrimError('missing_code', 'Authorization code not found in callback');\r\n }\r\n if (!state) {\r\n throw new AuthrimError('missing_state', 'State parameter not found in callback');\r\n }\r\n\r\n return { code, state };\r\n }\r\n\r\n /**\r\n * Exchange authorization code for tokens\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Exchange options\r\n * @returns Token set\r\n * @throws AuthrimError if exchange fails or nonce validation fails\r\n */\r\n async exchangeCode(\r\n discovery: OIDCDiscoveryDocument,\r\n options: ExchangeCodeOptions\r\n ): Promise<TokenSet> {\r\n const tokenEndpoint = discovery.token_endpoint;\r\n\r\n // Build token request body\r\n const body = new URLSearchParams({\r\n grant_type: 'authorization_code',\r\n client_id: this.clientId,\r\n code: options.code,\r\n redirect_uri: options.redirectUri,\r\n code_verifier: options.codeVerifier,\r\n });\r\n\r\n // Make token request\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n throw new AuthrimError('token_error', 'Token exchange failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Validate nonce in ID token\r\n if (tokenResponse.id_token) {\r\n const idTokenNonce = getIdTokenNonce(tokenResponse.id_token);\r\n if (idTokenNonce !== options.nonce) {\r\n // Do not include nonce values in error details (security sensitive)\r\n throw new AuthrimError('nonce_mismatch', 'ID token nonce does not match expected value');\r\n }\r\n }\r\n\r\n // Calculate expiresAt (epoch seconds)\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600; // Default to 1 hour if not provided\r\n\r\n // Build token set\r\n const tokenSet: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n return tokenSet;\r\n }\r\n}\r\n","/**\r\n * Token Types\r\n */\r\n\r\n/**\r\n * Token set returned from token endpoint\r\n *\r\n * Note: expiresAt is epoch seconds (not milliseconds)\r\n */\r\nexport interface TokenSet {\r\n /** Access token */\r\n accessToken: string;\r\n /** Refresh token (if provided) */\r\n refreshToken?: string;\r\n /** ID token (if openid scope was requested) */\r\n idToken?: string;\r\n /** Token type (always 'Bearer' for OAuth 2.0) */\r\n tokenType: 'Bearer';\r\n /**\r\n * Token expiration time as epoch seconds\r\n *\r\n * Calculated from expires_in at token exchange time:\r\n * expiresAt = Math.floor(Date.now() / 1000) + expires_in\r\n */\r\n expiresAt: number;\r\n /** Scope granted (may differ from requested scope) */\r\n scope?: string;\r\n}\r\n\r\n/**\r\n * Token endpoint response (raw)\r\n */\r\nexport interface TokenEndpointResponse {\r\n access_token: string;\r\n token_type: string;\r\n expires_in?: number;\r\n refresh_token?: string;\r\n id_token?: string;\r\n scope?: string;\r\n}\r\n\r\n/**\r\n * Alias for TokenEndpointResponse\r\n */\r\nexport type TokenResponse = TokenEndpointResponse;\r\n\r\n/**\r\n * Refresh token endpoint response (raw)\r\n */\r\nexport interface RefreshTokenResponse extends TokenEndpointResponse {\r\n // Refresh token rotation may return a new refresh token\r\n}\r\n\r\n/**\r\n * Token exchange request (RFC 8693)\r\n */\r\nexport interface TokenExchangeRequest {\r\n /** The subject token to exchange */\r\n subjectToken: string;\r\n /** Subject token type (default: access_token) */\r\n subjectTokenType?: 'access_token' | 'refresh_token' | 'id_token';\r\n /** Target audience for the new token */\r\n audience?: string;\r\n /** Requested scope for the new token */\r\n scope?: string;\r\n /** Requested token type (default: access_token) */\r\n requestedTokenType?: 'access_token' | 'refresh_token' | 'id_token';\r\n /** Actor token (for delegation) */\r\n actorToken?: string;\r\n /** Actor token type */\r\n actorTokenType?: 'access_token' | 'id_token';\r\n}\r\n\r\n/**\r\n * Token exchange response (RFC 8693)\r\n *\r\n * Extends standard token response with issued_token_type\r\n */\r\nexport interface TokenExchangeResponse extends TokenEndpointResponse {\r\n /** URI of the issued token type */\r\n issued_token_type: string;\r\n}\r\n\r\n/**\r\n * Token type URIs (RFC 8693)\r\n */\r\nexport const TOKEN_TYPE_URIS = {\r\n access_token: 'urn:ietf:params:oauth:token-type:access_token',\r\n refresh_token: 'urn:ietf:params:oauth:token-type:refresh_token',\r\n id_token: 'urn:ietf:params:oauth:token-type:id_token',\r\n} as const;\r\n\r\n/**\r\n * Token type URI type\r\n */\r\nexport type TokenTypeUri = (typeof TOKEN_TYPE_URIS)[keyof typeof TOKEN_TYPE_URIS];\r\n\r\n/**\r\n * Token exchange result\r\n */\r\nexport interface TokenExchangeResult {\r\n /** Token set from exchange */\r\n tokens: TokenSet;\r\n /** Issued token type URI */\r\n issuedTokenType: TokenTypeUri | string;\r\n}\r\n\r\n/**\r\n * Convert raw token response to TokenSet\r\n */\r\nexport function toTokenSet(response: TokenEndpointResponse): TokenSet {\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresIn = response.expires_in ?? 3600; // Default 1 hour\r\n\r\n return {\r\n accessToken: response.access_token,\r\n refreshToken: response.refresh_token,\r\n idToken: response.id_token,\r\n tokenType: 'Bearer',\r\n expiresAt: now + expiresIn,\r\n scope: response.scope,\r\n };\r\n}\r\n","/**\r\n * Token Manager\r\n *\r\n * Manages token storage, retrieval, and automatic refresh.\r\n * Implements in-flight request coalescing for concurrent refresh requests.\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type {\r\n TokenSet,\r\n TokenResponse,\r\n TokenExchangeRequest,\r\n TokenExchangeResponse,\r\n TokenExchangeResult,\r\n} from '../types/token.js';\r\nimport { TOKEN_TYPE_URIS } from '../types/token.js';\r\nimport type { EventEmitter } from '../events/emitter.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { STORAGE_KEYS } from '../auth/state.js';\r\n\r\n/**\r\n * Token manager options\r\n */\r\nexport interface TokenManagerOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Storage provider */\r\n storage: AuthrimStorage;\r\n /** Client ID */\r\n clientId: string;\r\n /** Issuer hash for storage keys */\r\n issuerHash: string;\r\n /** Client ID hash for storage keys */\r\n clientIdHash: string;\r\n /** Refresh skew in seconds (default: 30) */\r\n refreshSkewSeconds?: number;\r\n /** Event emitter for token events */\r\n eventEmitter?: EventEmitter;\r\n}\r\n\r\n/**\r\n * Token Manager\r\n *\r\n * Handles token storage, retrieval, and automatic refresh with\r\n * concurrent request coalescing.\r\n */\r\nexport class TokenManager {\r\n private readonly http: HttpClient;\r\n private readonly storage: AuthrimStorage;\r\n private readonly clientId: string;\r\n private readonly issuerHash: string;\r\n private readonly clientIdHash: string;\r\n private readonly refreshSkewSeconds: number;\r\n private readonly eventEmitter?: EventEmitter;\r\n\r\n /** In-flight refresh promise for request coalescing */\r\n private refreshPromise: Promise<TokenSet> | null = null;\r\n\r\n /** Discovery document (set externally) */\r\n private discovery: OIDCDiscoveryDocument | null = null;\r\n\r\n /** Default refresh skew: 30 seconds */\r\n private static readonly DEFAULT_REFRESH_SKEW_SECONDS = 30;\r\n\r\n constructor(options: TokenManagerOptions) {\r\n this.http = options.http;\r\n this.storage = options.storage;\r\n this.clientId = options.clientId;\r\n this.issuerHash = options.issuerHash;\r\n this.clientIdHash = options.clientIdHash;\r\n this.refreshSkewSeconds =\r\n options.refreshSkewSeconds ?? TokenManager.DEFAULT_REFRESH_SKEW_SECONDS;\r\n this.eventEmitter = options.eventEmitter;\r\n }\r\n\r\n /**\r\n * Set discovery document\r\n */\r\n setDiscovery(discovery: OIDCDiscoveryDocument): void {\r\n this.discovery = discovery;\r\n }\r\n\r\n /**\r\n * Get storage key for tokens\r\n */\r\n private get tokenKey(): string {\r\n return STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n }\r\n\r\n /**\r\n * Get storage key for ID token\r\n */\r\n private get idTokenKey(): string {\r\n return STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n }\r\n\r\n /**\r\n * Get current tokens from storage\r\n *\r\n * @returns Token set or null if not found\r\n */\r\n async getTokens(): Promise<TokenSet | null> {\r\n const stored = await this.storage.get(this.tokenKey);\r\n if (!stored) {\r\n return null;\r\n }\r\n\r\n try {\r\n return JSON.parse(stored) as TokenSet;\r\n } catch {\r\n // Corrupted data - clear and return null\r\n await this.clearTokens();\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Save tokens to storage\r\n *\r\n * @param tokens - Token set to save\r\n */\r\n async saveTokens(tokens: TokenSet): Promise<void> {\r\n await this.storage.set(this.tokenKey, JSON.stringify(tokens));\r\n\r\n // Also save ID token separately for logout\r\n if (tokens.idToken) {\r\n await this.storage.set(this.idTokenKey, tokens.idToken);\r\n }\r\n }\r\n\r\n /**\r\n * Clear all tokens from storage\r\n */\r\n async clearTokens(): Promise<void> {\r\n await this.storage.remove(this.tokenKey);\r\n await this.storage.remove(this.idTokenKey);\r\n }\r\n\r\n /**\r\n * Get access token, refreshing if necessary\r\n *\r\n * This method coalesces concurrent refresh requests - if multiple\r\n * calls are made while a refresh is in-flight, they all share the\r\n * same refresh operation.\r\n *\r\n * @returns Access token\r\n * @throws AuthrimError if no tokens available or refresh fails\r\n */\r\n async getAccessToken(): Promise<string> {\r\n const tokens = await this.getTokens();\r\n\r\n if (!tokens) {\r\n throw new AuthrimError('no_tokens', 'No tokens available. Please authenticate first.');\r\n }\r\n\r\n // Check if token needs refresh (with skew)\r\n if (this.shouldRefresh(tokens)) {\r\n if (!tokens.refreshToken) {\r\n throw new AuthrimError(\r\n 'token_expired',\r\n 'Access token expired and no refresh token available'\r\n );\r\n }\r\n return this.refreshWithLock(tokens.refreshToken);\r\n }\r\n\r\n return tokens.accessToken;\r\n }\r\n\r\n /**\r\n * Get ID token\r\n *\r\n * @returns ID token or null\r\n */\r\n async getIdToken(): Promise<string | null> {\r\n const tokens = await this.getTokens();\r\n return tokens?.idToken ?? null;\r\n }\r\n\r\n /**\r\n * Check if token needs refresh\r\n *\r\n * @param tokens - Token set to check\r\n * @returns True if token should be refreshed\r\n */\r\n private shouldRefresh(tokens: TokenSet): boolean {\r\n const now = Math.floor(Date.now() / 1000);\r\n return tokens.expiresAt - this.refreshSkewSeconds <= now;\r\n }\r\n\r\n /**\r\n * Refresh token with in-flight request coalescing\r\n *\r\n * If a refresh is already in progress, wait for it instead of\r\n * starting a new one.\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @returns Access token from new token set\r\n */\r\n private async refreshWithLock(refreshToken: string): Promise<string> {\r\n // If refresh is already in-flight, wait for it\r\n if (this.refreshPromise) {\r\n const tokens = await this.refreshPromise;\r\n return tokens.accessToken;\r\n }\r\n\r\n // Start new refresh operation\r\n this.refreshPromise = this.doRefreshWithRetry(refreshToken);\r\n\r\n try {\r\n const tokens = await this.refreshPromise;\r\n return tokens.accessToken;\r\n } finally {\r\n // ALWAYS clear promise (success or failure)\r\n this.refreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Perform refresh with single retry for network errors\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @param attemptedRetry - Whether retry has been attempted (stack-local)\r\n * @returns New token set\r\n */\r\n private async doRefreshWithRetry(\r\n refreshToken: string,\r\n attemptedRetry = false\r\n ): Promise<TokenSet> {\r\n try {\r\n return await this.doRefresh(refreshToken);\r\n } catch (error) {\r\n // Retry once for network errors only\r\n if (this.isRetryableError(error) && !attemptedRetry) {\r\n return this.doRefreshWithRetry(refreshToken, true);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Perform the actual token refresh\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @returns New token set\r\n */\r\n private async doRefresh(refreshToken: string): Promise<TokenSet> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not set');\r\n }\r\n\r\n const tokenEndpoint = this.discovery.token_endpoint;\r\n\r\n // Build refresh request\r\n const body = new URLSearchParams({\r\n grant_type: 'refresh_token',\r\n client_id: this.clientId,\r\n refresh_token: refreshToken,\r\n });\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n const authrimError = new AuthrimError('network_error', 'Token refresh request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n const authrimError = new AuthrimError('refresh_error', 'Token refresh failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Calculate expiresAt\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600;\r\n\r\n // Build new token set (preserve old refresh token if new one not provided)\r\n const newTokens: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token ?? refreshToken,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n // Save new tokens\r\n await this.saveTokens(newTokens);\r\n\r\n // Emit event\r\n this.eventEmitter?.emit('token:refreshed', { tokens: newTokens });\r\n\r\n return newTokens;\r\n }\r\n\r\n /**\r\n * Check if error is retryable (network errors only)\r\n */\r\n private isRetryableError(error: unknown): boolean {\r\n if (error instanceof AuthrimError) {\r\n return error.code === 'network_error';\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n *\r\n * @returns True if valid tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n const tokens = await this.getTokens();\r\n if (!tokens) {\r\n return false;\r\n }\r\n\r\n // Check if access token is expired (without skew)\r\n const now = Math.floor(Date.now() / 1000);\r\n if (tokens.expiresAt <= now) {\r\n // Token expired - check if we have refresh token\r\n return !!tokens.refreshToken;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Exchange a token using RFC 8693 Token Exchange\r\n *\r\n * This allows exchanging tokens for different audiences or scopes,\r\n * delegation scenarios, and cross-service token acquisition.\r\n *\r\n * @param request - Token exchange request parameters\r\n * @returns Token exchange result with new tokens and issued token type\r\n * @throws AuthrimError if exchange fails\r\n */\r\n async exchangeToken(request: TokenExchangeRequest): Promise<TokenExchangeResult> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not set');\r\n }\r\n\r\n const tokenEndpoint = this.discovery.token_endpoint;\r\n\r\n // Map short token type names to URIs\r\n const subjectTokenType = this.mapTokenTypeToUri(request.subjectTokenType ?? 'access_token');\r\n\r\n // Build token exchange request\r\n const body = new URLSearchParams({\r\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\r\n client_id: this.clientId,\r\n subject_token: request.subjectToken,\r\n subject_token_type: subjectTokenType,\r\n });\r\n\r\n // Optional parameters\r\n if (request.audience) {\r\n body.set('audience', request.audience);\r\n }\r\n if (request.scope) {\r\n body.set('scope', request.scope);\r\n }\r\n if (request.requestedTokenType) {\r\n body.set('requested_token_type', this.mapTokenTypeToUri(request.requestedTokenType));\r\n }\r\n if (request.actorToken) {\r\n body.set('actor_token', request.actorToken);\r\n if (request.actorTokenType) {\r\n body.set('actor_token_type', this.mapTokenTypeToUri(request.actorTokenType));\r\n }\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenExchangeResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n const authrimError = new AuthrimError('network_error', 'Token exchange request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n const authrimError = new AuthrimError('token_exchange_error', 'Token exchange failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Calculate expiresAt\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600;\r\n\r\n const tokens: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n const result: TokenExchangeResult = {\r\n tokens,\r\n issuedTokenType: tokenResponse.issued_token_type,\r\n };\r\n\r\n // Emit event\r\n this.eventEmitter?.emit('token:exchanged', {\r\n tokens,\r\n issuedTokenType: tokenResponse.issued_token_type,\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Map short token type to URI (RFC 8693)\r\n */\r\n private mapTokenTypeToUri(type: 'access_token' | 'refresh_token' | 'id_token'): string {\r\n return TOKEN_TYPE_URIS[type];\r\n }\r\n}\r\n","/**\r\n * Token Introspection (RFC 7662)\r\n *\r\n * Implements OAuth 2.0 Token Introspection to validate tokens server-side.\r\n * https://datatracker.ietf.org/doc/html/rfc7662\r\n */\r\n\r\nimport type { HttpClient, OAuthErrorResponse } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Token type hint for introspection\r\n */\r\nexport type IntrospectionTokenTypeHint = 'access_token' | 'refresh_token';\r\n\r\n/**\r\n * Token introspection response (RFC 7662)\r\n */\r\nexport interface IntrospectionResponse {\r\n /** Whether the token is active */\r\n active: boolean;\r\n /** Space-separated list of scopes (if active) */\r\n scope?: string;\r\n /** Client identifier (if active) */\r\n client_id?: string;\r\n /** Human-readable username (if active) */\r\n username?: string;\r\n /** Token type (e.g., \"Bearer\") */\r\n token_type?: string;\r\n /** Expiration time (Unix timestamp) */\r\n exp?: number;\r\n /** Issued at time (Unix timestamp) */\r\n iat?: number;\r\n /** Not before time (Unix timestamp) */\r\n nbf?: number;\r\n /** Subject identifier */\r\n sub?: string;\r\n /** Audience (single string or array) */\r\n aud?: string | string[];\r\n /** Issuer identifier */\r\n iss?: string;\r\n /** JWT ID */\r\n jti?: string;\r\n /** Additional claims */\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Token introspection options\r\n */\r\nexport interface IntrospectTokenOptions {\r\n /** Token to introspect */\r\n token: string;\r\n /** Hint about the token type (optional, helps server optimize lookup) */\r\n tokenTypeHint?: IntrospectionTokenTypeHint;\r\n}\r\n\r\n/**\r\n * Token introspector options\r\n */\r\nexport interface TokenIntrospectorOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n}\r\n\r\n/**\r\n * Token Introspector\r\n *\r\n * Handles token introspection requests to the authorization server.\r\n */\r\nexport class TokenIntrospector {\r\n private readonly http: HttpClient;\r\n private readonly clientId: string;\r\n\r\n constructor(options: TokenIntrospectorOptions) {\r\n this.http = options.http;\r\n this.clientId = options.clientId;\r\n }\r\n\r\n /**\r\n * Introspect a token\r\n *\r\n * Per RFC 7662, the introspection endpoint returns:\r\n * - { active: true, ... } for valid tokens with additional metadata\r\n * - { active: false } for invalid, expired, or revoked tokens\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Introspection options\r\n * @returns Introspection response\r\n * @throws AuthrimError if introspection endpoint is not available or request fails\r\n */\r\n async introspect(\r\n discovery: OIDCDiscoveryDocument,\r\n options: IntrospectTokenOptions\r\n ): Promise<IntrospectionResponse> {\r\n const endpoint = discovery.introspection_endpoint;\r\n\r\n if (!endpoint) {\r\n throw new AuthrimError(\r\n 'no_introspection_endpoint',\r\n 'Authorization server does not support token introspection'\r\n );\r\n }\r\n\r\n return this.introspectWithEndpoint(endpoint, options);\r\n }\r\n\r\n /**\r\n * Introspect a token directly using the endpoint URL\r\n *\r\n * Use this when you have the endpoint URL but not the full discovery document.\r\n *\r\n * @param endpoint - Introspection endpoint URL\r\n * @param options - Introspection options\r\n * @returns Introspection response\r\n * @throws AuthrimError if request fails\r\n */\r\n async introspectWithEndpoint(\r\n endpoint: string,\r\n options: IntrospectTokenOptions\r\n ): Promise<IntrospectionResponse> {\r\n // Build introspection request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<IntrospectionResponse | OAuthErrorResponse>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token introspection request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('introspection_error', 'Token introspection failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n return response.data as IntrospectionResponse;\r\n }\r\n\r\n /**\r\n * Check if a token is active\r\n *\r\n * Convenience method that returns only the active status.\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param token - Token to check\r\n * @returns True if token is active\r\n */\r\n async isActive(discovery: OIDCDiscoveryDocument, token: string): Promise<boolean> {\r\n const result = await this.introspect(discovery, { token });\r\n return result.active;\r\n }\r\n}\r\n","/**\r\n * Token Revocation (RFC 7009)\r\n *\r\n * Implements OAuth 2.0 Token Revocation to explicitly invalidate tokens.\r\n * https://datatracker.ietf.org/doc/html/rfc7009\r\n */\r\n\r\nimport type { HttpClient, OAuthErrorResponse } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Token type hint for revocation\r\n */\r\nexport type TokenTypeHint = 'access_token' | 'refresh_token';\r\n\r\n/**\r\n * Token revocation options\r\n */\r\nexport interface RevokeTokenOptions {\r\n /** Token to revoke */\r\n token: string;\r\n /** Hint about the token type (optional, helps server optimize lookup) */\r\n tokenTypeHint?: TokenTypeHint;\r\n}\r\n\r\n/**\r\n * Token revoker options\r\n */\r\nexport interface TokenRevokerOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n}\r\n\r\n/**\r\n * Token Revoker\r\n *\r\n * Handles token revocation requests to the authorization server.\r\n */\r\nexport class TokenRevoker {\r\n private readonly http: HttpClient;\r\n private readonly clientId: string;\r\n\r\n constructor(options: TokenRevokerOptions) {\r\n this.http = options.http;\r\n this.clientId = options.clientId;\r\n }\r\n\r\n /**\r\n * Revoke a token\r\n *\r\n * Per RFC 7009, the revocation endpoint:\r\n * - Returns 200 OK on success (even if token was already invalid)\r\n * - Returns 400 for invalid requests\r\n * - Returns 503 if temporarily unavailable\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Revocation options\r\n * @throws AuthrimError if revocation endpoint is not available or request fails\r\n */\r\n async revoke(discovery: OIDCDiscoveryDocument, options: RevokeTokenOptions): Promise<void> {\r\n const endpoint = discovery.revocation_endpoint;\r\n\r\n if (!endpoint) {\r\n throw new AuthrimError(\r\n 'no_revocation_endpoint',\r\n 'Authorization server does not support token revocation'\r\n );\r\n }\r\n\r\n // Build revocation request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<OAuthErrorResponse | void>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token revocation request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n // RFC 7009: 200 OK means success (even if token was already invalid)\r\n if (response.ok) {\r\n return;\r\n }\r\n\r\n // Handle error response\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('revocation_error', 'Token revocation failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Revoke a token directly using the endpoint URL\r\n *\r\n * Use this when you have the endpoint URL but not the full discovery document.\r\n *\r\n * @param endpoint - Revocation endpoint URL\r\n * @param options - Revocation options\r\n * @throws AuthrimError if request fails\r\n */\r\n async revokeWithEndpoint(endpoint: string, options: RevokeTokenOptions): Promise<void> {\r\n // Build revocation request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<OAuthErrorResponse | void>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token revocation request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (response.ok) {\r\n return;\r\n }\r\n\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('revocation_error', 'Token revocation failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n}\r\n","/**\r\n * Logout Handler\r\n *\r\n * Implements RP-Initiated Logout (OpenID Connect RP-Initiated Logout 1.0)\r\n * https://openid.net/specs/openid-connect-rpinitiated-1_0.html\r\n */\r\n\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet } from '../types/token.js';\r\nimport type { EventEmitter } from '../events/emitter.js';\r\nimport type { EndpointOverrides } from '../client/config.js';\r\nimport { STORAGE_KEYS } from '../auth/state.js';\r\nimport { TokenRevoker } from '../token/revocation.js';\r\n\r\n/**\r\n * Logout options\r\n */\r\nexport interface LogoutOptions {\r\n /** URI to redirect to after logout */\r\n postLogoutRedirectUri?: string;\r\n /** ID token hint (optional, uses stored ID token if not provided) */\r\n idTokenHint?: string;\r\n /** State parameter for post-logout redirect */\r\n state?: string;\r\n /** Whether to revoke tokens before logout (requires revocation_endpoint) */\r\n revokeTokens?: boolean;\r\n}\r\n\r\n/**\r\n * Logout result\r\n */\r\nexport interface LogoutResult {\r\n /** Logout URL to redirect to (if IdP supports end_session_endpoint) */\r\n logoutUrl?: string;\r\n /** True if only local logout was performed (IdP doesn't support RP-Initiated Logout) */\r\n localOnly: boolean;\r\n /** Token revocation result (if revokeTokens was true) */\r\n revocation?: {\r\n /** Whether revocation was attempted */\r\n attempted: boolean;\r\n /** Whether access token revocation succeeded */\r\n accessTokenRevoked?: boolean;\r\n /** Whether refresh token revocation succeeded */\r\n refreshTokenRevoked?: boolean;\r\n /** Error if revocation failed (logout still proceeds) */\r\n error?: Error;\r\n };\r\n}\r\n\r\n/**\r\n * Logout handler options\r\n */\r\nexport interface LogoutHandlerOptions {\r\n /** Storage provider */\r\n storage: AuthrimStorage;\r\n /** HTTP client (for token revocation) */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n /** Issuer hash for storage keys */\r\n issuerHash: string;\r\n /** Client ID hash for storage keys */\r\n clientIdHash: string;\r\n /** Event emitter */\r\n eventEmitter?: EventEmitter;\r\n /** Endpoint overrides */\r\n endpoints?: EndpointOverrides;\r\n}\r\n\r\n/**\r\n * Logout Handler\r\n */\r\nexport class LogoutHandler {\r\n private readonly storage: AuthrimStorage;\r\n private readonly clientId: string;\r\n private readonly issuerHash: string;\r\n private readonly clientIdHash: string;\r\n private readonly eventEmitter?: EventEmitter;\r\n private readonly endpoints?: EndpointOverrides;\r\n private readonly tokenRevoker: TokenRevoker;\r\n\r\n constructor(options: LogoutHandlerOptions) {\r\n this.storage = options.storage;\r\n this.clientId = options.clientId;\r\n this.issuerHash = options.issuerHash;\r\n this.clientIdHash = options.clientIdHash;\r\n this.eventEmitter = options.eventEmitter;\r\n this.endpoints = options.endpoints;\r\n this.tokenRevoker = new TokenRevoker({\r\n http: options.http,\r\n clientId: options.clientId,\r\n });\r\n }\r\n\r\n /**\r\n * Perform logout\r\n *\r\n * 1. Optionally revokes tokens at the authorization server (if revokeTokens=true)\r\n * 2. Clears local tokens (always)\r\n * 3. Emits session:ended event\r\n * 4. Builds logout URL if IdP supports end_session_endpoint\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Logout options\r\n * @returns Logout result\r\n */\r\n async logout(\r\n discovery: OIDCDiscoveryDocument | null,\r\n options?: LogoutOptions\r\n ): Promise<LogoutResult> {\r\n // Get stored tokens BEFORE clearing (for revocation and logout URL)\r\n const storedIdToken = await this.getStoredIdToken();\r\n const storedTokens = await this.getStoredTokens();\r\n\r\n // Revoke tokens if requested\r\n let revocationResult: LogoutResult['revocation'];\r\n if (options?.revokeTokens && discovery?.revocation_endpoint && storedTokens) {\r\n revocationResult = await this.revokeTokens(discovery, storedTokens);\r\n }\r\n\r\n // Clear local tokens\r\n await this.clearTokens();\r\n\r\n // Emit session ended event\r\n this.eventEmitter?.emit('session:ended', { reason: 'logout' });\r\n\r\n // Determine end_session_endpoint\r\n // Priority: config override > discovery > null\r\n let endSessionEndpoint: string | null | undefined;\r\n\r\n if (this.endpoints?.endSession !== undefined) {\r\n // Explicit override (can be null to disable)\r\n endSessionEndpoint = this.endpoints.endSession;\r\n } else if (discovery) {\r\n endSessionEndpoint = discovery.end_session_endpoint;\r\n }\r\n\r\n // If no end_session_endpoint, return local-only logout\r\n if (!endSessionEndpoint) {\r\n return { localOnly: true, revocation: revocationResult };\r\n }\r\n\r\n // Build logout URL (use stored token if not provided in options)\r\n const idToken = options?.idTokenHint ?? storedIdToken;\r\n\r\n const params = new URLSearchParams({\r\n client_id: this.clientId,\r\n });\r\n\r\n if (idToken) {\r\n params.set('id_token_hint', idToken);\r\n }\r\n if (options?.postLogoutRedirectUri) {\r\n params.set('post_logout_redirect_uri', options.postLogoutRedirectUri);\r\n }\r\n if (options?.state) {\r\n params.set('state', options.state);\r\n }\r\n\r\n return {\r\n logoutUrl: `${endSessionEndpoint}?${params.toString()}`,\r\n localOnly: false,\r\n revocation: revocationResult,\r\n };\r\n }\r\n\r\n /**\r\n * Revoke tokens at the authorization server\r\n *\r\n * Best-effort: if revocation fails, logout still proceeds\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param tokens - Tokens to revoke\r\n * @returns Revocation result\r\n */\r\n private async revokeTokens(\r\n discovery: OIDCDiscoveryDocument,\r\n tokens: TokenSet\r\n ): Promise<NonNullable<LogoutResult['revocation']>> {\r\n const result: NonNullable<LogoutResult['revocation']> = {\r\n attempted: true,\r\n };\r\n\r\n try {\r\n // Revoke refresh token first (if present) - this often invalidates access token too\r\n if (tokens.refreshToken) {\r\n await this.tokenRevoker.revoke(discovery, {\r\n token: tokens.refreshToken,\r\n tokenTypeHint: 'refresh_token',\r\n });\r\n result.refreshTokenRevoked = true;\r\n }\r\n\r\n // Revoke access token\r\n await this.tokenRevoker.revoke(discovery, {\r\n token: tokens.accessToken,\r\n tokenTypeHint: 'access_token',\r\n });\r\n result.accessTokenRevoked = true;\r\n } catch (error) {\r\n result.error = error instanceof Error ? error : new Error(String(error));\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Clear all tokens from storage\r\n */\r\n private async clearTokens(): Promise<void> {\r\n const tokenKey = STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n const idTokenKey = STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n\r\n await this.storage.remove(tokenKey);\r\n await this.storage.remove(idTokenKey);\r\n }\r\n\r\n /**\r\n * Get stored ID token\r\n */\r\n private async getStoredIdToken(): Promise<string | null> {\r\n const idTokenKey = STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n return this.storage.get(idTokenKey);\r\n }\r\n\r\n /**\r\n * Get stored tokens\r\n */\r\n private async getStoredTokens(): Promise<TokenSet | null> {\r\n const tokenKey = STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n const stored = await this.storage.get(tokenKey);\r\n if (!stored) {\r\n return null;\r\n }\r\n try {\r\n return JSON.parse(stored) as TokenSet;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n}\r\n","/**\r\n * Token API Client\r\n *\r\n * Provides session verification against the authorization server.\r\n * Uses the UserInfo endpoint or custom session check endpoint.\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Session check result\r\n */\r\nexport interface SessionCheckResult {\r\n /** Whether session is valid */\r\n valid: boolean;\r\n /** User info if session is valid */\r\n user?: UserInfo;\r\n /** Error if session is invalid */\r\n error?: AuthrimError;\r\n}\r\n\r\n/**\r\n * Token API client options\r\n */\r\nexport interface TokenApiClientOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n}\r\n\r\n/**\r\n * Token API Client\r\n *\r\n * Verifies session status with the authorization server.\r\n */\r\nexport class TokenApiClient {\r\n private readonly http: HttpClient;\r\n\r\n constructor(options: TokenApiClientOptions) {\r\n this.http = options.http;\r\n }\r\n\r\n /**\r\n * Check session validity by calling UserInfo endpoint\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param accessToken - Access token to verify\r\n * @returns Session check result\r\n */\r\n async checkSession(\r\n discovery: OIDCDiscoveryDocument,\r\n accessToken: string\r\n ): Promise<SessionCheckResult> {\r\n const userinfoEndpoint = discovery.userinfo_endpoint;\r\n\r\n if (!userinfoEndpoint) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('no_userinfo_endpoint', 'UserInfo endpoint not available'),\r\n };\r\n }\r\n\r\n try {\r\n const response = await this.http.fetch<UserInfo>(userinfoEndpoint, {\r\n method: 'GET',\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n },\r\n });\r\n\r\n if (!response.ok) {\r\n // 401 means token is invalid/expired\r\n if (response.status === 401) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_expired', 'Session has expired'),\r\n };\r\n }\r\n\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_check_failed', 'Session check failed', {\r\n details: { status: response.status },\r\n }),\r\n };\r\n }\r\n\r\n return {\r\n valid: true,\r\n user: response.data,\r\n };\r\n } catch (error) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('network_error', 'Failed to check session', {\r\n cause: error instanceof Error ? error : undefined,\r\n }),\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get user info from the authorization server\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param accessToken - Access token\r\n * @returns User info\r\n * @throws AuthrimError if request fails\r\n */\r\n async getUserInfo(discovery: OIDCDiscoveryDocument, accessToken: string): Promise<UserInfo> {\r\n const userinfoEndpoint = discovery.userinfo_endpoint;\r\n\r\n if (!userinfoEndpoint) {\r\n throw new AuthrimError('no_userinfo_endpoint', 'UserInfo endpoint not available');\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<UserInfo>(userinfoEndpoint, {\r\n method: 'GET',\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n },\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Failed to fetch user info', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n throw new AuthrimError('userinfo_error', 'Failed to get user info', {\r\n details: { status: response.status },\r\n });\r\n }\r\n\r\n return response.data;\r\n }\r\n}\r\n","/**\r\n * Session Manager\r\n *\r\n * Coordinates session-related operations including checking\r\n * session status and retrieving user information.\r\n */\r\n\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport type { TokenManager } from '../token/manager.js';\r\nimport { TokenApiClient, type SessionCheckResult } from './token-api.js';\r\nexport type { SessionCheckResult };\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Session manager options\r\n */\r\nexport interface SessionManagerOptions {\r\n /** Token manager */\r\n tokenManager: TokenManager;\r\n /** Token API client */\r\n tokenApiClient: TokenApiClient;\r\n}\r\n\r\n/**\r\n * Session Manager\r\n */\r\nexport class SessionManager {\r\n private readonly tokenManager: TokenManager;\r\n private readonly tokenApiClient: TokenApiClient;\r\n\r\n /** Discovery document */\r\n private discovery: OIDCDiscoveryDocument | null = null;\r\n\r\n constructor(options: SessionManagerOptions) {\r\n this.tokenManager = options.tokenManager;\r\n this.tokenApiClient = options.tokenApiClient;\r\n }\r\n\r\n /**\r\n * Set discovery document\r\n */\r\n setDiscovery(discovery: OIDCDiscoveryDocument): void {\r\n this.discovery = discovery;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated locally\r\n *\r\n * This checks if valid tokens exist in storage.\r\n * Does not verify with the authorization server.\r\n *\r\n * @returns True if tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n return this.tokenManager.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Check session validity with authorization server\r\n *\r\n * Calls the UserInfo endpoint to verify the session is still valid.\r\n *\r\n * @returns Session check result\r\n */\r\n async checkSession(): Promise<SessionCheckResult> {\r\n if (!this.discovery) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('no_discovery', 'Discovery document not available'),\r\n };\r\n }\r\n\r\n try {\r\n const accessToken = await this.tokenManager.getAccessToken();\r\n return this.tokenApiClient.checkSession(this.discovery, accessToken);\r\n } catch (error) {\r\n if (error instanceof AuthrimError) {\r\n return { valid: false, error };\r\n }\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_check_failed', 'Failed to check session', {\r\n cause: error instanceof Error ? error : undefined,\r\n }),\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get user information\r\n *\r\n * Fetches user info from the UserInfo endpoint.\r\n *\r\n * @returns User info\r\n * @throws AuthrimError if not authenticated or request fails\r\n */\r\n async getUser(): Promise<UserInfo> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not available');\r\n }\r\n\r\n const accessToken = await this.tokenManager.getAccessToken();\r\n return this.tokenApiClient.getUserInfo(this.discovery, accessToken);\r\n }\r\n}\r\n","/**\r\n * Authrim Client\r\n *\r\n * Main entry point for the Authrim SDK.\r\n */\r\n\r\nimport type { AuthrimClientConfig, ResolvedConfig } from './config.js';\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport type { TokenSet, TokenExchangeRequest, TokenExchangeResult } from '../types/token.js';\r\nimport type { AuthrimEventName, AuthrimEventHandler } from '../events/types.js';\r\nimport { resolveConfig } from './config.js';\r\nimport { DiscoveryClient, normalizeIssuer } from './discovery.js';\r\nimport { EventEmitter } from '../events/emitter.js';\r\nimport { PKCEHelper } from '../auth/pkce.js';\r\nimport { StateManager } from '../auth/state.js';\r\nimport {\r\n AuthorizationCodeFlow,\r\n type BuildAuthorizationUrlOptions,\r\n type AuthorizationUrlResult,\r\n} from '../auth/authorization-code.js';\r\nimport { TokenManager } from '../token/manager.js';\r\nimport {\r\n TokenIntrospector,\r\n type IntrospectionResponse,\r\n type IntrospectTokenOptions,\r\n} from '../token/introspection.js';\r\nimport { TokenRevoker, type RevokeTokenOptions } from '../token/revocation.js';\r\nimport { LogoutHandler, type LogoutOptions, type LogoutResult } from '../session/logout.js';\r\nimport { TokenApiClient } from '../session/token-api.js';\r\nimport { SessionManager } from '../session/manager.js';\r\nimport { base64urlEncode } from '../utils/base64url.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Hash a value for use in storage keys\r\n *\r\n * @param crypto - Crypto provider\r\n * @param value - Value to hash\r\n * @param length - Hash length (default: 16 = 96 bits)\r\n * @returns Base64url-encoded hash\r\n */\r\nasync function hashForKey(\r\n crypto: AuthrimClientConfig['crypto'],\r\n value: string,\r\n length = 16\r\n): Promise<string> {\r\n const hash = await crypto.sha256(value);\r\n return base64urlEncode(hash).slice(0, length);\r\n}\r\n\r\n/**\r\n * Authrim Client\r\n */\r\nexport class AuthrimClient {\r\n /** Resolved configuration */\r\n private readonly config: ResolvedConfig;\r\n\r\n /** Event emitter */\r\n private readonly events: EventEmitter;\r\n\r\n /** Discovery client */\r\n private readonly discoveryClient: DiscoveryClient;\r\n\r\n /** PKCE helper */\r\n private readonly pkce: PKCEHelper;\r\n\r\n /** State manager (initialized in initialize()) */\r\n private stateManager!: StateManager;\r\n\r\n /** Authorization code flow helper */\r\n private readonly authCodeFlow: AuthorizationCodeFlow;\r\n\r\n /** Token manager (initialized in initialize()) */\r\n private tokenManager!: TokenManager;\r\n\r\n /** Logout handler (initialized in initialize()) */\r\n private logoutHandler!: LogoutHandler;\r\n\r\n /** Session manager (initialized in initialize()) */\r\n private sessionManager!: SessionManager;\r\n\r\n /** Token introspector */\r\n private tokenIntrospector!: TokenIntrospector;\r\n\r\n /** Token revoker */\r\n private tokenRevoker!: TokenRevoker;\r\n\r\n /** Issuer hash for storage keys */\r\n private issuerHash!: string;\r\n\r\n /** Client ID hash for storage keys */\r\n private clientIdHash!: string;\r\n\r\n /** Normalized issuer URL */\r\n private readonly normalizedIssuer: string;\r\n\r\n /** Whether the client has been initialized */\r\n private initialized = false;\r\n\r\n /**\r\n * Create a new Authrim client\r\n *\r\n * @internal Use createAuthrimClient() instead\r\n */\r\n constructor(config: AuthrimClientConfig) {\r\n this.config = resolveConfig(config);\r\n this.normalizedIssuer = normalizeIssuer(config.issuer);\r\n\r\n // Initialize components that don't need hashes\r\n this.events = new EventEmitter();\r\n\r\n this.discoveryClient = new DiscoveryClient({\r\n http: this.config.http,\r\n cacheTtlMs: this.config.discoveryCacheTtlMs,\r\n });\r\n\r\n this.pkce = new PKCEHelper(this.config.crypto);\r\n\r\n this.authCodeFlow = new AuthorizationCodeFlow(this.config.http, this.config.clientId);\r\n }\r\n\r\n /**\r\n * Initialize the client\r\n *\r\n * @internal Called by createAuthrimClient()\r\n */\r\n async initialize(): Promise<void> {\r\n if (this.initialized) {\r\n return;\r\n }\r\n\r\n // Calculate hashes for storage keys\r\n const hashLength = this.config.hashOptions.hashLength;\r\n this.issuerHash = await hashForKey(this.config.crypto, this.normalizedIssuer, hashLength);\r\n this.clientIdHash = await hashForKey(this.config.crypto, this.config.clientId, hashLength);\r\n\r\n // Initialize state manager\r\n this.stateManager = new StateManager(\r\n this.config.crypto,\r\n this.config.storage,\r\n this.issuerHash,\r\n this.clientIdHash\r\n );\r\n\r\n // Initialize token manager\r\n this.tokenManager = new TokenManager({\r\n http: this.config.http,\r\n storage: this.config.storage,\r\n clientId: this.config.clientId,\r\n issuerHash: this.issuerHash,\r\n clientIdHash: this.clientIdHash,\r\n refreshSkewSeconds: this.config.refreshSkewSeconds,\r\n eventEmitter: this.events,\r\n });\r\n\r\n // Initialize logout handler\r\n this.logoutHandler = new LogoutHandler({\r\n storage: this.config.storage,\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n issuerHash: this.issuerHash,\r\n clientIdHash: this.clientIdHash,\r\n eventEmitter: this.events,\r\n endpoints: this.config.endpoints,\r\n });\r\n\r\n // Initialize token introspector\r\n this.tokenIntrospector = new TokenIntrospector({\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n });\r\n\r\n // Initialize token revoker\r\n this.tokenRevoker = new TokenRevoker({\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n });\r\n\r\n // Initialize token API client\r\n const tokenApiClient = new TokenApiClient({\r\n http: this.config.http,\r\n });\r\n\r\n // Initialize session manager\r\n this.sessionManager = new SessionManager({\r\n tokenManager: this.tokenManager,\r\n tokenApiClient,\r\n });\r\n\r\n // Clean up expired states (best-effort)\r\n await this.stateManager.cleanupExpiredStates();\r\n\r\n this.initialized = true;\r\n }\r\n\r\n /**\r\n * Ensure client is initialized\r\n */\r\n private ensureInitialized(): void {\r\n if (!this.initialized) {\r\n throw new AuthrimError(\r\n 'not_initialized',\r\n 'Client not initialized. Use createAuthrimClient().'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get OIDC discovery document\r\n *\r\n * @returns Discovery document\r\n */\r\n async discover(): Promise<OIDCDiscoveryDocument> {\r\n const discovery = await this.discoveryClient.discover(this.normalizedIssuer);\r\n\r\n // Update dependent components\r\n this.tokenManager.setDiscovery(discovery);\r\n this.sessionManager.setDiscovery(discovery);\r\n\r\n return discovery;\r\n }\r\n\r\n // ============================================================\r\n // Authorization Code Flow\r\n // ============================================================\r\n\r\n /**\r\n * Build authorization URL\r\n *\r\n * Generates the URL to redirect the user to for authentication.\r\n * Stores state, nonce, and code_verifier in storage.\r\n *\r\n * @param options - Authorization options\r\n * @returns Authorization URL result\r\n */\r\n async buildAuthorizationUrl(\r\n options: BuildAuthorizationUrlOptions\r\n ): Promise<AuthorizationUrlResult> {\r\n this.ensureInitialized();\r\n\r\n const discovery = await this.discover();\r\n\r\n // Generate PKCE pair\r\n const pkcePair = await this.pkce.generatePKCE();\r\n\r\n // Generate auth state\r\n const authState = await this.stateManager.generateAuthState({\r\n redirectUri: options.redirectUri,\r\n codeVerifier: pkcePair.codeVerifier,\r\n ttlSeconds: this.config.stateTtlSeconds,\r\n });\r\n\r\n // Build URL\r\n const result = this.authCodeFlow.buildAuthorizationUrl(discovery, authState, pkcePair, options);\r\n\r\n // Emit event\r\n this.events.emit('auth:redirecting', { url: result.url });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Handle authorization callback\r\n *\r\n * Processes the callback URL, validates state/nonce, and exchanges\r\n * the authorization code for tokens.\r\n *\r\n * @param callbackUrl - Callback URL or query string\r\n * @returns Token set\r\n */\r\n async handleCallback(callbackUrl: string): Promise<TokenSet> {\r\n this.ensureInitialized();\r\n\r\n // Parse callback\r\n const { code, state } = this.authCodeFlow.parseCallback(callbackUrl);\r\n\r\n // Emit event\r\n this.events.emit('auth:callback', { code, state });\r\n\r\n // Validate and consume state (always deletes)\r\n const authState = await this.stateManager.validateAndConsumeState(state);\r\n\r\n // Get discovery\r\n const discovery = await this.discover();\r\n\r\n // Exchange code for tokens\r\n const tokens = await this.authCodeFlow.exchangeCode(discovery, {\r\n code,\r\n state,\r\n redirectUri: authState.redirectUri,\r\n codeVerifier: authState.codeVerifier,\r\n nonce: authState.nonce,\r\n });\r\n\r\n // Save tokens\r\n await this.tokenManager.saveTokens(tokens);\r\n\r\n return tokens;\r\n }\r\n\r\n // ============================================================\r\n // Token API\r\n // ============================================================\r\n\r\n /**\r\n * Token API accessor\r\n */\r\n get token() {\r\n this.ensureInitialized();\r\n return {\r\n /**\r\n * Get access token (refreshes if needed)\r\n */\r\n getAccessToken: () => this.tokenManager.getAccessToken(),\r\n\r\n /**\r\n * Get current tokens\r\n */\r\n getTokens: () => this.tokenManager.getTokens(),\r\n\r\n /**\r\n * Get ID token\r\n */\r\n getIdToken: () => this.tokenManager.getIdToken(),\r\n\r\n /**\r\n * Check if authenticated\r\n */\r\n isAuthenticated: () => this.tokenManager.isAuthenticated(),\r\n\r\n /**\r\n * Exchange token (RFC 8693)\r\n *\r\n * Exchanges a token for a new token with different audience, scope,\r\n * or delegation. Useful for:\r\n * - Cross-service token acquisition\r\n * - Delegation (actor token)\r\n * - Scope reduction\r\n *\r\n * @param request - Token exchange request parameters\r\n * @returns Token exchange result\r\n */\r\n exchange: async (request: TokenExchangeRequest): Promise<TokenExchangeResult> => {\r\n // Ensure discovery is loaded\r\n await this.discover();\r\n return this.tokenManager.exchangeToken(request);\r\n },\r\n\r\n /**\r\n * Introspect a token (RFC 7662)\r\n *\r\n * Validates a token server-side and returns its metadata.\r\n * Useful for resource servers to validate access tokens.\r\n *\r\n * @param options - Introspection options (token and optional type hint)\r\n * @returns Introspection response with token metadata\r\n * @throws AuthrimError if introspection endpoint not available or request fails\r\n */\r\n introspect: async (options: IntrospectTokenOptions): Promise<IntrospectionResponse> => {\r\n const discovery = await this.discover();\r\n return this.tokenIntrospector.introspect(discovery, options);\r\n },\r\n\r\n /**\r\n * Revoke a token (RFC 7009)\r\n *\r\n * Explicitly invalidates a token at the authorization server.\r\n * Use this when you want to ensure a token can no longer be used.\r\n *\r\n * @param options - Revocation options (token and optional type hint)\r\n * @throws AuthrimError if revocation endpoint not available or request fails\r\n */\r\n revoke: async (options: RevokeTokenOptions): Promise<void> => {\r\n const discovery = await this.discover();\r\n return this.tokenRevoker.revoke(discovery, options);\r\n },\r\n };\r\n }\r\n\r\n // ============================================================\r\n // Session API\r\n // ============================================================\r\n\r\n /**\r\n * Session API accessor\r\n */\r\n get session() {\r\n this.ensureInitialized();\r\n return {\r\n /**\r\n * Check if authenticated locally\r\n */\r\n isAuthenticated: () => this.sessionManager.isAuthenticated(),\r\n\r\n /**\r\n * Check session with authorization server\r\n */\r\n check: () => this.sessionManager.checkSession(),\r\n };\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n *\r\n * @returns True if tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n this.ensureInitialized();\r\n return this.tokenManager.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Get user information\r\n *\r\n * @returns User info\r\n */\r\n async getUser(): Promise<UserInfo> {\r\n this.ensureInitialized();\r\n return this.sessionManager.getUser();\r\n }\r\n\r\n // ============================================================\r\n // Logout\r\n // ============================================================\r\n\r\n /**\r\n * Log out the user\r\n *\r\n * Clears local tokens and optionally redirects to IdP for logout.\r\n *\r\n * @param options - Logout options\r\n * @returns Logout result\r\n */\r\n async logout(options?: LogoutOptions): Promise<LogoutResult> {\r\n this.ensureInitialized();\r\n\r\n let discovery: OIDCDiscoveryDocument | null = null;\r\n try {\r\n discovery = await this.discover();\r\n } catch {\r\n // Discovery failure is OK for logout - we can still do local logout\r\n }\r\n\r\n return this.logoutHandler.logout(discovery, options);\r\n }\r\n\r\n // ============================================================\r\n // Events\r\n // ============================================================\r\n\r\n /**\r\n * Subscribe to an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n on<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n return this.events.on(event, handler);\r\n }\r\n\r\n /**\r\n * Subscribe to an event (one-time)\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n once<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n return this.events.once(event, handler);\r\n }\r\n\r\n /**\r\n * Unsubscribe from an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n */\r\n off<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): void {\r\n this.events.off(event, handler);\r\n }\r\n}\r\n\r\n/**\r\n * Create an Authrim client\r\n *\r\n * This is the main entry point for creating a client.\r\n * The client is fully initialized when returned.\r\n *\r\n * @param config - Client configuration\r\n * @returns Initialized Authrim client\r\n */\r\nexport async function createAuthrimClient(config: AuthrimClientConfig): Promise<AuthrimClient> {\r\n const client = new AuthrimClient(config);\r\n await client.initialize();\r\n return client;\r\n}\r\n","/**\r\n * Silent Authentication (prompt=none)\r\n *\r\n * Foundation for silent authentication using OIDC prompt=none.\r\n * This module provides the core logic for building silent auth requests\r\n * and parsing responses. The actual iframe/hidden frame implementation\r\n * is platform-specific and should be implemented in @authrim/web or similar.\r\n *\r\n * Silent authentication allows checking if a user has an active session\r\n * with the authorization server without user interaction.\r\n */\r\n\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet } from '../types/token.js';\r\nimport type { AuthState } from './state.js';\r\nimport type { PKCEPair } from './pkce.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Silent authentication options\r\n */\r\nexport interface SilentAuthOptions {\r\n /** Redirect URI for silent auth response (often an iframe callback page) */\r\n redirectUri: string;\r\n /** Scopes to request (default: 'openid') */\r\n scope?: string;\r\n /** Hint about the login identifier */\r\n loginHint?: string;\r\n /** ID token hint (helps IdP identify the user) */\r\n idTokenHint?: string;\r\n /** Additional custom parameters */\r\n extraParams?: Record<string, string>;\r\n /**\r\n * Expose state/nonce in result (for SSR/external storage)\r\n *\r\n * Default: false (security: state/nonce stay internal)\r\n */\r\n exposeState?: boolean;\r\n}\r\n\r\n/**\r\n * Result of building silent auth URL\r\n */\r\nexport interface SilentAuthUrlResult {\r\n /** Authorization URL with prompt=none */\r\n url: string;\r\n /** State parameter (only if exposeState: true) */\r\n state?: string;\r\n /** Nonce parameter (only if exposeState: true) */\r\n nonce?: string;\r\n}\r\n\r\n/**\r\n * Silent authentication result\r\n */\r\nexport interface SilentAuthResult {\r\n /** Whether silent auth succeeded */\r\n success: boolean;\r\n /** Tokens if successful */\r\n tokens?: TokenSet;\r\n /** Error if failed (e.g., login_required) */\r\n error?: AuthrimError;\r\n}\r\n\r\n/**\r\n * Silent auth error codes that indicate interactive login is needed\r\n */\r\nconst INTERACTIVE_LOGIN_REQUIRED_ERRORS = new Set([\r\n 'login_required',\r\n 'interaction_required',\r\n 'consent_required',\r\n 'account_selection_required',\r\n]);\r\n\r\n/**\r\n * Silent Authentication Handler\r\n *\r\n * Provides core logic for silent authentication. Platform-specific\r\n * implementations (iframe, hidden frame, etc.) should use this class.\r\n */\r\nexport class SilentAuthHandler {\r\n constructor(private readonly clientId: string) {}\r\n\r\n /**\r\n * Build silent authentication URL\r\n *\r\n * Creates an authorization URL with prompt=none for silent authentication.\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param authState - Auth state from StateManager\r\n * @param pkce - PKCE pair\r\n * @param options - Silent auth options\r\n * @returns Silent auth URL result\r\n */\r\n buildSilentAuthUrl(\r\n discovery: OIDCDiscoveryDocument,\r\n authState: AuthState,\r\n pkce: PKCEPair,\r\n options: SilentAuthOptions\r\n ): SilentAuthUrlResult {\r\n const endpoint = discovery.authorization_endpoint;\r\n const params = new URLSearchParams();\r\n\r\n // Required parameters\r\n params.set('client_id', this.clientId);\r\n params.set('response_type', 'code');\r\n params.set('redirect_uri', options.redirectUri);\r\n params.set('state', authState.state);\r\n params.set('nonce', authState.nonce);\r\n\r\n // Silent auth specific - MUST be prompt=none\r\n params.set('prompt', 'none');\r\n\r\n // PKCE parameters\r\n params.set('code_challenge', pkce.codeChallenge);\r\n params.set('code_challenge_method', pkce.codeChallengeMethod);\r\n\r\n // Scopes (default to openid only for silent auth)\r\n const scope = options.scope ?? 'openid';\r\n params.set('scope', scope);\r\n\r\n // Optional parameters\r\n if (options.loginHint) {\r\n params.set('login_hint', options.loginHint);\r\n }\r\n if (options.idTokenHint) {\r\n params.set('id_token_hint', options.idTokenHint);\r\n }\r\n\r\n // Extra custom parameters (with security parameter protection)\r\n if (options.extraParams) {\r\n const protectedParams = new Set([\r\n 'client_id',\r\n 'response_type',\r\n 'redirect_uri',\r\n 'state',\r\n 'nonce',\r\n 'code_challenge',\r\n 'code_challenge_method',\r\n 'scope',\r\n 'prompt', // Protect prompt=none\r\n ]);\r\n\r\n for (const [key, value] of Object.entries(options.extraParams)) {\r\n if (protectedParams.has(key.toLowerCase())) {\r\n continue;\r\n }\r\n params.set(key, value);\r\n }\r\n }\r\n\r\n const url = `${endpoint}?${params.toString()}`;\r\n\r\n const result: SilentAuthUrlResult = { url };\r\n\r\n if (options.exposeState) {\r\n result.state = authState.state;\r\n result.nonce = authState.nonce;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Parse silent authentication response URL\r\n *\r\n * Parses the callback URL from silent authentication and returns\r\n * either the authorization code (for token exchange) or an error.\r\n *\r\n * @param responseUrl - Response URL from silent auth (iframe callback)\r\n * @returns Parsed result with code/state or error\r\n */\r\n parseSilentAuthResponse(responseUrl: string): {\r\n success: true;\r\n code: string;\r\n state: string;\r\n } | {\r\n success: false;\r\n error: AuthrimError;\r\n } {\r\n let searchParams: URLSearchParams;\r\n\r\n // Support both full URL and query string\r\n if (responseUrl.includes('?')) {\r\n const url = responseUrl.startsWith('http')\r\n ? new URL(responseUrl)\r\n : new URL(responseUrl, 'https://dummy.local');\r\n searchParams = url.searchParams;\r\n } else {\r\n searchParams = new URLSearchParams(responseUrl);\r\n }\r\n\r\n // Check for OAuth error response\r\n const error = searchParams.get('error');\r\n if (error) {\r\n const errorDescription = searchParams.get('error_description');\r\n const errorCode = this.mapSilentAuthError(error);\r\n\r\n return {\r\n success: false,\r\n error: new AuthrimError(errorCode, errorDescription ?? this.getDefaultErrorMessage(error), {\r\n details: {\r\n error,\r\n error_description: errorDescription,\r\n error_uri: searchParams.get('error_uri'),\r\n },\r\n }),\r\n };\r\n }\r\n\r\n // Extract code and state\r\n const code = searchParams.get('code');\r\n const state = searchParams.get('state');\r\n\r\n if (!code) {\r\n return {\r\n success: false,\r\n error: new AuthrimError('missing_code', 'Authorization code not found in silent auth response'),\r\n };\r\n }\r\n if (!state) {\r\n return {\r\n success: false,\r\n error: new AuthrimError('missing_state', 'State parameter not found in silent auth response'),\r\n };\r\n }\r\n\r\n return { success: true, code, state };\r\n }\r\n\r\n /**\r\n * Check if an error indicates interactive login is required\r\n *\r\n * @param error - Error to check\r\n * @returns True if interactive login is needed\r\n */\r\n isInteractiveLoginRequired(error: AuthrimError): boolean {\r\n return INTERACTIVE_LOGIN_REQUIRED_ERRORS.has(error.code);\r\n }\r\n\r\n /**\r\n * Map OAuth error to AuthrimErrorCode\r\n */\r\n private mapSilentAuthError(\r\n error: string\r\n ): 'login_required' | 'interaction_required' | 'consent_required' | 'account_selection_required' | 'oauth_error' {\r\n if (INTERACTIVE_LOGIN_REQUIRED_ERRORS.has(error)) {\r\n return error as 'login_required' | 'interaction_required' | 'consent_required' | 'account_selection_required';\r\n }\r\n return 'oauth_error';\r\n }\r\n\r\n /**\r\n * Get default error message for silent auth errors\r\n */\r\n private getDefaultErrorMessage(error: string): string {\r\n switch (error) {\r\n case 'login_required':\r\n return 'User must log in - no active session found';\r\n case 'interaction_required':\r\n return 'User interaction required';\r\n case 'consent_required':\r\n return 'User consent required';\r\n case 'account_selection_required':\r\n return 'User must select an account';\r\n default:\r\n return 'Silent authentication failed';\r\n }\r\n }\r\n}\r\n","/**\r\n * Hash Utilities\r\n *\r\n * Provides hash calculation utilities for OIDC specifications.\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport { base64urlEncode } from './base64url.js';\r\n\r\n/**\r\n * Calculate ds_hash for Native SSO device_secret verification\r\n *\r\n * Algorithm: BASE64URL(left half of SHA-256(device_secret))\r\n * Reference: OIDC Native SSO 1.0 specification\r\n *\r\n * This is the same algorithm used for at_hash and c_hash in OIDC Core,\r\n * applied to the device_secret value.\r\n *\r\n * @param deviceSecret - The device_secret value to hash\r\n * @param crypto - Platform-specific crypto provider\r\n * @returns ds_hash value (BASE64URL encoded)\r\n *\r\n * @example\r\n * ```typescript\r\n * const dsHash = await calculateDsHash(deviceSecret, cryptoProvider);\r\n * // Compare with id_token.ds_hash claim\r\n * if (idToken.ds_hash === dsHash) {\r\n * // device_secret is valid\r\n * }\r\n * ```\r\n */\r\nexport async function calculateDsHash(\r\n deviceSecret: string,\r\n crypto: CryptoProvider\r\n): Promise<string> {\r\n // 1. Compute SHA-256 hash (32 bytes)\r\n const hash = await crypto.sha256(deviceSecret);\r\n\r\n // 2. Take the left half (16 bytes for SHA-256)\r\n const leftHalf = hash.slice(0, hash.length / 2);\r\n\r\n // 3. BASE64URL encode\r\n return base64urlEncode(leftHalf);\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/config.ts","../src/types/errors.ts","../src/client/discovery.ts","../src/events/emitter.ts","../src/auth/pkce.ts","../src/utils/base64url.ts","../src/auth/state.ts","../src/utils/jwt.ts","../src/auth/authorization-code.ts","../src/types/token.ts","../src/token/manager.ts","../src/token/introspection.ts","../src/token/revocation.ts","../src/session/logout.ts","../src/session/token-api.ts","../src/session/manager.ts","../src/client/index.ts","../src/auth/silent-auth.ts","../src/utils/hash.ts"],"names":["resolveConfig","config","AuthrimError","_AuthrimError","code","message","options","error","getErrorMeta","ERROR_META_MAP","normalizeIssuer","issuer","_DiscoveryClient","normalizedIssuer","cached","discoveryUrl","doc","response","docIssuer","DiscoveryClient","EventEmitter","event","handler","onceHandler","data","handlers","PKCEHelper","crypto","codeVerifier","codeChallenge","verifier","base64urlEncode","base64","len","i","byte1","byte2","byte3","triplet","BASE64_CHARS","base64urlDecode","str","paddingLen","outputLen","output","outputIndex","BASE64_LOOKUP","byte4","stringToBase64url","encoder","base64urlToString","base64url","STORAGE_KEYS","issuerHash","clientIdHash","state","_StateManager","storage","ttlSeconds","stateBytes","nonceBytes","nonce","now","authState","key","stored","prefix","all","value","StateManager","decodeJwt","jwt","parts","headerB64","payloadB64","signature","header","payload","decodeIdToken","idToken","isJwtExpired","skewSeconds","getIdTokenNonce","AuthorizationCodeFlow","http","clientId","discovery","pkce","endpoint","params","scope","protectedParams","result","callbackUrl","searchParams","errorDescription","tokenEndpoint","body","errorData","tokenResponse","expiresAt","TOKEN_TYPE_URIS","_TokenManager","tokens","refreshToken","attemptedRetry","authrimError","newTokens","request","subjectTokenType","type","TokenManager","TokenIntrospector","token","TokenRevoker","LogoutHandler","storedIdToken","storedTokens","revocationResult","endSessionEndpoint","tokenKey","idTokenKey","TokenApiClient","accessToken","userinfoEndpoint","SessionManager","hashForKey","length","hash","AuthrimClient","hashLength","tokenApiClient","pkcePair","createAuthrimClient","client","INTERACTIVE_LOGIN_REQUIRED_ERRORS","SilentAuthHandler","responseUrl","errorCode","calculateDsHash","deviceSecret","leftHalf"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Client Configuration\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\n\r\n/**\r\n * Endpoint overrides\r\n */\r\nexport interface EndpointOverrides {\r\n /** Authorization endpoint */\r\n authorization?: string;\r\n /** Token endpoint */\r\n token?: string;\r\n /** UserInfo endpoint */\r\n userinfo?: string;\r\n /** Revocation endpoint */\r\n revocation?: string;\r\n /** End session endpoint (null to disable) */\r\n endSession?: string | null;\r\n}\r\n\r\n/**\r\n * Hash options for storage key generation\r\n */\r\nexport interface HashOptions {\r\n /**\r\n * Hash length for storage keys\r\n *\r\n * Default: 16 characters (96 bits)\r\n * Alternative: 22 characters (132 bits) for lower collision risk\r\n */\r\n hashLength?: number;\r\n}\r\n\r\n/**\r\n * Authrim Client Configuration\r\n */\r\nexport interface AuthrimClientConfig {\r\n /**\r\n * OpenID Connect issuer URL\r\n *\r\n * This is used to fetch the discovery document and validate tokens.\r\n * Trailing slashes are automatically normalized.\r\n */\r\n issuer: string;\r\n\r\n /**\r\n * OAuth 2.0 client ID\r\n */\r\n clientId: string;\r\n\r\n /**\r\n * HTTP client implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides a default browser implementation.\r\n */\r\n http: HttpClient;\r\n\r\n /**\r\n * Crypto provider implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides a default browser implementation.\r\n */\r\n crypto: CryptoProvider;\r\n\r\n /**\r\n * Storage provider implementation\r\n *\r\n * Required for @authrim/core.\r\n * @authrim/web provides localStorage/sessionStorage implementations.\r\n */\r\n storage: AuthrimStorage;\r\n\r\n /**\r\n * Default redirect URI for authorization requests\r\n */\r\n redirectUri?: string;\r\n\r\n /**\r\n * Default scopes to request\r\n *\r\n * Default: ['openid', 'profile']\r\n */\r\n scopes?: string[];\r\n\r\n /**\r\n * Manual endpoint overrides\r\n *\r\n * Use these to override discovery document endpoints.\r\n * Set endSession to null to disable logout redirect.\r\n */\r\n endpoints?: EndpointOverrides;\r\n\r\n /**\r\n * Enable Flow Engine (server-driven UI)\r\n *\r\n * Default: false\r\n * Set to true to enable server-driven UI flows.\r\n * Note: Both SDK and server must have Flow Engine enabled for it to work.\r\n */\r\n flowEngine?: boolean;\r\n\r\n /**\r\n * Discovery cache TTL in milliseconds\r\n *\r\n * Default: 3600000 (1 hour)\r\n */\r\n discoveryCacheTtlMs?: number;\r\n\r\n /**\r\n * Token refresh skew in seconds\r\n *\r\n * Refresh tokens this many seconds before expiration.\r\n * Default: 30\r\n */\r\n refreshSkewSeconds?: number;\r\n\r\n /**\r\n * State/nonce TTL in seconds\r\n *\r\n * Default: 600 (10 minutes)\r\n */\r\n stateTtlSeconds?: number;\r\n\r\n /**\r\n * Hash options for storage key generation\r\n */\r\n hashOptions?: HashOptions;\r\n}\r\n\r\n/**\r\n * Resolved configuration with defaults applied\r\n */\r\nexport interface ResolvedConfig extends Required<\r\n Omit<AuthrimClientConfig, 'endpoints' | 'redirectUri' | 'hashOptions'>\r\n> {\r\n endpoints?: EndpointOverrides;\r\n redirectUri?: string;\r\n hashOptions: Required<HashOptions>;\r\n}\r\n\r\n/**\r\n * Apply defaults to configuration\r\n */\r\nexport function resolveConfig(config: AuthrimClientConfig): ResolvedConfig {\r\n return {\r\n ...config,\r\n scopes: config.scopes ?? ['openid', 'profile'],\r\n flowEngine: config.flowEngine ?? false,\r\n discoveryCacheTtlMs: config.discoveryCacheTtlMs ?? 3600 * 1000,\r\n refreshSkewSeconds: config.refreshSkewSeconds ?? 30,\r\n stateTtlSeconds: config.stateTtlSeconds ?? 600,\r\n hashOptions: {\r\n hashLength: config.hashOptions?.hashLength ?? 16,\r\n },\r\n };\r\n}\r\n","/**\r\n * Authrim SDK Error Types\r\n */\r\n\r\n/**\r\n * User action recommended for error recovery\r\n */\r\nexport type AuthrimErrorUserAction =\r\n | 'retry'\r\n | 'reauthenticate'\r\n | 'contact_support'\r\n | 'check_network'\r\n | 'none';\r\n\r\n/**\r\n * Error severity level\r\n */\r\nexport type AuthrimErrorSeverity = 'fatal' | 'error' | 'warning';\r\n\r\n/**\r\n * Error metadata for recovery information\r\n */\r\nexport interface AuthrimErrorMeta {\r\n /** Whether this is a transient error */\r\n transient: boolean;\r\n /** Whether automatic retry is possible */\r\n retryable: boolean;\r\n /** Suggested retry wait time in milliseconds */\r\n retryAfterMs?: number;\r\n /** Maximum number of retry attempts */\r\n maxRetries?: number;\r\n /** Recommended user action */\r\n userAction: AuthrimErrorUserAction;\r\n /** Error severity level */\r\n severity: AuthrimErrorSeverity;\r\n}\r\n\r\n/**\r\n * Error codes used by the SDK\r\n */\r\nexport type AuthrimErrorCode =\r\n // OAuth 2.0 / OIDC standard errors\r\n | 'invalid_request'\r\n | 'unauthorized_client'\r\n | 'access_denied'\r\n | 'unsupported_response_type'\r\n | 'invalid_scope'\r\n | 'server_error'\r\n | 'temporarily_unavailable'\r\n | 'invalid_grant'\r\n | 'invalid_token'\r\n // SDK-specific errors\r\n | 'invalid_state'\r\n | 'expired_state'\r\n | 'invalid_nonce'\r\n | 'nonce_mismatch'\r\n | 'session_expired'\r\n | 'session_check_failed'\r\n | 'network_error'\r\n | 'timeout_error'\r\n | 'discovery_error'\r\n | 'discovery_mismatch'\r\n | 'configuration_error'\r\n | 'storage_error'\r\n | 'flow_engine_error'\r\n // Token errors\r\n | 'no_tokens'\r\n | 'token_expired'\r\n | 'token_error'\r\n | 'refresh_error'\r\n | 'token_exchange_error'\r\n // Callback errors\r\n | 'oauth_error'\r\n | 'missing_code'\r\n | 'missing_state'\r\n // Initialization errors\r\n | 'not_initialized'\r\n | 'no_discovery'\r\n // Session errors\r\n | 'no_userinfo_endpoint'\r\n | 'userinfo_error'\r\n // Token introspection/revocation errors\r\n | 'introspection_error'\r\n | 'revocation_error'\r\n | 'no_introspection_endpoint'\r\n | 'no_revocation_endpoint'\r\n // Silent auth errors (OIDC prompt=none)\r\n | 'login_required'\r\n | 'interaction_required'\r\n | 'consent_required'\r\n | 'account_selection_required'\r\n // Browser/Popup auth errors (@authrim/web)\r\n | 'dom_not_ready'\r\n | 'state_mismatch'\r\n | 'popup_blocked'\r\n | 'popup_closed'\r\n | 'invalid_response';\r\n\r\n/**\r\n * Options for creating an AuthrimError\r\n */\r\nexport interface AuthrimErrorOptions {\r\n details?: Record<string, unknown>;\r\n errorUri?: string;\r\n cause?: Error;\r\n}\r\n\r\n/**\r\n * Authrim SDK Error class\r\n */\r\nexport class AuthrimError extends Error {\r\n /** Error code for programmatic handling */\r\n readonly code: AuthrimErrorCode;\r\n\r\n /** Additional error details */\r\n readonly details?: Record<string, unknown>;\r\n\r\n /** OAuth error_uri if provided */\r\n readonly errorUri?: string;\r\n\r\n /** Underlying cause */\r\n readonly cause?: Error;\r\n\r\n constructor(code: AuthrimErrorCode, message: string, options?: AuthrimErrorOptions) {\r\n super(message);\r\n this.name = 'AuthrimError';\r\n this.code = code;\r\n this.details = options?.details;\r\n this.errorUri = options?.errorUri;\r\n this.cause = options?.cause;\r\n }\r\n\r\n /**\r\n * Create an AuthrimError from an OAuth error response\r\n */\r\n static fromOAuthError(error: {\r\n error: string;\r\n error_description?: string;\r\n error_uri?: string;\r\n }): AuthrimError {\r\n const oauthCodes = [\r\n 'invalid_request',\r\n 'unauthorized_client',\r\n 'access_denied',\r\n 'unsupported_response_type',\r\n 'invalid_scope',\r\n 'server_error',\r\n 'temporarily_unavailable',\r\n 'invalid_grant',\r\n 'invalid_token',\r\n ] as const;\r\n\r\n type OAuthErrorCode = (typeof oauthCodes)[number];\r\n\r\n const code: AuthrimErrorCode = oauthCodes.includes(error.error as OAuthErrorCode)\r\n ? (error.error as OAuthErrorCode)\r\n : 'invalid_request';\r\n\r\n return new AuthrimError(code, error.error_description ?? error.error, {\r\n errorUri: error.error_uri,\r\n details: { originalError: error.error },\r\n });\r\n }\r\n\r\n /**\r\n * Check if an error is retryable (e.g., network errors)\r\n */\r\n isRetryable(): boolean {\r\n return this.code === 'network_error' || this.code === 'timeout_error';\r\n }\r\n\r\n /**\r\n * Get error metadata for recovery guidance\r\n */\r\n get meta(): AuthrimErrorMeta {\r\n return getErrorMeta(this.code);\r\n }\r\n}\r\n\r\n/**\r\n * Error metadata mapping for each error code\r\n */\r\nconst ERROR_META_MAP: Record<AuthrimErrorCode, AuthrimErrorMeta> = {\r\n // OAuth 2.0 / OIDC standard errors\r\n invalid_request: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n unauthorized_client: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n access_denied: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n unsupported_response_type: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n invalid_scope: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n server_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 5000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n temporarily_unavailable: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 10000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n invalid_grant: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n invalid_token: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n\r\n // SDK-specific errors\r\n invalid_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n expired_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n invalid_nonce: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n nonce_mismatch: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n session_expired: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n session_check_failed: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n network_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 3,\r\n userAction: 'check_network',\r\n severity: 'error',\r\n },\r\n timeout_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'warning',\r\n },\r\n discovery_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 5000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n discovery_mismatch: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n configuration_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'fatal',\r\n },\r\n storage_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 1000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n flow_engine_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n\r\n // Token errors\r\n no_tokens: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n token_expired: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n token_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n refresh_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n token_exchange_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Callback errors\r\n oauth_error: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n missing_code: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n missing_state: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n\r\n // Initialization errors\r\n not_initialized: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'fatal',\r\n },\r\n no_discovery: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 3000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Session errors\r\n no_userinfo_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n userinfo_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n\r\n // Token introspection/revocation errors\r\n introspection_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n revocation_error: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 2000,\r\n maxRetries: 2,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n no_introspection_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n no_revocation_endpoint: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n\r\n // Silent auth errors\r\n login_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n interaction_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n consent_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n account_selection_required: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n\r\n // Browser/Popup auth errors (@authrim/web)\r\n dom_not_ready: {\r\n transient: true,\r\n retryable: true,\r\n retryAfterMs: 100,\r\n maxRetries: 3,\r\n userAction: 'retry',\r\n severity: 'error',\r\n },\r\n state_mismatch: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'error',\r\n },\r\n popup_blocked: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'none',\r\n severity: 'warning',\r\n },\r\n popup_closed: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'reauthenticate',\r\n severity: 'warning',\r\n },\r\n invalid_response: {\r\n transient: false,\r\n retryable: false,\r\n userAction: 'contact_support',\r\n severity: 'error',\r\n },\r\n};\r\n\r\n/**\r\n * Get error metadata for a given error code\r\n */\r\nexport function getErrorMeta(code: AuthrimErrorCode): AuthrimErrorMeta {\r\n return ERROR_META_MAP[code];\r\n}\r\n","/**\r\n * OIDC Discovery Client\r\n *\r\n * Fetches and caches OIDC Discovery documents from the authorization server.\r\n * https://openid.net/specs/openid-connect-discovery-1_0.html\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Cached discovery document with timestamp\r\n */\r\ninterface CachedDiscovery {\r\n doc: OIDCDiscoveryDocument;\r\n fetchedAt: number;\r\n}\r\n\r\n/**\r\n * Discovery client options\r\n */\r\nexport interface DiscoveryClientOptions {\r\n /** HTTP client for making requests */\r\n http: HttpClient;\r\n /** Cache TTL in milliseconds (default: 1 hour) */\r\n cacheTtlMs?: number;\r\n}\r\n\r\n/**\r\n * Normalize issuer URL\r\n *\r\n * Removes trailing slashes to ensure consistent comparison.\r\n *\r\n * @param issuer - Issuer URL\r\n * @returns Normalized issuer URL\r\n */\r\nexport function normalizeIssuer(issuer: string): string {\r\n return issuer.replace(/\\/+$/, '');\r\n}\r\n\r\n/**\r\n * OIDC Discovery Client\r\n */\r\nexport class DiscoveryClient {\r\n private readonly http: HttpClient;\r\n private readonly cacheTtlMs: number;\r\n private readonly cache: Map<string, CachedDiscovery> = new Map();\r\n\r\n /** Default cache TTL: 1 hour */\r\n private static readonly DEFAULT_CACHE_TTL_MS = 3600 * 1000;\r\n\r\n constructor(options: DiscoveryClientOptions) {\r\n this.http = options.http;\r\n this.cacheTtlMs = options.cacheTtlMs ?? DiscoveryClient.DEFAULT_CACHE_TTL_MS;\r\n }\r\n\r\n /**\r\n * Fetch the OIDC Discovery document for an issuer\r\n *\r\n * @param issuer - Issuer URL\r\n * @returns Discovery document\r\n * @throws AuthrimError if discovery fails or issuer mismatch\r\n */\r\n async discover(issuer: string): Promise<OIDCDiscoveryDocument> {\r\n const normalizedIssuer = normalizeIssuer(issuer);\r\n const cached = this.cache.get(normalizedIssuer);\r\n\r\n // Return cached document if still valid\r\n if (cached && !this.isExpired(cached)) {\r\n return cached.doc;\r\n }\r\n\r\n // Fetch discovery document\r\n const discoveryUrl = `${normalizedIssuer}/.well-known/openid-configuration`;\r\n\r\n let doc: OIDCDiscoveryDocument;\r\n try {\r\n const response = await this.http.fetch<OIDCDiscoveryDocument>(discoveryUrl);\r\n if (!response.ok) {\r\n throw new AuthrimError('discovery_error', `Discovery request failed: ${response.status}`, {\r\n details: { status: response.status, statusText: response.statusText },\r\n });\r\n }\r\n doc = response.data;\r\n } catch (error) {\r\n if (error instanceof AuthrimError) {\r\n throw error;\r\n }\r\n throw new AuthrimError('discovery_error', 'Failed to fetch discovery document', {\r\n cause: error instanceof Error ? error : undefined,\r\n details: { url: discoveryUrl },\r\n });\r\n }\r\n\r\n // Validate issuer matches (security check)\r\n const docIssuer = normalizeIssuer(doc.issuer);\r\n if (docIssuer !== normalizedIssuer) {\r\n throw new AuthrimError(\r\n 'discovery_mismatch',\r\n `Issuer mismatch in discovery document: expected \"${normalizedIssuer}\", got \"${docIssuer}\"`,\r\n {\r\n details: {\r\n expected: normalizedIssuer,\r\n actual: docIssuer,\r\n },\r\n }\r\n );\r\n }\r\n\r\n // Cache the document\r\n this.cache.set(normalizedIssuer, {\r\n doc,\r\n fetchedAt: Date.now(),\r\n });\r\n\r\n return doc;\r\n }\r\n\r\n /**\r\n * Check if a cached document has expired\r\n */\r\n private isExpired(cached: CachedDiscovery): boolean {\r\n return Date.now() - cached.fetchedAt > this.cacheTtlMs;\r\n }\r\n\r\n /**\r\n * Clear the discovery cache (useful for testing)\r\n */\r\n clearCache(): void {\r\n this.cache.clear();\r\n }\r\n\r\n /**\r\n * Clear a specific issuer from the cache\r\n *\r\n * @param issuer - Issuer URL to clear\r\n */\r\n clearIssuer(issuer: string): void {\r\n this.cache.delete(normalizeIssuer(issuer));\r\n }\r\n}\r\n","/**\r\n * Event Emitter\r\n *\r\n * Simple typed event emitter for SDK events.\r\n */\r\n\r\nimport type { AuthrimEvents, AuthrimEventName, AuthrimEventHandler } from './types.js';\r\n\r\n/**\r\n * Typed Event Emitter\r\n */\r\nexport class EventEmitter {\r\n private listeners: Map<AuthrimEventName, Set<AuthrimEventHandler<AuthrimEventName>>> = new Map();\r\n\r\n /**\r\n * Subscribe to an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n on<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(handler as AuthrimEventHandler<AuthrimEventName>);\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.off(event, handler);\r\n };\r\n }\r\n\r\n /**\r\n * Subscribe to an event (one-time)\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n once<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n const onceHandler = ((data: AuthrimEvents[T]) => {\r\n this.off(event, onceHandler);\r\n handler(data);\r\n }) as AuthrimEventHandler<T>;\r\n\r\n return this.on(event, onceHandler);\r\n }\r\n\r\n /**\r\n * Unsubscribe from an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler to remove\r\n */\r\n off<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): void {\r\n const handlers = this.listeners.get(event);\r\n if (handlers) {\r\n handlers.delete(handler as AuthrimEventHandler<AuthrimEventName>);\r\n if (handlers.size === 0) {\r\n this.listeners.delete(event);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Emit an event\r\n *\r\n * @param event - Event name\r\n * @param data - Event data\r\n */\r\n emit<T extends AuthrimEventName>(event: T, data: AuthrimEvents[T]): void {\r\n const handlers = this.listeners.get(event);\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try {\r\n handler(data);\r\n } catch (error) {\r\n // Log but don't throw - one handler failure shouldn't affect others\r\n console.error(`Error in event handler for '${event}':`, error);\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Remove all listeners for an event (or all events)\r\n *\r\n * @param event - Event name (optional, removes all if not specified)\r\n */\r\n removeAllListeners(event?: AuthrimEventName): void {\r\n if (event) {\r\n this.listeners.delete(event);\r\n } else {\r\n this.listeners.clear();\r\n }\r\n }\r\n\r\n /**\r\n * Get the number of listeners for an event\r\n *\r\n * @param event - Event name\r\n * @returns Number of listeners\r\n */\r\n listenerCount(event: AuthrimEventName): number {\r\n return this.listeners.get(event)?.size ?? 0;\r\n }\r\n}\r\n","/**\r\n * PKCE (Proof Key for Code Exchange) Helper\r\n *\r\n * Implements RFC 7636 for Authorization Code Flow security.\r\n * https://tools.ietf.org/html/rfc7636\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\n\r\n/**\r\n * PKCE challenge method\r\n */\r\nexport type CodeChallengeMethod = 'S256' | 'plain';\r\n\r\n/**\r\n * PKCE pair (verifier and challenge)\r\n */\r\nexport interface PKCEPair {\r\n /** Code verifier (high-entropy random string) */\r\n codeVerifier: string;\r\n /** Code challenge (derived from verifier) */\r\n codeChallenge: string;\r\n /** Challenge method used */\r\n codeChallengeMethod: CodeChallengeMethod;\r\n}\r\n\r\n/**\r\n * PKCE Helper class\r\n */\r\nexport class PKCEHelper {\r\n constructor(private readonly crypto: CryptoProvider) {}\r\n\r\n /**\r\n * Generate a PKCE pair (verifier + challenge)\r\n *\r\n * Uses S256 method (SHA-256 hash, base64url-encoded).\r\n *\r\n * @returns PKCE pair\r\n */\r\n async generatePKCE(): Promise<PKCEPair> {\r\n const codeVerifier = await this.crypto.generateCodeVerifier();\r\n const codeChallenge = await this.crypto.generateCodeChallenge(codeVerifier);\r\n\r\n return {\r\n codeVerifier,\r\n codeChallenge,\r\n codeChallengeMethod: 'S256',\r\n };\r\n }\r\n\r\n /**\r\n * Generate only the code verifier\r\n *\r\n * @returns Code verifier string\r\n */\r\n async generateCodeVerifier(): Promise<string> {\r\n return this.crypto.generateCodeVerifier();\r\n }\r\n\r\n /**\r\n * Generate a code challenge from a verifier\r\n *\r\n * @param verifier - Code verifier\r\n * @returns Code challenge (base64url-encoded SHA-256 hash)\r\n */\r\n async generateCodeChallenge(verifier: string): Promise<string> {\r\n return this.crypto.generateCodeChallenge(verifier);\r\n }\r\n}\r\n","/**\r\n * Base64URL Encoding/Decoding Utilities\r\n *\r\n * Implements RFC 4648 Section 5 (Base64 URL and Filename Safe Alphabet)\r\n */\r\n\r\n/**\r\n * Encode a Uint8Array to base64url string\r\n *\r\n * @param data - Bytes to encode\r\n * @returns Base64URL encoded string (no padding)\r\n */\r\nexport function base64urlEncode(data: Uint8Array): string {\r\n // Convert to base64\r\n let base64 = '';\r\n\r\n // Use platform-agnostic conversion\r\n const len = data.length;\r\n for (let i = 0; i < len; i += 3) {\r\n const byte1 = data[i];\r\n const byte2 = i + 1 < len ? data[i + 1] : 0;\r\n const byte3 = i + 2 < len ? data[i + 2] : 0;\r\n\r\n const triplet = (byte1 << 16) | (byte2 << 8) | byte3;\r\n\r\n base64 += BASE64_CHARS[(triplet >> 18) & 0x3f];\r\n base64 += BASE64_CHARS[(triplet >> 12) & 0x3f];\r\n base64 += i + 1 < len ? BASE64_CHARS[(triplet >> 6) & 0x3f] : '';\r\n base64 += i + 2 < len ? BASE64_CHARS[triplet & 0x3f] : '';\r\n }\r\n\r\n // Convert to base64url (replace + with -, / with _)\r\n // Remove padding (=)\r\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\n\r\n/**\r\n * Decode a base64url string to Uint8Array\r\n *\r\n * @param str - Base64URL encoded string\r\n * @returns Decoded bytes\r\n */\r\nexport function base64urlDecode(str: string): Uint8Array {\r\n // Convert base64url to base64\r\n let base64 = str.replace(/-/g, '+').replace(/_/g, '/');\r\n\r\n // Add padding if needed\r\n while (base64.length % 4) {\r\n base64 += '=';\r\n }\r\n\r\n // Decode base64\r\n const len = base64.length;\r\n const paddingLen = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\r\n const outputLen = (len * 3) / 4 - paddingLen;\r\n const output = new Uint8Array(outputLen);\r\n\r\n let outputIndex = 0;\r\n for (let i = 0; i < len; i += 4) {\r\n const byte1 = BASE64_LOOKUP[base64.charCodeAt(i)];\r\n const byte2 = BASE64_LOOKUP[base64.charCodeAt(i + 1)];\r\n const byte3 = BASE64_LOOKUP[base64.charCodeAt(i + 2)];\r\n const byte4 = BASE64_LOOKUP[base64.charCodeAt(i + 3)];\r\n\r\n const triplet = (byte1 << 18) | (byte2 << 12) | (byte3 << 6) | byte4;\r\n\r\n if (outputIndex < outputLen) output[outputIndex++] = (triplet >> 16) & 0xff;\r\n if (outputIndex < outputLen) output[outputIndex++] = (triplet >> 8) & 0xff;\r\n if (outputIndex < outputLen) output[outputIndex++] = triplet & 0xff;\r\n }\r\n\r\n return output;\r\n}\r\n\r\n/**\r\n * Encode a string to base64url\r\n *\r\n * @param str - String to encode (UTF-8)\r\n * @returns Base64URL encoded string\r\n */\r\nexport function stringToBase64url(str: string): string {\r\n const encoder = new TextEncoder();\r\n return base64urlEncode(encoder.encode(str));\r\n}\r\n\r\n/**\r\n * Decode a base64url string to string\r\n *\r\n * @param base64url - Base64URL encoded string\r\n * @returns Decoded string (UTF-8)\r\n */\r\nexport function base64urlToString(base64url: string): string {\r\n const decoder = new TextDecoder();\r\n return decoder.decode(base64urlDecode(base64url));\r\n}\r\n\r\n// Base64 character set\r\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\r\n\r\n// Lookup table for decoding\r\nconst BASE64_LOOKUP = new Uint8Array(256);\r\nfor (let i = 0; i < BASE64_CHARS.length; i++) {\r\n BASE64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;\r\n}\r\n","/**\r\n * State/Nonce Manager\r\n *\r\n * Manages CSRF protection (state) and replay attack prevention (nonce)\r\n * for Authorization Code Flow.\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { base64urlEncode } from '../utils/base64url.js';\r\n\r\n/**\r\n * Auth state stored in storage\r\n */\r\nexport interface AuthState {\r\n /** State parameter (CSRF protection) */\r\n state: string;\r\n /** Nonce parameter (replay attack prevention) */\r\n nonce: string;\r\n /** PKCE code verifier */\r\n codeVerifier: string;\r\n /** Redirect URI used for this auth request */\r\n redirectUri: string;\r\n /** Timestamp when state was created */\r\n createdAt: number;\r\n /** Timestamp when state expires */\r\n expiresAt: number;\r\n}\r\n\r\n/**\r\n * Options for generating auth state\r\n */\r\nexport interface GenerateAuthStateOptions {\r\n /** Redirect URI for this auth request */\r\n redirectUri: string;\r\n /** Code verifier for PKCE */\r\n codeVerifier: string;\r\n /** TTL in seconds (default: 600 = 10 minutes) */\r\n ttlSeconds?: number;\r\n}\r\n\r\n/**\r\n * Storage keys factory\r\n */\r\nexport const STORAGE_KEYS = {\r\n /**\r\n * Auth state key (state-specific)\r\n */\r\n authState: (issuerHash: string, clientIdHash: string, state: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:auth:${state}`,\r\n\r\n /**\r\n * Token storage key\r\n */\r\n tokens: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:tokens`,\r\n\r\n /**\r\n * ID token storage key\r\n */\r\n idToken: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:id_token`,\r\n\r\n /**\r\n * Auth state prefix for cleanup\r\n */\r\n authStatePrefix: (issuerHash: string, clientIdHash: string): string =>\r\n `authrim:${issuerHash}:${clientIdHash}:auth:`,\r\n} as const;\r\n\r\n/**\r\n * State Manager\r\n */\r\nexport class StateManager {\r\n /** Default TTL: 10 minutes */\r\n private static readonly DEFAULT_TTL_SECONDS = 600;\r\n\r\n constructor(\r\n private readonly crypto: CryptoProvider,\r\n private readonly storage: AuthrimStorage,\r\n private readonly issuerHash: string,\r\n private readonly clientIdHash: string\r\n ) {}\r\n\r\n /**\r\n * Generate and store auth state\r\n *\r\n * Creates state, nonce, stores them with the code verifier.\r\n *\r\n * @param options - Generation options\r\n * @returns Generated state string\r\n */\r\n async generateAuthState(options: GenerateAuthStateOptions): Promise<AuthState> {\r\n const ttlSeconds = options.ttlSeconds ?? StateManager.DEFAULT_TTL_SECONDS;\r\n\r\n // Generate random state and nonce (32 bytes each)\r\n const stateBytes = await this.crypto.randomBytes(32);\r\n const nonceBytes = await this.crypto.randomBytes(32);\r\n\r\n const state = base64urlEncode(stateBytes);\r\n const nonce = base64urlEncode(nonceBytes);\r\n\r\n const now = Date.now();\r\n const authState: AuthState = {\r\n state,\r\n nonce,\r\n codeVerifier: options.codeVerifier,\r\n redirectUri: options.redirectUri,\r\n createdAt: now,\r\n expiresAt: now + ttlSeconds * 1000,\r\n };\r\n\r\n // Store in storage\r\n const key = STORAGE_KEYS.authState(this.issuerHash, this.clientIdHash, state);\r\n await this.storage.set(key, JSON.stringify(authState));\r\n\r\n return authState;\r\n }\r\n\r\n /**\r\n * Validate and consume state\r\n *\r\n * Retrieves, validates, and ALWAYS deletes the state (success or failure).\r\n * This ensures replay attack prevention and GC.\r\n *\r\n * @param state - State parameter from callback\r\n * @returns Auth state if valid\r\n * @throws AuthrimError if state is invalid or expired\r\n */\r\n async validateAndConsumeState(state: string): Promise<AuthState> {\r\n const key = STORAGE_KEYS.authState(this.issuerHash, this.clientIdHash, state);\r\n\r\n try {\r\n const stored = await this.storage.get(key);\r\n\r\n if (!stored) {\r\n throw new AuthrimError('invalid_state', 'State not found or already used');\r\n }\r\n\r\n let authState: AuthState;\r\n try {\r\n authState = JSON.parse(stored);\r\n } catch {\r\n throw new AuthrimError('invalid_state', 'Malformed state data');\r\n }\r\n\r\n // Check expiration (no skew - strict)\r\n if (Date.now() > authState.expiresAt) {\r\n throw new AuthrimError('expired_state', 'State has expired');\r\n }\r\n\r\n return authState;\r\n } finally {\r\n // ALWAYS delete (success, failure, or exception)\r\n // This is critical for:\r\n // 1. Replay attack prevention\r\n // 2. GC of used/expired states\r\n await this.storage.remove(key);\r\n }\r\n }\r\n\r\n /**\r\n * Clean up expired states\r\n *\r\n * This is a best-effort cleanup. Only works if storage.getAll() is available.\r\n * The primary GC mechanism is validateAndConsumeState()'s finally delete.\r\n *\r\n * Safe to call at startup or periodically.\r\n */\r\n async cleanupExpiredStates(): Promise<void> {\r\n // Only works if storage supports getAll()\r\n if (!this.storage.getAll) {\r\n return;\r\n }\r\n\r\n const prefix = STORAGE_KEYS.authStatePrefix(this.issuerHash, this.clientIdHash);\r\n const all = await this.storage.getAll();\r\n const now = Date.now();\r\n\r\n for (const [key, value] of Object.entries(all)) {\r\n if (!key.startsWith(prefix)) {\r\n continue;\r\n }\r\n\r\n try {\r\n const authState: AuthState = JSON.parse(value);\r\n if (now > authState.expiresAt) {\r\n await this.storage.remove(key);\r\n }\r\n } catch {\r\n // Parse failure - delete corrupted entry\r\n await this.storage.remove(key);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * JWT Utilities\r\n *\r\n * Note: This module only provides decoding (parsing) functionality.\r\n * JWT signature verification MUST be performed by the server.\r\n * Never trust decoded JWT claims without server-side verification.\r\n */\r\n\r\nimport { base64urlToString } from './base64url.js';\r\nimport type { IdTokenClaims } from '../types/oidc.js';\r\n\r\n/**\r\n * JWT Header\r\n */\r\nexport interface JwtHeader {\r\n alg: string;\r\n typ?: string;\r\n kid?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Decoded JWT structure\r\n */\r\nexport interface DecodedJwt<T = Record<string, unknown>> {\r\n header: JwtHeader;\r\n payload: T;\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Decode a JWT without verifying the signature\r\n *\r\n * WARNING: This function does NOT verify the JWT signature.\r\n * Use this only for reading claims after the token has been\r\n * validated by the authorization server.\r\n *\r\n * @param jwt - JWT string to decode\r\n * @returns Decoded JWT parts\r\n * @throws Error if the JWT format is invalid\r\n */\r\nexport function decodeJwt<T = Record<string, unknown>>(jwt: string): DecodedJwt<T> {\r\n const parts = jwt.split('.');\r\n if (parts.length !== 3) {\r\n throw new Error('Invalid JWT format: expected 3 parts');\r\n }\r\n\r\n const [headerB64, payloadB64, signature] = parts;\r\n\r\n try {\r\n const header = JSON.parse(base64urlToString(headerB64)) as JwtHeader;\r\n const payload = JSON.parse(base64urlToString(payloadB64)) as T;\r\n\r\n return {\r\n header,\r\n payload,\r\n signature,\r\n };\r\n } catch {\r\n throw new Error('Invalid JWT format: failed to decode');\r\n }\r\n}\r\n\r\n/**\r\n * Decode an ID token and extract claims\r\n *\r\n * WARNING: This function does NOT verify the ID token.\r\n * The token MUST be verified by the authorization server before use.\r\n *\r\n * @param idToken - ID token string\r\n * @returns ID token claims\r\n */\r\nexport function decodeIdToken(idToken: string): IdTokenClaims {\r\n const decoded = decodeJwt<IdTokenClaims>(idToken);\r\n return decoded.payload;\r\n}\r\n\r\n/**\r\n * Check if a JWT is expired\r\n *\r\n * @param jwt - Decoded JWT payload with exp claim\r\n * @param skewSeconds - Clock skew tolerance in seconds (default: 0)\r\n * @returns true if expired\r\n */\r\nexport function isJwtExpired(payload: { exp?: number }, skewSeconds: number = 0): boolean {\r\n if (payload.exp === undefined) {\r\n return false; // No expiration\r\n }\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n return payload.exp + skewSeconds < now;\r\n}\r\n\r\n/**\r\n * Get the nonce claim from an ID token\r\n *\r\n * @param idToken - ID token string\r\n * @returns nonce value or undefined\r\n */\r\nexport function getIdTokenNonce(idToken: string): string | undefined {\r\n try {\r\n const claims = decodeIdToken(idToken);\r\n return claims.nonce;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n","/**\r\n * Authorization Code Flow\r\n *\r\n * Implements OAuth 2.0 Authorization Code Flow with PKCE.\r\n * Uses 2-step pattern: buildAuthorizationUrl() + handleCallback()\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet, TokenResponse } from '../types/token.js';\r\nimport type { AuthState } from './state.js';\r\nimport type { PKCEPair } from './pkce.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { getIdTokenNonce } from '../utils/jwt.js';\r\n\r\n/**\r\n * Options for building authorization URL\r\n */\r\nexport interface BuildAuthorizationUrlOptions {\r\n /** Redirect URI (required) */\r\n redirectUri: string;\r\n /** Scopes to request (default: 'openid profile') */\r\n scope?: string;\r\n /** Prompt behavior */\r\n prompt?: 'none' | 'login' | 'consent' | 'select_account';\r\n /** Hint about the login identifier */\r\n loginHint?: string;\r\n /** Requested Authentication Context Class Reference values */\r\n acrValues?: string;\r\n /** Additional custom parameters */\r\n extraParams?: Record<string, string>;\r\n /**\r\n * Expose state/nonce in result (for SSR/external storage)\r\n *\r\n * Default: false (security: state/nonce stay internal)\r\n * Set to true only when you need to store state externally\r\n * (e.g., cookie, server-side session)\r\n */\r\n exposeState?: boolean;\r\n}\r\n\r\n/**\r\n * Result of buildAuthorizationUrl\r\n */\r\nexport interface AuthorizationUrlResult {\r\n /** Authorization URL to redirect to */\r\n url: string;\r\n /** State parameter (only if exposeState: true) */\r\n state?: string;\r\n /** Nonce parameter (only if exposeState: true) */\r\n nonce?: string;\r\n}\r\n\r\n/**\r\n * Internal authorization context (stored by StateManager)\r\n */\r\nexport interface AuthorizationContext {\r\n /** Auth state object */\r\n authState: AuthState;\r\n /** PKCE pair */\r\n pkce: PKCEPair;\r\n}\r\n\r\n/**\r\n * Options for exchanging authorization code\r\n */\r\nexport interface ExchangeCodeOptions {\r\n /** Authorization code */\r\n code: string;\r\n /** State parameter (for validation) */\r\n state: string;\r\n /** Redirect URI used in authorization request */\r\n redirectUri: string;\r\n /** Code verifier for PKCE */\r\n codeVerifier: string;\r\n /** Nonce to validate in ID token */\r\n nonce: string;\r\n}\r\n\r\n/**\r\n * Authorization Code Flow helper\r\n */\r\nexport class AuthorizationCodeFlow {\r\n constructor(\r\n private readonly http: HttpClient,\r\n private readonly clientId: string\r\n ) {}\r\n\r\n /**\r\n * Build authorization URL\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param authState - Auth state from StateManager\r\n * @param pkce - PKCE pair\r\n * @param options - Authorization options\r\n * @returns Authorization URL result\r\n */\r\n buildAuthorizationUrl(\r\n discovery: OIDCDiscoveryDocument,\r\n authState: AuthState,\r\n pkce: PKCEPair,\r\n options: BuildAuthorizationUrlOptions\r\n ): AuthorizationUrlResult {\r\n const endpoint = discovery.authorization_endpoint;\r\n const params = new URLSearchParams();\r\n\r\n // Required parameters\r\n params.set('client_id', this.clientId);\r\n params.set('response_type', 'code');\r\n params.set('redirect_uri', options.redirectUri);\r\n params.set('state', authState.state);\r\n params.set('nonce', authState.nonce);\r\n\r\n // PKCE parameters\r\n params.set('code_challenge', pkce.codeChallenge);\r\n params.set('code_challenge_method', pkce.codeChallengeMethod);\r\n\r\n // Scopes\r\n const scope = options.scope ?? 'openid profile';\r\n params.set('scope', scope);\r\n\r\n // Optional parameters\r\n if (options.prompt) {\r\n params.set('prompt', options.prompt);\r\n }\r\n if (options.loginHint) {\r\n params.set('login_hint', options.loginHint);\r\n }\r\n if (options.acrValues) {\r\n params.set('acr_values', options.acrValues);\r\n }\r\n\r\n // Extra custom parameters (with security parameter protection)\r\n if (options.extraParams) {\r\n // Security parameters that MUST NOT be overwritten\r\n const protectedParams = new Set([\r\n 'client_id',\r\n 'response_type',\r\n 'redirect_uri',\r\n 'state',\r\n 'nonce',\r\n 'code_challenge',\r\n 'code_challenge_method',\r\n 'scope',\r\n ]);\r\n\r\n for (const [key, value] of Object.entries(options.extraParams)) {\r\n if (protectedParams.has(key.toLowerCase())) {\r\n // Silently ignore attempts to override security parameters\r\n // This prevents CSRF, PKCE bypass, and other attacks\r\n continue;\r\n }\r\n params.set(key, value);\r\n }\r\n }\r\n\r\n const url = `${endpoint}?${params.toString()}`;\r\n\r\n // Build result\r\n const result: AuthorizationUrlResult = { url };\r\n\r\n if (options.exposeState) {\r\n result.state = authState.state;\r\n result.nonce = authState.nonce;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Parse callback URL and extract code/state\r\n *\r\n * @param callbackUrl - Callback URL or query string\r\n * @returns Parsed code and state\r\n * @throws AuthrimError if code or state is missing, or if error is present\r\n */\r\n parseCallback(callbackUrl: string): { code: string; state: string } {\r\n let searchParams: URLSearchParams;\r\n\r\n // Support both full URL and query string\r\n if (callbackUrl.includes('?')) {\r\n const url = callbackUrl.startsWith('http')\r\n ? new URL(callbackUrl)\r\n : new URL(callbackUrl, 'https://dummy.local');\r\n searchParams = url.searchParams;\r\n } else {\r\n searchParams = new URLSearchParams(callbackUrl);\r\n }\r\n\r\n // Check for OAuth error response\r\n const error = searchParams.get('error');\r\n if (error) {\r\n const errorDescription = searchParams.get('error_description') ?? 'Authorization failed';\r\n throw new AuthrimError('oauth_error', errorDescription, {\r\n details: {\r\n error,\r\n error_description: errorDescription,\r\n error_uri: searchParams.get('error_uri'),\r\n },\r\n });\r\n }\r\n\r\n // Extract code and state\r\n const code = searchParams.get('code');\r\n const state = searchParams.get('state');\r\n\r\n if (!code) {\r\n throw new AuthrimError('missing_code', 'Authorization code not found in callback');\r\n }\r\n if (!state) {\r\n throw new AuthrimError('missing_state', 'State parameter not found in callback');\r\n }\r\n\r\n return { code, state };\r\n }\r\n\r\n /**\r\n * Exchange authorization code for tokens\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Exchange options\r\n * @returns Token set\r\n * @throws AuthrimError if exchange fails or nonce validation fails\r\n */\r\n async exchangeCode(\r\n discovery: OIDCDiscoveryDocument,\r\n options: ExchangeCodeOptions\r\n ): Promise<TokenSet> {\r\n const tokenEndpoint = discovery.token_endpoint;\r\n\r\n // Build token request body\r\n const body = new URLSearchParams({\r\n grant_type: 'authorization_code',\r\n client_id: this.clientId,\r\n code: options.code,\r\n redirect_uri: options.redirectUri,\r\n code_verifier: options.codeVerifier,\r\n });\r\n\r\n // Make token request\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n throw new AuthrimError('token_error', 'Token exchange failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Validate nonce in ID token\r\n if (tokenResponse.id_token) {\r\n const idTokenNonce = getIdTokenNonce(tokenResponse.id_token);\r\n if (idTokenNonce !== options.nonce) {\r\n // Do not include nonce values in error details (security sensitive)\r\n throw new AuthrimError('nonce_mismatch', 'ID token nonce does not match expected value');\r\n }\r\n }\r\n\r\n // Calculate expiresAt (epoch seconds)\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600; // Default to 1 hour if not provided\r\n\r\n // Build token set\r\n const tokenSet: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n return tokenSet;\r\n }\r\n}\r\n","/**\r\n * Token Types\r\n */\r\n\r\n/**\r\n * Token set returned from token endpoint\r\n *\r\n * Note: expiresAt is epoch seconds (not milliseconds)\r\n */\r\nexport interface TokenSet {\r\n /** Access token */\r\n accessToken: string;\r\n /** Refresh token (if provided) */\r\n refreshToken?: string;\r\n /** ID token (if openid scope was requested) */\r\n idToken?: string;\r\n /** Token type (always 'Bearer' for OAuth 2.0) */\r\n tokenType: 'Bearer';\r\n /**\r\n * Token expiration time as epoch seconds\r\n *\r\n * Calculated from expires_in at token exchange time:\r\n * expiresAt = Math.floor(Date.now() / 1000) + expires_in\r\n */\r\n expiresAt: number;\r\n /** Scope granted (may differ from requested scope) */\r\n scope?: string;\r\n}\r\n\r\n/**\r\n * Token endpoint response (raw)\r\n */\r\nexport interface TokenEndpointResponse {\r\n access_token: string;\r\n token_type: string;\r\n expires_in?: number;\r\n refresh_token?: string;\r\n id_token?: string;\r\n scope?: string;\r\n}\r\n\r\n/**\r\n * Alias for TokenEndpointResponse\r\n */\r\nexport type TokenResponse = TokenEndpointResponse;\r\n\r\n/**\r\n * Refresh token endpoint response (raw)\r\n */\r\nexport interface RefreshTokenResponse extends TokenEndpointResponse {\r\n // Refresh token rotation may return a new refresh token\r\n}\r\n\r\n/**\r\n * Token exchange request (RFC 8693)\r\n */\r\nexport interface TokenExchangeRequest {\r\n /** The subject token to exchange */\r\n subjectToken: string;\r\n /** Subject token type (default: access_token) */\r\n subjectTokenType?: 'access_token' | 'refresh_token' | 'id_token';\r\n /** Target audience for the new token */\r\n audience?: string;\r\n /** Requested scope for the new token */\r\n scope?: string;\r\n /** Requested token type (default: access_token) */\r\n requestedTokenType?: 'access_token' | 'refresh_token' | 'id_token';\r\n /** Actor token (for delegation) */\r\n actorToken?: string;\r\n /** Actor token type */\r\n actorTokenType?: 'access_token' | 'id_token';\r\n}\r\n\r\n/**\r\n * Token exchange response (RFC 8693)\r\n *\r\n * Extends standard token response with issued_token_type\r\n */\r\nexport interface TokenExchangeResponse extends TokenEndpointResponse {\r\n /** URI of the issued token type */\r\n issued_token_type: string;\r\n}\r\n\r\n/**\r\n * Token type URIs (RFC 8693)\r\n */\r\nexport const TOKEN_TYPE_URIS = {\r\n access_token: 'urn:ietf:params:oauth:token-type:access_token',\r\n refresh_token: 'urn:ietf:params:oauth:token-type:refresh_token',\r\n id_token: 'urn:ietf:params:oauth:token-type:id_token',\r\n} as const;\r\n\r\n/**\r\n * Token type URI type\r\n */\r\nexport type TokenTypeUri = (typeof TOKEN_TYPE_URIS)[keyof typeof TOKEN_TYPE_URIS];\r\n\r\n/**\r\n * Token exchange result\r\n */\r\nexport interface TokenExchangeResult {\r\n /** Token set from exchange */\r\n tokens: TokenSet;\r\n /** Issued token type URI */\r\n issuedTokenType: TokenTypeUri | string;\r\n}\r\n\r\n/**\r\n * Convert raw token response to TokenSet\r\n */\r\nexport function toTokenSet(response: TokenEndpointResponse): TokenSet {\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresIn = response.expires_in ?? 3600; // Default 1 hour\r\n\r\n return {\r\n accessToken: response.access_token,\r\n refreshToken: response.refresh_token,\r\n idToken: response.id_token,\r\n tokenType: 'Bearer',\r\n expiresAt: now + expiresIn,\r\n scope: response.scope,\r\n };\r\n}\r\n","/**\r\n * Token Manager\r\n *\r\n * Manages token storage, retrieval, and automatic refresh.\r\n * Implements in-flight request coalescing for concurrent refresh requests.\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type {\r\n TokenSet,\r\n TokenResponse,\r\n TokenExchangeRequest,\r\n TokenExchangeResponse,\r\n TokenExchangeResult,\r\n} from '../types/token.js';\r\nimport { TOKEN_TYPE_URIS } from '../types/token.js';\r\nimport type { EventEmitter } from '../events/emitter.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\nimport { STORAGE_KEYS } from '../auth/state.js';\r\n\r\n/**\r\n * Token manager options\r\n */\r\nexport interface TokenManagerOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Storage provider */\r\n storage: AuthrimStorage;\r\n /** Client ID */\r\n clientId: string;\r\n /** Issuer hash for storage keys */\r\n issuerHash: string;\r\n /** Client ID hash for storage keys */\r\n clientIdHash: string;\r\n /** Refresh skew in seconds (default: 30) */\r\n refreshSkewSeconds?: number;\r\n /** Event emitter for token events */\r\n eventEmitter?: EventEmitter;\r\n}\r\n\r\n/**\r\n * Token Manager\r\n *\r\n * Handles token storage, retrieval, and automatic refresh with\r\n * concurrent request coalescing.\r\n */\r\nexport class TokenManager {\r\n private readonly http: HttpClient;\r\n private readonly storage: AuthrimStorage;\r\n private readonly clientId: string;\r\n private readonly issuerHash: string;\r\n private readonly clientIdHash: string;\r\n private readonly refreshSkewSeconds: number;\r\n private readonly eventEmitter?: EventEmitter;\r\n\r\n /** In-flight refresh promise for request coalescing */\r\n private refreshPromise: Promise<TokenSet> | null = null;\r\n\r\n /** Discovery document (set externally) */\r\n private discovery: OIDCDiscoveryDocument | null = null;\r\n\r\n /** Default refresh skew: 30 seconds */\r\n private static readonly DEFAULT_REFRESH_SKEW_SECONDS = 30;\r\n\r\n constructor(options: TokenManagerOptions) {\r\n this.http = options.http;\r\n this.storage = options.storage;\r\n this.clientId = options.clientId;\r\n this.issuerHash = options.issuerHash;\r\n this.clientIdHash = options.clientIdHash;\r\n this.refreshSkewSeconds =\r\n options.refreshSkewSeconds ?? TokenManager.DEFAULT_REFRESH_SKEW_SECONDS;\r\n this.eventEmitter = options.eventEmitter;\r\n }\r\n\r\n /**\r\n * Set discovery document\r\n */\r\n setDiscovery(discovery: OIDCDiscoveryDocument): void {\r\n this.discovery = discovery;\r\n }\r\n\r\n /**\r\n * Get storage key for tokens\r\n */\r\n private get tokenKey(): string {\r\n return STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n }\r\n\r\n /**\r\n * Get storage key for ID token\r\n */\r\n private get idTokenKey(): string {\r\n return STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n }\r\n\r\n /**\r\n * Get current tokens from storage\r\n *\r\n * @returns Token set or null if not found\r\n */\r\n async getTokens(): Promise<TokenSet | null> {\r\n const stored = await this.storage.get(this.tokenKey);\r\n if (!stored) {\r\n return null;\r\n }\r\n\r\n try {\r\n return JSON.parse(stored) as TokenSet;\r\n } catch {\r\n // Corrupted data - clear and return null\r\n await this.clearTokens();\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Save tokens to storage\r\n *\r\n * @param tokens - Token set to save\r\n */\r\n async saveTokens(tokens: TokenSet): Promise<void> {\r\n await this.storage.set(this.tokenKey, JSON.stringify(tokens));\r\n\r\n // Also save ID token separately for logout\r\n if (tokens.idToken) {\r\n await this.storage.set(this.idTokenKey, tokens.idToken);\r\n }\r\n }\r\n\r\n /**\r\n * Clear all tokens from storage\r\n */\r\n async clearTokens(): Promise<void> {\r\n await this.storage.remove(this.tokenKey);\r\n await this.storage.remove(this.idTokenKey);\r\n }\r\n\r\n /**\r\n * Get access token, refreshing if necessary\r\n *\r\n * This method coalesces concurrent refresh requests - if multiple\r\n * calls are made while a refresh is in-flight, they all share the\r\n * same refresh operation.\r\n *\r\n * @returns Access token\r\n * @throws AuthrimError if no tokens available or refresh fails\r\n */\r\n async getAccessToken(): Promise<string> {\r\n const tokens = await this.getTokens();\r\n\r\n if (!tokens) {\r\n throw new AuthrimError('no_tokens', 'No tokens available. Please authenticate first.');\r\n }\r\n\r\n // Check if token needs refresh (with skew)\r\n if (this.shouldRefresh(tokens)) {\r\n if (!tokens.refreshToken) {\r\n throw new AuthrimError(\r\n 'token_expired',\r\n 'Access token expired and no refresh token available'\r\n );\r\n }\r\n return this.refreshWithLock(tokens.refreshToken);\r\n }\r\n\r\n return tokens.accessToken;\r\n }\r\n\r\n /**\r\n * Get ID token\r\n *\r\n * @returns ID token or null\r\n */\r\n async getIdToken(): Promise<string | null> {\r\n const tokens = await this.getTokens();\r\n return tokens?.idToken ?? null;\r\n }\r\n\r\n /**\r\n * Check if token needs refresh\r\n *\r\n * @param tokens - Token set to check\r\n * @returns True if token should be refreshed\r\n */\r\n private shouldRefresh(tokens: TokenSet): boolean {\r\n const now = Math.floor(Date.now() / 1000);\r\n return tokens.expiresAt - this.refreshSkewSeconds <= now;\r\n }\r\n\r\n /**\r\n * Refresh token with in-flight request coalescing\r\n *\r\n * If a refresh is already in progress, wait for it instead of\r\n * starting a new one.\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @returns Access token from new token set\r\n */\r\n private async refreshWithLock(refreshToken: string): Promise<string> {\r\n // If refresh is already in-flight, wait for it\r\n if (this.refreshPromise) {\r\n const tokens = await this.refreshPromise;\r\n return tokens.accessToken;\r\n }\r\n\r\n // Start new refresh operation\r\n this.refreshPromise = this.doRefreshWithRetry(refreshToken);\r\n\r\n try {\r\n const tokens = await this.refreshPromise;\r\n return tokens.accessToken;\r\n } finally {\r\n // ALWAYS clear promise (success or failure)\r\n this.refreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Perform refresh with single retry for network errors\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @param attemptedRetry - Whether retry has been attempted (stack-local)\r\n * @returns New token set\r\n */\r\n private async doRefreshWithRetry(\r\n refreshToken: string,\r\n attemptedRetry = false\r\n ): Promise<TokenSet> {\r\n try {\r\n return await this.doRefresh(refreshToken);\r\n } catch (error) {\r\n // Retry once for network errors only\r\n if (this.isRetryableError(error) && !attemptedRetry) {\r\n return this.doRefreshWithRetry(refreshToken, true);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Perform the actual token refresh\r\n *\r\n * @param refreshToken - Refresh token to use\r\n * @returns New token set\r\n */\r\n private async doRefresh(refreshToken: string): Promise<TokenSet> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not set');\r\n }\r\n\r\n const tokenEndpoint = this.discovery.token_endpoint;\r\n\r\n // Build refresh request\r\n const body = new URLSearchParams({\r\n grant_type: 'refresh_token',\r\n client_id: this.clientId,\r\n refresh_token: refreshToken,\r\n });\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n const authrimError = new AuthrimError('network_error', 'Token refresh request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n const authrimError = new AuthrimError('refresh_error', 'Token refresh failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Calculate expiresAt\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600;\r\n\r\n // Build new token set (preserve old refresh token if new one not provided)\r\n const newTokens: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token ?? refreshToken,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n // Save new tokens\r\n await this.saveTokens(newTokens);\r\n\r\n // Emit event\r\n this.eventEmitter?.emit('token:refreshed', { tokens: newTokens });\r\n\r\n return newTokens;\r\n }\r\n\r\n /**\r\n * Check if error is retryable (network errors only)\r\n */\r\n private isRetryableError(error: unknown): boolean {\r\n if (error instanceof AuthrimError) {\r\n return error.code === 'network_error';\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n *\r\n * @returns True if valid tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n const tokens = await this.getTokens();\r\n if (!tokens) {\r\n return false;\r\n }\r\n\r\n // Check if access token is expired (without skew)\r\n const now = Math.floor(Date.now() / 1000);\r\n if (tokens.expiresAt <= now) {\r\n // Token expired - check if we have refresh token\r\n return !!tokens.refreshToken;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Exchange a token using RFC 8693 Token Exchange\r\n *\r\n * This allows exchanging tokens for different audiences or scopes,\r\n * delegation scenarios, and cross-service token acquisition.\r\n *\r\n * @param request - Token exchange request parameters\r\n * @returns Token exchange result with new tokens and issued token type\r\n * @throws AuthrimError if exchange fails\r\n */\r\n async exchangeToken(request: TokenExchangeRequest): Promise<TokenExchangeResult> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not set');\r\n }\r\n\r\n const tokenEndpoint = this.discovery.token_endpoint;\r\n\r\n // Map short token type names to URIs\r\n const subjectTokenType = this.mapTokenTypeToUri(request.subjectTokenType ?? 'access_token');\r\n\r\n // Build token exchange request\r\n const body = new URLSearchParams({\r\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\r\n client_id: this.clientId,\r\n subject_token: request.subjectToken,\r\n subject_token_type: subjectTokenType,\r\n });\r\n\r\n // Optional parameters\r\n if (request.audience) {\r\n body.set('audience', request.audience);\r\n }\r\n if (request.scope) {\r\n body.set('scope', request.scope);\r\n }\r\n if (request.requestedTokenType) {\r\n body.set('requested_token_type', this.mapTokenTypeToUri(request.requestedTokenType));\r\n }\r\n if (request.actorToken) {\r\n body.set('actor_token', request.actorToken);\r\n if (request.actorTokenType) {\r\n body.set('actor_token_type', this.mapTokenTypeToUri(request.actorTokenType));\r\n }\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<TokenExchangeResponse>(tokenEndpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n const authrimError = new AuthrimError('network_error', 'Token exchange request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as unknown as Record<string, unknown>;\r\n const authrimError = new AuthrimError('token_exchange_error', 'Token exchange failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n this.eventEmitter?.emit('token:error', { error: authrimError });\r\n throw authrimError;\r\n }\r\n\r\n const tokenResponse = response.data;\r\n\r\n // Calculate expiresAt\r\n const now = Math.floor(Date.now() / 1000);\r\n const expiresAt = tokenResponse.expires_in ? now + tokenResponse.expires_in : now + 3600;\r\n\r\n const tokens: TokenSet = {\r\n accessToken: tokenResponse.access_token,\r\n tokenType: (tokenResponse.token_type as 'Bearer') ?? 'Bearer',\r\n expiresAt,\r\n refreshToken: tokenResponse.refresh_token,\r\n idToken: tokenResponse.id_token,\r\n scope: tokenResponse.scope,\r\n };\r\n\r\n const result: TokenExchangeResult = {\r\n tokens,\r\n issuedTokenType: tokenResponse.issued_token_type,\r\n };\r\n\r\n // Emit event\r\n this.eventEmitter?.emit('token:exchanged', {\r\n tokens,\r\n issuedTokenType: tokenResponse.issued_token_type,\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Map short token type to URI (RFC 8693)\r\n */\r\n private mapTokenTypeToUri(type: 'access_token' | 'refresh_token' | 'id_token'): string {\r\n return TOKEN_TYPE_URIS[type];\r\n }\r\n}\r\n","/**\r\n * Token Introspection (RFC 7662)\r\n *\r\n * Implements OAuth 2.0 Token Introspection to validate tokens server-side.\r\n * https://datatracker.ietf.org/doc/html/rfc7662\r\n */\r\n\r\nimport type { HttpClient, OAuthErrorResponse } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Token type hint for introspection\r\n */\r\nexport type IntrospectionTokenTypeHint = 'access_token' | 'refresh_token';\r\n\r\n/**\r\n * Token introspection response (RFC 7662)\r\n */\r\nexport interface IntrospectionResponse {\r\n /** Whether the token is active */\r\n active: boolean;\r\n /** Space-separated list of scopes (if active) */\r\n scope?: string;\r\n /** Client identifier (if active) */\r\n client_id?: string;\r\n /** Human-readable username (if active) */\r\n username?: string;\r\n /** Token type (e.g., \"Bearer\") */\r\n token_type?: string;\r\n /** Expiration time (Unix timestamp) */\r\n exp?: number;\r\n /** Issued at time (Unix timestamp) */\r\n iat?: number;\r\n /** Not before time (Unix timestamp) */\r\n nbf?: number;\r\n /** Subject identifier */\r\n sub?: string;\r\n /** Audience (single string or array) */\r\n aud?: string | string[];\r\n /** Issuer identifier */\r\n iss?: string;\r\n /** JWT ID */\r\n jti?: string;\r\n /** Additional claims */\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Token introspection options\r\n */\r\nexport interface IntrospectTokenOptions {\r\n /** Token to introspect */\r\n token: string;\r\n /** Hint about the token type (optional, helps server optimize lookup) */\r\n tokenTypeHint?: IntrospectionTokenTypeHint;\r\n}\r\n\r\n/**\r\n * Token introspector options\r\n */\r\nexport interface TokenIntrospectorOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n}\r\n\r\n/**\r\n * Token Introspector\r\n *\r\n * Handles token introspection requests to the authorization server.\r\n */\r\nexport class TokenIntrospector {\r\n private readonly http: HttpClient;\r\n private readonly clientId: string;\r\n\r\n constructor(options: TokenIntrospectorOptions) {\r\n this.http = options.http;\r\n this.clientId = options.clientId;\r\n }\r\n\r\n /**\r\n * Introspect a token\r\n *\r\n * Per RFC 7662, the introspection endpoint returns:\r\n * - { active: true, ... } for valid tokens with additional metadata\r\n * - { active: false } for invalid, expired, or revoked tokens\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Introspection options\r\n * @returns Introspection response\r\n * @throws AuthrimError if introspection endpoint is not available or request fails\r\n */\r\n async introspect(\r\n discovery: OIDCDiscoveryDocument,\r\n options: IntrospectTokenOptions\r\n ): Promise<IntrospectionResponse> {\r\n const endpoint = discovery.introspection_endpoint;\r\n\r\n if (!endpoint) {\r\n throw new AuthrimError(\r\n 'no_introspection_endpoint',\r\n 'Authorization server does not support token introspection'\r\n );\r\n }\r\n\r\n return this.introspectWithEndpoint(endpoint, options);\r\n }\r\n\r\n /**\r\n * Introspect a token directly using the endpoint URL\r\n *\r\n * Use this when you have the endpoint URL but not the full discovery document.\r\n *\r\n * @param endpoint - Introspection endpoint URL\r\n * @param options - Introspection options\r\n * @returns Introspection response\r\n * @throws AuthrimError if request fails\r\n */\r\n async introspectWithEndpoint(\r\n endpoint: string,\r\n options: IntrospectTokenOptions\r\n ): Promise<IntrospectionResponse> {\r\n // Build introspection request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<IntrospectionResponse | OAuthErrorResponse>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token introspection request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('introspection_error', 'Token introspection failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n return response.data as IntrospectionResponse;\r\n }\r\n\r\n /**\r\n * Check if a token is active\r\n *\r\n * Convenience method that returns only the active status.\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param token - Token to check\r\n * @returns True if token is active\r\n */\r\n async isActive(discovery: OIDCDiscoveryDocument, token: string): Promise<boolean> {\r\n const result = await this.introspect(discovery, { token });\r\n return result.active;\r\n }\r\n}\r\n","/**\r\n * Token Revocation (RFC 7009)\r\n *\r\n * Implements OAuth 2.0 Token Revocation to explicitly invalidate tokens.\r\n * https://datatracker.ietf.org/doc/html/rfc7009\r\n */\r\n\r\nimport type { HttpClient, OAuthErrorResponse } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Token type hint for revocation\r\n */\r\nexport type TokenTypeHint = 'access_token' | 'refresh_token';\r\n\r\n/**\r\n * Token revocation options\r\n */\r\nexport interface RevokeTokenOptions {\r\n /** Token to revoke */\r\n token: string;\r\n /** Hint about the token type (optional, helps server optimize lookup) */\r\n tokenTypeHint?: TokenTypeHint;\r\n}\r\n\r\n/**\r\n * Token revoker options\r\n */\r\nexport interface TokenRevokerOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n}\r\n\r\n/**\r\n * Token Revoker\r\n *\r\n * Handles token revocation requests to the authorization server.\r\n */\r\nexport class TokenRevoker {\r\n private readonly http: HttpClient;\r\n private readonly clientId: string;\r\n\r\n constructor(options: TokenRevokerOptions) {\r\n this.http = options.http;\r\n this.clientId = options.clientId;\r\n }\r\n\r\n /**\r\n * Revoke a token\r\n *\r\n * Per RFC 7009, the revocation endpoint:\r\n * - Returns 200 OK on success (even if token was already invalid)\r\n * - Returns 400 for invalid requests\r\n * - Returns 503 if temporarily unavailable\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Revocation options\r\n * @throws AuthrimError if revocation endpoint is not available or request fails\r\n */\r\n async revoke(discovery: OIDCDiscoveryDocument, options: RevokeTokenOptions): Promise<void> {\r\n const endpoint = discovery.revocation_endpoint;\r\n\r\n if (!endpoint) {\r\n throw new AuthrimError(\r\n 'no_revocation_endpoint',\r\n 'Authorization server does not support token revocation'\r\n );\r\n }\r\n\r\n // Build revocation request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<OAuthErrorResponse | void>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token revocation request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n // RFC 7009: 200 OK means success (even if token was already invalid)\r\n if (response.ok) {\r\n return;\r\n }\r\n\r\n // Handle error response\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('revocation_error', 'Token revocation failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Revoke a token directly using the endpoint URL\r\n *\r\n * Use this when you have the endpoint URL but not the full discovery document.\r\n *\r\n * @param endpoint - Revocation endpoint URL\r\n * @param options - Revocation options\r\n * @throws AuthrimError if request fails\r\n */\r\n async revokeWithEndpoint(endpoint: string, options: RevokeTokenOptions): Promise<void> {\r\n // Build revocation request\r\n const body = new URLSearchParams({\r\n client_id: this.clientId,\r\n token: options.token,\r\n });\r\n\r\n if (options.tokenTypeHint) {\r\n body.set('token_type_hint', options.tokenTypeHint);\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<OAuthErrorResponse | void>(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: body.toString(),\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Token revocation request failed', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (response.ok) {\r\n return;\r\n }\r\n\r\n const errorData = response.data as OAuthErrorResponse | undefined;\r\n throw new AuthrimError('revocation_error', 'Token revocation failed', {\r\n details: {\r\n status: response.status,\r\n error: errorData?.error,\r\n error_description: errorData?.error_description,\r\n },\r\n });\r\n }\r\n}\r\n","/**\r\n * Logout Handler\r\n *\r\n * Implements RP-Initiated Logout (OpenID Connect RP-Initiated Logout 1.0)\r\n * https://openid.net/specs/openid-connect-rpinitiated-1_0.html\r\n */\r\n\r\nimport type { AuthrimStorage } from '../providers/storage.js';\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet } from '../types/token.js';\r\nimport type { EventEmitter } from '../events/emitter.js';\r\nimport type { EndpointOverrides } from '../client/config.js';\r\nimport { STORAGE_KEYS } from '../auth/state.js';\r\nimport { TokenRevoker } from '../token/revocation.js';\r\n\r\n/**\r\n * Logout options\r\n */\r\nexport interface LogoutOptions {\r\n /** URI to redirect to after logout */\r\n postLogoutRedirectUri?: string;\r\n /** ID token hint (optional, uses stored ID token if not provided) */\r\n idTokenHint?: string;\r\n /** State parameter for post-logout redirect */\r\n state?: string;\r\n /** Whether to revoke tokens before logout (requires revocation_endpoint) */\r\n revokeTokens?: boolean;\r\n}\r\n\r\n/**\r\n * Logout result\r\n */\r\nexport interface LogoutResult {\r\n /** Logout URL to redirect to (if IdP supports end_session_endpoint) */\r\n logoutUrl?: string;\r\n /** True if only local logout was performed (IdP doesn't support RP-Initiated Logout) */\r\n localOnly: boolean;\r\n /** Token revocation result (if revokeTokens was true) */\r\n revocation?: {\r\n /** Whether revocation was attempted */\r\n attempted: boolean;\r\n /** Whether access token revocation succeeded */\r\n accessTokenRevoked?: boolean;\r\n /** Whether refresh token revocation succeeded */\r\n refreshTokenRevoked?: boolean;\r\n /** Error if revocation failed (logout still proceeds) */\r\n error?: Error;\r\n };\r\n}\r\n\r\n/**\r\n * Logout handler options\r\n */\r\nexport interface LogoutHandlerOptions {\r\n /** Storage provider */\r\n storage: AuthrimStorage;\r\n /** HTTP client (for token revocation) */\r\n http: HttpClient;\r\n /** Client ID */\r\n clientId: string;\r\n /** Issuer hash for storage keys */\r\n issuerHash: string;\r\n /** Client ID hash for storage keys */\r\n clientIdHash: string;\r\n /** Event emitter */\r\n eventEmitter?: EventEmitter;\r\n /** Endpoint overrides */\r\n endpoints?: EndpointOverrides;\r\n}\r\n\r\n/**\r\n * Logout Handler\r\n */\r\nexport class LogoutHandler {\r\n private readonly storage: AuthrimStorage;\r\n private readonly clientId: string;\r\n private readonly issuerHash: string;\r\n private readonly clientIdHash: string;\r\n private readonly eventEmitter?: EventEmitter;\r\n private readonly endpoints?: EndpointOverrides;\r\n private readonly tokenRevoker: TokenRevoker;\r\n\r\n constructor(options: LogoutHandlerOptions) {\r\n this.storage = options.storage;\r\n this.clientId = options.clientId;\r\n this.issuerHash = options.issuerHash;\r\n this.clientIdHash = options.clientIdHash;\r\n this.eventEmitter = options.eventEmitter;\r\n this.endpoints = options.endpoints;\r\n this.tokenRevoker = new TokenRevoker({\r\n http: options.http,\r\n clientId: options.clientId,\r\n });\r\n }\r\n\r\n /**\r\n * Perform logout\r\n *\r\n * 1. Optionally revokes tokens at the authorization server (if revokeTokens=true)\r\n * 2. Clears local tokens (always)\r\n * 3. Emits session:ended event\r\n * 4. Builds logout URL if IdP supports end_session_endpoint\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param options - Logout options\r\n * @returns Logout result\r\n */\r\n async logout(\r\n discovery: OIDCDiscoveryDocument | null,\r\n options?: LogoutOptions\r\n ): Promise<LogoutResult> {\r\n // Get stored tokens BEFORE clearing (for revocation and logout URL)\r\n const storedIdToken = await this.getStoredIdToken();\r\n const storedTokens = await this.getStoredTokens();\r\n\r\n // Revoke tokens if requested\r\n let revocationResult: LogoutResult['revocation'];\r\n if (options?.revokeTokens && discovery?.revocation_endpoint && storedTokens) {\r\n revocationResult = await this.revokeTokens(discovery, storedTokens);\r\n }\r\n\r\n // Clear local tokens\r\n await this.clearTokens();\r\n\r\n // Emit session ended event\r\n this.eventEmitter?.emit('session:ended', { reason: 'logout' });\r\n\r\n // Determine end_session_endpoint\r\n // Priority: config override > discovery > null\r\n let endSessionEndpoint: string | null | undefined;\r\n\r\n if (this.endpoints?.endSession !== undefined) {\r\n // Explicit override (can be null to disable)\r\n endSessionEndpoint = this.endpoints.endSession;\r\n } else if (discovery) {\r\n endSessionEndpoint = discovery.end_session_endpoint;\r\n }\r\n\r\n // If no end_session_endpoint, return local-only logout\r\n if (!endSessionEndpoint) {\r\n return { localOnly: true, revocation: revocationResult };\r\n }\r\n\r\n // Build logout URL (use stored token if not provided in options)\r\n const idToken = options?.idTokenHint ?? storedIdToken;\r\n\r\n const params = new URLSearchParams({\r\n client_id: this.clientId,\r\n });\r\n\r\n if (idToken) {\r\n params.set('id_token_hint', idToken);\r\n }\r\n if (options?.postLogoutRedirectUri) {\r\n params.set('post_logout_redirect_uri', options.postLogoutRedirectUri);\r\n }\r\n if (options?.state) {\r\n params.set('state', options.state);\r\n }\r\n\r\n return {\r\n logoutUrl: `${endSessionEndpoint}?${params.toString()}`,\r\n localOnly: false,\r\n revocation: revocationResult,\r\n };\r\n }\r\n\r\n /**\r\n * Revoke tokens at the authorization server\r\n *\r\n * Best-effort: if revocation fails, logout still proceeds\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param tokens - Tokens to revoke\r\n * @returns Revocation result\r\n */\r\n private async revokeTokens(\r\n discovery: OIDCDiscoveryDocument,\r\n tokens: TokenSet\r\n ): Promise<NonNullable<LogoutResult['revocation']>> {\r\n const result: NonNullable<LogoutResult['revocation']> = {\r\n attempted: true,\r\n };\r\n\r\n try {\r\n // Revoke refresh token first (if present) - this often invalidates access token too\r\n if (tokens.refreshToken) {\r\n await this.tokenRevoker.revoke(discovery, {\r\n token: tokens.refreshToken,\r\n tokenTypeHint: 'refresh_token',\r\n });\r\n result.refreshTokenRevoked = true;\r\n }\r\n\r\n // Revoke access token\r\n await this.tokenRevoker.revoke(discovery, {\r\n token: tokens.accessToken,\r\n tokenTypeHint: 'access_token',\r\n });\r\n result.accessTokenRevoked = true;\r\n } catch (error) {\r\n result.error = error instanceof Error ? error : new Error(String(error));\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Clear all tokens from storage\r\n */\r\n private async clearTokens(): Promise<void> {\r\n const tokenKey = STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n const idTokenKey = STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n\r\n await this.storage.remove(tokenKey);\r\n await this.storage.remove(idTokenKey);\r\n }\r\n\r\n /**\r\n * Get stored ID token\r\n */\r\n private async getStoredIdToken(): Promise<string | null> {\r\n const idTokenKey = STORAGE_KEYS.idToken(this.issuerHash, this.clientIdHash);\r\n return this.storage.get(idTokenKey);\r\n }\r\n\r\n /**\r\n * Get stored tokens\r\n */\r\n private async getStoredTokens(): Promise<TokenSet | null> {\r\n const tokenKey = STORAGE_KEYS.tokens(this.issuerHash, this.clientIdHash);\r\n const stored = await this.storage.get(tokenKey);\r\n if (!stored) {\r\n return null;\r\n }\r\n try {\r\n return JSON.parse(stored) as TokenSet;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n}\r\n","/**\r\n * Token API Client\r\n *\r\n * Provides session verification against the authorization server.\r\n * Uses the UserInfo endpoint or custom session check endpoint.\r\n */\r\n\r\nimport type { HttpClient } from '../providers/http.js';\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Session check result\r\n */\r\nexport interface SessionCheckResult {\r\n /** Whether session is valid */\r\n valid: boolean;\r\n /** User info if session is valid */\r\n user?: UserInfo;\r\n /** Error if session is invalid */\r\n error?: AuthrimError;\r\n}\r\n\r\n/**\r\n * Token API client options\r\n */\r\nexport interface TokenApiClientOptions {\r\n /** HTTP client */\r\n http: HttpClient;\r\n}\r\n\r\n/**\r\n * Token API Client\r\n *\r\n * Verifies session status with the authorization server.\r\n */\r\nexport class TokenApiClient {\r\n private readonly http: HttpClient;\r\n\r\n constructor(options: TokenApiClientOptions) {\r\n this.http = options.http;\r\n }\r\n\r\n /**\r\n * Check session validity by calling UserInfo endpoint\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param accessToken - Access token to verify\r\n * @returns Session check result\r\n */\r\n async checkSession(\r\n discovery: OIDCDiscoveryDocument,\r\n accessToken: string\r\n ): Promise<SessionCheckResult> {\r\n const userinfoEndpoint = discovery.userinfo_endpoint;\r\n\r\n if (!userinfoEndpoint) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('no_userinfo_endpoint', 'UserInfo endpoint not available'),\r\n };\r\n }\r\n\r\n try {\r\n const response = await this.http.fetch<UserInfo>(userinfoEndpoint, {\r\n method: 'GET',\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n },\r\n });\r\n\r\n if (!response.ok) {\r\n // 401 means token is invalid/expired\r\n if (response.status === 401) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_expired', 'Session has expired'),\r\n };\r\n }\r\n\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_check_failed', 'Session check failed', {\r\n details: { status: response.status },\r\n }),\r\n };\r\n }\r\n\r\n return {\r\n valid: true,\r\n user: response.data,\r\n };\r\n } catch (error) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('network_error', 'Failed to check session', {\r\n cause: error instanceof Error ? error : undefined,\r\n }),\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get user info from the authorization server\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param accessToken - Access token\r\n * @returns User info\r\n * @throws AuthrimError if request fails\r\n */\r\n async getUserInfo(discovery: OIDCDiscoveryDocument, accessToken: string): Promise<UserInfo> {\r\n const userinfoEndpoint = discovery.userinfo_endpoint;\r\n\r\n if (!userinfoEndpoint) {\r\n throw new AuthrimError('no_userinfo_endpoint', 'UserInfo endpoint not available');\r\n }\r\n\r\n let response;\r\n try {\r\n response = await this.http.fetch<UserInfo>(userinfoEndpoint, {\r\n method: 'GET',\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n },\r\n });\r\n } catch (error) {\r\n throw new AuthrimError('network_error', 'Failed to fetch user info', {\r\n cause: error instanceof Error ? error : undefined,\r\n });\r\n }\r\n\r\n if (!response.ok) {\r\n throw new AuthrimError('userinfo_error', 'Failed to get user info', {\r\n details: { status: response.status },\r\n });\r\n }\r\n\r\n return response.data;\r\n }\r\n}\r\n","/**\r\n * Session Manager\r\n *\r\n * Coordinates session-related operations including checking\r\n * session status and retrieving user information.\r\n */\r\n\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport type { TokenManager } from '../token/manager.js';\r\nimport { TokenApiClient, type SessionCheckResult } from './token-api.js';\r\nexport type { SessionCheckResult };\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Session manager options\r\n */\r\nexport interface SessionManagerOptions {\r\n /** Token manager */\r\n tokenManager: TokenManager;\r\n /** Token API client */\r\n tokenApiClient: TokenApiClient;\r\n}\r\n\r\n/**\r\n * Session Manager\r\n */\r\nexport class SessionManager {\r\n private readonly tokenManager: TokenManager;\r\n private readonly tokenApiClient: TokenApiClient;\r\n\r\n /** Discovery document */\r\n private discovery: OIDCDiscoveryDocument | null = null;\r\n\r\n constructor(options: SessionManagerOptions) {\r\n this.tokenManager = options.tokenManager;\r\n this.tokenApiClient = options.tokenApiClient;\r\n }\r\n\r\n /**\r\n * Set discovery document\r\n */\r\n setDiscovery(discovery: OIDCDiscoveryDocument): void {\r\n this.discovery = discovery;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated locally\r\n *\r\n * This checks if valid tokens exist in storage.\r\n * Does not verify with the authorization server.\r\n *\r\n * @returns True if tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n return this.tokenManager.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Check session validity with authorization server\r\n *\r\n * Calls the UserInfo endpoint to verify the session is still valid.\r\n *\r\n * @returns Session check result\r\n */\r\n async checkSession(): Promise<SessionCheckResult> {\r\n if (!this.discovery) {\r\n return {\r\n valid: false,\r\n error: new AuthrimError('no_discovery', 'Discovery document not available'),\r\n };\r\n }\r\n\r\n try {\r\n const accessToken = await this.tokenManager.getAccessToken();\r\n return this.tokenApiClient.checkSession(this.discovery, accessToken);\r\n } catch (error) {\r\n if (error instanceof AuthrimError) {\r\n return { valid: false, error };\r\n }\r\n return {\r\n valid: false,\r\n error: new AuthrimError('session_check_failed', 'Failed to check session', {\r\n cause: error instanceof Error ? error : undefined,\r\n }),\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get user information\r\n *\r\n * Fetches user info from the UserInfo endpoint.\r\n *\r\n * @returns User info\r\n * @throws AuthrimError if not authenticated or request fails\r\n */\r\n async getUser(): Promise<UserInfo> {\r\n if (!this.discovery) {\r\n throw new AuthrimError('no_discovery', 'Discovery document not available');\r\n }\r\n\r\n const accessToken = await this.tokenManager.getAccessToken();\r\n return this.tokenApiClient.getUserInfo(this.discovery, accessToken);\r\n }\r\n}\r\n","/**\r\n * Authrim Client\r\n *\r\n * Main entry point for the Authrim SDK.\r\n */\r\n\r\nimport type { AuthrimClientConfig, ResolvedConfig } from './config.js';\r\nimport type { OIDCDiscoveryDocument, UserInfo } from '../types/oidc.js';\r\nimport type { TokenSet, TokenExchangeRequest, TokenExchangeResult } from '../types/token.js';\r\nimport type { AuthrimEventName, AuthrimEventHandler } from '../events/types.js';\r\nimport { resolveConfig } from './config.js';\r\nimport { DiscoveryClient, normalizeIssuer } from './discovery.js';\r\nimport { EventEmitter } from '../events/emitter.js';\r\nimport { PKCEHelper } from '../auth/pkce.js';\r\nimport { StateManager } from '../auth/state.js';\r\nimport {\r\n AuthorizationCodeFlow,\r\n type BuildAuthorizationUrlOptions,\r\n type AuthorizationUrlResult,\r\n} from '../auth/authorization-code.js';\r\nimport { TokenManager } from '../token/manager.js';\r\nimport {\r\n TokenIntrospector,\r\n type IntrospectionResponse,\r\n type IntrospectTokenOptions,\r\n} from '../token/introspection.js';\r\nimport { TokenRevoker, type RevokeTokenOptions } from '../token/revocation.js';\r\nimport { LogoutHandler, type LogoutOptions, type LogoutResult } from '../session/logout.js';\r\nimport { TokenApiClient } from '../session/token-api.js';\r\nimport { SessionManager } from '../session/manager.js';\r\nimport { base64urlEncode } from '../utils/base64url.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Hash a value for use in storage keys\r\n *\r\n * @param crypto - Crypto provider\r\n * @param value - Value to hash\r\n * @param length - Hash length (default: 16 = 96 bits)\r\n * @returns Base64url-encoded hash\r\n */\r\nasync function hashForKey(\r\n crypto: AuthrimClientConfig['crypto'],\r\n value: string,\r\n length = 16\r\n): Promise<string> {\r\n const hash = await crypto.sha256(value);\r\n return base64urlEncode(hash).slice(0, length);\r\n}\r\n\r\n/**\r\n * Authrim Client\r\n */\r\nexport class AuthrimClient {\r\n /** Resolved configuration */\r\n private readonly config: ResolvedConfig;\r\n\r\n /** Event emitter */\r\n private readonly events: EventEmitter;\r\n\r\n /** Discovery client */\r\n private readonly discoveryClient: DiscoveryClient;\r\n\r\n /** PKCE helper */\r\n private readonly pkce: PKCEHelper;\r\n\r\n /** State manager (initialized in initialize()) */\r\n private stateManager!: StateManager;\r\n\r\n /** Authorization code flow helper */\r\n private readonly authCodeFlow: AuthorizationCodeFlow;\r\n\r\n /** Token manager (initialized in initialize()) */\r\n private tokenManager!: TokenManager;\r\n\r\n /** Logout handler (initialized in initialize()) */\r\n private logoutHandler!: LogoutHandler;\r\n\r\n /** Session manager (initialized in initialize()) */\r\n private sessionManager!: SessionManager;\r\n\r\n /** Token introspector */\r\n private tokenIntrospector!: TokenIntrospector;\r\n\r\n /** Token revoker */\r\n private tokenRevoker!: TokenRevoker;\r\n\r\n /** Issuer hash for storage keys */\r\n private issuerHash!: string;\r\n\r\n /** Client ID hash for storage keys */\r\n private clientIdHash!: string;\r\n\r\n /** Normalized issuer URL */\r\n private readonly normalizedIssuer: string;\r\n\r\n /** Whether the client has been initialized */\r\n private initialized = false;\r\n\r\n /**\r\n * Create a new Authrim client\r\n *\r\n * @internal Use createAuthrimClient() instead\r\n */\r\n constructor(config: AuthrimClientConfig) {\r\n this.config = resolveConfig(config);\r\n this.normalizedIssuer = normalizeIssuer(config.issuer);\r\n\r\n // Initialize components that don't need hashes\r\n this.events = new EventEmitter();\r\n\r\n this.discoveryClient = new DiscoveryClient({\r\n http: this.config.http,\r\n cacheTtlMs: this.config.discoveryCacheTtlMs,\r\n });\r\n\r\n this.pkce = new PKCEHelper(this.config.crypto);\r\n\r\n this.authCodeFlow = new AuthorizationCodeFlow(this.config.http, this.config.clientId);\r\n }\r\n\r\n /**\r\n * Initialize the client\r\n *\r\n * @internal Called by createAuthrimClient()\r\n */\r\n async initialize(): Promise<void> {\r\n if (this.initialized) {\r\n return;\r\n }\r\n\r\n // Calculate hashes for storage keys\r\n const hashLength = this.config.hashOptions.hashLength;\r\n this.issuerHash = await hashForKey(this.config.crypto, this.normalizedIssuer, hashLength);\r\n this.clientIdHash = await hashForKey(this.config.crypto, this.config.clientId, hashLength);\r\n\r\n // Initialize state manager\r\n this.stateManager = new StateManager(\r\n this.config.crypto,\r\n this.config.storage,\r\n this.issuerHash,\r\n this.clientIdHash\r\n );\r\n\r\n // Initialize token manager\r\n this.tokenManager = new TokenManager({\r\n http: this.config.http,\r\n storage: this.config.storage,\r\n clientId: this.config.clientId,\r\n issuerHash: this.issuerHash,\r\n clientIdHash: this.clientIdHash,\r\n refreshSkewSeconds: this.config.refreshSkewSeconds,\r\n eventEmitter: this.events,\r\n });\r\n\r\n // Initialize logout handler\r\n this.logoutHandler = new LogoutHandler({\r\n storage: this.config.storage,\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n issuerHash: this.issuerHash,\r\n clientIdHash: this.clientIdHash,\r\n eventEmitter: this.events,\r\n endpoints: this.config.endpoints,\r\n });\r\n\r\n // Initialize token introspector\r\n this.tokenIntrospector = new TokenIntrospector({\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n });\r\n\r\n // Initialize token revoker\r\n this.tokenRevoker = new TokenRevoker({\r\n http: this.config.http,\r\n clientId: this.config.clientId,\r\n });\r\n\r\n // Initialize token API client\r\n const tokenApiClient = new TokenApiClient({\r\n http: this.config.http,\r\n });\r\n\r\n // Initialize session manager\r\n this.sessionManager = new SessionManager({\r\n tokenManager: this.tokenManager,\r\n tokenApiClient,\r\n });\r\n\r\n // Clean up expired states (best-effort)\r\n await this.stateManager.cleanupExpiredStates();\r\n\r\n this.initialized = true;\r\n }\r\n\r\n /**\r\n * Ensure client is initialized\r\n */\r\n private ensureInitialized(): void {\r\n if (!this.initialized) {\r\n throw new AuthrimError(\r\n 'not_initialized',\r\n 'Client not initialized. Use createAuthrimClient().'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get OIDC discovery document\r\n *\r\n * @returns Discovery document\r\n */\r\n async discover(): Promise<OIDCDiscoveryDocument> {\r\n const discovery = await this.discoveryClient.discover(this.normalizedIssuer);\r\n\r\n // Update dependent components\r\n this.tokenManager.setDiscovery(discovery);\r\n this.sessionManager.setDiscovery(discovery);\r\n\r\n return discovery;\r\n }\r\n\r\n // ============================================================\r\n // Authorization Code Flow\r\n // ============================================================\r\n\r\n /**\r\n * Build authorization URL\r\n *\r\n * Generates the URL to redirect the user to for authentication.\r\n * Stores state, nonce, and code_verifier in storage.\r\n *\r\n * @param options - Authorization options\r\n * @returns Authorization URL result\r\n */\r\n async buildAuthorizationUrl(\r\n options: BuildAuthorizationUrlOptions\r\n ): Promise<AuthorizationUrlResult> {\r\n this.ensureInitialized();\r\n\r\n const discovery = await this.discover();\r\n\r\n // Generate PKCE pair\r\n const pkcePair = await this.pkce.generatePKCE();\r\n\r\n // Generate auth state\r\n const authState = await this.stateManager.generateAuthState({\r\n redirectUri: options.redirectUri,\r\n codeVerifier: pkcePair.codeVerifier,\r\n ttlSeconds: this.config.stateTtlSeconds,\r\n });\r\n\r\n // Build URL\r\n const result = this.authCodeFlow.buildAuthorizationUrl(discovery, authState, pkcePair, options);\r\n\r\n // Emit event\r\n this.events.emit('auth:redirecting', { url: result.url });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Handle authorization callback\r\n *\r\n * Processes the callback URL, validates state/nonce, and exchanges\r\n * the authorization code for tokens.\r\n *\r\n * @param callbackUrl - Callback URL or query string\r\n * @returns Token set\r\n */\r\n async handleCallback(callbackUrl: string): Promise<TokenSet> {\r\n this.ensureInitialized();\r\n\r\n // Parse callback\r\n const { code, state } = this.authCodeFlow.parseCallback(callbackUrl);\r\n\r\n // Emit event\r\n this.events.emit('auth:callback', { code, state });\r\n\r\n // Validate and consume state (always deletes)\r\n const authState = await this.stateManager.validateAndConsumeState(state);\r\n\r\n // Get discovery\r\n const discovery = await this.discover();\r\n\r\n // Exchange code for tokens\r\n const tokens = await this.authCodeFlow.exchangeCode(discovery, {\r\n code,\r\n state,\r\n redirectUri: authState.redirectUri,\r\n codeVerifier: authState.codeVerifier,\r\n nonce: authState.nonce,\r\n });\r\n\r\n // Save tokens\r\n await this.tokenManager.saveTokens(tokens);\r\n\r\n return tokens;\r\n }\r\n\r\n // ============================================================\r\n // Token API\r\n // ============================================================\r\n\r\n /**\r\n * Token API accessor\r\n */\r\n get token() {\r\n this.ensureInitialized();\r\n return {\r\n /**\r\n * Get access token (refreshes if needed)\r\n */\r\n getAccessToken: () => this.tokenManager.getAccessToken(),\r\n\r\n /**\r\n * Get current tokens\r\n */\r\n getTokens: () => this.tokenManager.getTokens(),\r\n\r\n /**\r\n * Get ID token\r\n */\r\n getIdToken: () => this.tokenManager.getIdToken(),\r\n\r\n /**\r\n * Check if authenticated\r\n */\r\n isAuthenticated: () => this.tokenManager.isAuthenticated(),\r\n\r\n /**\r\n * Exchange token (RFC 8693)\r\n *\r\n * Exchanges a token for a new token with different audience, scope,\r\n * or delegation. Useful for:\r\n * - Cross-service token acquisition\r\n * - Delegation (actor token)\r\n * - Scope reduction\r\n *\r\n * @param request - Token exchange request parameters\r\n * @returns Token exchange result\r\n */\r\n exchange: async (request: TokenExchangeRequest): Promise<TokenExchangeResult> => {\r\n // Ensure discovery is loaded\r\n await this.discover();\r\n return this.tokenManager.exchangeToken(request);\r\n },\r\n\r\n /**\r\n * Introspect a token (RFC 7662)\r\n *\r\n * Validates a token server-side and returns its metadata.\r\n * Useful for resource servers to validate access tokens.\r\n *\r\n * @param options - Introspection options (token and optional type hint)\r\n * @returns Introspection response with token metadata\r\n * @throws AuthrimError if introspection endpoint not available or request fails\r\n */\r\n introspect: async (options: IntrospectTokenOptions): Promise<IntrospectionResponse> => {\r\n const discovery = await this.discover();\r\n return this.tokenIntrospector.introspect(discovery, options);\r\n },\r\n\r\n /**\r\n * Revoke a token (RFC 7009)\r\n *\r\n * Explicitly invalidates a token at the authorization server.\r\n * Use this when you want to ensure a token can no longer be used.\r\n *\r\n * @param options - Revocation options (token and optional type hint)\r\n * @throws AuthrimError if revocation endpoint not available or request fails\r\n */\r\n revoke: async (options: RevokeTokenOptions): Promise<void> => {\r\n const discovery = await this.discover();\r\n return this.tokenRevoker.revoke(discovery, options);\r\n },\r\n };\r\n }\r\n\r\n // ============================================================\r\n // Session API\r\n // ============================================================\r\n\r\n /**\r\n * Session API accessor\r\n */\r\n get session() {\r\n this.ensureInitialized();\r\n return {\r\n /**\r\n * Check if authenticated locally\r\n */\r\n isAuthenticated: () => this.sessionManager.isAuthenticated(),\r\n\r\n /**\r\n * Check session with authorization server\r\n */\r\n check: () => this.sessionManager.checkSession(),\r\n };\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n *\r\n * @returns True if tokens exist\r\n */\r\n async isAuthenticated(): Promise<boolean> {\r\n this.ensureInitialized();\r\n return this.tokenManager.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Get user information\r\n *\r\n * @returns User info\r\n */\r\n async getUser(): Promise<UserInfo> {\r\n this.ensureInitialized();\r\n return this.sessionManager.getUser();\r\n }\r\n\r\n // ============================================================\r\n // Logout\r\n // ============================================================\r\n\r\n /**\r\n * Log out the user\r\n *\r\n * Clears local tokens and optionally redirects to IdP for logout.\r\n *\r\n * @param options - Logout options\r\n * @returns Logout result\r\n */\r\n async logout(options?: LogoutOptions): Promise<LogoutResult> {\r\n this.ensureInitialized();\r\n\r\n let discovery: OIDCDiscoveryDocument | null = null;\r\n try {\r\n discovery = await this.discover();\r\n } catch {\r\n // Discovery failure is OK for logout - we can still do local logout\r\n }\r\n\r\n return this.logoutHandler.logout(discovery, options);\r\n }\r\n\r\n // ============================================================\r\n // Events\r\n // ============================================================\r\n\r\n /**\r\n * Subscribe to an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n on<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n return this.events.on(event, handler);\r\n }\r\n\r\n /**\r\n * Subscribe to an event (one-time)\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n * @returns Unsubscribe function\r\n */\r\n once<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): () => void {\r\n return this.events.once(event, handler);\r\n }\r\n\r\n /**\r\n * Unsubscribe from an event\r\n *\r\n * @param event - Event name\r\n * @param handler - Event handler\r\n */\r\n off<T extends AuthrimEventName>(event: T, handler: AuthrimEventHandler<T>): void {\r\n this.events.off(event, handler);\r\n }\r\n}\r\n\r\n/**\r\n * Create an Authrim client\r\n *\r\n * This is the main entry point for creating a client.\r\n * The client is fully initialized when returned.\r\n *\r\n * @param config - Client configuration\r\n * @returns Initialized Authrim client\r\n */\r\nexport async function createAuthrimClient(config: AuthrimClientConfig): Promise<AuthrimClient> {\r\n const client = new AuthrimClient(config);\r\n await client.initialize();\r\n return client;\r\n}\r\n","/**\r\n * Silent Authentication (prompt=none)\r\n *\r\n * Foundation for silent authentication using OIDC prompt=none.\r\n * This module provides the core logic for building silent auth requests\r\n * and parsing responses. The actual iframe/hidden frame implementation\r\n * is platform-specific and should be implemented in @authrim/web or similar.\r\n *\r\n * Silent authentication allows checking if a user has an active session\r\n * with the authorization server without user interaction.\r\n */\r\n\r\nimport type { OIDCDiscoveryDocument } from '../types/oidc.js';\r\nimport type { TokenSet } from '../types/token.js';\r\nimport type { AuthState } from './state.js';\r\nimport type { PKCEPair } from './pkce.js';\r\nimport { AuthrimError } from '../types/errors.js';\r\n\r\n/**\r\n * Silent authentication options\r\n */\r\nexport interface SilentAuthOptions {\r\n /** Redirect URI for silent auth response (often an iframe callback page) */\r\n redirectUri: string;\r\n /** Scopes to request (default: 'openid') */\r\n scope?: string;\r\n /** Hint about the login identifier */\r\n loginHint?: string;\r\n /** ID token hint (helps IdP identify the user) */\r\n idTokenHint?: string;\r\n /** Additional custom parameters */\r\n extraParams?: Record<string, string>;\r\n /**\r\n * Expose state/nonce in result (for SSR/external storage)\r\n *\r\n * Default: false (security: state/nonce stay internal)\r\n */\r\n exposeState?: boolean;\r\n}\r\n\r\n/**\r\n * Result of building silent auth URL\r\n */\r\nexport interface SilentAuthUrlResult {\r\n /** Authorization URL with prompt=none */\r\n url: string;\r\n /** State parameter (only if exposeState: true) */\r\n state?: string;\r\n /** Nonce parameter (only if exposeState: true) */\r\n nonce?: string;\r\n}\r\n\r\n/**\r\n * Silent authentication result\r\n */\r\nexport interface SilentAuthResult {\r\n /** Whether silent auth succeeded */\r\n success: boolean;\r\n /** Tokens if successful */\r\n tokens?: TokenSet;\r\n /** Error if failed (e.g., login_required) */\r\n error?: AuthrimError;\r\n}\r\n\r\n/**\r\n * Silent auth error codes that indicate interactive login is needed\r\n */\r\nconst INTERACTIVE_LOGIN_REQUIRED_ERRORS = new Set([\r\n 'login_required',\r\n 'interaction_required',\r\n 'consent_required',\r\n 'account_selection_required',\r\n]);\r\n\r\n/**\r\n * Silent Authentication Handler\r\n *\r\n * Provides core logic for silent authentication. Platform-specific\r\n * implementations (iframe, hidden frame, etc.) should use this class.\r\n */\r\nexport class SilentAuthHandler {\r\n constructor(private readonly clientId: string) {}\r\n\r\n /**\r\n * Build silent authentication URL\r\n *\r\n * Creates an authorization URL with prompt=none for silent authentication.\r\n *\r\n * @param discovery - OIDC discovery document\r\n * @param authState - Auth state from StateManager\r\n * @param pkce - PKCE pair\r\n * @param options - Silent auth options\r\n * @returns Silent auth URL result\r\n */\r\n buildSilentAuthUrl(\r\n discovery: OIDCDiscoveryDocument,\r\n authState: AuthState,\r\n pkce: PKCEPair,\r\n options: SilentAuthOptions\r\n ): SilentAuthUrlResult {\r\n const endpoint = discovery.authorization_endpoint;\r\n const params = new URLSearchParams();\r\n\r\n // Required parameters\r\n params.set('client_id', this.clientId);\r\n params.set('response_type', 'code');\r\n params.set('redirect_uri', options.redirectUri);\r\n params.set('state', authState.state);\r\n params.set('nonce', authState.nonce);\r\n\r\n // Silent auth specific - MUST be prompt=none\r\n params.set('prompt', 'none');\r\n\r\n // PKCE parameters\r\n params.set('code_challenge', pkce.codeChallenge);\r\n params.set('code_challenge_method', pkce.codeChallengeMethod);\r\n\r\n // Scopes (default to openid only for silent auth)\r\n const scope = options.scope ?? 'openid';\r\n params.set('scope', scope);\r\n\r\n // Optional parameters\r\n if (options.loginHint) {\r\n params.set('login_hint', options.loginHint);\r\n }\r\n if (options.idTokenHint) {\r\n params.set('id_token_hint', options.idTokenHint);\r\n }\r\n\r\n // Extra custom parameters (with security parameter protection)\r\n if (options.extraParams) {\r\n const protectedParams = new Set([\r\n 'client_id',\r\n 'response_type',\r\n 'redirect_uri',\r\n 'state',\r\n 'nonce',\r\n 'code_challenge',\r\n 'code_challenge_method',\r\n 'scope',\r\n 'prompt', // Protect prompt=none\r\n ]);\r\n\r\n for (const [key, value] of Object.entries(options.extraParams)) {\r\n if (protectedParams.has(key.toLowerCase())) {\r\n continue;\r\n }\r\n params.set(key, value);\r\n }\r\n }\r\n\r\n const url = `${endpoint}?${params.toString()}`;\r\n\r\n const result: SilentAuthUrlResult = { url };\r\n\r\n if (options.exposeState) {\r\n result.state = authState.state;\r\n result.nonce = authState.nonce;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Parse silent authentication response URL\r\n *\r\n * Parses the callback URL from silent authentication and returns\r\n * either the authorization code (for token exchange) or an error.\r\n *\r\n * @param responseUrl - Response URL from silent auth (iframe callback)\r\n * @returns Parsed result with code/state or error\r\n */\r\n parseSilentAuthResponse(responseUrl: string): {\r\n success: true;\r\n code: string;\r\n state: string;\r\n } | {\r\n success: false;\r\n error: AuthrimError;\r\n } {\r\n let searchParams: URLSearchParams;\r\n\r\n // Support both full URL and query string\r\n if (responseUrl.includes('?')) {\r\n const url = responseUrl.startsWith('http')\r\n ? new URL(responseUrl)\r\n : new URL(responseUrl, 'https://dummy.local');\r\n searchParams = url.searchParams;\r\n } else {\r\n searchParams = new URLSearchParams(responseUrl);\r\n }\r\n\r\n // Check for OAuth error response\r\n const error = searchParams.get('error');\r\n if (error) {\r\n const errorDescription = searchParams.get('error_description');\r\n const errorCode = this.mapSilentAuthError(error);\r\n\r\n return {\r\n success: false,\r\n error: new AuthrimError(errorCode, errorDescription ?? this.getDefaultErrorMessage(error), {\r\n details: {\r\n error,\r\n error_description: errorDescription,\r\n error_uri: searchParams.get('error_uri'),\r\n },\r\n }),\r\n };\r\n }\r\n\r\n // Extract code and state\r\n const code = searchParams.get('code');\r\n const state = searchParams.get('state');\r\n\r\n if (!code) {\r\n return {\r\n success: false,\r\n error: new AuthrimError('missing_code', 'Authorization code not found in silent auth response'),\r\n };\r\n }\r\n if (!state) {\r\n return {\r\n success: false,\r\n error: new AuthrimError('missing_state', 'State parameter not found in silent auth response'),\r\n };\r\n }\r\n\r\n return { success: true, code, state };\r\n }\r\n\r\n /**\r\n * Check if an error indicates interactive login is required\r\n *\r\n * @param error - Error to check\r\n * @returns True if interactive login is needed\r\n */\r\n isInteractiveLoginRequired(error: AuthrimError): boolean {\r\n return INTERACTIVE_LOGIN_REQUIRED_ERRORS.has(error.code);\r\n }\r\n\r\n /**\r\n * Map OAuth error to AuthrimErrorCode\r\n */\r\n private mapSilentAuthError(\r\n error: string\r\n ): 'login_required' | 'interaction_required' | 'consent_required' | 'account_selection_required' | 'oauth_error' {\r\n if (INTERACTIVE_LOGIN_REQUIRED_ERRORS.has(error)) {\r\n return error as 'login_required' | 'interaction_required' | 'consent_required' | 'account_selection_required';\r\n }\r\n return 'oauth_error';\r\n }\r\n\r\n /**\r\n * Get default error message for silent auth errors\r\n */\r\n private getDefaultErrorMessage(error: string): string {\r\n switch (error) {\r\n case 'login_required':\r\n return 'User must log in - no active session found';\r\n case 'interaction_required':\r\n return 'User interaction required';\r\n case 'consent_required':\r\n return 'User consent required';\r\n case 'account_selection_required':\r\n return 'User must select an account';\r\n default:\r\n return 'Silent authentication failed';\r\n }\r\n }\r\n}\r\n","/**\r\n * Hash Utilities\r\n *\r\n * Provides hash calculation utilities for OIDC specifications.\r\n */\r\n\r\nimport type { CryptoProvider } from '../providers/crypto.js';\r\nimport { base64urlEncode } from './base64url.js';\r\n\r\n/**\r\n * Calculate ds_hash for Native SSO device_secret verification\r\n *\r\n * Algorithm: BASE64URL(left half of SHA-256(device_secret))\r\n * Reference: OIDC Native SSO 1.0 specification\r\n *\r\n * This is the same algorithm used for at_hash and c_hash in OIDC Core,\r\n * applied to the device_secret value.\r\n *\r\n * @param deviceSecret - The device_secret value to hash\r\n * @param crypto - Platform-specific crypto provider\r\n * @returns ds_hash value (BASE64URL encoded)\r\n *\r\n * @example\r\n * ```typescript\r\n * const dsHash = await calculateDsHash(deviceSecret, cryptoProvider);\r\n * // Compare with id_token.ds_hash claim\r\n * if (idToken.ds_hash === dsHash) {\r\n * // device_secret is valid\r\n * }\r\n * ```\r\n */\r\nexport async function calculateDsHash(\r\n deviceSecret: string,\r\n crypto: CryptoProvider\r\n): Promise<string> {\r\n // 1. Compute SHA-256 hash (32 bytes)\r\n const hash = await crypto.sha256(deviceSecret);\r\n\r\n // 2. Take the left half (16 bytes for SHA-256)\r\n const leftHalf = hash.slice(0, hash.length / 2);\r\n\r\n // 3. BASE64URL encode\r\n return base64urlEncode(leftHalf);\r\n}\r\n"]}
|