@absolutejs/auth 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{OAuth2RequestError as cN,ArcticFetchError as dN}from"arctic";import{Elysia as oN}from"elysia";import{generateState as VN,generateCodeVerifier as zN}from"arctic";import{Elysia as RN}from"elysia";import{AmazonCognito as v,AniList as h,Apple as l,Atlassian as u,Auth0 as c,Authentik as d,Bitbucket as o,Box as t,Coinbase as s,Discord as a,Dribbble as i,Dropbox as e,Facebook as r,Figma as NN,Intuit as EN,GitHub as IN,GitLab as SN,Google as TN,Kakao as UN,KeyCloak as mN,Lichess as MN,Line as xN,Linear as ON,LinkedIn as wN,MicrosoftEntraId as DN,MyAnimeList as $N,Notion as AN,Okta as nN,Osu as LN,Patreon as HN,Reddit as CN,Roblox as fN,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 PN,FortyTwo as KN}from"arctic";var q={AmazonCognito:v,AniList:h,Apple:l,Atlassian:u,Auth0:c,Authentik:d,Bitbucket:o,Box:t,Coinbase:s,Discord:a,Dribbble:i,Dropbox:e,Facebook:r,Figma:NN,FortyTwo:KN,GitHub:IN,GitLab:SN,Google:TN,Intuit:EN,Kakao:UN,KeyCloak:mN,Lichess:MN,Line:xN,Linear:ON,LinkedIn:wN,MicrosoftEntraId:DN,MyAnimeList:$N,Notion:AN,Okta:nN,Osu:LN,Patreon:HN,Reddit:CN,Roblox:fN,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:PN},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,P=(N)=>N!==null&&N!==void 0&&N.trim()!=="";var K=86400000;var F=1800;var V=({clientProviders:N,authorizeRoute:S="authorize",onAuthorize:U})=>new RN().get(`/${S}/:provider`,({error:E,redirect:I,cookie:{state:T,code_verifier:O,auth_provider:m,redirect_url:x},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:C,scopes:n,searchParams:D}=N[w],M=A.referer??"/";x.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:M}),m.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:$});let L=VN();T.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:L});let H=C.createAuthorizationURL.toString().includes("codeVerifier"),f=H?zN():void 0;H&&O.set({httpOnly:!0,maxAge:F,path:"/",sameSite:"lax",secure:!0,value:f??""});let Q=H?C.createAuthorizationURL(L,f,n):C.createAuthorizationURL(L,n);return D.forEach(([Y,j])=>{Q.searchParams.set(Y,j)}),U?.(),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 z=({clientProviders:N,callbackRoute:S="authorize/callback",onCallback:U})=>new yN().use(X()).get(`/${S}`,async({error:E,redirect:I,store:{session:T},cookie:{state:O,code_verifier:m,redirect_url:x,user_session_id:$,auth_provider:A},query:{code:w,state:C}})=>{if(!P(w)||O.value===void 0)return E("Bad Request","Invalid callback request");if(C!==O.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 n=A.value.toLowerCase(),{providerInstance:D}=N[n];try{O.remove();let M=D.validateAuthorizationCode.toString().includes("codeVerifier"),L=m.value;if(M&&L===void 0)return E("Bad Request","Code verifier not found and is required");let H=L??"",f=await(M?D.validateAuthorizationCode(w,H):D.validateAuthorizationCode(w));if(M)m.remove();let Q=Object.fromEntries(Object.entries(kN(f.idToken())).map(([B,W])=>[B,typeof W==="string"?W:void 0])),Y=A.value;await U?.({authProvider:Y,decodedIdToken:Q,session:T,user_session_id:$});let j=x.value??"/";return I(j)}catch(M){if(M instanceof Error)return E("Internal Server Error",`${M.message} - ${M.stack??""}`);return E("Internal Server Error",`Failed to validate authorization code: Unknown error: ${M}`)}});import{Elysia as _N}from"elysia";var R=({logoutRoute:N="logout",onLogout:S})=>new _N().post(`/${N}`,async({error:U,cookie:{user_session_id:E,auth_provider:I}})=>{if(I.value===void 0)return U("Unauthorized","No auth provider found");try{return S?.(),E.remove(),I.remove(),new Response("Succesfuly Logged Out",{status:204})}catch(T){if(T instanceof Error)return U("Internal Server Error",`Failed to logout: ${T.message}`);return U("Internal Server Error",`Failed to logout: Unknown error: ${T}`)}});import{Elysia as vN}from"elysia";var b=()=>new vN().use(X()).derive(({store:{session:N},cookie:{user_session_id:S},error:U})=>({protectRoute:async(E,I)=>{if(S.value===void 0)return I?.()??U("Unauthorized","No session ID found");if(N[S.value]===void 0)return I?.()??U("Unauthorized","No session found");return E()}})).as("plugin");import{Elysia as hN}from"elysia";var k=({clientProviders:N,refreshRoute:S="refresh",onRefresh:U})=>new hN().post(`/${S}`,async({error:E,cookie:{user_refresh_token:I,auth_provider:T}})=>{if(I.value===void 0)return E("Unauthorized","No refresh token found");if(T.value===void 0)return E("Unauthorized","No auth provider found");let O=T.value.toLowerCase(),{providerInstance:m}=N[O];if(!Z(m))return E("Not Implemented","Provider is not refreshable");try{return await m.refreshAccessToken(I.value),U?.(),new Response("Token refreshed",{status:204})}catch(x){if(x instanceof Error)return E("Internal Server Error",`Failed to refresh token: ${x.message}`);return E("Internal Server Error",`Faile to refresh token: Unknown error: ${x}`)}});import{Elysia as lN}from"elysia";var y=({clientProviders:N,revokeRoute:S="revoke",onRevoke:U})=>new lN().post(`/${S}/access-token`,async({error:E,cookie:{user_refresh_token:I,auth_provider:T}})=>{if(I.value===void 0)return E("Unauthorized","No refresh token found");if(T.value===void 0)return E("Unauthorized","No auth provider found");let O=T.value.toLowerCase(),{providerInstance:m}=N[O];if(!p(m))return E("Not Implemented","Provider does not support revocation");try{return await m.revokeAccessToken(I.value),U?.(),new Response("Token revoked",{status:204})}catch(x){if(x instanceof Error)return E("Internal Server Error",`Failed to revoke token: ${x.message}`);return E("Internal Server Error",`Failed to revoke token: Unknown error: ${x}`)}});import{Elysia as uN}from"elysia";var _=({clientProviders:N,statusRoute:S="auth-status",onStatus:U})=>new uN().use(X()).get(`/${S}`,async({error:E,cookie:{user_session_id:I,auth_provider:T},store:{session:O}})=>{try{if(I.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});if(T.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let m=T.value.toLowerCase(),{providerInstance:x}=N[m];if(!Z(x))return E("Not Implemented","Provider is not refreshable");let $=O[I.value];if($===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let{user:A}=$;return U?.(),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 BE=async({user_session_id:N,session:S,getUser:U,createUser:E})=>{let I=await U();if(I=I??await E(),!G(I))throw new Error("Internal Server Error - Invalid user schema");let T=crypto.randomUUID();S[T]={expiresAt:Date.now()+K,user:I},N.set({httpOnly:!0,sameSite:"lax",secure:!0,value:T})},gE=(N)=>N;var hE=({config:N,authorizeRoute:S,callbackRoute:U,logoutRoute:E,statusRoute:I,refreshRoute:T,revokeRoute:O,onAuthorize:m,onCallback:x,onStatus:$,onRefresh:A,onLogout:w,onRevoke:C})=>{let n=Object.keys(N).reduce((D,M)=>{if(!Object.prototype.hasOwnProperty.call(N,M))return D;if(!J(M))return console.error(`Provider ${M} is not supported`),D;let L=N[M];if(!L)return D;let H=M.toLowerCase(),f=g[H];if(!J(f))return console.error(`Provider ${M} is not supported`),D;let Q=q[f],{credentials:Y,scopes:j=[],searchParams:B=[]}=L,W=new Q(...Y);return D[H]={providerInstance:W,scopes:j,searchParams:B},D},{});return new oN().error("OAUTH2_REQUEST_ERROR",cN).error("ARCTIC_FETCH_ERROR",dN).use(R({logoutRoute:E,onLogout:w})).use(y({clientProviders:n,onRevoke:C,revokeRoute:O})).use(_({clientProviders:n,onStatus:$,statusRoute:I})).use(k({clientProviders:n,onRefresh:A,refreshRoute:T})).use(V({authorizeRoute:S,clientProviders:n,onAuthorize:m})).use(z({callbackRoute:U,clientProviders:n,onCallback:x})).use(b()).as("plugin")};export{BE as instantiateUserSession,gE as createAuthConfig,hE as absoluteAuth};
2
+ import{OAuth2RequestError as oN,ArcticFetchError as sN}from"arctic";import{Elysia as aN}from"elysia";import{generateState as RN,generateCodeVerifier as bN}from"arctic";import{Elysia as kN}from"elysia";import{AmazonCognito as l,AniList as u,Apple as c,Atlassian as d,Auth0 as t,Authentik as o,Bitbucket as s,Box as a,Coinbase as i,Discord as e,Dribbble as r,Dropbox as NN,Facebook as EN,Figma as IN,Intuit as SN,GitHub as UN,GitLab as mN,Google as MN,Kakao as TN,KeyCloak as wN,Lichess as xN,Line as DN,Linear as ON,LinkedIn as $N,MicrosoftEntraId as AN,MyAnimeList as LN,Notion as HN,Okta as CN,Osu as JN,Patreon as QN,Reddit as XN,Roblox as YN,Salesforce as jN,Shikimori as FN,Slack as WN,Spotify as ZN,Strava as qN,Tiltify as BN,Tumblr as nN,Twitch as GN,Twitter as VN,VK as KN,WorkOS as gN,Yahoo as pN,Yandex as zN,Zoom as PN,FortyTwo as fN}from"arctic";var Z={AmazonCognito:l,AniList:u,Apple:c,Atlassian:d,Auth0:t,Authentik:o,Bitbucket:s,Box:a,Coinbase:i,Discord:e,Dribbble:r,Dropbox:NN,Facebook:EN,Figma:IN,FortyTwo:fN,GitHub:UN,GitLab:mN,Google:MN,Intuit:SN,Kakao:TN,KeyCloak:wN,Lichess:xN,Line:DN,Linear:ON,LinkedIn:$N,MicrosoftEntraId:AN,MyAnimeList:LN,Notion:HN,Okta:CN,Osu:JN,Patreon:QN,Reddit:XN,Roblox:YN,Salesforce:jN,Shikimori:FN,Slack:WN,Spotify:ZN,Strava:qN,Tiltify:BN,Tumblr:nN,Twitch:GN,Twitter:VN,VK:KN,WorkOS:gN,Yahoo:pN,Yandex:zN,Zoom:PN},K=Object.keys(Z).reduce((N,m)=>{return N[m.toLowerCase()]=m,N},{});var B=(N)=>("refreshAccessToken"in N)&&typeof N.refreshAccessToken==="function",g=(N)=>("revokeAccessToken"in N)&&typeof N.revokeAccessToken==="function",Y=(N)=>Object.keys(Z).map((m)=>m.toLowerCase()).includes(N.toLowerCase()),p=(N)=>!0,z=(N)=>N!==null&&N!==void 0&&N.trim()!=="";var P=86400000;var q=1800;var f=({clientProviders:N,authorizeRoute:m="authorize",onAuthorize:I})=>new kN().get(`/${m}/:provider`,({error:E,redirect:S,cookie:{state:U,code_verifier:x,auth_provider:M,redirect_url:T},params:{provider:D},headers:A})=>{if(D===void 0)return E("Bad Request","Provider is required");if(!Y(D))return E("Bad Request","Invalid provider");try{let O=D.toLowerCase(),{providerInstance:X,scopes:L,searchParams:$}=N[O],w=A.referer??"/";T.set({httpOnly:!0,maxAge:q,path:"/",sameSite:"lax",secure:!0,value:w}),M.set({httpOnly:!0,maxAge:q,path:"/",sameSite:"lax",secure:!0,value:D});let H=RN();U.set({httpOnly:!0,maxAge:q,path:"/",sameSite:"lax",secure:!0,value:H});let C=X.createAuthorizationURL.toString().includes("codeVerifier"),J=C?bN():void 0;C&&x.set({httpOnly:!0,maxAge:q,path:"/",sameSite:"lax",secure:!0,value:J??""});let Q=C?X.createAuthorizationURL(H,J,L):X.createAuthorizationURL(H,L);return $.forEach(([j,W])=>{Q.searchParams.set(j,W)}),I?.(),S(Q.toString())}catch(O){if(O instanceof Error)return E("Internal Server Error",`${O.message} - ${O.stack??""}`);return E("Internal Server Error",`Unknown error: ${O}`)}});import{decodeIdToken as vN}from"arctic";import{Elysia as hN}from"elysia";import{Elysia as yN}from"elysia";var F=()=>{let N={};return new yN({name:"sessionStore"}).state({session:N})};async function R(N,m){let I=_N[N];if(!I)throw new Error(`Unknown provider: ${N}`);let E=I.endpoint,S=I.method??"GET",U={};if(I.headers)Object.assign(U,I.headers);if(I.authIn==="header")U.Authorization=`Bearer ${m}`;else{let T=I.tokenParam??"access_token",D=E.includes("?")?"&":"?";E=`${E}${D}${T}=${encodeURIComponent(m)}`}let x={method:S,headers:U};if(S==="POST"&&I.body)U["Content-Type"]="application/json",x.body=JSON.stringify(I.body);let M=await fetch(E,x);if(!M.ok){let T=await M.text();throw new Error(`Failed to fetch ${N} profile: ${M.status} ${T}`)}return M.json()}var _N={facebook:{endpoint:"https://graph.facebook.com/me?fields=id,name,picture,email",method:"GET",authIn:"query",tokenParam:"access_token"},anilist:{endpoint:"https://graphql.anilist.co",method:"POST",authIn:"header",headers:{"Content-Type":"application/json",Accept:"application/json"},body:{query:"query { Viewer { id name } }"}},atlassian:{endpoint:"https://api.atlassian.com/me",method:"GET",authIn:"header"},battlenet:{endpoint:"https://oauth.battle.net/userinfo",method:"GET",authIn:"header"},bitbucket:{endpoint:"https://api.bitbucket.org/2.0/user",method:"GET",authIn:"header"},box:{endpoint:"https://api.box.com/2.0/users/me",method:"GET",authIn:"header"},bungie:{endpoint:"https://www.bungie.net/Platform/User/GetCurrentBungieNetUser",method:"GET",authIn:"header",headers:{"X-API-Key":"<YOUR_API_KEY>"}},coinbase:{endpoint:"https://api.coinbase.com/v2/user",method:"GET",authIn:"header"},discord:{endpoint:"https://discord.com/api/users/@me",method:"GET",authIn:"header"},donationAlerts:{endpoint:"https://www.donationalerts.com/api/v1/user",method:"GET",authIn:"header"},dribbble:{endpoint:"https://api.dribbble.com/v2/user",method:"GET",authIn:"header"},dropbox:{endpoint:"https://api.dropboxapi.com/2/users/get_current_account",method:"GET",authIn:"header"},epicGames:{endpoint:"https://api.epicgames.dev/epic/oauth/v2/userInfo",method:"GET",authIn:"header"},etsy:{endpoint:"https://openapi.etsy.com/v3/application/users/me",method:"GET",authIn:"header"},figma:{endpoint:"https://api.figma.com/v1/me",method:"GET",authIn:"header"},gitea:{endpoint:"https://<YOUR_GITEA_DOMAIN>/api/v1/user",method:"GET",authIn:"header"},github:{endpoint:"https://api.github.com/user",method:"GET",authIn:"header"},gitlab:{endpoint:"https://gitlab.com/api/v4/user",method:"GET",authIn:"header"},intuit:{endpoint:"https://oauth.platform.intuit.com/oauth2/v1/userinfo",method:"GET",authIn:"header"},kakao:{endpoint:"https://kapi.kakao.com/v2/user/me",method:"GET",authIn:"header"},kick:{endpoint:"https://api.kick.com/v1/user",method:"GET",authIn:"header"},lichess:{endpoint:"https://lichess.org/api/account",method:"GET",authIn:"header"},line:{endpoint:"https://api.line.me/v2/profile",method:"GET",authIn:"header"},linear:{endpoint:"https://api.linear.app/graphql",method:"POST",authIn:"header",headers:{"Content-Type":"application/json",Accept:"application/json"},body:{query:"query { viewer { id name } }"}},mastodon:{endpoint:"https://<YOUR_INSTANCE>/api/v1/accounts/verify_credentials",method:"GET",authIn:"header"},mercadoLibre:{endpoint:"https://api.mercadolibre.com/users/me",method:"GET",authIn:"header"},mercadoPago:{endpoint:"https://api.mercadopago.com/v1/users/me",method:"GET",authIn:"header"},myAnimeList:{endpoint:"https://api.myanimelist.net/v2/users/@me",method:"GET",authIn:"header"},naver:{endpoint:"https://openapi.naver.com/v1/nid/me",method:"GET",authIn:"header"},notion:{endpoint:"https://api.notion.com/v1/users/me",method:"GET",authIn:"header"},osu:{endpoint:"https://osu.ppy.sh/api/v2/me",method:"GET",authIn:"header"},patreon:{endpoint:"https://www.patreon.com/api/oauth2/v2/identity",method:"GET",authIn:"header"},polar:{endpoint:"https://www.polaraccesslink.com/v3/users/<USER_ID>",method:"GET",authIn:"header"},reddit:{endpoint:"https://oauth.reddit.com/api/v1/me",method:"GET",authIn:"header"},roblox:{endpoint:"https://apis.roblox.com/oauth/v1/userinfo",method:"GET",authIn:"header"},shikimori:{endpoint:"https://shikimori.one/api/users/whoami",method:"GET",authIn:"header"},slack:{endpoint:"https://slack.com/api/users.identity",method:"GET",authIn:"query",tokenParam:"token"},spotify:{endpoint:"https://api.spotify.com/v1/me",method:"GET",authIn:"header"},startGg:{endpoint:"https://api.start.gg/gql/alpha",method:"POST",authIn:"header",headers:{"Content-Type":"application/json",Accept:"application/json"},body:{query:"query { currentUser { id slug email player { gamerTag } } }"}},strava:{endpoint:"https://www.strava.com/api/v3/athlete",method:"GET",authIn:"header"},synology:{endpoint:"https://<YOUR_DOMAIN>/webman/sso/SSOUserInfo.cgi",method:"GET",authIn:"header"},tiktok:{endpoint:"https://open.douyin.com/oauth/userinfo",method:"GET",authIn:"query",tokenParam:"access_token"},tiltify:{endpoint:"https://tiltify.com/api/v3/me",method:"GET",authIn:"header"},tumblr:{endpoint:"https://api.tumblr.com/v2/user/info",method:"GET",authIn:"header"},twitch:{endpoint:"https://api.twitch.tv/helix/users",method:"GET",authIn:"header"},twitter:{endpoint:"https://api.twitter.com/2/users/me",method:"GET",authIn:"header"},vk:{endpoint:"https://api.vk.com/method/users.get",method:"GET",authIn:"query",tokenParam:"access_token"},zoom:{endpoint:"https://api.zoom.us/v2/users/me",method:"GET",authIn:"header"}};var b=({clientProviders:N,callbackRoute:m="authorize/callback",onCallback:I})=>new hN().use(F()).get(`/${m}`,async({error:E,redirect:S,store:{session:U},cookie:{state:x,code_verifier:M,redirect_url:T,user_session_id:D,auth_provider:A},query:{code:O,state:X}})=>{if(!z(O)||x.value===void 0)return E("Bad Request","Invalid callback request");if(X!==x.value)return E("Bad Request","Invalid state mismatch");if(A.value===void 0)return E("Unauthorized","No auth provider found");if(!Y(A.value))return E("Unauthorized","Invalid provider");let L=A.value.toLowerCase(),{providerInstance:$}=N[L];try{x.remove();let w=$.validateAuthorizationCode.toString().includes("codeVerifier"),H=M.value;if(w&&H===void 0)return E("Bad Request","Code verifier not found and is required");let C=H??"",J=await(w?$.validateAuthorizationCode(O,C):$.validateAuthorizationCode(O));if(w)M.remove();let Q,j=A.value;try{Q=Object.fromEntries(Object.entries(vN(J.idToken())).map(([G,V])=>[G,typeof V==="string"?V:void 0]))}catch(n){Q=await R(j,J.accessToken())}await I?.({authProvider:j,userProfile:Q,session:U,user_session_id:D});let W=T.value??"/";return S(W)}catch(w){if(w instanceof Error)return E("Internal Server Error",`${w.message} - ${w.stack??""}`);return E("Internal Server Error",`Failed to validate authorization code: Unknown error: ${w}`)}});import{Elysia as lN}from"elysia";var k=({logoutRoute:N="logout",onLogout:m})=>new lN().post(`/${N}`,async({error:I,cookie:{user_session_id:E,auth_provider:S}})=>{if(S.value===void 0)return I("Unauthorized","No auth provider found");try{return m?.(),E.remove(),S.remove(),new Response("Succesfuly Logged Out",{status:204})}catch(U){if(U instanceof Error)return I("Internal Server Error",`Failed to logout: ${U.message}`);return I("Internal Server Error",`Failed to logout: Unknown error: ${U}`)}});import{Elysia as uN}from"elysia";var y=()=>new uN().use(F()).derive(({store:{session:N},cookie:{user_session_id:m},error:I})=>({protectRoute:async(E,S)=>{if(m.value===void 0)return S?.()??I("Unauthorized","No session ID found");if(N[m.value]===void 0)return S?.()??I("Unauthorized","No session found");return E()}})).as("plugin");import{Elysia as cN}from"elysia";var _=({clientProviders:N,refreshRoute:m="refresh",onRefresh:I})=>new cN().post(`/${m}`,async({error:E,cookie:{user_refresh_token:S,auth_provider:U}})=>{if(S.value===void 0)return E("Unauthorized","No refresh token found");if(U.value===void 0)return E("Unauthorized","No auth provider found");let x=U.value.toLowerCase(),{providerInstance:M}=N[x];if(!B(M))return E("Not Implemented","Provider is not refreshable");try{return await M.refreshAccessToken(S.value),I?.(),new Response("Token refreshed",{status:204})}catch(T){if(T instanceof Error)return E("Internal Server Error",`Failed to refresh token: ${T.message}`);return E("Internal Server Error",`Faile to refresh token: Unknown error: ${T}`)}});import{Elysia as dN}from"elysia";var v=({clientProviders:N,revokeRoute:m="revoke",onRevoke:I})=>new dN().post(`/${m}/access-token`,async({error:E,cookie:{user_refresh_token:S,auth_provider:U}})=>{if(S.value===void 0)return E("Unauthorized","No refresh token found");if(U.value===void 0)return E("Unauthorized","No auth provider found");let x=U.value.toLowerCase(),{providerInstance:M}=N[x];if(!g(M))return E("Not Implemented","Provider does not support revocation");try{return await M.revokeAccessToken(S.value),I?.(),new Response("Token revoked",{status:204})}catch(T){if(T instanceof Error)return E("Internal Server Error",`Failed to revoke token: ${T.message}`);return E("Internal Server Error",`Failed to revoke token: Unknown error: ${T}`)}});import{Elysia as tN}from"elysia";var h=({clientProviders:N,statusRoute:m="auth-status",onStatus:I})=>new tN().use(F()).get(`/${m}`,async({error:E,cookie:{user_session_id:S,auth_provider:U},store:{session:x}})=>{try{if(S.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});if(U.value===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let M=U.value.toLowerCase(),{providerInstance:T}=N[M];if(!B(T))return E("Not Implemented","Provider is not refreshable");let D=x[S.value];if(D===void 0)return new Response(JSON.stringify({isLoggedIn:!1,user:null}),{headers:{"Content-Type":"application/json"}});let{user:A}=D;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 zE=async({user_session_id:N,session:m,getUser:I,createUser:E})=>{let S=await I();if(S=S??await E(),!p(S))throw new Error("Internal Server Error - Invalid user schema");let U=crypto.randomUUID();m[U]={expiresAt:Date.now()+P,user:S},N.set({httpOnly:!0,sameSite:"lax",secure:!0,value:U})},PE=(N)=>N;var tE=({config:N,authorizeRoute:m,callbackRoute:I,logoutRoute:E,statusRoute:S,refreshRoute:U,revokeRoute:x,onAuthorize:M,onCallback:T,onStatus:D,onRefresh:A,onLogout:O,onRevoke:X})=>{let L=Object.keys(N).reduce(($,w)=>{if(!Object.prototype.hasOwnProperty.call(N,w))return $;if(!Y(w))return console.error(`Provider ${w} is not supported`),$;let H=N[w];if(!H)return $;let C=w.toLowerCase(),J=K[C];if(!Y(J))return console.error(`Provider ${w} is not supported`),$;let Q=Z[J],{credentials:j,scopes:W=[],searchParams:n=[]}=H,G=new Q(...j);return $[C]={providerInstance:G,scopes:W,searchParams:n},$},{});return new aN().error("OAUTH2_REQUEST_ERROR",oN).error("ARCTIC_FETCH_ERROR",sN).use(k({logoutRoute:E,onLogout:O})).use(v({clientProviders:L,onRevoke:X,revokeRoute:x})).use(h({clientProviders:L,onStatus:D,statusRoute:S})).use(_({clientProviders:L,onRefresh:A,refreshRoute:U})).use(f({authorizeRoute:m,clientProviders:L,onAuthorize:M})).use(b({callbackRoute:I,clientProviders:L,onCallback:T})).use(y()).as("plugin")};export{zE as instantiateUserSession,PE as createAuthConfig,tE as absoluteAuth};
@@ -0,0 +1,10 @@
1
+ export type ProfileRequest = {
2
+ endpoint: string;
3
+ method?: 'GET' | 'POST';
4
+ authIn: 'header' | 'query';
5
+ tokenParam?: string;
6
+ headers?: Record<string, string>;
7
+ body?: any;
8
+ };
9
+ export declare function fetchUserProfile(provider: string, accessToken: string): Promise<any>;
10
+ export declare const profileConfigs: Record<string, ProfileRequest>;
@@ -15,15 +15,15 @@ export type Providers = keyof typeof providers;
15
15
  export type SessionRecord<UserType> = Record<string, SessionData<UserType> | undefined>;
16
16
  export type UserFunctionProps = {
17
17
  authProvider: string;
18
- decodedIdToken: {
18
+ userProfile: {
19
19
  [key: string]: string | undefined;
20
20
  };
21
21
  };
22
- export type CreateUser<UserType> = ({ decodedIdToken, authProvider }: UserFunctionProps) => Promise<UserType>;
23
- export type GetUser<UserType> = ({ decodedIdToken, authProvider }: UserFunctionProps) => Promise<UserType | null>;
24
- export type OnCallback<UserType> = ({ authProvider, decodedIdToken, session, user_session_id }: {
22
+ export type CreateUser<UserType> = ({ userProfile, authProvider }: UserFunctionProps) => Promise<UserType>;
23
+ export type GetUser<UserType> = ({ userProfile, authProvider }: UserFunctionProps) => Promise<UserType | null>;
24
+ export type OnCallback<UserType> = ({ authProvider, userProfile, session, user_session_id }: {
25
25
  authProvider: string;
26
- decodedIdToken: {
26
+ userProfile: {
27
27
  [key: string]: string | undefined;
28
28
  };
29
29
  session: SessionRecord<UserType>;
@@ -49,4 +49,14 @@ export type ClientProviders = Record<string, {
49
49
  scopes: string[];
50
50
  searchParams: [string, string][];
51
51
  }>;
52
+ export type InsantiateUserSessionProps<UserType> = {
53
+ authProvider: string;
54
+ userProfile: {
55
+ [key: string]: string | undefined;
56
+ };
57
+ session: SessionRecord<UserType>;
58
+ user_session_id: Cookie<string | undefined>;
59
+ createUser: () => UserType | Promise<UserType>;
60
+ getUser: () => UserType | Promise<UserType | null>;
61
+ };
52
62
  export {};
@@ -1,15 +1,3 @@
1
- import { Cookie } from 'elysia';
2
- import { AbsoluteAuthProps, SessionRecord } from './types';
3
- type InsantiateUserSessionProps<UserType> = {
4
- authProvider: string;
5
- decodedIdToken: {
6
- [key: string]: string | undefined;
7
- };
8
- session: SessionRecord<UserType>;
9
- user_session_id: Cookie<string | undefined>;
10
- createUser: () => UserType | Promise<UserType>;
11
- getUser: () => UserType | Promise<UserType | null>;
12
- };
1
+ import { AbsoluteAuthProps, InsantiateUserSessionProps } from './types';
13
2
  export declare const instantiateUserSession: <UserType>({ user_session_id, session, getUser, createUser }: InsantiateUserSessionProps<UserType>) => Promise<void>;
14
3
  export declare const createAuthConfig: <UserType>(props: AbsoluteAuthProps<UserType>) => AbsoluteAuthProps<UserType>;
15
- export {};
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.3.2",
2
+ "version": "0.4.0",
3
3
  "name": "@absolutejs/auth",
4
4
  "description": "An authorization library for absolutejs",
5
5
  "repository": {
package/src/callback.ts CHANGED
@@ -3,6 +3,7 @@ import { Elysia } from 'elysia';
3
3
  import { sessionStore } from './sessionStore';
4
4
  import { isNonEmptyString, isValidProviderKey } from './typeGuards';
5
5
  import { ClientProviders, OnCallback } from './types';
6
+ import { fetchUserProfile } from './profiles';
6
7
 
7
8
  type CallbackProps<UserType> = {
8
9
  clientProviders: ClientProviders;
@@ -78,22 +79,36 @@ export const callback = <UserType>({
78
79
  )
79
80
  : // @ts-expect-error - This is a dynamic check
80
81
  providerInstance.validateAuthorizationCode(code));
81
- if (hasCodeVerifier) code_verifier.remove();
82
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
- );
83
+ if (hasCodeVerifier) code_verifier.remove();
91
84
 
85
+ let userProfile;
92
86
  const authProvider = auth_provider.value;
93
87
 
88
+ // TODO : Change if arctic ever updates
89
+ try {
90
+ const decodedIdToken = Object.fromEntries(
91
+ Object.entries(decodeIdToken(tokens.idToken())).map(
92
+ ([key, value]) => [
93
+ key,
94
+ typeof value === 'string'
95
+ ? value
96
+ : undefined
97
+ ]
98
+ )
99
+ );
100
+ userProfile = decodedIdToken;
101
+ } catch (err) {
102
+ // TODO : This has type any see if it can be fixed
103
+ userProfile = await fetchUserProfile(
104
+ authProvider,
105
+ tokens.accessToken()
106
+ );
107
+ }
108
+
94
109
  await onCallback?.({
95
110
  authProvider,
96
- decodedIdToken,
111
+ userProfile,
97
112
  session,
98
113
  user_session_id
99
114
  });
@@ -0,0 +1,322 @@
1
+ export type ProfileRequest = {
2
+ endpoint: string;
3
+ method?: 'GET' | 'POST';
4
+ authIn: 'header' | 'query';
5
+ tokenParam?: string;
6
+ headers?: Record<string, string>;
7
+ body?: any;
8
+ };
9
+
10
+ export async function fetchUserProfile(provider: string, accessToken: string) {
11
+ const cfg: ProfileRequest | undefined = profileConfigs[provider];
12
+ if (!cfg) {
13
+ throw new Error(`Unknown provider: ${provider}`);
14
+ }
15
+
16
+ let url = cfg.endpoint;
17
+ const method = cfg.method ?? 'GET';
18
+ const headers: Record<string, string> = {};
19
+
20
+ // Merge any static headers first
21
+ if (cfg.headers) {
22
+ Object.assign(headers, cfg.headers);
23
+ }
24
+
25
+ // Place token in header or query
26
+ if (cfg.authIn === 'header') {
27
+ headers['Authorization'] = `Bearer ${accessToken}`;
28
+ } else {
29
+ const tokenKey = cfg.tokenParam ?? 'access_token';
30
+ const sep = url.includes('?') ? '&' : '?';
31
+ url = `${url}${sep}${tokenKey}=${encodeURIComponent(accessToken)}`;
32
+ }
33
+
34
+ const init: RequestInit = { method, headers };
35
+
36
+ if (method === 'POST' && cfg.body) {
37
+ headers['Content-Type'] = 'application/json';
38
+ init.body = JSON.stringify(cfg.body);
39
+ }
40
+
41
+ const res = await fetch(url, init);
42
+ if (!res.ok) {
43
+ const errText = await res.text();
44
+ throw new Error(
45
+ `Failed to fetch ${provider} profile: ${res.status} ${errText}`
46
+ );
47
+ }
48
+
49
+ return res.json();
50
+ }
51
+
52
+ export const profileConfigs: Record<string, ProfileRequest> = {
53
+ facebook: {
54
+ endpoint: 'https://graph.facebook.com/me?fields=id,name,picture,email',
55
+ method: 'GET',
56
+ authIn: 'query',
57
+ tokenParam: 'access_token'
58
+ },
59
+ anilist: {
60
+ endpoint: 'https://graphql.anilist.co',
61
+ method: 'POST',
62
+ authIn: 'header',
63
+ headers: {
64
+ 'Content-Type': 'application/json',
65
+ Accept: 'application/json'
66
+ },
67
+ body: {
68
+ query: `query { Viewer { id name } }`
69
+ }
70
+ },
71
+ atlassian: {
72
+ endpoint: 'https://api.atlassian.com/me',
73
+ method: 'GET',
74
+ authIn: 'header'
75
+ },
76
+ battlenet: {
77
+ endpoint: 'https://oauth.battle.net/userinfo',
78
+ method: 'GET',
79
+ authIn: 'header'
80
+ },
81
+ bitbucket: {
82
+ endpoint: 'https://api.bitbucket.org/2.0/user',
83
+ method: 'GET',
84
+ authIn: 'header'
85
+ },
86
+ box: {
87
+ endpoint: 'https://api.box.com/2.0/users/me',
88
+ method: 'GET',
89
+ authIn: 'header'
90
+ },
91
+ bungie: {
92
+ endpoint:
93
+ 'https://www.bungie.net/Platform/User/GetCurrentBungieNetUser',
94
+ method: 'GET',
95
+ authIn: 'header',
96
+ headers: {
97
+ 'X-API-Key': '<YOUR_API_KEY>'
98
+ }
99
+ },
100
+ coinbase: {
101
+ endpoint: 'https://api.coinbase.com/v2/user',
102
+ method: 'GET',
103
+ authIn: 'header'
104
+ },
105
+ discord: {
106
+ endpoint: 'https://discord.com/api/users/@me',
107
+ method: 'GET',
108
+ authIn: 'header'
109
+ },
110
+ donationAlerts: {
111
+ endpoint: 'https://www.donationalerts.com/api/v1/user',
112
+ method: 'GET',
113
+ authIn: 'header'
114
+ },
115
+ dribbble: {
116
+ endpoint: 'https://api.dribbble.com/v2/user',
117
+ method: 'GET',
118
+ authIn: 'header'
119
+ },
120
+ dropbox: {
121
+ endpoint: 'https://api.dropboxapi.com/2/users/get_current_account',
122
+ method: 'GET',
123
+ authIn: 'header'
124
+ },
125
+ epicGames: {
126
+ endpoint: 'https://api.epicgames.dev/epic/oauth/v2/userInfo',
127
+ method: 'GET',
128
+ authIn: 'header'
129
+ },
130
+ etsy: {
131
+ endpoint: 'https://openapi.etsy.com/v3/application/users/me',
132
+ method: 'GET',
133
+ authIn: 'header'
134
+ },
135
+ figma: {
136
+ endpoint: 'https://api.figma.com/v1/me',
137
+ method: 'GET',
138
+ authIn: 'header'
139
+ },
140
+ gitea: {
141
+ endpoint: 'https://<YOUR_GITEA_DOMAIN>/api/v1/user',
142
+ method: 'GET',
143
+ authIn: 'header'
144
+ },
145
+ github: {
146
+ endpoint: 'https://api.github.com/user',
147
+ method: 'GET',
148
+ authIn: 'header'
149
+ },
150
+ gitlab: {
151
+ endpoint: 'https://gitlab.com/api/v4/user',
152
+ method: 'GET',
153
+ authIn: 'header'
154
+ },
155
+ intuit: {
156
+ endpoint: 'https://oauth.platform.intuit.com/oauth2/v1/userinfo',
157
+ method: 'GET',
158
+ authIn: 'header'
159
+ },
160
+ kakao: {
161
+ endpoint: 'https://kapi.kakao.com/v2/user/me',
162
+ method: 'GET',
163
+ authIn: 'header'
164
+ },
165
+ kick: {
166
+ endpoint: 'https://api.kick.com/v1/user',
167
+ method: 'GET',
168
+ authIn: 'header'
169
+ },
170
+ lichess: {
171
+ endpoint: 'https://lichess.org/api/account',
172
+ method: 'GET',
173
+ authIn: 'header'
174
+ },
175
+ line: {
176
+ endpoint: 'https://api.line.me/v2/profile',
177
+ method: 'GET',
178
+ authIn: 'header'
179
+ },
180
+ linear: {
181
+ endpoint: 'https://api.linear.app/graphql',
182
+ method: 'POST',
183
+ authIn: 'header',
184
+ headers: {
185
+ 'Content-Type': 'application/json',
186
+ Accept: 'application/json'
187
+ },
188
+ body: {
189
+ query: `query { viewer { id name } }`
190
+ }
191
+ },
192
+ mastodon: {
193
+ endpoint: 'https://<YOUR_INSTANCE>/api/v1/accounts/verify_credentials',
194
+ method: 'GET',
195
+ authIn: 'header'
196
+ },
197
+ mercadoLibre: {
198
+ endpoint: 'https://api.mercadolibre.com/users/me',
199
+ method: 'GET',
200
+ authIn: 'header'
201
+ },
202
+ mercadoPago: {
203
+ endpoint: 'https://api.mercadopago.com/v1/users/me',
204
+ method: 'GET',
205
+ authIn: 'header'
206
+ },
207
+ myAnimeList: {
208
+ endpoint: 'https://api.myanimelist.net/v2/users/@me',
209
+ method: 'GET',
210
+ authIn: 'header'
211
+ },
212
+ naver: {
213
+ endpoint: 'https://openapi.naver.com/v1/nid/me',
214
+ method: 'GET',
215
+ authIn: 'header'
216
+ },
217
+ notion: {
218
+ endpoint: 'https://api.notion.com/v1/users/me',
219
+ method: 'GET',
220
+ authIn: 'header'
221
+ },
222
+ osu: {
223
+ endpoint: 'https://osu.ppy.sh/api/v2/me',
224
+ method: 'GET',
225
+ authIn: 'header'
226
+ },
227
+ patreon: {
228
+ endpoint: 'https://www.patreon.com/api/oauth2/v2/identity',
229
+ method: 'GET',
230
+ authIn: 'header'
231
+ },
232
+ polar: {
233
+ endpoint: 'https://www.polaraccesslink.com/v3/users/<USER_ID>',
234
+ method: 'GET',
235
+ authIn: 'header'
236
+ },
237
+ reddit: {
238
+ endpoint: 'https://oauth.reddit.com/api/v1/me',
239
+ method: 'GET',
240
+ authIn: 'header'
241
+ },
242
+ roblox: {
243
+ endpoint: 'https://apis.roblox.com/oauth/v1/userinfo',
244
+ method: 'GET',
245
+ authIn: 'header'
246
+ },
247
+ shikimori: {
248
+ endpoint: 'https://shikimori.one/api/users/whoami',
249
+ method: 'GET',
250
+ authIn: 'header'
251
+ },
252
+ slack: {
253
+ endpoint: 'https://slack.com/api/users.identity',
254
+ method: 'GET',
255
+ authIn: 'query',
256
+ tokenParam: 'token'
257
+ },
258
+ spotify: {
259
+ endpoint: 'https://api.spotify.com/v1/me',
260
+ method: 'GET',
261
+ authIn: 'header'
262
+ },
263
+ startGg: {
264
+ endpoint: 'https://api.start.gg/gql/alpha',
265
+ method: 'POST',
266
+ authIn: 'header',
267
+ headers: {
268
+ 'Content-Type': 'application/json',
269
+ Accept: 'application/json'
270
+ },
271
+ body: {
272
+ query: `query { currentUser { id slug email player { gamerTag } } }`
273
+ }
274
+ },
275
+ strava: {
276
+ endpoint: 'https://www.strava.com/api/v3/athlete',
277
+ method: 'GET',
278
+ authIn: 'header'
279
+ },
280
+ synology: {
281
+ endpoint: 'https://<YOUR_DOMAIN>/webman/sso/SSOUserInfo.cgi',
282
+ method: 'GET',
283
+ authIn: 'header'
284
+ },
285
+ tiktok: {
286
+ endpoint: 'https://open.douyin.com/oauth/userinfo',
287
+ method: 'GET',
288
+ authIn: 'query',
289
+ tokenParam: 'access_token'
290
+ },
291
+ tiltify: {
292
+ endpoint: 'https://tiltify.com/api/v3/me',
293
+ method: 'GET',
294
+ authIn: 'header'
295
+ },
296
+ tumblr: {
297
+ endpoint: 'https://api.tumblr.com/v2/user/info',
298
+ method: 'GET',
299
+ authIn: 'header'
300
+ },
301
+ twitch: {
302
+ endpoint: 'https://api.twitch.tv/helix/users',
303
+ method: 'GET',
304
+ authIn: 'header'
305
+ },
306
+ twitter: {
307
+ endpoint: 'https://api.twitter.com/2/users/me',
308
+ method: 'GET',
309
+ authIn: 'header'
310
+ },
311
+ vk: {
312
+ endpoint: 'https://api.vk.com/method/users.get',
313
+ method: 'GET',
314
+ authIn: 'query',
315
+ tokenParam: 'access_token'
316
+ },
317
+ zoom: {
318
+ endpoint: 'https://api.zoom.us/v2/users/me',
319
+ method: 'GET',
320
+ authIn: 'header'
321
+ }
322
+ };
package/src/types.ts CHANGED
@@ -23,29 +23,29 @@ export type SessionRecord<UserType> = Record<
23
23
 
24
24
  export type UserFunctionProps = {
25
25
  authProvider: string;
26
- decodedIdToken: {
26
+ userProfile: {
27
27
  [key: string]: string | undefined;
28
28
  };
29
29
  };
30
30
 
31
31
  export type CreateUser<UserType> = ({
32
- decodedIdToken,
32
+ userProfile,
33
33
  authProvider
34
34
  }: UserFunctionProps) => Promise<UserType>;
35
35
 
36
36
  export type GetUser<UserType> = ({
37
- decodedIdToken,
37
+ userProfile,
38
38
  authProvider
39
39
  }: UserFunctionProps) => Promise<UserType | null>;
40
40
 
41
41
  export type OnCallback<UserType> = ({
42
42
  authProvider,
43
- decodedIdToken,
43
+ userProfile,
44
44
  session,
45
45
  user_session_id
46
46
  }: {
47
47
  authProvider: string;
48
- decodedIdToken: {
48
+ userProfile: {
49
49
  [key: string]: string | undefined;
50
50
  };
51
51
  session: SessionRecord<UserType>;
@@ -76,3 +76,14 @@ export type ClientProviders = Record<
76
76
  searchParams: [string, string][];
77
77
  }
78
78
  >;
79
+
80
+ export type InsantiateUserSessionProps<UserType> = {
81
+ authProvider: string;
82
+ userProfile: {
83
+ [key: string]: string | undefined;
84
+ };
85
+ session: SessionRecord<UserType>;
86
+ user_session_id: Cookie<string | undefined>;
87
+ createUser: () => UserType | Promise<UserType>;
88
+ getUser: () => UserType | Promise<UserType | null>;
89
+ };
package/src/utils.ts CHANGED
@@ -1,19 +1,7 @@
1
- import { Cookie } from 'elysia';
2
1
  import { MILLISECONDS_IN_A_DAY } from './constants';
3
- import { AbsoluteAuthProps, SessionRecord } from './types';
2
+ import { AbsoluteAuthProps, InsantiateUserSessionProps } from './types';
4
3
  import { isValidUser } from './typeGuards';
5
4
 
6
- type InsantiateUserSessionProps<UserType> = {
7
- authProvider: string;
8
- decodedIdToken: {
9
- [key: string]: string | undefined;
10
- };
11
- session: SessionRecord<UserType>;
12
- user_session_id: Cookie<string | undefined>;
13
- createUser: () => UserType | Promise<UserType>;
14
- getUser: () => UserType | Promise<UserType | null>;
15
- };
16
-
17
5
  export const instantiateUserSession = async <UserType>({
18
6
  user_session_id,
19
7
  session,