@absolutejs/auth 0.2.4 → 0.2.5

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.
Files changed (37) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc.json +9 -0
  3. package/dist/index.js +1 -1
  4. package/package.json +2 -8
  5. package/src/authorize.ts +119 -0
  6. package/src/callback.ts +127 -0
  7. package/src/constants.ts +13 -0
  8. package/src/index.ts +88 -0
  9. package/src/logout.ts +39 -0
  10. package/src/protectRoute.ts +33 -0
  11. package/src/providers.ts +109 -0
  12. package/src/refresh.ts +63 -0
  13. package/src/revoke.ts +61 -0
  14. package/src/sessionStore.ts +10 -0
  15. package/src/status.ts +100 -0
  16. package/src/typeGuards.ts +32 -0
  17. package/src/types.ts +78 -0
  18. package/src/utils.ts +55 -0
  19. package/tsconfig.json +17 -0
  20. package/dist/example/components/AuthOptions.d.ts +0 -1
  21. package/dist/example/components/Example.d.ts +0 -1
  22. package/dist/example/components/Head.d.ts +0 -6
  23. package/dist/example/components/Modal.d.ts +0 -8
  24. package/dist/example/components/Navbar.d.ts +0 -10
  25. package/dist/example/components/NotAuthorized.d.ts +0 -1
  26. package/dist/example/components/Protected.d.ts +0 -1
  27. package/dist/example/db/migrate.d.ts +0 -1
  28. package/dist/example/db/schema.d.ts +0 -248
  29. package/dist/example/hooks/useAuthStatus.d.ts +0 -19
  30. package/dist/example/indexes/ExampleIndex.d.ts +0 -1
  31. package/dist/example/indexes/NotAuthorizedIndex.d.ts +0 -1
  32. package/dist/example/indexes/ProtectedIndex.d.ts +0 -1
  33. package/dist/example/server.d.ts +0 -1
  34. package/dist/example/utils/networking.d.ts +0 -1
  35. package/dist/example/utils/pageUtils.d.ts +0 -2
  36. package/dist/example/utils/styles.d.ts +0 -15
  37. package/dist/example/utils/userUtils.d.ts +0 -36
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ build
4
+ *.min.js
@@ -0,0 +1,9 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "none",
4
+ "singleQuote": true,
5
+ "printWidth": 80,
6
+ "tabWidth": 4,
7
+ "endOfLine": "auto",
8
+ "useTabs": true
9
+ }
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{OAuth2RequestError as cN,ArcticFetchError as oN}from"arctic";import{Elysia as tN}from"elysia";import{generateState as zN,generateCodeVerifier as PN}from"arctic";import{Elysia as RN}from"elysia";import{AmazonCognito as v,AniList as l,Apple as h,Atlassian as u,Auth0 as c,Authentik as o,Bitbucket as t,Box as s,Coinbase as d,Discord as a,Dribbble as i,Dropbox as e,Facebook as r,Figma as NN,Intuit as EN,GitHub as SN,GitLab as IN,Google as UN,Kakao as mN,KeyCloak as MN,Lichess as TN,Line as xN,Linear as ON,LinkedIn as wN,MicrosoftEntraId as DN,MyAnimeList as $N,Notion as AN,Okta as LN,Osu as HN,Patreon as CN,Reddit as fN,Roblox as nN,Salesforce as JN,Shikimori as QN,Slack as XN,Spotify as YN,Strava as jN,Tiltify as qN,Tumblr as FN,Twitch as WN,Twitter as ZN,VK as BN,WorkOS as gN,Yahoo as pN,Yandex as GN,Zoom as KN,FortyTwo as VN}from"arctic";var q={AmazonCognito:v,AniList:l,Apple:h,Atlassian:u,Auth0:c,Authentik:o,Bitbucket:t,Box:s,Coinbase:d,Discord:a,Dribbble:i,Dropbox:e,Facebook:r,Figma:NN,FortyTwo:VN,GitHub:SN,GitLab:IN,Google:UN,Intuit:EN,Kakao:mN,KeyCloak:MN,Lichess:TN,Line:xN,Linear:ON,LinkedIn:wN,MicrosoftEntraId:DN,MyAnimeList:$N,Notion:AN,Okta:LN,Osu:HN,Patreon:CN,Reddit:fN,Roblox:nN,Salesforce:JN,Shikimori:QN,Slack:XN,Spotify:YN,Strava:jN,Tiltify:qN,Tumblr:FN,Twitch:WN,Twitter:ZN,VK:BN,WorkOS:gN,Yahoo:pN,Yandex:GN,Zoom:KN},g=Object.keys(q).reduce((N,S)=>{return N[S.toLowerCase()]=S,N},{});var Z=(N)=>("refreshAccessToken"in N)&&typeof N.refreshAccessToken==="function",p=(N)=>("revokeAccessToken"in N)&&typeof N.revokeAccessToken==="function",J=(N)=>Object.keys(q).map((S)=>S.toLowerCase()).includes(N.toLowerCase()),G=(N)=>!0,K=(N)=>N!==null&&N!==void 0&&N.trim()!=="";var V=86400000;var F=1800;var z=({clientProviders:N,authorizeRoute:S="authorize",onAuthorize:M})=>new RN().get(`/${S}/:provider`,({error:E,redirect:I,cookie:{state:m,code_verifier:T,auth_provider:U,redirect_url:O},params:{provider:$},headers:A})=>{if($===void 0)return E("Bad Request","Provider is required");if(!J($))return E("Bad Request","Invalid provider");try{let w=$.toLowerCase(),{providerInstance:f,scopes:L,searchParams:D}=N[w],x=A.referer??"/";O.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:x}),U.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:$});let H=zN();m.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:H});let C=f.createAuthorizationURL.toString().includes("codeVerifier"),n=C?PN():void 0;C&&T.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:n??""});let Q=C?f.createAuthorizationURL(H,n,L):f.createAuthorizationURL(H,L);return D.forEach(([Y,j])=>{Q.searchParams.set(Y,j)}),M?.(),I(Q.toString())}catch(w){if(w instanceof Error)return E("Internal Server Error",`${w.message} - ${w.stack??""}`);return E("Internal Server Error",`Unknown error: ${w}`)}});import{decodeIdToken as kN}from"arctic";import{Elysia as yN}from"elysia";import{Elysia as bN}from"elysia";var X=()=>{let N={};return new bN({name:"sessionStore"}).state({session:N})};var P=({clientProviders:N,callbackRoute:S="authorize/callback",onCallback:M})=>new yN().use(X()).get(`/${S}`,async({error:E,redirect:I,store:{session:m},cookie:{state:T,code_verifier:U,redirect_url:O,user_session_id:$,auth_provider:A},query:{code:w,state:f}})=>{if(!K(w)||T.value===void 0)return E("Bad Request","Invalid callback request");if(f!==T.value)return E("Bad Request","Invalid state mismatch");if(A.value===void 0)return E("Unauthorized","No auth provider found");if(!J(A.value))return E("Unauthorized","Invalid provider");let L=A.value.toLowerCase(),{providerInstance:D}=N[L];try{T.remove();let x=D.validateAuthorizationCode.toString().includes("codeVerifier"),H=U.value;if(x&&H===void 0)return E("Bad Request","Code verifier not found and is required");let C=H??"",n=await(x?D.validateAuthorizationCode(w,C):D.validateAuthorizationCode(w));if(x)U.remove();let Q=Object.fromEntries(Object.entries(kN(n.idToken())).map(([B,W])=>[B,typeof W==="string"?W:void 0])),Y=A.value;await M?.({authProvider:Y,decodedIdToken:Q,session:m,user_session_id:$});let j=O.value??"/";return I(j)}catch(x){if(x instanceof Error)return E("Internal Server Error",`${x.message} - ${x.stack??""}`);return E("Internal Server Error",`Failed to validate authorization code: Unknown error: ${x}`)}});import{Elysia as _N}from"elysia";var R=({logoutRoute:N="logout",onLogout:S})=>new _N().post(`/${N}`,async({error:M,cookie:{user_session_id:E,auth_provider:I}})=>{if(I.value===void 0)return M("Unauthorized","No auth provider found");try{return S?.(),E.remove(),I.remove(),new Response("Succesfuly Logged Out",{status:204})}catch(m){if(m instanceof Error)return M("Internal Server Error",`Failed to logout: ${m.message}`);return M("Internal Server Error",`Failed to logout: Unknown error: ${m}`)}});import{Elysia as vN}from"elysia";var b=()=>new vN().use(X()).derive(({store:{session:N},cookie:{user_session_id:S},error:M})=>({protectRoute:async(E,I)=>{if(S.value===void 0)return I?.()??M("Unauthorized","No session ID found");if(N[S.value]===void 0)return I?.()??M("Unauthorized","No session found");return E()}})).as("plugin");import{Elysia as lN}from"elysia";var k=({clientProviders:N,refreshRoute:S="refresh",onRefresh:M})=>new lN().post(`/${S}`,async({error:E,cookie:{user_refresh_token:I,auth_provider:m}})=>{if(I.value===void 0)return E("Unauthorized","No refresh token found");if(m.value===void 0)return E("Unauthorized","No auth provider found");let T=m.value.toLowerCase(),{providerInstance:U}=N[T];if(!Z(U))return E("Not Implemented","Provider is not refreshable");try{return await U.refreshAccessToken(I.value),M?.(),new Response("Token refreshed",{status:204})}catch(O){if(O instanceof Error)return E("Internal Server Error",`Failed to refresh token: ${O.message}`);return E("Internal Server Error",`Faile to refresh token: Unknown error: ${O}`)}});import{Elysia as hN}from"elysia";var y=({clientProviders:N,revokeRoute:S="revoke",onRevoke:M})=>new hN().post(`/${S}/access-token`,async({error:E,cookie:{user_refresh_token:I,auth_provider:m}})=>{if(I.value===void 0)return E("Unauthorized","No refresh token found");if(m.value===void 0)return E("Unauthorized","No auth provider found");let T=m.value.toLowerCase(),{providerInstance:U}=N[T];if(!p(U))return E("Not Implemented","Provider does not support revocation");try{return await U.revokeAccessToken(I.value),M?.(),new Response("Token revoked",{status:204})}catch(O){if(O instanceof Error)return E("Internal Server Error",`Failed to revoke token: ${O.message}`);return E("Internal Server Error",`Failed to revoke token: Unknown error: ${O}`)}});import{Elysia as uN}from"elysia";var _=({clientProviders:N,statusRoute:S="auth-status",onStatus:M})=>new uN().use(X()).get(`/${S}`,async({error:E,cookie:{user_session_id:I,auth_provider:m},store:{session:T}})=>{try{if(I.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});if(m.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let U=m.value.toLowerCase(),{providerInstance:O}=N[U];if(!Z(O))return E("Not Implemented","Provider is not refreshable");let $=T[I.value];if($===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let{user:A}=$;return M?.(),new Response(JSON.stringify({isLoggedIn:!0,user:A}),{headers:{"Content-Type":"application/json"}})}catch(U){if(U instanceof Error)return E("Internal Server Error",`Error: ${U.message} - ${U.stack??""}`);return E("Internal Server Error",`Unknown Error: ${U}`)}});var BE=async({authProvider:N,decodedIdToken:S,user_session_id:M,session:E,getUser:I,createUser:m})=>{let T=await I?.({authProvider:N,decodedIdToken:S});if(T=T??await m?.({authProvider:N,decodedIdToken:S}),!G(T))throw new Error("Internal Server Error - Invalid user schema");let U=crypto.randomUUID();E[U]={expiresAt:Date.now()+V,user:T},M.set({httpOnly:!0,sameSite:"lax",secure:!0,value:U})},gE=(N)=>N;var lE=({config:N,authorizeRoute:S,callbackRoute:M,logoutRoute:E,statusRoute:I,refreshRoute:m,revokeRoute:T,onAuthorize:U,onCallback:O,onStatus:$,onRefresh:A,onLogout:w,onRevoke:f})=>{let L=Object.keys(N).reduce((D,x)=>{if(!Object.prototype.hasOwnProperty.call(N,x))return D;if(!J(x))return console.error(`Provider ${x} is not supported`),D;let H=N[x];if(!H)return D;let C=x.toLowerCase(),n=g[C];if(!J(n))return console.error(`Provider ${x} is not supported`),D;let Q=q[n],{credentials:Y,scopes:j=[],searchParams:B=[]}=H,W=new Q(...Y);return D[C]={providerInstance:W,scopes:j,searchParams:B},D},{});return new tN().error("OAUTH2_REQUEST_ERROR",cN).error("ARCTIC_FETCH_ERROR",oN).use(R({logoutRoute:E,onLogout:w})).use(y({clientProviders:L,onRevoke:f,revokeRoute:T})).use(_({clientProviders:L,onStatus:$,statusRoute:I})).use(k({clientProviders:L,onRefresh:A,refreshRoute:m})).use(z({authorizeRoute:S,clientProviders:L,onAuthorize:U})).use(P({callbackRoute:M,clientProviders:L,onCallback:O})).use(b()).as("plugin")};export{BE as instantiateUserSession,gE as createAuthConfig,lE as absoluteAuth};
2
+ import{OAuth2RequestError as cN,ArcticFetchError as tN}from"arctic";import{Elysia as oN}from"elysia";import{generateState as zN,generateCodeVerifier as PN}from"arctic";import{Elysia as RN}from"elysia";import{AmazonCognito as _,AniList as l,Apple as h,Atlassian as u,Auth0 as c,Authentik as t,Bitbucket as o,Box as d,Coinbase as s,Discord as a,Dribbble as i,Dropbox as e,Facebook as r,Figma as NN,Intuit as EN,GitHub as SN,GitLab as IN,Google as UN,Kakao as mN,KeyCloak as MN,Lichess as TN,Line as xN,Linear as ON,LinkedIn as wN,MicrosoftEntraId as DN,MyAnimeList as $N,Notion as AN,Okta as LN,Osu as HN,Patreon as CN,Reddit as fN,Roblox as JN,Salesforce as QN,Shikimori as XN,Slack as YN,Spotify as jN,Strava as qN,Tiltify as FN,Tumblr as WN,Twitch as ZN,Twitter as BN,VK as gN,WorkOS as pN,Yahoo as nN,Yandex as GN,Zoom as KN,FortyTwo as VN}from"arctic";var F={AmazonCognito:_,AniList:l,Apple:h,Atlassian:u,Auth0:c,Authentik:t,Bitbucket:o,Box:d,Coinbase:s,Discord:a,Dribbble:i,Dropbox:e,Facebook:r,Figma:NN,FortyTwo:VN,GitHub:SN,GitLab:IN,Google:UN,Intuit:EN,Kakao:mN,KeyCloak:MN,Lichess:TN,Line:xN,Linear:ON,LinkedIn:wN,MicrosoftEntraId:DN,MyAnimeList:$N,Notion:AN,Okta:LN,Osu:HN,Patreon:CN,Reddit:fN,Roblox:JN,Salesforce:QN,Shikimori:XN,Slack:YN,Spotify:jN,Strava:qN,Tiltify:FN,Tumblr:WN,Twitch:ZN,Twitter:BN,VK:gN,WorkOS:pN,Yahoo:nN,Yandex:GN,Zoom:KN},p=Object.keys(F).reduce((N,S)=>{return N[S.toLowerCase()]=S,N},{});var B=(N)=>("refreshAccessToken"in N)&&typeof N.refreshAccessToken==="function",n=(N)=>("revokeAccessToken"in N)&&typeof N.revokeAccessToken==="function",Q=(N)=>Object.keys(F).map((S)=>S.toLowerCase()).includes(N.toLowerCase()),G=(N)=>!0,K=(N)=>N!==null&&N!==void 0&&N.trim()!=="";var V=86400000;var W=1800;var z=({clientProviders:N,authorizeRoute:S="authorize",onAuthorize:I})=>new RN().get(`/${S}/:provider`,({error:E,redirect:U,cookie:{state:M,code_verifier:T,auth_provider:m,redirect_url:O},params:{provider:w},headers:A})=>{if(w===void 0)return E("Bad Request","Provider is required");if(!Q(w))return E("Bad Request","Invalid provider");try{let D=w.toLowerCase(),{providerInstance:f,scopes:L,searchParams:$}=N[D],x=A.referer??"/";O.set({httpOnly:!0,maxAge:W,path:"/",sameSite:"lax",secure:!0,value:x}),m.set({httpOnly:!0,maxAge:W,path:"/",sameSite:"lax",secure:!0,value:w});let H=zN();M.set({httpOnly:!0,maxAge:W,path:"/",sameSite:"lax",secure:!0,value:H});let C=f.createAuthorizationURL.toString().includes("codeVerifier"),J=C?PN():void 0;C&&T.set({httpOnly:!0,maxAge:W,path:"/",sameSite:"lax",secure:!0,value:J??""});let X=C?f.createAuthorizationURL(H,J,L):f.createAuthorizationURL(H,L);return $.forEach(([j,q])=>{X.searchParams.set(j,q)}),I?.(),U(X.toString())}catch(D){if(D instanceof Error)return E("Internal Server Error",`${D.message} - ${D.stack??""}`);return E("Internal Server Error",`Unknown error: ${D}`)}});import{decodeIdToken as kN}from"arctic";import{Elysia as yN}from"elysia";import{Elysia as bN}from"elysia";var Y=()=>{let N={};return new bN({name:"sessionStore"}).state({session:N})};var P=({clientProviders:N,callbackRoute:S="authorize/callback",onCallback:I})=>new yN().use(Y()).get(`/${S}`,async({error:E,redirect:U,store:{session:M},cookie:{state:T,code_verifier:m,redirect_url:O,user_session_id:w,auth_provider:A},query:{code:D,state:f}})=>{if(!K(D)||T.value===void 0)return E("Bad Request","Invalid callback request");if(f!==T.value)return E("Bad Request","Invalid state mismatch");if(A.value===void 0)return E("Unauthorized","No auth provider found");if(!Q(A.value))return E("Unauthorized","Invalid provider");let L=A.value.toLowerCase(),{providerInstance:$}=N[L];try{T.remove();let x=$.validateAuthorizationCode.toString().includes("codeVerifier"),H=m.value;if(x&&H===void 0)return E("Bad Request","Code verifier not found and is required");let C=H??"",J=await(x?$.validateAuthorizationCode(D,C):$.validateAuthorizationCode(D));if(x)m.remove();let X=Object.fromEntries(Object.entries(kN(J.idToken())).map(([g,Z])=>[g,typeof Z==="string"?Z:void 0])),j=A.value;console.log("in route user_session_id before",w.value),await I?.({authProvider:j,decodedIdToken:X,session:M,user_session_id:w}),console.log("in route user_session_id after",w.value);let q=O.value??"/";return U(q)}catch(x){if(x instanceof Error)return E("Internal Server Error",`${x.message} - ${x.stack??""}`);return E("Internal Server Error",`Failed to validate authorization code: Unknown error: ${x}`)}});import{Elysia as vN}from"elysia";var R=({logoutRoute:N="logout",onLogout:S})=>new vN().post(`/${N}`,async({error:I,cookie:{user_session_id:E,auth_provider:U}})=>{if(U.value===void 0)return I("Unauthorized","No auth provider found");try{return S?.(),E.remove(),U.remove(),new Response("Succesfuly Logged Out",{status:204})}catch(M){if(M instanceof Error)return I("Internal Server Error",`Failed to logout: ${M.message}`);return I("Internal Server Error",`Failed to logout: Unknown error: ${M}`)}});import{Elysia as _N}from"elysia";var b=()=>new _N().use(Y()).derive(({store:{session:N},cookie:{user_session_id:S},error:I})=>({protectRoute:async(E,U)=>{if(S.value===void 0)return U?.()??I("Unauthorized","No session ID found");if(N[S.value]===void 0)return U?.()??I("Unauthorized","No session found");return E()}})).as("plugin");import{Elysia as lN}from"elysia";var k=({clientProviders:N,refreshRoute:S="refresh",onRefresh:I})=>new lN().post(`/${S}`,async({error:E,cookie:{user_refresh_token:U,auth_provider:M}})=>{if(U.value===void 0)return E("Unauthorized","No refresh token found");if(M.value===void 0)return E("Unauthorized","No auth provider found");let T=M.value.toLowerCase(),{providerInstance:m}=N[T];if(!B(m))return E("Not Implemented","Provider is not refreshable");try{return await m.refreshAccessToken(U.value),I?.(),new Response("Token refreshed",{status:204})}catch(O){if(O instanceof Error)return E("Internal Server Error",`Failed to refresh token: ${O.message}`);return E("Internal Server Error",`Faile to refresh token: Unknown error: ${O}`)}});import{Elysia as hN}from"elysia";var y=({clientProviders:N,revokeRoute:S="revoke",onRevoke:I})=>new hN().post(`/${S}/access-token`,async({error:E,cookie:{user_refresh_token:U,auth_provider:M}})=>{if(U.value===void 0)return E("Unauthorized","No refresh token found");if(M.value===void 0)return E("Unauthorized","No auth provider found");let T=M.value.toLowerCase(),{providerInstance:m}=N[T];if(!n(m))return E("Not Implemented","Provider does not support revocation");try{return await m.revokeAccessToken(U.value),I?.(),new Response("Token revoked",{status:204})}catch(O){if(O instanceof Error)return E("Internal Server Error",`Failed to revoke token: ${O.message}`);return E("Internal Server Error",`Failed to revoke token: Unknown error: ${O}`)}});import{Elysia as uN}from"elysia";var v=({clientProviders:N,statusRoute:S="auth-status",onStatus:I})=>new uN().use(Y()).get(`/${S}`,async({error:E,cookie:{user_session_id:U,auth_provider:M},store:{session:T}})=>{try{if(U.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});if(M.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let m=M.value.toLowerCase(),{providerInstance:O}=N[m];if(!B(O))return E("Not Implemented","Provider is not refreshable");let w=T[U.value];if(w===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let{user:A}=w;return I?.(),new Response(JSON.stringify({isLoggedIn:!0,user:A}),{headers:{"Content-Type":"application/json"}})}catch(m){if(m instanceof Error)return E("Internal Server Error",`Error: ${m.message} - ${m.stack??""}`);return E("Internal Server Error",`Unknown Error: ${m}`)}});var gE=async({authProvider:N,decodedIdToken:S,user_session_id:I,session:E,getUser:U,createUser:M})=>{let T=await U?.({authProvider:N,decodedIdToken:S});if(T=T??await M?.({authProvider:N,decodedIdToken:S}),!G(T))throw new Error("Internal Server Error - Invalid user schema");let m=crypto.randomUUID();E[m]={expiresAt:Date.now()+V,user:T},console.log("in func user_session_id before set",I.value),I.set({httpOnly:!0,sameSite:"lax",secure:!0,value:m}),console.log("in func user_session_id after set",I.value)},pE=(N)=>N;var lE=({config:N,authorizeRoute:S,callbackRoute:I,logoutRoute:E,statusRoute:U,refreshRoute:M,revokeRoute:T,onAuthorize:m,onCallback:O,onStatus:w,onRefresh:A,onLogout:D,onRevoke:f})=>{let L=Object.keys(N).reduce(($,x)=>{if(!Object.prototype.hasOwnProperty.call(N,x))return $;if(!Q(x))return console.error(`Provider ${x} is not supported`),$;let H=N[x];if(!H)return $;let C=x.toLowerCase(),J=p[C];if(!Q(J))return console.error(`Provider ${x} is not supported`),$;let X=F[J],{credentials:j,scopes:q=[],searchParams:g=[]}=H,Z=new X(...j);return $[C]={providerInstance:Z,scopes:q,searchParams:g},$},{});return new oN().error("OAUTH2_REQUEST_ERROR",cN).error("ARCTIC_FETCH_ERROR",tN).use(R({logoutRoute:E,onLogout:D})).use(y({clientProviders:L,onRevoke:f,revokeRoute:T})).use(v({clientProviders:L,onStatus:w,statusRoute:U})).use(k({clientProviders:L,onRefresh:A,refreshRoute:M})).use(z({authorizeRoute:S,clientProviders:L,onAuthorize:m})).use(P({callbackRoute:I,clientProviders:L,onCallback:O})).use(b()).as("plugin")};export{gE as instantiateUserSession,pE as createAuthConfig,lE as absoluteAuth};
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.4",
2
+ "version": "0.2.5",
3
3
  "name": "@absolutejs/auth",
4
4
  "description": "An authorization library for absolutejs",
5
5
  "repository": {
@@ -27,7 +27,6 @@
27
27
  "oauth"
28
28
  ],
29
29
  "types": "dist/src/index.d.ts",
30
- "dependencies": {},
31
30
  "peerDependencies": {
32
31
  "arctic": ">= 3.6.0",
33
32
  "elysia": ">= 1.2.0"
@@ -47,10 +46,5 @@
47
46
  "elysia": "1.2.25",
48
47
  "typescript": "5.8.3"
49
48
  },
50
- "module": "dist/index.js",
51
- "files": [
52
- "dist/",
53
- "README.md",
54
- "LICENSE"
55
- ]
49
+ "module": "dist/index.js"
56
50
  }
@@ -0,0 +1,119 @@
1
+ import { generateState, generateCodeVerifier } from 'arctic';
2
+ import { Elysia } from 'elysia';
3
+ import { isValidProviderKey } from './typeGuards';
4
+ import { ClientProviders } from './types';
5
+ import { COOKIE_DURATION } from './constants';
6
+
7
+ type AuthorizeProps = {
8
+ clientProviders: ClientProviders;
9
+ authorizeRoute?: string;
10
+ onAuthorize?: () => void;
11
+ };
12
+
13
+ export const authorize = ({
14
+ clientProviders,
15
+ authorizeRoute = 'authorize',
16
+ onAuthorize
17
+ }: AuthorizeProps) =>
18
+ new Elysia().get(
19
+ `/${authorizeRoute}/:provider`,
20
+ ({
21
+ error,
22
+ redirect,
23
+ cookie: { state, code_verifier, auth_provider, redirect_url },
24
+ params: { provider },
25
+ headers
26
+ }) => {
27
+ if (provider === undefined)
28
+ return error('Bad Request', 'Provider is required');
29
+
30
+ if (!isValidProviderKey(provider)) {
31
+ return error('Bad Request', 'Invalid provider');
32
+ }
33
+
34
+ try {
35
+ const normalizedProvider = provider.toLowerCase();
36
+ const { providerInstance, scopes, searchParams } =
37
+ clientProviders[normalizedProvider];
38
+
39
+ const redirectUrl = headers['referer'] ?? '/';
40
+
41
+ redirect_url.set({
42
+ httpOnly: true,
43
+ maxAge: COOKIE_DURATION,
44
+ path: '/',
45
+ sameSite: 'lax',
46
+ secure: true,
47
+ value: redirectUrl
48
+ });
49
+
50
+ auth_provider.set({
51
+ httpOnly: true,
52
+ maxAge: COOKIE_DURATION,
53
+ path: '/',
54
+ sameSite: 'lax',
55
+ secure: true,
56
+ value: provider
57
+ });
58
+
59
+ const currentState = generateState();
60
+
61
+ state.set({
62
+ httpOnly: true,
63
+ maxAge: COOKIE_DURATION,
64
+ path: '/',
65
+ sameSite: 'lax',
66
+ secure: true,
67
+ value: currentState
68
+ });
69
+
70
+ const usesCodeVerifier = providerInstance.createAuthorizationURL
71
+ .toString()
72
+ .includes('codeVerifier');
73
+ const verifier = usesCodeVerifier
74
+ ? generateCodeVerifier()
75
+ : undefined;
76
+ void (
77
+ usesCodeVerifier &&
78
+ code_verifier.set({
79
+ httpOnly: true,
80
+ maxAge: COOKIE_DURATION,
81
+ path: '/',
82
+ sameSite: 'lax',
83
+ secure: true,
84
+ value: verifier ?? ''
85
+ })
86
+ );
87
+
88
+ const authorizationURL = usesCodeVerifier
89
+ ? providerInstance.createAuthorizationURL(
90
+ currentState,
91
+ // @ts-expect-error - This is a dynamic check
92
+ verifier,
93
+ scopes
94
+ )
95
+ : // @ts-expect-error - This is a dynamic check
96
+ providerInstance.createAuthorizationURL(
97
+ currentState,
98
+ scopes
99
+ );
100
+
101
+ searchParams.forEach(([key, value]) => {
102
+ authorizationURL.searchParams.set(key, value);
103
+ });
104
+
105
+ onAuthorize?.();
106
+
107
+ return redirect(authorizationURL.toString());
108
+ } catch (err) {
109
+ if (err instanceof Error) {
110
+ return error(
111
+ 'Internal Server Error',
112
+ `${err.message} - ${err.stack ?? ''}`
113
+ );
114
+ }
115
+
116
+ return error('Internal Server Error', `Unknown error: ${err}`);
117
+ }
118
+ }
119
+ );
@@ -0,0 +1,127 @@
1
+ import { decodeIdToken } from 'arctic';
2
+ import { Elysia } from 'elysia';
3
+ import { sessionStore } from './sessionStore';
4
+ import { isNonEmptyString, isValidProviderKey } from './typeGuards';
5
+ import { ClientProviders, OnCallback } from './types';
6
+
7
+ type CallbackProps<UserType> = {
8
+ clientProviders: ClientProviders;
9
+ callbackRoute?: string;
10
+ onCallback?: OnCallback<UserType>;
11
+ };
12
+
13
+ export const callback = <UserType>({
14
+ clientProviders,
15
+ callbackRoute = 'authorize/callback',
16
+ onCallback
17
+ }: CallbackProps<UserType>) =>
18
+ new Elysia()
19
+ .use(sessionStore<UserType>())
20
+ .get(
21
+ `/${callbackRoute}`,
22
+ async ({
23
+ error,
24
+ redirect,
25
+ store: { session },
26
+ cookie: {
27
+ state: stored_state,
28
+ code_verifier,
29
+ redirect_url,
30
+ user_session_id,
31
+ auth_provider
32
+ },
33
+ query: { code, state: callback_state }
34
+ }) => {
35
+ if (
36
+ !isNonEmptyString(code) ||
37
+ stored_state.value === undefined
38
+ ) {
39
+ return error('Bad Request', 'Invalid callback request');
40
+ }
41
+
42
+ if (callback_state !== stored_state.value) {
43
+ return error('Bad Request', 'Invalid state mismatch');
44
+ }
45
+
46
+ if (auth_provider.value === undefined) {
47
+ return error('Unauthorized', 'No auth provider found');
48
+ }
49
+
50
+ if (!isValidProviderKey(auth_provider.value)) {
51
+ return error('Unauthorized', 'Invalid provider');
52
+ }
53
+
54
+ const normalizedProvider = auth_provider.value.toLowerCase();
55
+ const { providerInstance } =
56
+ clientProviders[normalizedProvider];
57
+
58
+ try {
59
+ // Clear the stored state so the next request doesn't use it
60
+ stored_state.remove();
61
+
62
+ const hasCodeVerifier =
63
+ providerInstance.validateAuthorizationCode
64
+ .toString()
65
+ .includes('codeVerifier');
66
+ const verifier = code_verifier.value;
67
+ if (hasCodeVerifier && verifier === undefined)
68
+ return error(
69
+ 'Bad Request',
70
+ 'Code verifier not found and is required'
71
+ );
72
+
73
+ const safeVerifier = verifier ?? '';
74
+ const tokens = await (hasCodeVerifier
75
+ ? providerInstance.validateAuthorizationCode(
76
+ code,
77
+ safeVerifier
78
+ )
79
+ : // @ts-expect-error - This is a dynamic check
80
+ providerInstance.validateAuthorizationCode(code));
81
+ if (hasCodeVerifier) code_verifier.remove();
82
+
83
+ const decodedIdToken = Object.fromEntries(
84
+ Object.entries(decodeIdToken(tokens.idToken())).map(
85
+ ([key, value]) => [
86
+ key,
87
+ typeof value === 'string' ? value : undefined
88
+ ]
89
+ )
90
+ );
91
+
92
+ const authProvider = auth_provider.value;
93
+
94
+ console.log(
95
+ 'in route user_session_id before',
96
+ user_session_id.value
97
+ );
98
+
99
+ await onCallback?.({
100
+ authProvider,
101
+ decodedIdToken,
102
+ session,
103
+ user_session_id
104
+ });
105
+
106
+ console.log(
107
+ 'in route user_session_id after',
108
+ user_session_id.value
109
+ );
110
+
111
+ const redirectUrl = redirect_url.value ?? '/';
112
+
113
+ return redirect(redirectUrl);
114
+ } catch (err) {
115
+ if (err instanceof Error)
116
+ return error(
117
+ 'Internal Server Error',
118
+ `${err.message} - ${err.stack ?? ''}`
119
+ );
120
+
121
+ return error(
122
+ 'Internal Server Error',
123
+ `Failed to validate authorization code: Unknown error: ${err}`
124
+ );
125
+ }
126
+ }
127
+ );
@@ -0,0 +1,13 @@
1
+ export const SECONDS_IN_A_MINUTE = 60;
2
+ export const MILLISECONDS_IN_A_SECOND = 1000;
3
+ export const MILLISECONDS_IN_A_MINUTE =
4
+ MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
5
+ export const MINUTES_IN_AN_HOUR = 60;
6
+ export const HOURS_IN_A_DAY = 24;
7
+ export const MILLISECONDS_IN_A_DAY =
8
+ MILLISECONDS_IN_A_SECOND *
9
+ SECONDS_IN_A_MINUTE *
10
+ MINUTES_IN_AN_HOUR *
11
+ HOURS_IN_A_DAY;
12
+ export const COOKIE_MINUTES = 30; // TODO : See if i can remove
13
+ export const COOKIE_DURATION = SECONDS_IN_A_MINUTE * COOKIE_MINUTES;
package/src/index.ts ADDED
@@ -0,0 +1,88 @@
1
+ import { OAuth2RequestError, ArcticFetchError } from 'arctic';
2
+ import { Elysia } from 'elysia';
3
+ import { authorize } from './authorize';
4
+ import { callback } from './callback';
5
+ import { logout } from './logout';
6
+ import { protectRoute } from './protectRoute';
7
+ import { normalizedProviderKeys, providers } from './providers';
8
+ import { refresh } from './refresh';
9
+ import { revoke } from './revoke';
10
+ import { status } from './status';
11
+ import { isValidProviderKey } from './typeGuards';
12
+ import { AbsoluteAuthProps, ClientProviders } from './types';
13
+
14
+ export const absoluteAuth = <UserType>({
15
+ config,
16
+ authorizeRoute,
17
+ callbackRoute,
18
+ logoutRoute,
19
+ statusRoute,
20
+ refreshRoute,
21
+ revokeRoute,
22
+ onAuthorize,
23
+ onCallback,
24
+ onStatus,
25
+ onRefresh,
26
+ onLogout,
27
+ onRevoke
28
+ }: AbsoluteAuthProps<UserType>) => {
29
+ const clientProviders = Object.keys(config).reduce<ClientProviders>(
30
+ (acc, key) => {
31
+ if (!Object.prototype.hasOwnProperty.call(config, key)) return acc;
32
+
33
+ if (!isValidProviderKey(key)) {
34
+ console.error(`Provider ${key} is not supported`);
35
+
36
+ return acc;
37
+ }
38
+
39
+ const options = config[key];
40
+ if (!options) return acc;
41
+
42
+ const normalizedProvider = key.toLowerCase();
43
+ const originalProviderKey =
44
+ normalizedProviderKeys[normalizedProvider];
45
+
46
+ if (!isValidProviderKey(originalProviderKey)) {
47
+ console.error(`Provider ${key} is not supported`);
48
+
49
+ return acc;
50
+ }
51
+
52
+ const Provider = providers[originalProviderKey];
53
+ const { credentials, scopes = [], searchParams = [] } = options;
54
+
55
+ // @ts-expect-error: dynamic constructor parameters
56
+ const providerInstance = new Provider(...credentials);
57
+
58
+ acc[normalizedProvider] = {
59
+ providerInstance,
60
+ scopes,
61
+ searchParams
62
+ };
63
+
64
+ return acc;
65
+ },
66
+ {}
67
+ );
68
+
69
+ return new Elysia()
70
+ .error('OAUTH2_REQUEST_ERROR', OAuth2RequestError)
71
+ .error('ARCTIC_FETCH_ERROR', ArcticFetchError)
72
+ .use(logout({ logoutRoute, onLogout }))
73
+ .use(revoke({ clientProviders, onRevoke, revokeRoute }))
74
+ .use(status<UserType>({ clientProviders, onStatus, statusRoute }))
75
+ .use(refresh({ clientProviders, onRefresh, refreshRoute }))
76
+ .use(authorize({ authorizeRoute, clientProviders, onAuthorize }))
77
+ .use(
78
+ callback<UserType>({
79
+ callbackRoute,
80
+ clientProviders,
81
+ onCallback
82
+ })
83
+ )
84
+ .use(protectRoute())
85
+ .as('plugin');
86
+ };
87
+
88
+ export * from './utils';
package/src/logout.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { Elysia } from 'elysia';
2
+
3
+ type LogoutProps = {
4
+ logoutRoute?: string;
5
+ onLogout?: () => void;
6
+ };
7
+
8
+ export const logout = ({ logoutRoute = 'logout', onLogout }: LogoutProps) =>
9
+ new Elysia().post(
10
+ `/${logoutRoute}`,
11
+ async ({ error, cookie: { user_session_id, auth_provider } }) => {
12
+ if (auth_provider.value === undefined) {
13
+ return error('Unauthorized', 'No auth provider found');
14
+ }
15
+
16
+ try {
17
+ onLogout?.();
18
+
19
+ user_session_id.remove();
20
+ auth_provider.remove();
21
+
22
+ return new Response('Succesfuly Logged Out', {
23
+ status: 204
24
+ });
25
+ } catch (err) {
26
+ if (err instanceof Error) {
27
+ return error(
28
+ 'Internal Server Error',
29
+ `Failed to logout: ${err.message}`
30
+ );
31
+ }
32
+
33
+ return error(
34
+ 'Internal Server Error',
35
+ `Failed to logout: Unknown error: ${err}`
36
+ );
37
+ }
38
+ }
39
+ );
@@ -0,0 +1,33 @@
1
+ import { Elysia } from 'elysia';
2
+ import { sessionStore } from './sessionStore';
3
+
4
+ export const protectRoute = <UserType>() =>
5
+ new Elysia()
6
+ .use(sessionStore<UserType>())
7
+ .derive(
8
+ ({ store: { session }, cookie: { user_session_id }, error }) => ({
9
+ protectRoute: async (
10
+ handleAuth: () => Promise<Response>,
11
+ handleAuthFail?: () => Promise<Response>
12
+ ) => {
13
+ if (user_session_id.value === undefined) {
14
+ return (
15
+ handleAuthFail?.() ??
16
+ error('Unauthorized', 'No session ID found')
17
+ );
18
+ }
19
+
20
+ const userSession = session[user_session_id.value];
21
+
22
+ if (userSession === undefined) {
23
+ return (
24
+ handleAuthFail?.() ??
25
+ error('Unauthorized', 'No session found')
26
+ );
27
+ }
28
+
29
+ return handleAuth();
30
+ }
31
+ })
32
+ )
33
+ .as('plugin');
@@ -0,0 +1,109 @@
1
+ import {
2
+ AmazonCognito,
3
+ AniList,
4
+ Apple,
5
+ Atlassian,
6
+ Auth0,
7
+ Authentik,
8
+ Bitbucket,
9
+ Box,
10
+ Coinbase,
11
+ Discord,
12
+ Dribbble,
13
+ Dropbox,
14
+ Facebook,
15
+ Figma,
16
+ Intuit,
17
+ GitHub,
18
+ GitLab,
19
+ Google,
20
+ Kakao,
21
+ KeyCloak,
22
+ Lichess,
23
+ Line,
24
+ Linear,
25
+ LinkedIn,
26
+ MicrosoftEntraId,
27
+ MyAnimeList,
28
+ Notion,
29
+ Okta,
30
+ Osu,
31
+ Patreon,
32
+ Reddit,
33
+ Roblox,
34
+ Salesforce,
35
+ Shikimori,
36
+ Slack,
37
+ Spotify,
38
+ Strava,
39
+ Tiltify,
40
+ Tumblr,
41
+ Twitch,
42
+ Twitter,
43
+ VK,
44
+ WorkOS,
45
+ Yahoo,
46
+ Yandex,
47
+ Zoom,
48
+ FortyTwo
49
+ } from 'arctic';
50
+
51
+ // TODO: When arctic adds better way to get the providers give a type to the object
52
+ // eslint-disable-next-line custom/explicit-object-types
53
+ export const providers = {
54
+ AmazonCognito,
55
+ AniList,
56
+ Apple,
57
+ Atlassian,
58
+ Auth0,
59
+ Authentik,
60
+ Bitbucket,
61
+ Box,
62
+ Coinbase,
63
+ Discord,
64
+ Dribbble,
65
+ Dropbox,
66
+ Facebook,
67
+ Figma,
68
+ FortyTwo,
69
+ GitHub,
70
+ GitLab,
71
+ Google,
72
+ Intuit,
73
+ Kakao,
74
+ KeyCloak,
75
+ Lichess,
76
+ Line,
77
+ Linear,
78
+ LinkedIn,
79
+ MicrosoftEntraId,
80
+ MyAnimeList,
81
+ Notion,
82
+ Okta,
83
+ Osu,
84
+ Patreon,
85
+ Reddit,
86
+ Roblox,
87
+ Salesforce,
88
+ Shikimori,
89
+ Slack,
90
+ Spotify,
91
+ Strava,
92
+ Tiltify,
93
+ Tumblr,
94
+ Twitch,
95
+ Twitter,
96
+ VK,
97
+ WorkOS,
98
+ Yahoo,
99
+ Yandex,
100
+ Zoom
101
+ };
102
+
103
+ export const normalizedProviderKeys = Object.keys(providers).reduce<
104
+ Record<string, string>
105
+ >((map, key) => {
106
+ map[key.toLowerCase()] = key;
107
+
108
+ return map;
109
+ }, {});