UNPKG

authrix

Version:

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

3 lines 5.83 kB
'use strict';var m={AUTH:"https://appleid.apple.com/auth/authorize",TOKEN:"https://appleid.apple.com/auth/token",REVOKE:"https://appleid.apple.com/auth/revoke"},y=["name","email"],u=null,g=null;function p(){if(u)return u;let t=process.env.APPLE_CLIENT_ID||process.env.APPLE_SERVICE_ID,e=process.env.APPLE_TEAM_ID,n=process.env.APPLE_KEY_ID,r=process.env.APPLE_PRIVATE_KEY,s=process.env.APPLE_REDIRECT_URI||process.env.APPLE_OAUTH_REDIRECT_URI;if(!t||!e||!n||!r||!s){let i=[];throw t||i.push("APPLE_CLIENT_ID"),e||i.push("APPLE_TEAM_ID"),n||i.push("APPLE_KEY_ID"),r||i.push("APPLE_PRIVATE_KEY"),s||i.push("APPLE_REDIRECT_URI"),new Error(`Missing Apple OAuth environment variables: ${i.join(", ")}. These are required when using Apple OAuth functionality. Visit https://developer.apple.com/account/resources to configure Sign in with Apple.`)}return u={clientId:t,teamId:e,keyId:n,privateKey:r.replace(/\\n/g,` `),redirectUri:s},u}function A(){let t=p();if(g&&g.expiresAt>Date.now())return g.secret;let e=Math.floor(Date.now()/1e3),n=3600*24*180,r=e+n,s={alg:"ES256",kid:t.keyId,typ:"JWT"},i={iss:t.teamId,iat:e,exp:r,aud:"https://appleid.apple.com",sub:t.clientId},a=c=>Buffer.from(c).toString("base64").replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"),h=a(JSON.stringify(s)),o=a(JSON.stringify(i)),l=`${h}.${o}`,f=a(E(l,t.privateKey)),d=`${l}.${f}`;return g={secret:d,expiresAt:(r-60)*1e3},d}function E(t,e){return "signature_placeholder"}function _(t){try{let[,e]=t.split(".");if(!e)return null;let n=e+"=".repeat((4-e.length%4)%4),r=atob(n.replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(r)}catch{return null}}function k(t={}){let e=p(),n=new URLSearchParams({client_id:e.clientId,redirect_uri:t.redirectUri||e.redirectUri,response_type:"code id_token",response_mode:t.responseMode||"form_post",scope:(t.scopes||y).join(" ")});return t.state&&n.set("state",t.state),t.nonce&&n.set("nonce",t.nonce),`${m.AUTH}?${n.toString()}`}async function w(t,e={}){let n=p();try{let r=A(),s=await fetch(m.TOKEN,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({client_id:n.clientId,client_secret:r,code:t,grant_type:"authorization_code",redirect_uri:e.redirectUri||n.redirectUri}).toString()});if(!s.ok){let c=await s.text();throw new Error(`Token exchange failed: ${s.status} - ${c}`)}let i=await s.json();if(!i.id_token)throw new Error("No ID token received from Apple");let a=_(i.id_token);if(!a)throw new Error("Failed to decode ID token");if(a.aud!==n.clientId)throw new Error("Token audience mismatch");if(a.iss!=="https://appleid.apple.com")throw new Error("Invalid token issuer");let h=Math.floor(Date.now()/1e3);if(a.exp<h)throw new Error("ID token has expired");let o={};if(e.user)if(typeof e.user=="string")try{let c=JSON.parse(e.user);o={firstName:c.name?.firstName,lastName:c.name?.lastName,email:c.email};}catch{}else typeof e.user=="object"&&(o={firstName:e.user.name?.firstName,lastName:e.user.name?.lastName,email:e.user.email});let l=a.email||o.email,f=a.email_verified==="true"||a.email_verified===!0;if(!e.skipEmailVerification&&l&&!f)throw new Error("Email address is not verified");let d={id:a.sub,email:l,name:o.firstName&&o.lastName?`${o.firstName} ${o.lastName}`.trim():o.firstName||o.lastName||void 0,provider:"apple",emailVerified:f,metadata:{firstName:o.firstName,lastName:o.lastName,realUserStatus:a.real_user_status}};return e.includeTokens?{...d,tokens:{access:i.access_token,refresh:i.refresh_token,idToken:i.id_token}}:d}catch(r){throw r instanceof Error?r.message.includes("fetch")?new Error("Network error during Apple authentication. Please try again."):r.message.includes("audience")||r.message.includes("issuer")?new Error("Security validation failed. Please try again."):new Error(`Apple authentication failed: ${r.message}`):new Error("An unexpected error occurred during Apple authentication.")}}async function P(t,e="access_token"){try{let n=p(),r=A();return (await fetch(m.REVOKE,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:n.clientId,client_secret:r,token:t,token_type_hint:e}).toString()})).ok}catch{return false}}async function S(t){try{let e=p(),n=A(),r=await fetch(m.TOKEN,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({client_id:e.clientId,client_secret:n,grant_type:"refresh_token",refresh_token:t}).toString()});return r.ok?await r.json():null}catch{return null}}async function T(t){try{let e=_(t);if(!e)return null;let n=p();if(e.aud!==n.clientId||e.iss!=="https://appleid.apple.com")return null;let r=Math.floor(Date.now()/1e3);return e.exp<r?null:e}catch{return null}}function I(t){let e=crypto.getRandomValues(new Uint8Array(32)),n=Array.from(e,r=>r.toString(16).padStart(2,"0")).join("");if(t){let r=JSON.stringify(t),s=btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"");return `${n}.${s}`}return n}function N(t){let e=t.split(".");if(e.length===1)return {token:e[0]};try{let n=e[1],r=n+"=".repeat((4-n.length%4)%4),s=atob(r.replace(/-/g,"+").replace(/_/g,"/")),i=JSON.parse(s);return {token:e[0],data:i}}catch{return {token:e[0]}}}function O(){let t=crypto.getRandomValues(new Uint8Array(16));return Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}function b(){u=null,g=null;}async function v(t,e){if(typeof globalThis.crypto>"u"){let r=(await import('crypto')).createSign("SHA256");return r.update(t),r.end(),r.sign({key:e,format:"pem",type:"pkcs8"}).toString("base64").replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}throw new Error("Web Crypto API implementation for ES256 signing not provided")} exports.a=k;exports.b=w;exports.c=P;exports.d=S;exports.e=T;exports.f=I;exports.g=N;exports.h=O;exports.i=b;exports.j=v;