UNPKG

authrix

Version:

Lightweight, flexible authentication library for Node.js and TypeScript.

2 lines 6.25 kB
import {a as a$1}from'./chunk-3SAEGOMQ.mjs';var a={AUTHORIZE:"https://twitter.com/i/oauth2/authorize",TOKEN:"https://api.twitter.com/2/oauth2/token",REVOKE:"https://api.twitter.com/2/oauth2/revoke",USER:"https://api.twitter.com/2/users/me",USER_BY_ID:r=>`https://api.twitter.com/2/users/${r}`},k=["tweet.read","users.read","offline.access"],E=["id","name","username","created_at","description","location","url","verified","verified_type","profile_image_url","public_metrics","protected"],l=null,u=new Map;function p(){if(l)return l;let r=process.env.X_CLIENT_ID||process.env.TWITTER_CLIENT_ID,e=process.env.X_CLIENT_SECRET||process.env.TWITTER_CLIENT_SECRET,t=process.env.X_REDIRECT_URI||process.env.TWITTER_REDIRECT_URI;if(!r||!e||!t){let n=[];throw r||n.push("X_CLIENT_ID or TWITTER_CLIENT_ID"),e||n.push("X_CLIENT_SECRET or TWITTER_CLIENT_SECRET"),t||n.push("X_REDIRECT_URI or TWITTER_REDIRECT_URI"),new Error(`Missing X/Twitter OAuth environment variables: ${n.join(", ")}. These are required when using X/Twitter OAuth functionality. Visit https://developer.twitter.com/en/portal/dashboard to obtain these values.`)}return l={clientId:r,clientSecret:e,redirectUri:t},l}var c=class{static generateCodeVerifier(){let e=new Uint8Array(32);return crypto.getRandomValues(e),this.base64URLEncode(e)}static async generateCodeChallenge(e){let n=new TextEncoder().encode(e);if(typeof globalThis.crypto?.subtle<"u"){let o=await crypto.subtle.digest("SHA-256",n);return this.base64URLEncode(new Uint8Array(o))}else {let s=a$1("crypto").createHash("sha256").update(e).digest();return this.base64URLEncode(s)}}static base64URLEncode(e){return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}static generateState(){let e=new Uint8Array(16);return crypto.getRandomValues(e),this.base64URLEncode(e)}};async function T(r,e,t={}){let n=await fetch(r,{...t,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json",...t.headers}});if(!n.ok){let o=await n.text(),s=`X API request failed: ${n.status}`;try{let d=JSON.parse(o);s=d.detail||d.error||s;}catch{}throw new Error(s)}return n.json()}async function y(r={}){let e=p(),t=c.generateCodeVerifier(),n=await c.generateCodeChallenge(t),o=r.state||c.generateState();u.set(o,{codeVerifier:t,codeChallenge:n,state:o}),setTimeout(()=>{u.delete(o);},10*60*1e3);let s=new URLSearchParams({client_id:e.clientId,redirect_uri:r.redirectUri||e.redirectUri,response_type:"code",scope:(r.scopes||k).join(" "),state:o,code_challenge:n,code_challenge_method:"S256"});return r.prompt&&s.set("prompt",r.prompt),r.accessType==="offline"&&s.set("access_type","offline"),{url:`${a.AUTHORIZE}?${s.toString()}`,state:o,codeVerifier:t}}async function b(r,e,t={}){let n=p();try{let o=t.codeVerifier;if(!o){let h=u.get(e);if(!h)throw new Error("PKCE challenge not found. Session may have expired.");o=h.codeVerifier,u.delete(e);}let s=new URLSearchParams({grant_type:"authorization_code",client_id:n.clientId,code:r,redirect_uri:t.redirectUri||n.redirectUri,code_verifier:o}),d=btoa(`${n.clientId}:${n.clientSecret}`),g=await fetch(a.TOKEN,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${d}`},body:s.toString()});if(!g.ok){let h=await g.text();throw new Error(`Token exchange failed: ${g.status} - ${h}`)}let f=await g.json();if(!f.access_token)throw new Error("No access token received from X");let m=new URL(a.USER);m.searchParams.set("user.fields",E.join(","));let w=await T(m.toString(),f.access_token);if(!w.data)throw new Error("No user data received from X");let i=w.data,_={id:i.id,username:i.username,name:i.name,avatar:i.profile_image_url?.replace("_normal","_400x400"),provider:"x",emailVerified:!1,metadata:{description:i.description,location:i.location,url:i.url,verified:i.verified,verifiedType:i.verified_type,protected:i.protected,followersCount:i.public_metrics?.followers_count,followingCount:i.public_metrics?.following_count,tweetCount:i.public_metrics?.tweet_count,likeCount:i.public_metrics?.like_count,createdAt:i.created_at}};return t.includeEmail&&(_.email=void 0),t.includeToken?{..._,token:f.access_token,refreshToken:f.refresh_token}:_}catch(o){throw o instanceof Error?o.message.includes("fetch")?new Error("Network error during X authentication. Please try again."):o.message.includes("401")||o.message.includes("403")?new Error("X authentication failed. Please try again."):new Error(`X authentication failed: ${o.message}`):new Error("An unexpected error occurred during X authentication.")}}async function C(r){let e=p();try{let t=btoa(`${e.clientId}:${e.clientSecret}`),n=await fetch(a.TOKEN,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${t}`},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:r,client_id:e.clientId}).toString()});if(!n.ok)throw new Error("Token refresh failed");let o=await n.json();return {accessToken:o.access_token,refreshToken:o.refresh_token}}catch{throw new Error("Failed to refresh X authentication token")}}async function U(r,e="access_token"){let t=p();try{let n=btoa(`${t.clientId}:${t.clientSecret}`);return (await fetch(a.REVOKE,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${n}`},body:new URLSearchParams({token:r,token_type_hint:e,client_id:t.clientId}).toString()})).ok}catch{return false}}async function S(r,e){try{let t=new URL(a.USER_BY_ID(r));return t.searchParams.set("user.fields",E.join(",")),await T(t.toString(),e)}catch{return null}}async function I(r){try{let e=new URL(a.USER);return e.searchParams.set("user.fields","id"),(await fetch(e.toString(),{headers:{Authorization:`Bearer ${r}`}})).ok}catch{return false}}function X(r){let e=c.generateState();if(r){let t=JSON.stringify(r),n=btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"");return `${e}.${n}`}return e}function A(r){let e=r.split(".");if(e.length===1)return {token:e[0]};try{let t=e[1],n=t+"=".repeat((4-t.length%4)%4),o=atob(n.replace(/-/g,"+").replace(/_/g,"/")),s=JSON.parse(o);return {token:e[0],data:s}}catch{return {token:e[0]}}}function v(){u.clear();}function O(){l=null;} export{c as a,y as b,b as c,C as d,U as e,S as f,I as g,X as h,A as i,v as j,O as k};