@absolutejs/auth 0.4.0 → 0.5.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/.env.example +9 -0
- package/README.md +3 -3
- package/dist/index.js +2026 -1
- package/dist/src/authorize.d.ts +34 -3
- package/dist/src/callback.d.ts +4 -4
- package/dist/src/index.d.ts +77 -32
- package/dist/src/profile.d.ts +47 -0
- package/dist/src/protectRoute.d.ts +1 -1
- package/dist/src/refresh.d.ts +11 -8
- package/dist/src/revoke.d.ts +19 -18
- package/dist/src/{logout.d.ts → signout.d.ts} +7 -5
- package/dist/src/status.d.ts +4 -2
- package/dist/src/typeGuards.d.ts +0 -10
- package/dist/src/types.d.ts +37 -27
- package/dist/src/utils.d.ts +1 -1
- package/drizzle.config.ts +12 -0
- package/eslint.config.mjs +245 -0
- package/package.json +23 -6
- package/dist/src/profiles.d.ts +0 -10
- package/dist/src/providers.d.ts +0 -51
- package/src/authorize.ts +0 -119
- package/src/callback.ts +0 -132
- package/src/constants.ts +0 -13
- package/src/index.ts +0 -88
- package/src/logout.ts +0 -39
- package/src/profiles.ts +0 -322
- package/src/protectRoute.ts +0 -33
- package/src/providers.ts +0 -109
- package/src/refresh.ts +0 -63
- package/src/revoke.ts +0 -61
- package/src/sessionStore.ts +0 -10
- package/src/status.ts +0 -100
- package/src/typeGuards.ts +0 -32
- package/src/types.ts +0 -89
- package/src/utils.ts +0 -35
- package/tsconfig.json +0 -17
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2027 @@
|
|
|
1
1
|
// @bun
|
|
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};
|
|
2
|
+
// node_modules/citra/dist/index.js
|
|
3
|
+
var NUM_GENERATOR_BYTES = 32;
|
|
4
|
+
var BASE64_BLOCK_SIZE = 4;
|
|
5
|
+
var createOAuth2FetchError = async (response) => {
|
|
6
|
+
const clone = response.clone();
|
|
7
|
+
const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;
|
|
8
|
+
const payload = await response.json().catch(() => null);
|
|
9
|
+
if (payload && typeof payload === "object" && Object.keys(payload).length) {
|
|
10
|
+
return new Error(`${prefix}
|
|
11
|
+
${JSON.stringify(payload)}`);
|
|
12
|
+
}
|
|
13
|
+
const text = await clone.text().catch(() => "");
|
|
14
|
+
if (text) {
|
|
15
|
+
return new Error(`${prefix}
|
|
16
|
+
${text}`);
|
|
17
|
+
}
|
|
18
|
+
return new Error(prefix);
|
|
19
|
+
};
|
|
20
|
+
var encodeBase64 = (input) => {
|
|
21
|
+
let raw;
|
|
22
|
+
if (typeof input === "string") {
|
|
23
|
+
raw = input;
|
|
24
|
+
} else {
|
|
25
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
26
|
+
raw = bytes.reduce((acc, byte) => acc + String.fromCharCode(byte), "");
|
|
27
|
+
}
|
|
28
|
+
return btoa(raw);
|
|
29
|
+
};
|
|
30
|
+
var decodeBase64 = (input, toUint8Array = false) => {
|
|
31
|
+
const b64 = input.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(0, (BASE64_BLOCK_SIZE - input.length % BASE64_BLOCK_SIZE) % BASE64_BLOCK_SIZE);
|
|
32
|
+
const raw = atob(b64);
|
|
33
|
+
if (!toUint8Array) {
|
|
34
|
+
return raw;
|
|
35
|
+
}
|
|
36
|
+
const bytes = new Uint8Array(raw.length);
|
|
37
|
+
for (let i = 0;i < raw.length; i++) {
|
|
38
|
+
bytes[i] = raw.charCodeAt(i);
|
|
39
|
+
}
|
|
40
|
+
return bytes;
|
|
41
|
+
};
|
|
42
|
+
var decodeJWT = (tokenString) => {
|
|
43
|
+
const [headerSegment, payloadSegment, signatureSegment] = tokenString.split(".");
|
|
44
|
+
if (!headerSegment || !payloadSegment || !signatureSegment) {
|
|
45
|
+
throw new Error("Invalid JWT format");
|
|
46
|
+
}
|
|
47
|
+
const decodedPayload = decodeBase64(payloadSegment);
|
|
48
|
+
if (typeof decodedPayload !== "string") {
|
|
49
|
+
throw new Error("Expected JWT payload to be a UTF-8 string");
|
|
50
|
+
}
|
|
51
|
+
return JSON.parse(decodedPayload);
|
|
52
|
+
};
|
|
53
|
+
var createOAuth2Request = ({
|
|
54
|
+
url,
|
|
55
|
+
body,
|
|
56
|
+
authIn,
|
|
57
|
+
headers,
|
|
58
|
+
encoding,
|
|
59
|
+
clientId,
|
|
60
|
+
clientSecret
|
|
61
|
+
}) => {
|
|
62
|
+
const oauthHeaders = new Headers(headers);
|
|
63
|
+
oauthHeaders.set("Accept", "application/json");
|
|
64
|
+
oauthHeaders.set("User-Agent", "citra");
|
|
65
|
+
if (authIn === "header") {
|
|
66
|
+
if (!clientSecret) {
|
|
67
|
+
throw new Error("clientSecret required for header auth");
|
|
68
|
+
}
|
|
69
|
+
oauthHeaders.set("Authorization", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);
|
|
70
|
+
}
|
|
71
|
+
if (encoding === "json") {
|
|
72
|
+
oauthHeaders.set("Content-Type", "application/json");
|
|
73
|
+
return new Request(url, {
|
|
74
|
+
body: JSON.stringify(body),
|
|
75
|
+
headers: oauthHeaders,
|
|
76
|
+
method: "POST"
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
oauthHeaders.set("Content-Type", "application/x-www-form-urlencoded");
|
|
80
|
+
const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === "string");
|
|
81
|
+
const params = new URLSearchParams(entries);
|
|
82
|
+
if (authIn === "body") {
|
|
83
|
+
params.set("client_id", clientId);
|
|
84
|
+
clientSecret && params.set("client_secret", clientSecret);
|
|
85
|
+
}
|
|
86
|
+
return new Request(url, {
|
|
87
|
+
body: params.toString(),
|
|
88
|
+
headers: oauthHeaders,
|
|
89
|
+
method: "POST"
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
var createS256CodeChallenge = async (codeVerifier) => {
|
|
93
|
+
const data = new TextEncoder().encode(codeVerifier);
|
|
94
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
95
|
+
return base64Url(hashBuffer);
|
|
96
|
+
};
|
|
97
|
+
var createRandomBase64UrlGenerator = (length) => () => {
|
|
98
|
+
const buffer = crypto.getRandomValues(new Uint8Array(length));
|
|
99
|
+
return base64Url(buffer);
|
|
100
|
+
};
|
|
101
|
+
var generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
102
|
+
var generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
103
|
+
var base64Url = (input) => encodeBase64(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
104
|
+
var isValidProviderOption = (option) => {
|
|
105
|
+
const normalizedOption = option.toLowerCase();
|
|
106
|
+
return Object.keys(normalizedProviders).includes(normalizedOption);
|
|
107
|
+
};
|
|
108
|
+
var isPKCEProviderOption = (option) => normalizedProviders[option.toLowerCase()]?.PKCEMethod !== undefined;
|
|
109
|
+
var isRefreshableOAuth2Client = (providerName, client) => {
|
|
110
|
+
const normalizedOption = providerName.toLowerCase();
|
|
111
|
+
const provider = normalizedProviders[normalizedOption];
|
|
112
|
+
return isValidProviderOption(providerName) && provider?.isRefreshable === true;
|
|
113
|
+
};
|
|
114
|
+
var isRevocableOAuth2Client = (providerName, client) => {
|
|
115
|
+
const normalizedOption = providerName.toLowerCase();
|
|
116
|
+
const provider = normalizedProviders[normalizedOption];
|
|
117
|
+
return isValidProviderOption(providerName) && provider?.revocationRequest !== undefined;
|
|
118
|
+
};
|
|
119
|
+
var hasClientSecret = (credentials) => {
|
|
120
|
+
if (typeof credentials !== "object" || credentials === null)
|
|
121
|
+
return false;
|
|
122
|
+
const secret = Reflect.get(credentials, "clientSecret");
|
|
123
|
+
return typeof secret === "string";
|
|
124
|
+
};
|
|
125
|
+
var defineProviders = (providers2) => providers2;
|
|
126
|
+
var providers = defineProviders({
|
|
127
|
+
"42": {
|
|
128
|
+
authorizationUrl: "https://api.intra.42.fr/oauth/authorize",
|
|
129
|
+
isOIDC: false,
|
|
130
|
+
isRefreshable: true,
|
|
131
|
+
profileRequest: {
|
|
132
|
+
authIn: "header",
|
|
133
|
+
method: "GET",
|
|
134
|
+
url: "https://api.intra.42.fr/v2/me"
|
|
135
|
+
},
|
|
136
|
+
scopeRequired: false,
|
|
137
|
+
tokenRequest: {
|
|
138
|
+
authIn: "body",
|
|
139
|
+
encoding: "form",
|
|
140
|
+
url: "https://api.intra.42.fr/oauth/token"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
AmazonCognito: {
|
|
144
|
+
authorizationUrl: "https://${domain}/oauth2/authorize",
|
|
145
|
+
isOIDC: true,
|
|
146
|
+
isRefreshable: true,
|
|
147
|
+
PKCEMethod: "S256",
|
|
148
|
+
profileRequest: {
|
|
149
|
+
authIn: "header",
|
|
150
|
+
method: "GET",
|
|
151
|
+
url: (config) => `https://${config.domain}/oauth2/userInfo`
|
|
152
|
+
},
|
|
153
|
+
revocationRequest: {
|
|
154
|
+
authIn: "body",
|
|
155
|
+
tokenParamName: "token",
|
|
156
|
+
url: (config) => `https://${config.domain}/oauth2/revoke`
|
|
157
|
+
},
|
|
158
|
+
scopeRequired: false,
|
|
159
|
+
tokenRequest: {
|
|
160
|
+
authIn: "body",
|
|
161
|
+
encoding: "form",
|
|
162
|
+
url: (config) => `https://${config.domain}/oauth2/token`
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
AniList: {
|
|
166
|
+
authorizationUrl: "https://anilist.co/api/v2/oauth/authorize",
|
|
167
|
+
isOIDC: false,
|
|
168
|
+
isRefreshable: true,
|
|
169
|
+
profileRequest: {
|
|
170
|
+
authIn: "header",
|
|
171
|
+
body: {
|
|
172
|
+
query: `query { Viewer { id name } }`
|
|
173
|
+
},
|
|
174
|
+
headers: {
|
|
175
|
+
Accept: "application/json",
|
|
176
|
+
"Content-Type": "application/json"
|
|
177
|
+
},
|
|
178
|
+
method: "POST",
|
|
179
|
+
url: "https://graphql.anilist.co"
|
|
180
|
+
},
|
|
181
|
+
scopeRequired: false,
|
|
182
|
+
tokenRequest: {
|
|
183
|
+
authIn: "body",
|
|
184
|
+
encoding: "form",
|
|
185
|
+
url: "https://anilist.co/api/v2/oauth/token"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
Apple: {
|
|
189
|
+
authorizationUrl: "https://appleid.apple.com/auth/authorize",
|
|
190
|
+
isOIDC: true,
|
|
191
|
+
isRefreshable: true,
|
|
192
|
+
PKCEMethod: "S256",
|
|
193
|
+
profileRequest: {
|
|
194
|
+
authIn: "header",
|
|
195
|
+
method: "GET",
|
|
196
|
+
url: "https://appleid.apple.com/auth/userinfo"
|
|
197
|
+
},
|
|
198
|
+
scopeRequired: false,
|
|
199
|
+
tokenRequest: {
|
|
200
|
+
authIn: "body",
|
|
201
|
+
encoding: "form",
|
|
202
|
+
url: "https://appleid.apple.com/auth/token"
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
Atlassian: {
|
|
206
|
+
authorizationUrl: "https://auth.atlassian.com/authorize",
|
|
207
|
+
createAuthorizationURLSearchParams: {
|
|
208
|
+
audience: "api.atlassian.com"
|
|
209
|
+
},
|
|
210
|
+
isOIDC: false,
|
|
211
|
+
isRefreshable: true,
|
|
212
|
+
profileRequest: {
|
|
213
|
+
authIn: "header",
|
|
214
|
+
method: "GET",
|
|
215
|
+
url: "https://api.atlassian.com/me"
|
|
216
|
+
},
|
|
217
|
+
scopeRequired: true,
|
|
218
|
+
tokenRequest: {
|
|
219
|
+
authIn: "body",
|
|
220
|
+
encoding: "form",
|
|
221
|
+
url: "https://auth.atlassian.com/oauth/token"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
Auth0: {
|
|
225
|
+
authorizationUrl: (config) => `https://${config.domain}/authorize`,
|
|
226
|
+
isOIDC: true,
|
|
227
|
+
isRefreshable: true,
|
|
228
|
+
PKCEMethod: "S256",
|
|
229
|
+
profileRequest: {
|
|
230
|
+
authIn: "header",
|
|
231
|
+
method: "GET",
|
|
232
|
+
url: (config) => `https://${config.domain}/userinfo`
|
|
233
|
+
},
|
|
234
|
+
revocationRequest: {
|
|
235
|
+
authIn: "body",
|
|
236
|
+
body: new URLSearchParams({
|
|
237
|
+
token_type_hint: "refresh_token"
|
|
238
|
+
}),
|
|
239
|
+
tokenParamName: "token",
|
|
240
|
+
url: (config) => `https://${config.domain}/oauth/revoke`
|
|
241
|
+
},
|
|
242
|
+
scopeRequired: false,
|
|
243
|
+
tokenRequest: {
|
|
244
|
+
authIn: "body",
|
|
245
|
+
encoding: "form",
|
|
246
|
+
url: (config) => `https://${config.domain}/oauth/token`
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
Authentik: {
|
|
250
|
+
authorizationUrl: (config) => `https://${config.baseURL}/oauth/authorize`,
|
|
251
|
+
isOIDC: true,
|
|
252
|
+
isRefreshable: true,
|
|
253
|
+
PKCEMethod: "S256",
|
|
254
|
+
profileRequest: {
|
|
255
|
+
authIn: "header",
|
|
256
|
+
method: "GET",
|
|
257
|
+
url: (config) => `https://${config.baseURL}/api/v3/user/`
|
|
258
|
+
},
|
|
259
|
+
scopeRequired: false,
|
|
260
|
+
tokenRequest: {
|
|
261
|
+
authIn: "body",
|
|
262
|
+
encoding: "form",
|
|
263
|
+
url: (config) => `https://${config.baseURL}/oauth/token`
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
Autodesk: {
|
|
267
|
+
authorizationUrl: "https://developer.api.autodesk.com/authentication/v2/authorize",
|
|
268
|
+
isOIDC: true,
|
|
269
|
+
isRefreshable: true,
|
|
270
|
+
PKCEMethod: "S256",
|
|
271
|
+
profileRequest: {
|
|
272
|
+
authIn: "header",
|
|
273
|
+
method: "GET",
|
|
274
|
+
url: "https://api.userprofile.autodesk.com/userinfo"
|
|
275
|
+
},
|
|
276
|
+
scopeRequired: false,
|
|
277
|
+
tokenRequest: {
|
|
278
|
+
authIn: "body",
|
|
279
|
+
encoding: "form",
|
|
280
|
+
url: "https://developer.api.autodesk.com/authentication/v2/token"
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
Battlenet: {
|
|
284
|
+
authorizationUrl: "https://oauth.battle.net/authorize",
|
|
285
|
+
isOIDC: true,
|
|
286
|
+
isRefreshable: false,
|
|
287
|
+
profileRequest: {
|
|
288
|
+
authIn: "header",
|
|
289
|
+
method: "GET",
|
|
290
|
+
url: "https://oauth.battle.net/userinfo"
|
|
291
|
+
},
|
|
292
|
+
scopeRequired: false,
|
|
293
|
+
tokenRequest: {
|
|
294
|
+
authIn: "body",
|
|
295
|
+
encoding: "form",
|
|
296
|
+
url: "https://oauth.battle.net/token"
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
Bitbucket: {
|
|
300
|
+
authorizationUrl: "https://bitbucket.org/site/oauth2/authorize",
|
|
301
|
+
isOIDC: false,
|
|
302
|
+
isRefreshable: true,
|
|
303
|
+
profileRequest: {
|
|
304
|
+
authIn: "header",
|
|
305
|
+
method: "GET",
|
|
306
|
+
url: "https://api.bitbucket.org/2.0/user"
|
|
307
|
+
},
|
|
308
|
+
scopeRequired: false,
|
|
309
|
+
tokenRequest: {
|
|
310
|
+
authIn: "body",
|
|
311
|
+
encoding: "form",
|
|
312
|
+
url: "https://bitbucket.org/site/oauth2/access_token"
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
Box: {
|
|
316
|
+
authorizationUrl: "https://account.box.com/api/oauth2/authorize",
|
|
317
|
+
isOIDC: false,
|
|
318
|
+
isRefreshable: true,
|
|
319
|
+
profileRequest: {
|
|
320
|
+
authIn: "header",
|
|
321
|
+
method: "GET",
|
|
322
|
+
url: "https://api.box.com/2.0/users/me"
|
|
323
|
+
},
|
|
324
|
+
revocationRequest: {
|
|
325
|
+
authIn: "body",
|
|
326
|
+
tokenParamName: "token",
|
|
327
|
+
url: "https://api.box.com/oauth2/revoke"
|
|
328
|
+
},
|
|
329
|
+
scopeRequired: false,
|
|
330
|
+
tokenRequest: {
|
|
331
|
+
authIn: "body",
|
|
332
|
+
encoding: "form",
|
|
333
|
+
url: "https://api.box.com/oauth2/token"
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
Bungie: {
|
|
337
|
+
authorizationUrl: "https://www.bungie.net/en/OAuth/Authorize",
|
|
338
|
+
isOIDC: false,
|
|
339
|
+
isRefreshable: true,
|
|
340
|
+
profileRequest: {
|
|
341
|
+
authIn: "header",
|
|
342
|
+
headers: {
|
|
343
|
+
"X-API-Key": "<YOUR_API_KEY>"
|
|
344
|
+
},
|
|
345
|
+
method: "GET",
|
|
346
|
+
url: "https://www.bungie.net/Platform/User/GetCurrentBungieNetUser"
|
|
347
|
+
},
|
|
348
|
+
scopeRequired: false,
|
|
349
|
+
tokenRequest: {
|
|
350
|
+
authIn: "body",
|
|
351
|
+
encoding: "form",
|
|
352
|
+
url: "https://www.bungie.net/Platform/App/OAuth/token"
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
Coinbase: {
|
|
356
|
+
authorizationUrl: "https://www.coinbase.com/oauth/authorize",
|
|
357
|
+
isOIDC: false,
|
|
358
|
+
isRefreshable: true,
|
|
359
|
+
profileRequest: {
|
|
360
|
+
authIn: "header",
|
|
361
|
+
method: "GET",
|
|
362
|
+
url: "https://api.coinbase.com/v2/user"
|
|
363
|
+
},
|
|
364
|
+
scopeRequired: false,
|
|
365
|
+
tokenRequest: {
|
|
366
|
+
authIn: "body",
|
|
367
|
+
encoding: "form",
|
|
368
|
+
url: "https://api.coinbase.com/oauth/token"
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
Discord: {
|
|
372
|
+
authorizationUrl: "https://discord.com/api/oauth2/authorize",
|
|
373
|
+
isOIDC: false,
|
|
374
|
+
isRefreshable: true,
|
|
375
|
+
PKCEMethod: "S256",
|
|
376
|
+
profileRequest: {
|
|
377
|
+
authIn: "header",
|
|
378
|
+
method: "GET",
|
|
379
|
+
url: "https://discord.com/api/users/@me"
|
|
380
|
+
},
|
|
381
|
+
scopeRequired: true,
|
|
382
|
+
tokenRequest: {
|
|
383
|
+
authIn: "body",
|
|
384
|
+
encoding: "form",
|
|
385
|
+
url: "https://discord.com/api/oauth2/token"
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
DonationAlerts: {
|
|
389
|
+
authorizationUrl: "https://www.donationalerts.com/oauth/authorize",
|
|
390
|
+
isOIDC: false,
|
|
391
|
+
isRefreshable: true,
|
|
392
|
+
profileRequest: {
|
|
393
|
+
authIn: "header",
|
|
394
|
+
method: "GET",
|
|
395
|
+
url: "https://www.donationalerts.com/api/v1/user/oauth"
|
|
396
|
+
},
|
|
397
|
+
scopeRequired: false,
|
|
398
|
+
tokenRequest: {
|
|
399
|
+
authIn: "body",
|
|
400
|
+
encoding: "form",
|
|
401
|
+
url: "https://www.donationalerts.com/oauth/token"
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
Dribbble: {
|
|
405
|
+
authorizationUrl: "https://dribbble.com/oauth/authorize",
|
|
406
|
+
isOIDC: false,
|
|
407
|
+
isRefreshable: false,
|
|
408
|
+
profileRequest: {
|
|
409
|
+
authIn: "header",
|
|
410
|
+
method: "GET",
|
|
411
|
+
url: "https://api.dribbble.com/v2/user"
|
|
412
|
+
},
|
|
413
|
+
scopeRequired: false,
|
|
414
|
+
tokenRequest: {
|
|
415
|
+
authIn: "body",
|
|
416
|
+
encoding: "form",
|
|
417
|
+
url: "https://dribbble.com/oauth/token"
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
Dropbox: {
|
|
421
|
+
authorizationUrl: "https://www.dropbox.com/oauth2/authorize",
|
|
422
|
+
isOIDC: false,
|
|
423
|
+
isRefreshable: true,
|
|
424
|
+
profileRequest: {
|
|
425
|
+
authIn: "header",
|
|
426
|
+
method: "POST",
|
|
427
|
+
url: "https://api.dropboxapi.com/2/users/get_current_account"
|
|
428
|
+
},
|
|
429
|
+
revocationRequest: {
|
|
430
|
+
authIn: "header",
|
|
431
|
+
url: "https://api.dropboxapi.com/2/auth/token/revoke"
|
|
432
|
+
},
|
|
433
|
+
scopeRequired: false,
|
|
434
|
+
tokenRequest: {
|
|
435
|
+
authIn: "body",
|
|
436
|
+
encoding: "form",
|
|
437
|
+
url: "https://api.dropboxapi.com/oauth2/token"
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
EpicGames: {
|
|
441
|
+
authorizationUrl: "https://www.epicgames.com/id/authorize",
|
|
442
|
+
isOIDC: false,
|
|
443
|
+
isRefreshable: true,
|
|
444
|
+
profileRequest: {
|
|
445
|
+
authIn: "header",
|
|
446
|
+
method: "GET",
|
|
447
|
+
url: "https://api.epicgames.dev/epic/oauth/v2/userInfo"
|
|
448
|
+
},
|
|
449
|
+
scopeRequired: false,
|
|
450
|
+
tokenRequest: {
|
|
451
|
+
authIn: "body",
|
|
452
|
+
encoding: "form",
|
|
453
|
+
url: "https://api.epicgames.dev/epic/oauth/v1/token"
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
Etsy: {
|
|
457
|
+
authorizationUrl: "https://www.etsy.com/oauth/connect",
|
|
458
|
+
isOIDC: false,
|
|
459
|
+
isRefreshable: true,
|
|
460
|
+
profileRequest: {
|
|
461
|
+
authIn: "header",
|
|
462
|
+
method: "GET",
|
|
463
|
+
url: "https://openapi.etsy.com/v3/application/users/me"
|
|
464
|
+
},
|
|
465
|
+
scopeRequired: false,
|
|
466
|
+
tokenRequest: {
|
|
467
|
+
authIn: "body",
|
|
468
|
+
encoding: "form",
|
|
469
|
+
url: "https://api.etsy.com/v3/public/oauth/token"
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
Facebook: {
|
|
473
|
+
authorizationUrl: "https://www.facebook.com/v16.0/dialog/oauth",
|
|
474
|
+
isOIDC: true,
|
|
475
|
+
isRefreshable: false,
|
|
476
|
+
PKCEMethod: "S256",
|
|
477
|
+
profileRequest: {
|
|
478
|
+
authIn: "query",
|
|
479
|
+
method: "GET",
|
|
480
|
+
searchParams: [["fields", "id,name,email,picture"]],
|
|
481
|
+
url: "https://graph.facebook.com/me"
|
|
482
|
+
},
|
|
483
|
+
scopeRequired: false,
|
|
484
|
+
tokenRequest: {
|
|
485
|
+
authIn: "body",
|
|
486
|
+
encoding: "form",
|
|
487
|
+
url: "https://graph.facebook.com/v16.0/oauth/access_token"
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
Figma: {
|
|
491
|
+
authorizationUrl: "https://www.figma.com/oauth",
|
|
492
|
+
isOIDC: false,
|
|
493
|
+
isRefreshable: true,
|
|
494
|
+
PKCEMethod: "S256",
|
|
495
|
+
profileRequest: {
|
|
496
|
+
authIn: "header",
|
|
497
|
+
method: "GET",
|
|
498
|
+
url: "https://api.figma.com/v1/me"
|
|
499
|
+
},
|
|
500
|
+
scopeRequired: true,
|
|
501
|
+
tokenRequest: {
|
|
502
|
+
authIn: "body",
|
|
503
|
+
encoding: "form",
|
|
504
|
+
url: "https://api.figma.com/v1/oauth/token"
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
Gitea: {
|
|
508
|
+
authorizationUrl: (config) => `${config.baseURL}/login/oauth/authorize`,
|
|
509
|
+
isOIDC: true,
|
|
510
|
+
isRefreshable: true,
|
|
511
|
+
PKCEMethod: "S256",
|
|
512
|
+
profileRequest: {
|
|
513
|
+
authIn: "header",
|
|
514
|
+
method: "GET",
|
|
515
|
+
url: (config) => `${config.baseURL}/api/v1/user`
|
|
516
|
+
},
|
|
517
|
+
scopeRequired: false,
|
|
518
|
+
tokenRequest: {
|
|
519
|
+
authIn: "body",
|
|
520
|
+
encoding: "form",
|
|
521
|
+
url: (config) => `${config.baseURL}/login/oauth/access_token`
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
GitHub: {
|
|
525
|
+
authorizationUrl: "https://github.com/login/oauth/authorize",
|
|
526
|
+
isOIDC: false,
|
|
527
|
+
isRefreshable: false,
|
|
528
|
+
profileRequest: {
|
|
529
|
+
authIn: "header",
|
|
530
|
+
method: "GET",
|
|
531
|
+
url: "https://api.github.com/user"
|
|
532
|
+
},
|
|
533
|
+
scopeRequired: false,
|
|
534
|
+
tokenRequest: {
|
|
535
|
+
authIn: "body",
|
|
536
|
+
encoding: "form",
|
|
537
|
+
url: "https://github.com/login/oauth/access_token"
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
GitLab: {
|
|
541
|
+
authorizationUrl: (config) => `${config.baseURL}/oauth/authorize`,
|
|
542
|
+
isOIDC: true,
|
|
543
|
+
isRefreshable: true,
|
|
544
|
+
PKCEMethod: "S256",
|
|
545
|
+
profileRequest: {
|
|
546
|
+
authIn: "header",
|
|
547
|
+
method: "GET",
|
|
548
|
+
url: "https://gitlab.com/api/v4/user"
|
|
549
|
+
},
|
|
550
|
+
revocationRequest: {
|
|
551
|
+
authIn: "body",
|
|
552
|
+
tokenParamName: "token",
|
|
553
|
+
url: (config) => `${config.baseURL}/oauth/revoke`
|
|
554
|
+
},
|
|
555
|
+
scopeRequired: false,
|
|
556
|
+
tokenRequest: {
|
|
557
|
+
authIn: "body",
|
|
558
|
+
encoding: "form",
|
|
559
|
+
url: (config) => `${config.baseURL}/oauth/token`
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
Google: {
|
|
563
|
+
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
564
|
+
isOIDC: true,
|
|
565
|
+
isRefreshable: true,
|
|
566
|
+
PKCEMethod: "S256",
|
|
567
|
+
profileRequest: {
|
|
568
|
+
authIn: "header",
|
|
569
|
+
method: "GET",
|
|
570
|
+
searchParams: [["personFields", "names,emailAddresses,photos"]],
|
|
571
|
+
url: "https://people.googleapis.com/v1/people/me"
|
|
572
|
+
},
|
|
573
|
+
revocationRequest: {
|
|
574
|
+
authIn: "body",
|
|
575
|
+
tokenParamName: "token",
|
|
576
|
+
url: "https://oauth2.googleapis.com/revoke"
|
|
577
|
+
},
|
|
578
|
+
scopeRequired: true,
|
|
579
|
+
tokenRequest: {
|
|
580
|
+
authIn: "body",
|
|
581
|
+
encoding: "form",
|
|
582
|
+
url: "https://oauth2.googleapis.com/token"
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
Intuit: {
|
|
586
|
+
authorizationUrl: "https://appcenter.intuit.com/connect/oauth2",
|
|
587
|
+
isOIDC: true,
|
|
588
|
+
isRefreshable: true,
|
|
589
|
+
profileRequest: {
|
|
590
|
+
authIn: "header",
|
|
591
|
+
method: "GET",
|
|
592
|
+
url: (config) => config.environment === "production" ? "https://accounts.platform.intuit.com/v1/openid_connect/userinfo" : "https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo"
|
|
593
|
+
},
|
|
594
|
+
revocationRequest: {
|
|
595
|
+
authIn: "body",
|
|
596
|
+
headers: (config) => ({
|
|
597
|
+
Authorization: `Basic ${encodeBase64(`${config.clientId}:${config.clientSecret}`)}`
|
|
598
|
+
}),
|
|
599
|
+
tokenParamName: "token",
|
|
600
|
+
url: "https://developer.api.intuit.com/v2/oauth2/tokens/revoke"
|
|
601
|
+
},
|
|
602
|
+
scopeRequired: true,
|
|
603
|
+
tokenRequest: {
|
|
604
|
+
authIn: "body",
|
|
605
|
+
encoding: "form",
|
|
606
|
+
url: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
Kakao: {
|
|
610
|
+
authorizationUrl: "https://kauth.kakao.com/oauth/authorize",
|
|
611
|
+
isOIDC: true,
|
|
612
|
+
isRefreshable: true,
|
|
613
|
+
PKCEMethod: "S256",
|
|
614
|
+
profileRequest: {
|
|
615
|
+
authIn: "header",
|
|
616
|
+
method: "GET",
|
|
617
|
+
url: "https://kapi.kakao.com/v2/user/me"
|
|
618
|
+
},
|
|
619
|
+
scopeRequired: false,
|
|
620
|
+
tokenRequest: {
|
|
621
|
+
authIn: "body",
|
|
622
|
+
encoding: "form",
|
|
623
|
+
url: "https://kauth.kakao.com/oauth/token"
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
Keycloak: {
|
|
627
|
+
authorizationUrl: (config) => `${config.realmURL}/protocol/openid-connect/auth`,
|
|
628
|
+
isOIDC: true,
|
|
629
|
+
isRefreshable: true,
|
|
630
|
+
PKCEMethod: "S256",
|
|
631
|
+
profileRequest: {
|
|
632
|
+
authIn: "header",
|
|
633
|
+
method: "GET",
|
|
634
|
+
url: "https://api.kick.com/v1/user"
|
|
635
|
+
},
|
|
636
|
+
revocationRequest: {
|
|
637
|
+
authIn: "body",
|
|
638
|
+
tokenParamName: "token",
|
|
639
|
+
url: (config) => `${config.realmURL}/protocol/openid-connect/revoke`
|
|
640
|
+
},
|
|
641
|
+
scopeRequired: false,
|
|
642
|
+
tokenRequest: {
|
|
643
|
+
authIn: "body",
|
|
644
|
+
encoding: "form",
|
|
645
|
+
url: (config) => `${config.realmURL}/protocol/openid-connect/token`
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
Kick: {
|
|
649
|
+
authorizationUrl: "https://id.kick.com/oauth/authorize",
|
|
650
|
+
isOIDC: false,
|
|
651
|
+
isRefreshable: true,
|
|
652
|
+
PKCEMethod: "S256",
|
|
653
|
+
profileRequest: {
|
|
654
|
+
authIn: "header",
|
|
655
|
+
method: "GET",
|
|
656
|
+
url: "https://id.kick.com/v1/user"
|
|
657
|
+
},
|
|
658
|
+
revocationRequest: {
|
|
659
|
+
authIn: "body",
|
|
660
|
+
tokenParamName: "token",
|
|
661
|
+
url: "https://id.kick.com/oauth/revoke"
|
|
662
|
+
},
|
|
663
|
+
scopeRequired: false,
|
|
664
|
+
tokenRequest: {
|
|
665
|
+
authIn: "body",
|
|
666
|
+
encoding: "form",
|
|
667
|
+
url: "https://id.kick.com/oauth/token"
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
Lichess: {
|
|
671
|
+
authorizationUrl: "https://lichess.org/oauth/authorize",
|
|
672
|
+
isOIDC: false,
|
|
673
|
+
isRefreshable: false,
|
|
674
|
+
PKCEMethod: "S256",
|
|
675
|
+
profileRequest: {
|
|
676
|
+
authIn: "header",
|
|
677
|
+
method: "GET",
|
|
678
|
+
url: "https://lichess.org/api/account"
|
|
679
|
+
},
|
|
680
|
+
scopeRequired: false,
|
|
681
|
+
tokenRequest: {
|
|
682
|
+
authIn: "body",
|
|
683
|
+
encoding: "form",
|
|
684
|
+
url: "https://lichess.org/api/token"
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
LINE: {
|
|
688
|
+
authorizationUrl: "https://access.line.me/oauth2/v2.1/authorize",
|
|
689
|
+
isOIDC: true,
|
|
690
|
+
isRefreshable: true,
|
|
691
|
+
PKCEMethod: "S256",
|
|
692
|
+
profileRequest: {
|
|
693
|
+
authIn: "header",
|
|
694
|
+
method: "GET",
|
|
695
|
+
url: "https://api.line.me/v2/profile"
|
|
696
|
+
},
|
|
697
|
+
scopeRequired: true,
|
|
698
|
+
tokenRequest: {
|
|
699
|
+
authIn: "body",
|
|
700
|
+
encoding: "form",
|
|
701
|
+
url: "https://api.line.me/oauth2/v2.1/token"
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
Linear: {
|
|
705
|
+
authorizationUrl: "https://linear.app/oauth/authorize",
|
|
706
|
+
isOIDC: false,
|
|
707
|
+
isRefreshable: false,
|
|
708
|
+
profileRequest: {
|
|
709
|
+
authIn: "header",
|
|
710
|
+
body: {
|
|
711
|
+
query: `query { viewer { id name } }`
|
|
712
|
+
},
|
|
713
|
+
headers: {
|
|
714
|
+
Accept: "application/json",
|
|
715
|
+
"Content-Type": "application/json"
|
|
716
|
+
},
|
|
717
|
+
method: "POST",
|
|
718
|
+
url: "https://api.linear.app/graphql"
|
|
719
|
+
},
|
|
720
|
+
revocationRequest: {
|
|
721
|
+
authIn: "header",
|
|
722
|
+
url: "https://api.linear.app/oauth/revoke"
|
|
723
|
+
},
|
|
724
|
+
scopeRequired: false,
|
|
725
|
+
tokenRequest: {
|
|
726
|
+
authIn: "body",
|
|
727
|
+
encoding: "form",
|
|
728
|
+
url: "https://api.linear.app/oauth/token"
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
LinkedIn: {
|
|
732
|
+
authorizationUrl: "https://www.linkedin.com/oauth/v2/authorization",
|
|
733
|
+
isOIDC: true,
|
|
734
|
+
isRefreshable: true,
|
|
735
|
+
PKCEMethod: "S256",
|
|
736
|
+
profileRequest: {
|
|
737
|
+
authIn: "header",
|
|
738
|
+
method: "GET",
|
|
739
|
+
url: "https://api.linkedin.com/v2/me"
|
|
740
|
+
},
|
|
741
|
+
scopeRequired: true,
|
|
742
|
+
tokenRequest: {
|
|
743
|
+
authIn: "body",
|
|
744
|
+
encoding: "form",
|
|
745
|
+
url: "https://www.linkedin.com/oauth/v2/accessToken"
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
Mastodon: {
|
|
749
|
+
authorizationUrl: (config) => `${config.baseURL}/oauth/authorize`,
|
|
750
|
+
isOIDC: false,
|
|
751
|
+
isRefreshable: false,
|
|
752
|
+
PKCEMethod: "S256",
|
|
753
|
+
profileRequest: {
|
|
754
|
+
authIn: "header",
|
|
755
|
+
method: "GET",
|
|
756
|
+
url: (config) => `${config.baseURL}/api/v1/accounts/verify_credentials`
|
|
757
|
+
},
|
|
758
|
+
revocationRequest: {
|
|
759
|
+
authIn: "body",
|
|
760
|
+
tokenParamName: "token",
|
|
761
|
+
url: (config) => `${config.baseURL}/oauth/revoke`
|
|
762
|
+
},
|
|
763
|
+
scopeRequired: false,
|
|
764
|
+
tokenRequest: {
|
|
765
|
+
authIn: "body",
|
|
766
|
+
encoding: "form",
|
|
767
|
+
url: (config) => `${config.baseURL}/oauth/token`
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
MercadoLibre: {
|
|
771
|
+
authorizationUrl: "https://auth.mercadolibre.com/authorization",
|
|
772
|
+
isOIDC: false,
|
|
773
|
+
isRefreshable: true,
|
|
774
|
+
PKCEMethod: "S256",
|
|
775
|
+
profileRequest: {
|
|
776
|
+
authIn: "header",
|
|
777
|
+
method: "GET",
|
|
778
|
+
url: "https://api.mercadolibre.com/users/me"
|
|
779
|
+
},
|
|
780
|
+
scopeRequired: false,
|
|
781
|
+
tokenRequest: {
|
|
782
|
+
authIn: "body",
|
|
783
|
+
encoding: "form",
|
|
784
|
+
url: "https://api.mercadolibre.com/oauth/token"
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
MercadoPago: {
|
|
788
|
+
authorizationUrl: "https://auth.mercadopago.com/authorization",
|
|
789
|
+
isOIDC: false,
|
|
790
|
+
isRefreshable: true,
|
|
791
|
+
profileRequest: {
|
|
792
|
+
authIn: "header",
|
|
793
|
+
method: "GET",
|
|
794
|
+
url: "https://api.mercadopago.com/v1/users/me"
|
|
795
|
+
},
|
|
796
|
+
scopeRequired: false,
|
|
797
|
+
tokenRequest: {
|
|
798
|
+
authIn: "body",
|
|
799
|
+
encoding: "form",
|
|
800
|
+
url: "https://api.mercadopago.com/oauth/token"
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
MicrosoftEntraId: {
|
|
804
|
+
authorizationUrl: (config) => `https://${config.tenantId}.b2clogin.com/${config.tenantId}/oauth2/v2.0/authorize`,
|
|
805
|
+
isOIDC: true,
|
|
806
|
+
isRefreshable: true,
|
|
807
|
+
PKCEMethod: "S256",
|
|
808
|
+
profileRequest: {
|
|
809
|
+
authIn: "header",
|
|
810
|
+
method: "GET",
|
|
811
|
+
url: (config) => `https://${config.tenantId}.b2clogin.com/${config.tenantId}/openid/userinfo`
|
|
812
|
+
},
|
|
813
|
+
scopeRequired: false,
|
|
814
|
+
tokenRequest: {
|
|
815
|
+
authIn: "body",
|
|
816
|
+
encoding: "form",
|
|
817
|
+
url: (config) => `https://${config.tenantId}.b2clogin.com/${config.tenantId}/oauth2/v2.0/token`
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
MyAnimeList: {
|
|
821
|
+
authorizationUrl: "https://myanimelist.net/v1/oauth2/authorize",
|
|
822
|
+
isOIDC: false,
|
|
823
|
+
isRefreshable: true,
|
|
824
|
+
PKCEMethod: "plain",
|
|
825
|
+
profileRequest: {
|
|
826
|
+
authIn: "header",
|
|
827
|
+
method: "GET",
|
|
828
|
+
url: "https://api.myanimelist.net/v2/users/@me"
|
|
829
|
+
},
|
|
830
|
+
scopeRequired: false,
|
|
831
|
+
tokenRequest: {
|
|
832
|
+
authIn: "body",
|
|
833
|
+
encoding: "form",
|
|
834
|
+
url: "https://myanimelist.net/v1/oauth2/token"
|
|
835
|
+
}
|
|
836
|
+
},
|
|
837
|
+
Naver: {
|
|
838
|
+
authorizationUrl: "https://nid.naver.com/oauth2.0/authorize",
|
|
839
|
+
isOIDC: false,
|
|
840
|
+
isRefreshable: true,
|
|
841
|
+
profileRequest: {
|
|
842
|
+
authIn: "header",
|
|
843
|
+
method: "GET",
|
|
844
|
+
url: "https://openapi.naver.com/v1/nid/me"
|
|
845
|
+
},
|
|
846
|
+
scopeRequired: false,
|
|
847
|
+
tokenRequest: {
|
|
848
|
+
authIn: "body",
|
|
849
|
+
encoding: "form",
|
|
850
|
+
url: "https://nid.naver.com/oauth2.0/token"
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
Notion: {
|
|
854
|
+
authorizationUrl: "https://api.notion.com/v1/oauth/authorize",
|
|
855
|
+
isOIDC: false,
|
|
856
|
+
isRefreshable: false,
|
|
857
|
+
profileRequest: {
|
|
858
|
+
authIn: "header",
|
|
859
|
+
headers: {
|
|
860
|
+
"Notion-Version": "2022-06-28"
|
|
861
|
+
},
|
|
862
|
+
method: "GET",
|
|
863
|
+
url: "https://api.notion.com/v1/users/me"
|
|
864
|
+
},
|
|
865
|
+
scopeRequired: false,
|
|
866
|
+
tokenRequest: {
|
|
867
|
+
authIn: "header",
|
|
868
|
+
encoding: "json",
|
|
869
|
+
url: "https://api.notion.com/v1/oauth/token"
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
Okta: {
|
|
873
|
+
authorizationUrl: (config) => `https://${config.domain}/oauth2/default/v1/authorize`,
|
|
874
|
+
isOIDC: true,
|
|
875
|
+
isRefreshable: true,
|
|
876
|
+
PKCEMethod: "S256",
|
|
877
|
+
profileRequest: {
|
|
878
|
+
authIn: "header",
|
|
879
|
+
method: "GET",
|
|
880
|
+
url: (config) => `https://${config.domain}/oauth2/default/v1/userinfo`
|
|
881
|
+
},
|
|
882
|
+
revocationRequest: {
|
|
883
|
+
authIn: "body",
|
|
884
|
+
tokenParamName: "token",
|
|
885
|
+
url: (config) => `https://${config.domain}/oauth2/default/v1/revoke`
|
|
886
|
+
},
|
|
887
|
+
scopeRequired: true,
|
|
888
|
+
tokenRequest: {
|
|
889
|
+
authIn: "body",
|
|
890
|
+
encoding: "form",
|
|
891
|
+
url: (config) => `https://${config.domain}/oauth2/default/v1/token`
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
Osu: {
|
|
895
|
+
authorizationUrl: "https://osu.ppy.sh/oauth/authorize",
|
|
896
|
+
isOIDC: false,
|
|
897
|
+
isRefreshable: true,
|
|
898
|
+
profileRequest: {
|
|
899
|
+
authIn: "header",
|
|
900
|
+
method: "GET",
|
|
901
|
+
url: "https://osu.ppy.sh/api/v2/me"
|
|
902
|
+
},
|
|
903
|
+
scopeRequired: false,
|
|
904
|
+
tokenRequest: {
|
|
905
|
+
authIn: "body",
|
|
906
|
+
encoding: "form",
|
|
907
|
+
url: "https://osu.ppy.sh/oauth/token"
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
Patreon: {
|
|
911
|
+
authorizationUrl: "https://www.patreon.com/oauth2/authorize",
|
|
912
|
+
isOIDC: false,
|
|
913
|
+
isRefreshable: true,
|
|
914
|
+
profileRequest: {
|
|
915
|
+
authIn: "header",
|
|
916
|
+
method: "GET",
|
|
917
|
+
url: "https://www.patreon.com/api/oauth2/v2/identity"
|
|
918
|
+
},
|
|
919
|
+
scopeRequired: false,
|
|
920
|
+
tokenRequest: {
|
|
921
|
+
authIn: "body",
|
|
922
|
+
encoding: "form",
|
|
923
|
+
url: "https://www.patreon.com/api/oauth2/token"
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
Polar: {
|
|
927
|
+
authorizationUrl: "https://polar.sh/oauth2/authorize",
|
|
928
|
+
isOIDC: true,
|
|
929
|
+
isRefreshable: true,
|
|
930
|
+
PKCEMethod: "S256",
|
|
931
|
+
profileRequest: {
|
|
932
|
+
authIn: "header",
|
|
933
|
+
method: "GET",
|
|
934
|
+
url: "https://api.polar.sh/v1/oauth2/userinfo"
|
|
935
|
+
},
|
|
936
|
+
revocationRequest: {
|
|
937
|
+
authIn: "body",
|
|
938
|
+
tokenParamName: "token",
|
|
939
|
+
url: "https://api.polar.sh/v1/oauth2/revoke"
|
|
940
|
+
},
|
|
941
|
+
scopeRequired: true,
|
|
942
|
+
tokenRequest: {
|
|
943
|
+
authIn: "body",
|
|
944
|
+
encoding: "form",
|
|
945
|
+
url: "https://api.polar.sh/v1/oauth2/token"
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
PolarAccessLink: {
|
|
949
|
+
authorizationUrl: "https://flow.polar.com/oauth2/authorization",
|
|
950
|
+
isOIDC: false,
|
|
951
|
+
isRefreshable: false,
|
|
952
|
+
PKCEMethod: "S256",
|
|
953
|
+
profileRequest: {
|
|
954
|
+
authIn: "header",
|
|
955
|
+
method: "GET",
|
|
956
|
+
url: "https://www.polaraccesslink.com/v3/users/me"
|
|
957
|
+
},
|
|
958
|
+
scopeRequired: false,
|
|
959
|
+
tokenRequest: {
|
|
960
|
+
authIn: "header",
|
|
961
|
+
encoding: "form",
|
|
962
|
+
url: "https://polarremote.com/v2/oauth2/token"
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
PolarTeamPro: {
|
|
966
|
+
authorizationUrl: "https://auth.polar.com/oauth/authorize",
|
|
967
|
+
isOIDC: false,
|
|
968
|
+
isRefreshable: true,
|
|
969
|
+
PKCEMethod: "S256",
|
|
970
|
+
profileRequest: {
|
|
971
|
+
authIn: "header",
|
|
972
|
+
method: "GET",
|
|
973
|
+
url: "https://www.polaraccesslink.com/v3/users/<USER_ID>"
|
|
974
|
+
},
|
|
975
|
+
scopeRequired: false,
|
|
976
|
+
tokenRequest: {
|
|
977
|
+
authIn: "header",
|
|
978
|
+
encoding: "form",
|
|
979
|
+
url: "https://auth.polar.com/oauth/token"
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
Reddit: {
|
|
983
|
+
authorizationUrl: "https://www.reddit.com/api/v1/authorize",
|
|
984
|
+
isOIDC: false,
|
|
985
|
+
isRefreshable: true,
|
|
986
|
+
profileRequest: {
|
|
987
|
+
authIn: "header",
|
|
988
|
+
method: "GET",
|
|
989
|
+
url: "https://oauth.reddit.com/api/v1/me"
|
|
990
|
+
},
|
|
991
|
+
revocationRequest: {
|
|
992
|
+
authIn: "header",
|
|
993
|
+
body: new URLSearchParams({
|
|
994
|
+
token_type_hint: "refresh_token"
|
|
995
|
+
}),
|
|
996
|
+
url: "https://www.reddit.com/api/v1/revoke_token"
|
|
997
|
+
},
|
|
998
|
+
scopeRequired: true,
|
|
999
|
+
tokenRequest: {
|
|
1000
|
+
authIn: "header",
|
|
1001
|
+
encoding: "form",
|
|
1002
|
+
url: "https://www.reddit.com/api/v1/access_token"
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
Roblox: {
|
|
1006
|
+
authorizationUrl: "https://apis.roblox.com/oauth/v1/authorize",
|
|
1007
|
+
isOIDC: true,
|
|
1008
|
+
isRefreshable: true,
|
|
1009
|
+
PKCEMethod: "S256",
|
|
1010
|
+
profileRequest: {
|
|
1011
|
+
authIn: "header",
|
|
1012
|
+
method: "GET",
|
|
1013
|
+
url: "https://apis.roblox.com/oauth/v1/userinfo"
|
|
1014
|
+
},
|
|
1015
|
+
scopeRequired: true,
|
|
1016
|
+
tokenRequest: {
|
|
1017
|
+
authIn: "body",
|
|
1018
|
+
encoding: "form",
|
|
1019
|
+
url: "https://apis.roblox.com/oauth/v1/token"
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
Salesforce: {
|
|
1023
|
+
authorizationUrl: "https://login.salesforce.com/services/oauth2/authorize",
|
|
1024
|
+
isOIDC: true,
|
|
1025
|
+
isRefreshable: true,
|
|
1026
|
+
PKCEMethod: "S256",
|
|
1027
|
+
profileRequest: {
|
|
1028
|
+
authIn: "header",
|
|
1029
|
+
method: "GET",
|
|
1030
|
+
url: "https://login.salesforce.com/services/oauth2/userinfo"
|
|
1031
|
+
},
|
|
1032
|
+
revocationRequest: {
|
|
1033
|
+
authIn: "header",
|
|
1034
|
+
url: "https://login.salesforce.com/services/oauth2/revoke"
|
|
1035
|
+
},
|
|
1036
|
+
scopeRequired: false,
|
|
1037
|
+
tokenRequest: {
|
|
1038
|
+
authIn: "body",
|
|
1039
|
+
encoding: "form",
|
|
1040
|
+
url: "https://login.salesforce.com/services/oauth2/token"
|
|
1041
|
+
}
|
|
1042
|
+
},
|
|
1043
|
+
Shikimori: {
|
|
1044
|
+
authorizationUrl: "https://shikimori.org/oauth/authorize",
|
|
1045
|
+
isOIDC: false,
|
|
1046
|
+
isRefreshable: true,
|
|
1047
|
+
profileRequest: {
|
|
1048
|
+
authIn: "header",
|
|
1049
|
+
method: "GET",
|
|
1050
|
+
url: "https://shikimori.one/api/users/whoami"
|
|
1051
|
+
},
|
|
1052
|
+
scopeRequired: false,
|
|
1053
|
+
tokenRequest: {
|
|
1054
|
+
authIn: "body",
|
|
1055
|
+
encoding: "form",
|
|
1056
|
+
url: "https://shikimori.org/oauth/token"
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
Slack: {
|
|
1060
|
+
authorizationUrl: "https://slack.com/openid/connect/authorize",
|
|
1061
|
+
isOIDC: true,
|
|
1062
|
+
isRefreshable: true,
|
|
1063
|
+
profileRequest: {
|
|
1064
|
+
authIn: "query",
|
|
1065
|
+
method: "GET",
|
|
1066
|
+
url: "https://slack.com/api/users.identity"
|
|
1067
|
+
},
|
|
1068
|
+
revocationRequest: {
|
|
1069
|
+
authIn: "body",
|
|
1070
|
+
tokenParamName: "token",
|
|
1071
|
+
url: "https://slack.com/api/auth.revoke"
|
|
1072
|
+
},
|
|
1073
|
+
scopeRequired: true,
|
|
1074
|
+
tokenRequest: {
|
|
1075
|
+
authIn: "body",
|
|
1076
|
+
encoding: "form",
|
|
1077
|
+
url: "https://slack.com/api/openid.connect.token"
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
Spotify: {
|
|
1081
|
+
authorizationUrl: "https://accounts.spotify.com/authorize",
|
|
1082
|
+
isOIDC: false,
|
|
1083
|
+
isRefreshable: true,
|
|
1084
|
+
PKCEMethod: "S256",
|
|
1085
|
+
profileRequest: {
|
|
1086
|
+
authIn: "header",
|
|
1087
|
+
method: "GET",
|
|
1088
|
+
url: "https://api.spotify.com/v1/me"
|
|
1089
|
+
},
|
|
1090
|
+
scopeRequired: false,
|
|
1091
|
+
tokenRequest: {
|
|
1092
|
+
authIn: "body",
|
|
1093
|
+
encoding: "form",
|
|
1094
|
+
url: "https://accounts.spotify.com/api/token"
|
|
1095
|
+
}
|
|
1096
|
+
},
|
|
1097
|
+
StartGG: {
|
|
1098
|
+
authorizationUrl: "https://start.gg/oauth/authorize",
|
|
1099
|
+
isOIDC: false,
|
|
1100
|
+
isRefreshable: true,
|
|
1101
|
+
profileRequest: {
|
|
1102
|
+
authIn: "header",
|
|
1103
|
+
body: {
|
|
1104
|
+
query: `query { currentUser { id slug email player { gamerTag } } }`
|
|
1105
|
+
},
|
|
1106
|
+
headers: {
|
|
1107
|
+
Accept: "application/json",
|
|
1108
|
+
"Content-Type": "application/json"
|
|
1109
|
+
},
|
|
1110
|
+
method: "POST",
|
|
1111
|
+
url: "https://api.start.gg/gql/alpha"
|
|
1112
|
+
},
|
|
1113
|
+
scopeRequired: false,
|
|
1114
|
+
tokenRequest: {
|
|
1115
|
+
authIn: "body",
|
|
1116
|
+
encoding: "form",
|
|
1117
|
+
url: "https://api.start.gg/oauth/access_token"
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
Strava: {
|
|
1121
|
+
authorizationUrl: "https://www.strava.com/oauth/authorize",
|
|
1122
|
+
isOIDC: false,
|
|
1123
|
+
isRefreshable: true,
|
|
1124
|
+
PKCEMethod: "S256",
|
|
1125
|
+
profileRequest: {
|
|
1126
|
+
authIn: "header",
|
|
1127
|
+
method: "GET",
|
|
1128
|
+
url: "https://www.strava.com/api/v3/athlete"
|
|
1129
|
+
},
|
|
1130
|
+
revocationRequest: {
|
|
1131
|
+
authIn: "query",
|
|
1132
|
+
body: new URLSearchParams({
|
|
1133
|
+
token_type_hint: "access_token"
|
|
1134
|
+
}),
|
|
1135
|
+
tokenParamName: "access_token",
|
|
1136
|
+
url: "https://www.strava.com/oauth/deauthorize"
|
|
1137
|
+
},
|
|
1138
|
+
scopeRequired: false,
|
|
1139
|
+
tokenRequest: {
|
|
1140
|
+
authIn: "body",
|
|
1141
|
+
encoding: "form",
|
|
1142
|
+
url: "https://www.strava.com/oauth/token"
|
|
1143
|
+
}
|
|
1144
|
+
},
|
|
1145
|
+
Synology: {
|
|
1146
|
+
authorizationUrl: (config) => `${config.baseURL}/webman/sso/SSOOauth.cgi?client_id=${config.clientId}&response_type=code&redirect_uri=${config.redirectUri}`,
|
|
1147
|
+
isOIDC: false,
|
|
1148
|
+
isRefreshable: false,
|
|
1149
|
+
profileRequest: {
|
|
1150
|
+
authIn: "header",
|
|
1151
|
+
method: "GET",
|
|
1152
|
+
url: (config) => `${config.baseURL}/webman/sso/SSOUserInfo.cgi?client_id=${config.clientId}&access_token=${config.accessToken}`
|
|
1153
|
+
},
|
|
1154
|
+
scopeRequired: false,
|
|
1155
|
+
tokenRequest: {
|
|
1156
|
+
authIn: "body",
|
|
1157
|
+
encoding: "form",
|
|
1158
|
+
url: (config) => `${config.baseURL}/webman/sso/SSOAccessToken.cgi?client_id=${config.clientId}&client_secret=${config.clientSecret}`
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
TikTok: {
|
|
1162
|
+
authorizationUrl: "https://www.tiktok.com/v2/auth/authorize",
|
|
1163
|
+
createAuthorizationURLSearchParams: (config) => ({
|
|
1164
|
+
client_key: config.clientId
|
|
1165
|
+
}),
|
|
1166
|
+
isOIDC: false,
|
|
1167
|
+
isRefreshable: true,
|
|
1168
|
+
PKCEMethod: "S256",
|
|
1169
|
+
profileRequest: {
|
|
1170
|
+
authIn: "query",
|
|
1171
|
+
method: "GET",
|
|
1172
|
+
url: "https://open.douyin.com/oauth/userinfo"
|
|
1173
|
+
},
|
|
1174
|
+
revocationRequest: {
|
|
1175
|
+
authIn: "body",
|
|
1176
|
+
tokenParamName: "token",
|
|
1177
|
+
url: "https://open.tiktokapis.com/v2/oauth/revoke/"
|
|
1178
|
+
},
|
|
1179
|
+
scopeRequired: false,
|
|
1180
|
+
tokenRequest: {
|
|
1181
|
+
authIn: "body",
|
|
1182
|
+
encoding: "form",
|
|
1183
|
+
url: "https://open.tiktokapis.com/v2/oauth/token/"
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
Tiltify: {
|
|
1187
|
+
authorizationUrl: "https://v5api.tiltify.com/oauth/authorize",
|
|
1188
|
+
isOIDC: false,
|
|
1189
|
+
isRefreshable: true,
|
|
1190
|
+
profileRequest: {
|
|
1191
|
+
authIn: "header",
|
|
1192
|
+
method: "GET",
|
|
1193
|
+
url: "https://v5api.tiltify.com/api/public/current-user"
|
|
1194
|
+
},
|
|
1195
|
+
scopeRequired: false,
|
|
1196
|
+
tokenRequest: {
|
|
1197
|
+
authIn: "body",
|
|
1198
|
+
encoding: "form",
|
|
1199
|
+
url: "https://v5api.tiltify.com/oauth/token"
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
Tumblr: {
|
|
1203
|
+
authorizationUrl: "https://www.tumblr.com/oauth2/authorize",
|
|
1204
|
+
isOIDC: false,
|
|
1205
|
+
isRefreshable: true,
|
|
1206
|
+
profileRequest: {
|
|
1207
|
+
authIn: "header",
|
|
1208
|
+
method: "GET",
|
|
1209
|
+
url: "https://api.tumblr.com/v2/user/info"
|
|
1210
|
+
},
|
|
1211
|
+
scopeRequired: false,
|
|
1212
|
+
tokenRequest: {
|
|
1213
|
+
authIn: "body",
|
|
1214
|
+
encoding: "form",
|
|
1215
|
+
url: "https://api.tumblr.com/v2/oauth2/token"
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
Twitch: {
|
|
1219
|
+
authorizationUrl: "https://id.twitch.tv/oauth2/authorize",
|
|
1220
|
+
isOIDC: true,
|
|
1221
|
+
isRefreshable: true,
|
|
1222
|
+
profileRequest: {
|
|
1223
|
+
authIn: "header",
|
|
1224
|
+
headers: (config) => ({
|
|
1225
|
+
"Client-Id": config.clientId
|
|
1226
|
+
}),
|
|
1227
|
+
method: "GET",
|
|
1228
|
+
url: "https://api.twitch.tv/helix/users"
|
|
1229
|
+
},
|
|
1230
|
+
revocationRequest: {
|
|
1231
|
+
authIn: "query",
|
|
1232
|
+
headers: (config) => ({
|
|
1233
|
+
"Client-Id": config.clientId
|
|
1234
|
+
}),
|
|
1235
|
+
tokenParamName: "token",
|
|
1236
|
+
url: "https://id.twitch.tv/oauth2/revoke"
|
|
1237
|
+
},
|
|
1238
|
+
scopeRequired: false,
|
|
1239
|
+
tokenRequest: {
|
|
1240
|
+
authIn: "body",
|
|
1241
|
+
encoding: "form",
|
|
1242
|
+
url: "https://id.twitch.tv/oauth2/token"
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
Twitter: {
|
|
1246
|
+
authorizationUrl: "https://twitter.com/i/oauth2/authorize",
|
|
1247
|
+
isOIDC: false,
|
|
1248
|
+
isRefreshable: true,
|
|
1249
|
+
PKCEMethod: "S256",
|
|
1250
|
+
profileRequest: {
|
|
1251
|
+
authIn: "header",
|
|
1252
|
+
method: "GET",
|
|
1253
|
+
url: "https://api.twitter.com/2/users/me"
|
|
1254
|
+
},
|
|
1255
|
+
revocationRequest: {
|
|
1256
|
+
authIn: "header",
|
|
1257
|
+
url: "https://api.twitter.com/2/oauth2/revoke"
|
|
1258
|
+
},
|
|
1259
|
+
scopeRequired: false,
|
|
1260
|
+
tokenRequest: {
|
|
1261
|
+
authIn: "body",
|
|
1262
|
+
encoding: "form",
|
|
1263
|
+
url: "https://api.twitter.com/2/oauth2/token"
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
VK: {
|
|
1267
|
+
authorizationUrl: "https://oauth.vk.com/authorize",
|
|
1268
|
+
isOIDC: false,
|
|
1269
|
+
isRefreshable: false,
|
|
1270
|
+
profileRequest: {
|
|
1271
|
+
authIn: "query",
|
|
1272
|
+
method: "GET",
|
|
1273
|
+
url: "https://api.vk.com/method/users.get"
|
|
1274
|
+
},
|
|
1275
|
+
scopeRequired: false,
|
|
1276
|
+
tokenRequest: {
|
|
1277
|
+
authIn: "body",
|
|
1278
|
+
encoding: "form",
|
|
1279
|
+
url: "https://oauth.vk.com/access_token"
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
WorkOS: {
|
|
1283
|
+
authorizationUrl: "https://api.workos.com/sso/authorize",
|
|
1284
|
+
isOIDC: true,
|
|
1285
|
+
isRefreshable: true,
|
|
1286
|
+
PKCEMethod: "S256",
|
|
1287
|
+
profileRequest: {
|
|
1288
|
+
authIn: "header",
|
|
1289
|
+
method: "GET",
|
|
1290
|
+
url: "https://api.workos.com/sso/userinfo"
|
|
1291
|
+
},
|
|
1292
|
+
scopeRequired: false,
|
|
1293
|
+
tokenRequest: {
|
|
1294
|
+
authIn: "body",
|
|
1295
|
+
encoding: "form",
|
|
1296
|
+
url: "https://api.workos.com/sso/token"
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
Yahoo: {
|
|
1300
|
+
authorizationUrl: "https://api.login.yahoo.com/oauth2/request_auth",
|
|
1301
|
+
isOIDC: true,
|
|
1302
|
+
isRefreshable: true,
|
|
1303
|
+
PKCEMethod: "S256",
|
|
1304
|
+
profileRequest: {
|
|
1305
|
+
authIn: "header",
|
|
1306
|
+
method: "GET",
|
|
1307
|
+
url: "https://api.login.yahoo.com/openid/v1/userinfo"
|
|
1308
|
+
},
|
|
1309
|
+
revocationRequest: {
|
|
1310
|
+
authIn: "header",
|
|
1311
|
+
url: "https://api.login.yahoo.com/oauth2/revoke"
|
|
1312
|
+
},
|
|
1313
|
+
scopeRequired: false,
|
|
1314
|
+
tokenRequest: {
|
|
1315
|
+
authIn: "body",
|
|
1316
|
+
encoding: "form",
|
|
1317
|
+
url: "https://api.login.yahoo.com/oauth2/get_token"
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
Yandex: {
|
|
1321
|
+
authorizationUrl: "https://oauth.yandex.com/authorize",
|
|
1322
|
+
createAuthorizationURLSearchParams: {
|
|
1323
|
+
device_id: crypto.randomUUID(),
|
|
1324
|
+
device_name: `${navigator.platform ?? "Unknown"} \u2014 ${(navigator.userAgent.split(")")[0] || "").split("(").pop() || "Unknown"}`
|
|
1325
|
+
},
|
|
1326
|
+
isOIDC: false,
|
|
1327
|
+
isRefreshable: true,
|
|
1328
|
+
PKCEMethod: "S256",
|
|
1329
|
+
profileRequest: {
|
|
1330
|
+
authIn: "header",
|
|
1331
|
+
method: "GET",
|
|
1332
|
+
url: "https://login.yandex.ru/info"
|
|
1333
|
+
},
|
|
1334
|
+
revocationRequest: {
|
|
1335
|
+
authIn: "body",
|
|
1336
|
+
tokenParamName: "access_token",
|
|
1337
|
+
url: "https://oauth.yandex.com/revoke_token"
|
|
1338
|
+
},
|
|
1339
|
+
scopeRequired: false,
|
|
1340
|
+
tokenRequest: {
|
|
1341
|
+
authIn: "body",
|
|
1342
|
+
encoding: "form",
|
|
1343
|
+
url: "https://oauth.yandex.com/token"
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
Zoom: {
|
|
1347
|
+
authorizationUrl: "https://zoom.us/oauth/authorize",
|
|
1348
|
+
isOIDC: false,
|
|
1349
|
+
isRefreshable: true,
|
|
1350
|
+
PKCEMethod: "S256",
|
|
1351
|
+
profileRequest: {
|
|
1352
|
+
authIn: "header",
|
|
1353
|
+
method: "GET",
|
|
1354
|
+
url: "https://api.zoom.us/v2/users/me"
|
|
1355
|
+
},
|
|
1356
|
+
revocationRequest: {
|
|
1357
|
+
authIn: "query",
|
|
1358
|
+
headers: (config) => ({
|
|
1359
|
+
Authorization: `Basic ${btoa(`${config.clientId}:${config.clientSecret}`)}`
|
|
1360
|
+
}),
|
|
1361
|
+
tokenParamName: "token",
|
|
1362
|
+
url: "https://zoom.us/oauth/revoke"
|
|
1363
|
+
},
|
|
1364
|
+
scopeRequired: false,
|
|
1365
|
+
tokenRequest: {
|
|
1366
|
+
authIn: "body",
|
|
1367
|
+
encoding: "form",
|
|
1368
|
+
url: "https://zoom.us/oauth/token"
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
var normalizedProviders = Object.fromEntries(Object.entries(providers).map(([key, def]) => [key.toLowerCase(), def]));
|
|
1373
|
+
var providerOptions = Object.keys(providers).filter(isValidProviderOption);
|
|
1374
|
+
var createOAuth2Client = (providerName, config) => {
|
|
1375
|
+
const meta = providers[providerName];
|
|
1376
|
+
const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === "function";
|
|
1377
|
+
const resolveConfigProp = (cfgProp) => isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;
|
|
1378
|
+
const authorizationUrl = resolveConfigProp(meta.authorizationUrl);
|
|
1379
|
+
const tokenUrl = resolveConfigProp(meta.tokenRequest.url);
|
|
1380
|
+
return {
|
|
1381
|
+
async createAuthorizationUrl(opts) {
|
|
1382
|
+
const { state, scope = [], searchParams = [], codeVerifier } = opts;
|
|
1383
|
+
const url = new URL(authorizationUrl);
|
|
1384
|
+
url.searchParams.set("response_type", "code");
|
|
1385
|
+
url.searchParams.set("client_id", config.clientId);
|
|
1386
|
+
if (config.redirectUri)
|
|
1387
|
+
url.searchParams.set("redirect_uri", config.redirectUri);
|
|
1388
|
+
if (state)
|
|
1389
|
+
url.searchParams.set("state", state);
|
|
1390
|
+
if (scope.length)
|
|
1391
|
+
url.searchParams.set("scope", scope.join(" "));
|
|
1392
|
+
if (meta.PKCEMethod !== undefined) {
|
|
1393
|
+
if (!codeVerifier) {
|
|
1394
|
+
throw new Error("`codeVerifier` is required when PKCE is enabled");
|
|
1395
|
+
}
|
|
1396
|
+
const codeChallenge = meta.PKCEMethod === "S256" ? await createS256CodeChallenge(codeVerifier) : codeVerifier;
|
|
1397
|
+
url.searchParams.set("code_challenge_method", meta.PKCEMethod);
|
|
1398
|
+
url.searchParams.set("code_challenge", codeChallenge);
|
|
1399
|
+
}
|
|
1400
|
+
Object.entries(resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
1401
|
+
searchParams.forEach(([key, value]) => url.searchParams.set(key, value));
|
|
1402
|
+
return url;
|
|
1403
|
+
},
|
|
1404
|
+
async fetchUserProfile(accessToken) {
|
|
1405
|
+
const { profileRequest } = meta;
|
|
1406
|
+
const {
|
|
1407
|
+
url,
|
|
1408
|
+
method,
|
|
1409
|
+
authIn,
|
|
1410
|
+
searchParams,
|
|
1411
|
+
body: profileBody,
|
|
1412
|
+
headers
|
|
1413
|
+
} = profileRequest;
|
|
1414
|
+
const endpoint = new URL(resolveConfigProp(url));
|
|
1415
|
+
new URLSearchParams(searchParams).forEach((value, key) => endpoint.searchParams.append(key, value));
|
|
1416
|
+
let headerEntries = [];
|
|
1417
|
+
const rawHeaders = headers ? resolveConfigProp(headers) : undefined;
|
|
1418
|
+
if (rawHeaders instanceof Headers)
|
|
1419
|
+
headerEntries = Array.from(rawHeaders.entries());
|
|
1420
|
+
else if (Array.isArray(rawHeaders))
|
|
1421
|
+
headerEntries = rawHeaders;
|
|
1422
|
+
else if (rawHeaders && typeof rawHeaders === "object")
|
|
1423
|
+
headerEntries = Object.entries(rawHeaders);
|
|
1424
|
+
const profileHeaders = Object.fromEntries(headerEntries.filter(([, value]) => value !== ""));
|
|
1425
|
+
if (authIn === "header") {
|
|
1426
|
+
profileHeaders.Authorization = `Bearer ${accessToken}`;
|
|
1427
|
+
} else {
|
|
1428
|
+
endpoint.searchParams.append("access_token", accessToken);
|
|
1429
|
+
}
|
|
1430
|
+
const init = { headers: profileHeaders, method };
|
|
1431
|
+
if (method === "POST" && profileBody) {
|
|
1432
|
+
profileHeaders["Content-Type"] = "application/json";
|
|
1433
|
+
init.body = JSON.stringify(profileBody);
|
|
1434
|
+
}
|
|
1435
|
+
const response = await fetch(endpoint.toString(), init);
|
|
1436
|
+
if (!response.ok)
|
|
1437
|
+
throw await createOAuth2FetchError(response);
|
|
1438
|
+
return response.json();
|
|
1439
|
+
},
|
|
1440
|
+
async refreshAccessToken(refreshToken) {
|
|
1441
|
+
const { authIn, encoding } = meta.tokenRequest;
|
|
1442
|
+
const params = new URLSearchParams(meta.refreshAccessTokenBody);
|
|
1443
|
+
params.set("grant_type", "refresh_token");
|
|
1444
|
+
params.set("refresh_token", refreshToken);
|
|
1445
|
+
const { clientId } = config;
|
|
1446
|
+
let clientSecretValue;
|
|
1447
|
+
if (hasClientSecret(config)) {
|
|
1448
|
+
clientSecretValue = config.clientSecret;
|
|
1449
|
+
params.set("client_id", clientId);
|
|
1450
|
+
params.set("client_secret", clientSecretValue);
|
|
1451
|
+
}
|
|
1452
|
+
const request = createOAuth2Request({
|
|
1453
|
+
authIn,
|
|
1454
|
+
body: params,
|
|
1455
|
+
clientId,
|
|
1456
|
+
clientSecret: clientSecretValue,
|
|
1457
|
+
encoding,
|
|
1458
|
+
url: tokenUrl
|
|
1459
|
+
});
|
|
1460
|
+
const response = await fetch(request);
|
|
1461
|
+
if (!response.ok)
|
|
1462
|
+
throw await createOAuth2FetchError(response);
|
|
1463
|
+
return response.json();
|
|
1464
|
+
},
|
|
1465
|
+
async revokeToken(token) {
|
|
1466
|
+
const { revocationRequest } = meta;
|
|
1467
|
+
if (!revocationRequest) {
|
|
1468
|
+
throw new Error("Token revocation not defined for this provider");
|
|
1469
|
+
}
|
|
1470
|
+
const { url, authIn, body, headers, tokenParamName } = revocationRequest;
|
|
1471
|
+
const endpoint = resolveConfigProp(url);
|
|
1472
|
+
const revocationBody = body ?? new URLSearchParams;
|
|
1473
|
+
const revocationHeaders = new Headers(headers && resolveConfigProp(headers));
|
|
1474
|
+
const { clientId } = config;
|
|
1475
|
+
const clientSecret = hasClientSecret(config) ? config.clientSecret : undefined;
|
|
1476
|
+
let request;
|
|
1477
|
+
if (authIn === "body") {
|
|
1478
|
+
revocationBody.set(tokenParamName, token);
|
|
1479
|
+
revocationBody.set("client_id", clientId);
|
|
1480
|
+
if (clientSecret)
|
|
1481
|
+
revocationBody.set("client_secret", clientSecret);
|
|
1482
|
+
request = createOAuth2Request({
|
|
1483
|
+
authIn: "body",
|
|
1484
|
+
body: revocationBody,
|
|
1485
|
+
clientId,
|
|
1486
|
+
clientSecret,
|
|
1487
|
+
encoding: "form",
|
|
1488
|
+
headers: revocationHeaders,
|
|
1489
|
+
url: endpoint.toString()
|
|
1490
|
+
});
|
|
1491
|
+
} else if (authIn === "header") {
|
|
1492
|
+
revocationHeaders.set("Authorization", `Bearer ${token}`);
|
|
1493
|
+
request = createOAuth2Request({
|
|
1494
|
+
authIn: "header",
|
|
1495
|
+
body: revocationBody,
|
|
1496
|
+
clientId,
|
|
1497
|
+
clientSecret,
|
|
1498
|
+
encoding: "form",
|
|
1499
|
+
headers: revocationHeaders,
|
|
1500
|
+
url: endpoint.toString()
|
|
1501
|
+
});
|
|
1502
|
+
} else {
|
|
1503
|
+
request = createOAuth2Request({
|
|
1504
|
+
authIn: "query",
|
|
1505
|
+
body: revocationBody,
|
|
1506
|
+
clientId,
|
|
1507
|
+
clientSecret,
|
|
1508
|
+
encoding: "form",
|
|
1509
|
+
headers: revocationHeaders,
|
|
1510
|
+
url: `${endpoint.toString()}?${tokenParamName}=${token}`
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
const response = await fetch(request);
|
|
1514
|
+
if (!response.ok)
|
|
1515
|
+
throw await createOAuth2FetchError(response);
|
|
1516
|
+
},
|
|
1517
|
+
async validateAuthorizationCode(opts) {
|
|
1518
|
+
const { code, codeVerifier } = opts;
|
|
1519
|
+
const { authIn, encoding } = meta.tokenRequest;
|
|
1520
|
+
const bodyObj = {};
|
|
1521
|
+
for (const key in meta.validateAuthorizationCodeBody ?? {}) {
|
|
1522
|
+
const value = meta.validateAuthorizationCodeBody[key];
|
|
1523
|
+
if (typeof value === "string")
|
|
1524
|
+
bodyObj[key] = value;
|
|
1525
|
+
}
|
|
1526
|
+
bodyObj.grant_type = "authorization_code";
|
|
1527
|
+
bodyObj.code = code;
|
|
1528
|
+
if (config.redirectUri)
|
|
1529
|
+
bodyObj.redirect_uri = config.redirectUri;
|
|
1530
|
+
if (meta.PKCEMethod !== undefined) {
|
|
1531
|
+
if (!codeVerifier) {
|
|
1532
|
+
throw new Error("codeVerifier required when PKCE is enabled");
|
|
1533
|
+
}
|
|
1534
|
+
bodyObj.code_verifier = codeVerifier;
|
|
1535
|
+
}
|
|
1536
|
+
const payload = encoding === "json" ? bodyObj : new URLSearchParams(bodyObj);
|
|
1537
|
+
const request = createOAuth2Request({
|
|
1538
|
+
authIn,
|
|
1539
|
+
body: payload,
|
|
1540
|
+
clientId: config.clientId,
|
|
1541
|
+
clientSecret: hasClientSecret(config) ? config.clientSecret : undefined,
|
|
1542
|
+
encoding,
|
|
1543
|
+
url: tokenUrl
|
|
1544
|
+
});
|
|
1545
|
+
const response = await fetch(request);
|
|
1546
|
+
if (!response.ok)
|
|
1547
|
+
throw await createOAuth2FetchError(response);
|
|
1548
|
+
return response.json();
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
};
|
|
1552
|
+
|
|
1553
|
+
// src/index.ts
|
|
1554
|
+
import { Elysia as Elysia10 } from "elysia";
|
|
1555
|
+
|
|
1556
|
+
// src/authorize.ts
|
|
1557
|
+
import { Elysia } from "elysia";
|
|
1558
|
+
|
|
1559
|
+
// src/constants.ts
|
|
1560
|
+
var SECONDS_IN_A_MINUTE = 60;
|
|
1561
|
+
var MILLISECONDS_IN_A_SECOND = 1000;
|
|
1562
|
+
var MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
1563
|
+
var MINUTES_IN_AN_HOUR = 60;
|
|
1564
|
+
var HOURS_IN_A_DAY = 24;
|
|
1565
|
+
var MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_A_DAY;
|
|
1566
|
+
var COOKIE_MINUTES = 30;
|
|
1567
|
+
var COOKIE_DURATION = SECONDS_IN_A_MINUTE * COOKIE_MINUTES;
|
|
1568
|
+
|
|
1569
|
+
// src/authorize.ts
|
|
1570
|
+
var authorize = ({
|
|
1571
|
+
clientProviders,
|
|
1572
|
+
authorizeRoute = "/oauth2/:provider/authorization",
|
|
1573
|
+
onAuthorize
|
|
1574
|
+
}) => new Elysia().get(authorizeRoute, async ({
|
|
1575
|
+
error,
|
|
1576
|
+
redirect,
|
|
1577
|
+
cookie: { state, code_verifier, auth_provider, redirect_url },
|
|
1578
|
+
params: { provider },
|
|
1579
|
+
headers
|
|
1580
|
+
}) => {
|
|
1581
|
+
if (auth_provider === undefined || redirect_url === undefined || state === undefined || code_verifier === undefined)
|
|
1582
|
+
return error("Bad Request", "Cookies are missing");
|
|
1583
|
+
if (provider === undefined)
|
|
1584
|
+
return error("Bad Request", "Provider is required");
|
|
1585
|
+
if (!isValidProviderOption(provider)) {
|
|
1586
|
+
return error("Bad Request", "Invalid provider");
|
|
1587
|
+
}
|
|
1588
|
+
const providerConfig = clientProviders[provider];
|
|
1589
|
+
if (!providerConfig) {
|
|
1590
|
+
return error("Unauthorized", "Invalid provider");
|
|
1591
|
+
}
|
|
1592
|
+
const { providerInstance, scope, searchParams } = providerConfig;
|
|
1593
|
+
const normalizedProvider = provider.toLowerCase();
|
|
1594
|
+
try {
|
|
1595
|
+
const redirectUrl = headers["referer"] ?? "/";
|
|
1596
|
+
redirect_url.set({
|
|
1597
|
+
httpOnly: true,
|
|
1598
|
+
maxAge: COOKIE_DURATION,
|
|
1599
|
+
path: "/",
|
|
1600
|
+
sameSite: "lax",
|
|
1601
|
+
secure: true,
|
|
1602
|
+
value: redirectUrl
|
|
1603
|
+
});
|
|
1604
|
+
auth_provider.set({
|
|
1605
|
+
httpOnly: true,
|
|
1606
|
+
maxAge: COOKIE_DURATION,
|
|
1607
|
+
path: "/",
|
|
1608
|
+
sameSite: "lax",
|
|
1609
|
+
secure: true,
|
|
1610
|
+
value: normalizedProvider
|
|
1611
|
+
});
|
|
1612
|
+
const currentState = generateState();
|
|
1613
|
+
state.set({
|
|
1614
|
+
httpOnly: true,
|
|
1615
|
+
maxAge: COOKIE_DURATION,
|
|
1616
|
+
path: "/",
|
|
1617
|
+
sameSite: "lax",
|
|
1618
|
+
secure: true,
|
|
1619
|
+
value: currentState
|
|
1620
|
+
});
|
|
1621
|
+
const codeVerifier = isPKCEProviderOption(provider) ? generateCodeVerifier() : undefined;
|
|
1622
|
+
codeVerifier && code_verifier.set({
|
|
1623
|
+
httpOnly: true,
|
|
1624
|
+
maxAge: COOKIE_DURATION,
|
|
1625
|
+
path: "/",
|
|
1626
|
+
sameSite: "lax",
|
|
1627
|
+
secure: true,
|
|
1628
|
+
value: codeVerifier
|
|
1629
|
+
});
|
|
1630
|
+
const authorizationURL = await providerInstance.createAuthorizationUrl(codeVerifier ? { codeVerifier, scope, state: currentState } : { scope, state: currentState });
|
|
1631
|
+
searchParams?.forEach(([key, value]) => authorizationURL.searchParams.set(key, value));
|
|
1632
|
+
onAuthorize?.();
|
|
1633
|
+
return redirect(authorizationURL.toString());
|
|
1634
|
+
} catch (err) {
|
|
1635
|
+
if (err instanceof Error) {
|
|
1636
|
+
return error("Internal Server Error", `${err.message} - ${err.stack ?? ""}`);
|
|
1637
|
+
}
|
|
1638
|
+
return error("Internal Server Error", `Unknown error: ${err}`);
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
// src/callback.ts
|
|
1643
|
+
import { Elysia as Elysia3 } from "elysia";
|
|
1644
|
+
|
|
1645
|
+
// src/sessionStore.ts
|
|
1646
|
+
import { Elysia as Elysia2 } from "elysia";
|
|
1647
|
+
var sessionStore = () => {
|
|
1648
|
+
const initialSession = {};
|
|
1649
|
+
return new Elysia2({ name: "sessionStore" }).state({
|
|
1650
|
+
session: initialSession
|
|
1651
|
+
});
|
|
1652
|
+
};
|
|
1653
|
+
|
|
1654
|
+
// src/typeGuards.ts
|
|
1655
|
+
var isValidUser = (user) => true;
|
|
1656
|
+
var isNonEmptyString = (str) => str !== null && str !== undefined && str.trim() !== "";
|
|
1657
|
+
|
|
1658
|
+
// src/callback.ts
|
|
1659
|
+
var callback = ({
|
|
1660
|
+
clientProviders,
|
|
1661
|
+
callbackRoute = "/oauth2/callback",
|
|
1662
|
+
onCallback
|
|
1663
|
+
}) => new Elysia3().use(sessionStore()).get(callbackRoute, async ({
|
|
1664
|
+
error,
|
|
1665
|
+
redirect,
|
|
1666
|
+
store: { session },
|
|
1667
|
+
cookie: {
|
|
1668
|
+
state: stored_state,
|
|
1669
|
+
code_verifier,
|
|
1670
|
+
redirect_url,
|
|
1671
|
+
user_session_id,
|
|
1672
|
+
auth_provider
|
|
1673
|
+
},
|
|
1674
|
+
query: { code, state: callback_state }
|
|
1675
|
+
}) => {
|
|
1676
|
+
if (stored_state === undefined || code_verifier === undefined || auth_provider === undefined || user_session_id === undefined)
|
|
1677
|
+
return error("Bad Request", "Cookies are missing");
|
|
1678
|
+
if (!isNonEmptyString(code) || stored_state.value === undefined) {
|
|
1679
|
+
return error("Bad Request", "Invalid callback request");
|
|
1680
|
+
}
|
|
1681
|
+
if (callback_state !== stored_state.value) {
|
|
1682
|
+
return error("Bad Request", "Invalid state mismatch");
|
|
1683
|
+
}
|
|
1684
|
+
if (auth_provider.value === undefined) {
|
|
1685
|
+
return error("Unauthorized", "No auth provider found");
|
|
1686
|
+
}
|
|
1687
|
+
if (!isValidProviderOption(auth_provider.value)) {
|
|
1688
|
+
return error("Unauthorized", "Invalid provider");
|
|
1689
|
+
}
|
|
1690
|
+
const providerConfig = clientProviders[auth_provider.value];
|
|
1691
|
+
if (!providerConfig) {
|
|
1692
|
+
return error("Unauthorized", "Invalid provider");
|
|
1693
|
+
}
|
|
1694
|
+
const { providerInstance } = providerConfig;
|
|
1695
|
+
try {
|
|
1696
|
+
stored_state.remove();
|
|
1697
|
+
const authProvider = auth_provider.value;
|
|
1698
|
+
const requiresPKCE = isPKCEProviderOption(authProvider);
|
|
1699
|
+
const verifier = requiresPKCE ? code_verifier.value : undefined;
|
|
1700
|
+
if (requiresPKCE && verifier === undefined) {
|
|
1701
|
+
return error("Bad Request", "Code verifier not found and is required");
|
|
1702
|
+
}
|
|
1703
|
+
const tokenResponse = await providerInstance.validateAuthorizationCode(requiresPKCE ? { code, codeVerifier: verifier } : { code });
|
|
1704
|
+
const userProfile = tokenResponse.id_token ? decodeJWT(tokenResponse.id_token) : await providerInstance.fetchUserProfile(tokenResponse.access_token);
|
|
1705
|
+
await onCallback?.({
|
|
1706
|
+
authProvider,
|
|
1707
|
+
session,
|
|
1708
|
+
tokenResponse,
|
|
1709
|
+
user_session_id,
|
|
1710
|
+
userProfile
|
|
1711
|
+
});
|
|
1712
|
+
const redirectUrl = redirect_url?.value ?? "/";
|
|
1713
|
+
return redirect(redirectUrl);
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
return err instanceof Error ? error("Internal Server Error", `${err.message} - ${err.stack ?? ""}`) : error("Internal Server Error", `Failed to validate authorization code: Unknown error: ${err}`);
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
// src/profile.ts
|
|
1720
|
+
import { Elysia as Elysia4 } from "elysia";
|
|
1721
|
+
var profile = ({
|
|
1722
|
+
clientProviders,
|
|
1723
|
+
profileRoute = "/oauth2/profile",
|
|
1724
|
+
onProfile
|
|
1725
|
+
}) => new Elysia4().use(sessionStore()).get(profileRoute, async ({
|
|
1726
|
+
error,
|
|
1727
|
+
store: { session },
|
|
1728
|
+
cookie: { user_session_id, auth_provider }
|
|
1729
|
+
}) => {
|
|
1730
|
+
if (auth_provider === undefined || user_session_id === undefined)
|
|
1731
|
+
return error("Bad Request", "Cookies are missing");
|
|
1732
|
+
if (auth_provider.value === undefined) {
|
|
1733
|
+
return error("Unauthorized", "No auth provider found");
|
|
1734
|
+
}
|
|
1735
|
+
if (!isValidProviderOption(auth_provider.value)) {
|
|
1736
|
+
return error("Unauthorized", "Invalid provider");
|
|
1737
|
+
}
|
|
1738
|
+
if (user_session_id.value === undefined) {
|
|
1739
|
+
return error("Unauthorized", "No user session found");
|
|
1740
|
+
}
|
|
1741
|
+
const providerConfig = clientProviders[auth_provider.value];
|
|
1742
|
+
if (!providerConfig) {
|
|
1743
|
+
return error("Unauthorized", "Invalid provider");
|
|
1744
|
+
}
|
|
1745
|
+
const { providerInstance } = providerConfig;
|
|
1746
|
+
const userSession = session[user_session_id.value];
|
|
1747
|
+
if (userSession === undefined) {
|
|
1748
|
+
return error("Unauthorized", "No user session found");
|
|
1749
|
+
}
|
|
1750
|
+
const { accessToken } = userSession;
|
|
1751
|
+
try {
|
|
1752
|
+
const userProfile = await providerInstance.fetchUserProfile(accessToken);
|
|
1753
|
+
await onProfile?.({
|
|
1754
|
+
userProfile
|
|
1755
|
+
});
|
|
1756
|
+
return new Response(JSON.stringify(userProfile));
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
return err instanceof Error ? error("Internal Server Error", `${err.message} - ${err.stack ?? ""}`) : error("Internal Server Error", `Failed to validate authorization code: Unknown error: ${err}`);
|
|
1759
|
+
}
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
// src/protectRoute.ts
|
|
1763
|
+
import { Elysia as Elysia5 } from "elysia";
|
|
1764
|
+
var protectRoute = () => new Elysia5().use(sessionStore()).derive(({ store: { session }, cookie: { user_session_id }, error }) => ({
|
|
1765
|
+
protectRoute: async (handleAuth, handleAuthFail) => {
|
|
1766
|
+
if (user_session_id === undefined)
|
|
1767
|
+
return error("Bad Request", "Cookies are missing");
|
|
1768
|
+
if (user_session_id.value === undefined) {
|
|
1769
|
+
return handleAuthFail?.() ?? error("Unauthorized", "No session ID found");
|
|
1770
|
+
}
|
|
1771
|
+
const userSession = session[user_session_id.value];
|
|
1772
|
+
if (userSession === undefined) {
|
|
1773
|
+
return handleAuthFail?.() ?? error("Unauthorized", "No session found");
|
|
1774
|
+
}
|
|
1775
|
+
return handleAuth();
|
|
1776
|
+
}
|
|
1777
|
+
})).as("plugin");
|
|
1778
|
+
|
|
1779
|
+
// src/refresh.ts
|
|
1780
|
+
import { Elysia as Elysia6 } from "elysia";
|
|
1781
|
+
var refresh = ({
|
|
1782
|
+
clientProviders,
|
|
1783
|
+
refreshRoute = "/oauth2/tokens",
|
|
1784
|
+
onRefresh
|
|
1785
|
+
}) => new Elysia6().use(sessionStore()).post(refreshRoute, async ({
|
|
1786
|
+
error,
|
|
1787
|
+
store: { session },
|
|
1788
|
+
cookie: { user_session_id, auth_provider }
|
|
1789
|
+
}) => {
|
|
1790
|
+
if (auth_provider === undefined || user_session_id === undefined)
|
|
1791
|
+
return error("Bad Request", "Cookies are missing");
|
|
1792
|
+
if (auth_provider.value === undefined) {
|
|
1793
|
+
return error("Unauthorized", "No auth provider found");
|
|
1794
|
+
}
|
|
1795
|
+
if (!isValidProviderOption(auth_provider.value)) {
|
|
1796
|
+
return error("Bad Request", "Invalid provider");
|
|
1797
|
+
}
|
|
1798
|
+
if (user_session_id.value === undefined) {
|
|
1799
|
+
return error("Unauthorized", "No user session found");
|
|
1800
|
+
}
|
|
1801
|
+
const providerConfig = clientProviders[auth_provider.value];
|
|
1802
|
+
if (!providerConfig) {
|
|
1803
|
+
return error("Unauthorized", "Invalid provider");
|
|
1804
|
+
}
|
|
1805
|
+
const { providerInstance } = providerConfig;
|
|
1806
|
+
const userSession = session[user_session_id.value];
|
|
1807
|
+
if (userSession === undefined) {
|
|
1808
|
+
return error("Unauthorized", "No user session found");
|
|
1809
|
+
}
|
|
1810
|
+
const { refreshToken } = userSession;
|
|
1811
|
+
if (!isRefreshableOAuth2Client(auth_provider.value, providerInstance)) {
|
|
1812
|
+
return error("Not Implemented", "Provider is not refreshable");
|
|
1813
|
+
}
|
|
1814
|
+
if (refreshToken === undefined) {
|
|
1815
|
+
return error("Bad Request", "No refresh token found");
|
|
1816
|
+
}
|
|
1817
|
+
try {
|
|
1818
|
+
const tokenResponse = await providerInstance.refreshAccessToken(refreshToken);
|
|
1819
|
+
onRefresh?.({ tokenResponse });
|
|
1820
|
+
return new Response("Token refreshed", {
|
|
1821
|
+
status: 204
|
|
1822
|
+
});
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
if (err instanceof Error) {
|
|
1825
|
+
return error("Internal Server Error", `Failed to refresh token: ${err.message}`);
|
|
1826
|
+
}
|
|
1827
|
+
return error("Internal Server Error", `Failed to refresh token: Unknown error: ${err}`);
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1831
|
+
// src/revoke.ts
|
|
1832
|
+
import { Elysia as Elysia7 } from "elysia";
|
|
1833
|
+
var revoke = ({
|
|
1834
|
+
clientProviders,
|
|
1835
|
+
revokeRoute = "/oauth2/revocation",
|
|
1836
|
+
onRevocation
|
|
1837
|
+
}) => new Elysia7().use(sessionStore()).post(revokeRoute, async ({
|
|
1838
|
+
error,
|
|
1839
|
+
store: { session },
|
|
1840
|
+
cookie: { user_session_id, auth_provider }
|
|
1841
|
+
}) => {
|
|
1842
|
+
if (auth_provider === undefined || user_session_id === undefined)
|
|
1843
|
+
return error("Bad Request", "Cookies are missing");
|
|
1844
|
+
if (auth_provider.value === undefined) {
|
|
1845
|
+
return error("Unauthorized", "No auth provider found");
|
|
1846
|
+
}
|
|
1847
|
+
if (!isValidProviderOption(auth_provider.value)) {
|
|
1848
|
+
return error("Bad Request", "Invalid provider");
|
|
1849
|
+
}
|
|
1850
|
+
if (user_session_id.value === undefined) {
|
|
1851
|
+
return error("Unauthorized", "No user session found");
|
|
1852
|
+
}
|
|
1853
|
+
const providerConfig = clientProviders[auth_provider.value];
|
|
1854
|
+
if (!providerConfig) {
|
|
1855
|
+
return error("Unauthorized", "Invalid provider");
|
|
1856
|
+
}
|
|
1857
|
+
const { providerInstance } = providerConfig;
|
|
1858
|
+
if (!isRevocableOAuth2Client(auth_provider.value, providerInstance)) {
|
|
1859
|
+
return error("Not Implemented", "Provider does not support revocation");
|
|
1860
|
+
}
|
|
1861
|
+
const userSession = session[user_session_id.value];
|
|
1862
|
+
if (userSession === undefined) {
|
|
1863
|
+
return error("Unauthorized", "No user session found");
|
|
1864
|
+
}
|
|
1865
|
+
const { accessToken } = userSession;
|
|
1866
|
+
try {
|
|
1867
|
+
await providerInstance.revokeToken(accessToken);
|
|
1868
|
+
onRevocation?.({ tokenToRevoke: accessToken });
|
|
1869
|
+
return new Response("Token revoked", {
|
|
1870
|
+
status: 204
|
|
1871
|
+
});
|
|
1872
|
+
} catch (err) {
|
|
1873
|
+
if (err instanceof Error) {
|
|
1874
|
+
return error("Internal Server Error", `Failed to revoke token: ${err.message}`);
|
|
1875
|
+
}
|
|
1876
|
+
return error("Internal Server Error", `Failed to revoke token: Unknown error: ${err}`);
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
// src/signout.ts
|
|
1881
|
+
import { Elysia as Elysia8 } from "elysia";
|
|
1882
|
+
var signout = ({
|
|
1883
|
+
signoutRoute = "/oauth2/signout",
|
|
1884
|
+
onSignOut
|
|
1885
|
+
}) => new Elysia8().post(signoutRoute, async ({ error, cookie: { user_session_id, auth_provider } }) => {
|
|
1886
|
+
if (auth_provider === undefined || user_session_id === undefined)
|
|
1887
|
+
return error("Bad Request", "Cookies are missing");
|
|
1888
|
+
if (auth_provider.value === undefined) {
|
|
1889
|
+
return error("Unauthorized", "No auth provider found");
|
|
1890
|
+
}
|
|
1891
|
+
try {
|
|
1892
|
+
onSignOut?.();
|
|
1893
|
+
user_session_id.remove();
|
|
1894
|
+
auth_provider.remove();
|
|
1895
|
+
return new Response("Succesfuly Logged Out", {
|
|
1896
|
+
status: 204
|
|
1897
|
+
});
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
if (err instanceof Error) {
|
|
1900
|
+
return error("Internal Server Error", `Failed to signout: ${err.message}`);
|
|
1901
|
+
}
|
|
1902
|
+
return error("Internal Server Error", `Failed to signout: Unknown error: ${err}`);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
// src/status.ts
|
|
1907
|
+
import { Elysia as Elysia9 } from "elysia";
|
|
1908
|
+
var status = ({
|
|
1909
|
+
clientProviders,
|
|
1910
|
+
statusRoute = "/oauth2/status",
|
|
1911
|
+
onStatus
|
|
1912
|
+
}) => new Elysia9().use(sessionStore()).get(statusRoute, async ({
|
|
1913
|
+
error,
|
|
1914
|
+
cookie: { user_session_id, auth_provider },
|
|
1915
|
+
store: { session }
|
|
1916
|
+
}) => {
|
|
1917
|
+
if (auth_provider === undefined || user_session_id === undefined)
|
|
1918
|
+
return error("Bad Request", "Cookies are missing");
|
|
1919
|
+
try {
|
|
1920
|
+
if (user_session_id.value === undefined) {
|
|
1921
|
+
return new Response(JSON.stringify({ isLoggedIn: false, user: null }), {
|
|
1922
|
+
headers: { "Content-Type": "application/json" }
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
if (auth_provider.value === undefined) {
|
|
1926
|
+
return new Response(JSON.stringify({ isLoggedIn: false, user: null }), {
|
|
1927
|
+
headers: { "Content-Type": "application/json" }
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
const providerConfig = clientProviders[auth_provider.value];
|
|
1931
|
+
if (!providerConfig) {
|
|
1932
|
+
return error("Unauthorized", "Invalid provider");
|
|
1933
|
+
}
|
|
1934
|
+
const { providerInstance } = providerConfig;
|
|
1935
|
+
if (!isRefreshableOAuth2Client("Google", providerInstance)) {
|
|
1936
|
+
return error("Not Implemented", "Provider is not refreshable");
|
|
1937
|
+
}
|
|
1938
|
+
const userSession = session[user_session_id.value];
|
|
1939
|
+
if (userSession === undefined) {
|
|
1940
|
+
return new Response(JSON.stringify({ isLoggedIn: false, user: null }), {
|
|
1941
|
+
headers: { "Content-Type": "application/json" }
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
const { user } = userSession;
|
|
1945
|
+
onStatus?.();
|
|
1946
|
+
return new Response(JSON.stringify({ isLoggedIn: true, user }), {
|
|
1947
|
+
headers: { "Content-Type": "application/json" }
|
|
1948
|
+
});
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
if (err instanceof Error) {
|
|
1951
|
+
return error("Internal Server Error", `Error: ${err.message} - ${err.stack ?? ""}`);
|
|
1952
|
+
}
|
|
1953
|
+
return error("Internal Server Error", `Unknown Error: ${err}`);
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
// src/utils.ts
|
|
1958
|
+
var instantiateUserSession = async ({
|
|
1959
|
+
user_session_id,
|
|
1960
|
+
session,
|
|
1961
|
+
tokenResponse,
|
|
1962
|
+
getUser,
|
|
1963
|
+
createUser
|
|
1964
|
+
}) => {
|
|
1965
|
+
let user = await getUser();
|
|
1966
|
+
user = user ?? await createUser();
|
|
1967
|
+
if (!isValidUser(user))
|
|
1968
|
+
throw new Error("Internal Server Error - Invalid user schema");
|
|
1969
|
+
const sessionKey = crypto.randomUUID();
|
|
1970
|
+
session[sessionKey] = {
|
|
1971
|
+
accessToken: tokenResponse.access_token,
|
|
1972
|
+
expiresAt: Date.now() + MILLISECONDS_IN_A_DAY,
|
|
1973
|
+
refreshToken: tokenResponse.refresh_token,
|
|
1974
|
+
user
|
|
1975
|
+
};
|
|
1976
|
+
user_session_id.set({
|
|
1977
|
+
httpOnly: true,
|
|
1978
|
+
sameSite: "lax",
|
|
1979
|
+
secure: true,
|
|
1980
|
+
value: sessionKey
|
|
1981
|
+
});
|
|
1982
|
+
};
|
|
1983
|
+
var createAuthConfig = (props) => props;
|
|
1984
|
+
|
|
1985
|
+
// src/index.ts
|
|
1986
|
+
var absoluteAuth = ({
|
|
1987
|
+
config,
|
|
1988
|
+
authorizeRoute,
|
|
1989
|
+
callbackRoute,
|
|
1990
|
+
profileRoute,
|
|
1991
|
+
signoutRoute,
|
|
1992
|
+
statusRoute,
|
|
1993
|
+
refreshRoute,
|
|
1994
|
+
revokeRoute,
|
|
1995
|
+
onAuthorize,
|
|
1996
|
+
onProfile,
|
|
1997
|
+
onCallback,
|
|
1998
|
+
onStatus,
|
|
1999
|
+
onRefresh,
|
|
2000
|
+
onSignOut,
|
|
2001
|
+
onRevocation
|
|
2002
|
+
}) => {
|
|
2003
|
+
const clientProviders = Object.entries(config).reduce((acc, [providerName, providerConfig]) => {
|
|
2004
|
+
if (isValidProviderOption(providerName)) {
|
|
2005
|
+
acc[providerName.toLocaleLowerCase()] = {
|
|
2006
|
+
providerInstance: createOAuth2Client(providerName, providerConfig.credentials),
|
|
2007
|
+
scope: providerConfig.scope,
|
|
2008
|
+
searchParams: providerConfig.searchParams
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
return acc;
|
|
2012
|
+
}, {});
|
|
2013
|
+
return new Elysia10().use(signout({ onSignOut, signoutRoute })).use(revoke({ clientProviders, onRevocation, revokeRoute })).use(status({ clientProviders, onStatus, statusRoute })).use(refresh({ clientProviders, onRefresh, refreshRoute })).use(authorize({ authorizeRoute, clientProviders, onAuthorize })).use(callback({
|
|
2014
|
+
callbackRoute,
|
|
2015
|
+
clientProviders,
|
|
2016
|
+
onCallback
|
|
2017
|
+
})).use(profile({
|
|
2018
|
+
clientProviders,
|
|
2019
|
+
onProfile,
|
|
2020
|
+
profileRoute
|
|
2021
|
+
})).use(protectRoute()).as("plugin");
|
|
2022
|
+
};
|
|
2023
|
+
export {
|
|
2024
|
+
instantiateUserSession,
|
|
2025
|
+
createAuthConfig,
|
|
2026
|
+
absoluteAuth
|
|
2027
|
+
};
|