aui-auth 0.0.0 → 0.0.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/README.md +3 -0
- package/dist/cookies-D3lpM5ER.js +1 -0
- package/dist/index-BDndKDqC.d.ts +59 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/next.d.ts +10 -0
- package/dist/next.js +1 -0
- package/dist/pg.d.ts +17 -0
- package/dist/pg.js +14 -0
- package/dist/types-DdCYMKxG.d.ts +87 -0
- package/package.json +41 -6
- package/index.d.ts +0 -1
- package/index.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){if(!e)return;let t=e.split(`,`);return t[t.length-1].trim()||void 0}function t(t){let n=new URL(t.url);return`${e(t.headers.get(`x-forwarded-proto`))||n.protocol.replace(`:`,``)}://${e(t.headers.get(`x-forwarded-host`))||t.headers.get(`host`)||n.host}`}function n(e){return new URL(e).protocol===`https:`}function r(e,t){return t?`__Secure-${e}`:e}function i(e){let t=new Map;if(!e)return t;for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e===-1)continue;let r=n.slice(0,e).trim();!r||t.has(r)||t.set(r,n.slice(e+1).trim())}return t}function a(e,t){let n=i(e.headers.get(`cookie`));return n.get(r(t,!0))||n.get(r(t,!1))||null}function o(e,t,n,r){let i=[`${e}=${t}`,`Path=/`,`HttpOnly`,`SameSite=Lax`,`Max-Age=${r}`];return n&&i.push(`Secure`),i.join(`; `)}function s(e,t,i,a,s){let c=n(a);e.append(`set-cookie`,o(r(t,c),i,c,Math.floor(s/1e3)))}function c(e,t,i){let a=n(i);for(let n of[a,!a])e.append(`set-cookie`,o(r(t,n),``,n,0))}function l(e,t=307){return new Response(null,{status:t,headers:{location:e.toString()}})}function u(e,t){return`${e}_flow_${t}`}function d(e){return btoa(JSON.stringify(e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/g,``)}function f(e){if(!e)return null;try{let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=JSON.parse(atob(t));return typeof n.verifier==`string`?{verifier:n.verifier,redirectPath:typeof n.redirectPath==`string`?n.redirectPath:`/`,silent:n.silent===!0}:null}catch{return null}}function p(e,t){if(!e)return`/`;let n;try{n=new URL(e,t)}catch{return`/`}return n.origin===t?`${n.pathname}${n.search}${n.hash}`:`/`}export{u as a,t as c,d as i,p as l,r as n,a as o,f as r,l as s,c as t,s as u};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { a as AccountsSession, c as AccountsTokens, d as ResolvedAccountsAuthConfig, l as AccountsUser, s as AccountsSessionStore, t as AccountsAuthConfig } from "./types-DdCYMKxG.js";
|
|
2
|
+
//#region src/guard.d.ts
|
|
3
|
+
type GuardOptions = {
|
|
4
|
+
publicPaths?: string[];
|
|
5
|
+
apiPaths?: string[];
|
|
6
|
+
redirectPath?: (request: Request) => string;
|
|
7
|
+
};
|
|
8
|
+
type GuardResult<Data> = {
|
|
9
|
+
kind: "ok";
|
|
10
|
+
session: AccountsSession<Data> | null;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "response";
|
|
13
|
+
response: Response;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/cookies.d.ts
|
|
17
|
+
declare function requestOrigin(request: Request): string;
|
|
18
|
+
declare function sanitizeRedirectPath(rawRedirect: string | null | undefined, origin: string): string;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/oidc.d.ts
|
|
21
|
+
declare function accountsEndpoint(issuer: string, path: string): string;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/store/memory.d.ts
|
|
24
|
+
declare function createMemorySessionStore<Data>(): AccountsSessionStore<Data> & {
|
|
25
|
+
rows: Map<string, AccountsSession<Data>>;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/index.d.ts
|
|
29
|
+
declare function createAccountsAuth<Data>(input: AccountsAuthConfig<Data>): {
|
|
30
|
+
config: ResolvedAccountsAuthConfig<Data>;
|
|
31
|
+
sessions: {
|
|
32
|
+
load: (cookieValue: string | null) => Promise<AccountsSession<Data> | null>;
|
|
33
|
+
start: (input: {
|
|
34
|
+
user: AccountsUser;
|
|
35
|
+
tokens: AccountsTokens;
|
|
36
|
+
data: Data;
|
|
37
|
+
}) => Promise<{
|
|
38
|
+
session: AccountsSession<Data>;
|
|
39
|
+
cookieValue: string;
|
|
40
|
+
}>;
|
|
41
|
+
ensureFreshTokens: (session: AccountsSession<Data>) => Promise<AccountsSession<Data> | null>;
|
|
42
|
+
revalidate: (session: AccountsSession<Data>) => Promise<AccountsSession<Data> | null>;
|
|
43
|
+
end: (session: AccountsSession<Data>) => Promise<void>;
|
|
44
|
+
};
|
|
45
|
+
handlers: {
|
|
46
|
+
login: (request: Request) => Promise<Response>;
|
|
47
|
+
callback: (request: Request) => Promise<Response>;
|
|
48
|
+
logout: (request: Request) => Promise<Response>;
|
|
49
|
+
};
|
|
50
|
+
guard: (request: Request, options?: GuardOptions) => Promise<GuardResult<Data>>;
|
|
51
|
+
sessionFromCookie: (cookieValue: string | null) => Promise<AccountsSession<Data> | null>;
|
|
52
|
+
sessionFromRequest: (request: Request) => Promise<AccountsSession<Data> | null>;
|
|
53
|
+
accessTokenFromCookie: (cookieValue: string | null) => Promise<string | null>;
|
|
54
|
+
accessTokenFromRequest: (request: Request) => Promise<string | null>;
|
|
55
|
+
updateSessionData: (cookieValue: string | null, update: (data: Data) => Data) => Promise<AccountsSession<Data> | null>;
|
|
56
|
+
};
|
|
57
|
+
type AccountsAuth<Data> = ReturnType<typeof createAccountsAuth<Data>>;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { requestOrigin as a, GuardResult as c, accountsEndpoint as i, createAccountsAuth as n, sanitizeRedirectPath as o, createMemorySessionStore as r, GuardOptions as s, AccountsAuth as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as AccountsSession, c as AccountsTokens, f as RevalidateInput, i as AccountsRequestError, l as AccountsUser, n as AccountsLifetimes, o as AccountsSessionPatch, p as SignInInput, r as AccountsPaths, s as AccountsSessionStore, t as AccountsAuthConfig, u as OAuthTokenError } from "./types-DdCYMKxG.js";
|
|
2
|
+
import { a as requestOrigin, c as GuardResult, i as accountsEndpoint, n as createAccountsAuth, o as sanitizeRedirectPath, r as createMemorySessionStore, s as GuardOptions, t as AccountsAuth } from "./index-BDndKDqC.js";
|
|
3
|
+
export { AccountsAuth, type AccountsAuthConfig, type AccountsLifetimes, type AccountsPaths, AccountsRequestError, type AccountsSession, type AccountsSessionPatch, type AccountsSessionStore, type AccountsTokens, type AccountsUser, type GuardOptions, type GuardResult, OAuthTokenError, type RevalidateInput, type SignInInput, accountsEndpoint, createAccountsAuth, createMemorySessionStore, requestOrigin, sanitizeRedirectPath };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,i as n,l as r,o as i,r as a,s as o,t as s,u as c}from"./cookies-D3lpM5ER.js";function l(e,n){return async function(t,a={}){let o=new URL(t.url);if((a.publicPaths??[e.paths.login,e.paths.callback,e.paths.error]).some(e=>o.pathname.startsWith(e)))return{kind:`ok`,session:null};let s=i(t,e.cookieName),c=await n.load(s);if(!c)return r(t,a,s!==null);let l=c;try{let e=await n.revalidate(c);if(!e)return r(t,a,!0);l=e}catch{return{kind:`ok`,session:l}}if(l.tokens.accessTokenExpiresAt<=e.now()){let e=await n.ensureFreshTokens(l).catch(()=>l);if(!e)return r(t,a,!0);l=e}return{kind:`ok`,session:l}};function r(n,r,i){let a=t(n),c=new URL(n.url);if((r.apiPaths??[`/api`,`/trpc`]).some(e=>c.pathname.startsWith(e))){let t=Response.json({error:`Unauthorized`},{status:401});return i&&s(t.headers,e.cookieName,a),{kind:`response`,response:t}}let l=r.redirectPath?.(n)??`${c.pathname}${c.search}`,u=new URL(e.paths.login,a);u.searchParams.set(`redirect`,l),i&&u.searchParams.set(`silent`,`1`);let d=o(u);return i&&s(d.headers,e.cookieName,a),{kind:`response`,response:d}}}const u={login:`/api/auth/login`,callback:`/api/auth/callback`,logout:`/api/auth/logout`,logoutComplete:`/api/auth/logout-complete`,error:`/auth/error`},d={absoluteMaxAgeMs:2592e6,idleMaxAgeMs:6048e5,revalidateIntervalMs:12e4,refreshBeforeExpiryMs:3e5,flowMaxAgeMs:6e5,refreshLeaseMs:2e4,leaseWaitMs:5e3};function f(e){return{...e,scope:e.scope??`openid profile email offline_access`,paths:{...u,...e.paths},lifetimes:{...d,...e.lifetimes},fetch:e.fetch??globalThis.fetch.bind(globalThis),now:e.now??Date.now}}var p=class extends Error{status;constructor(e,t){super(e),this.name=`AccountsRequestError`,this.status=t}},m=class extends p{code;constructor(e,t,n){super(`Token request failed: ${e} ${t} ${n}`,e),this.name=`OAuthTokenError`,this.code=t}};function h(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/g,``)}function g(e){return h(crypto.getRandomValues(new Uint8Array(e)))}async function _(e){let t=await crypto.subtle.digest(`SHA-256`,new TextEncoder().encode(e));return h(new Uint8Array(t))}function v(e,t){return new URL(t,e).toString()}async function y(e,t){let n=g(24),r=g(32),i=new URL(v(e.issuer,`/api/auth/oauth2/authorize`));return i.searchParams.set(`client_id`,e.clientId),i.searchParams.set(`redirect_uri`,new URL(e.paths.callback,t.origin).toString()),i.searchParams.set(`response_type`,`code`),i.searchParams.set(`scope`,e.scope),i.searchParams.set(`state`,n),i.searchParams.set(`code_challenge`,await _(r)),i.searchParams.set(`code_challenge_method`,`S256`),t.silent&&i.searchParams.set(`prompt`,`none`),{url:i.toString(),state:n,verifier:r}}function b(e,t){let n=new URL(v(e.issuer,`/api/auth/oauth2/end-session`));return n.searchParams.set(`id_token_hint`,t.idToken),n.searchParams.set(`client_id`,e.clientId),n.searchParams.set(`post_logout_redirect_uri`,new URL(e.paths.logoutComplete,t.origin).toString()),n.toString()}async function x(e,t){let n=new URLSearchParams({client_id:e.clientId,...t});e.clientSecret&&n.set(`client_secret`,e.clientSecret);let r=await e.fetch(v(e.issuer,`/api/auth/oauth2/token`),{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:n,cache:`no-store`});if(!r.ok){let e=await r.text().catch(()=>``),t=`unknown_error`,n=e;try{let r=JSON.parse(e);t=r.error??t,n=r.error_description??n}catch{}throw new m(r.status,t,n)}return await r.json()}function S(e,t){return x(e,{grant_type:`authorization_code`,code:t.code,code_verifier:t.verifier,redirect_uri:t.redirectUri})}function C(e,t){return x(e,{grant_type:`refresh_token`,refresh_token:t})}async function w(e,t){let n=await e.fetch(v(e.issuer,`/api/auth/oauth2/userinfo`),{headers:{Authorization:`Bearer ${t}`},cache:`no-store`});if(!n.ok)throw new p(`Failed to load user info: ${n.status}`,n.status);let r=await n.json();return{id:r.sub,email:r.email??``,name:r.name??r.email??r.sub,image:r.picture??null}}function T(e,t,n){return{accessToken:e.access_token,refreshToken:e.refresh_token??t?.refreshToken??null,idToken:e.id_token??t?.idToken??null,accessTokenExpiresAt:typeof e.expires_at==`number`?e.expires_at*1e3:n+(e.expires_in??3600)*1e3}}const E=new Set([`login_required`,`consent_required`,`interaction_required`,`account_selection_required`]);function D(l,u){function d(e,t){let n=new URL(l.paths.login,e);return n.searchParams.set(`redirect`,t),n}function f(e,t,n){let r=new URL(l.paths.error,e);return r.searchParams.set(`reason`,t),n&&r.searchParams.set(`description`,n),r}async function p(i){let a=t(i),s=new URL(i.url).searchParams,u=r(s.get(`redirect`),a),d=s.get(`silent`)===`1`,{url:f,state:p,verifier:m}=await y(l,{origin:a,silent:d}),h=o(f);return c(h.headers,e(l.cookieName,p),n({verifier:m,redirectPath:u,silent:d}),a,l.lifetimes.flowMaxAgeMs),h}async function m(n){let r=t(n),p=new URL(n.url).searchParams,m=p.get(`state`),h=m?e(l.cookieName,m):null,g=h?a(i(n,h)):null,_=e=>(h&&s(e.headers,h,r),e);if(!g)return _(o(f(r,`invalid_state`,null)));let v=p.get(`error`);if(v)return g.silent&&E.has(v)?_(o(d(r,g.redirectPath))):_(o(f(r,v,p.get(`error_description`))));let y=p.get(`code`);if(!y)return _(o(f(r,`invalid_request`,null)));let b=T(await S(l,{code:y,verifier:g.verifier,redirectUri:new URL(l.paths.callback,r).toString()}),null,l.now()),x=await w(l,b.accessToken),C=await l.onSignIn({user:x,accessToken:b.accessToken,redirectPath:g.redirectPath,previous:null});if(C===null)return _(o(f(r,`access_denied`,null)));let{cookieValue:D}=await u.start({user:x,tokens:b,data:C}),O=o(new URL(g.redirectPath,r));return c(O.headers,l.cookieName,D,r,l.lifetimes.absoluteMaxAgeMs),_(O)}async function h(e){let n=t(e),r=await u.load(i(e,l.cookieName));r&&await u.end(r);let a=r?.tokens.idToken?b(l,{origin:n,idToken:r.tokens.idToken}):new URL(l.paths.logoutComplete,n).toString(),c=o(a,303);return s(c.headers,l.cookieName,n),c}return{login:p,callback:m,logout:h}}const O=e=>new Promise(t=>setTimeout(t,e));function k(e){let{store:t,lifetimes:n}=e;function r(e,t){return t-e.createdAt>=n.absoluteMaxAgeMs||t-e.lastValidatedAt>=n.idleMaxAgeMs}function i(e,t){return e.accessTokenExpiresAt-t<=n.refreshBeforeExpiryMs}async function a(n){if(!n)return null;let i=await t.read(await _(n));return i?r(i,e.now())?(await t.delete(i.id),null):i:null}async function o(n){let r=g(32),i=e.now(),a={id:await _(r),user:n.user,tokens:n.tokens,data:n.data,createdAt:i,lastValidatedAt:i,refreshLeaseUntil:null};return await t.create(a),{session:a,cookieValue:r}}async function s(r){let a=e.now();if(!i(r.tokens,a))return r;if(!r.tokens.refreshToken)return await t.delete(r.id),null;let o=await t.claimRefreshLease(r.id,a+n.refreshLeaseMs,a);if(!o)return c(r.id,a);if(!i(o.tokens,a))return t.update(o.id,{refreshLeaseUntil:null});try{let n=await C(e,o.tokens.refreshToken);return await t.update(o.id,{tokens:T(n,o.tokens,e.now()),refreshLeaseUntil:null})}catch(n){if(n instanceof m&&n.code===`invalid_grant`)return await t.delete(o.id),null;let r=await t.update(o.id,{refreshLeaseUntil:null});if(o.tokens.accessTokenExpiresAt>e.now())return r??o;throw n}}async function c(r,a){for(;;){await O(100);let o=await t.read(r);if(!o)return null;let c=e.now();if(!i(o.tokens,c))return o;if(o.refreshLeaseUntil===null||o.refreshLeaseUntil<=c)return s(o);if(c-a>=n.leaseWaitMs)return o}}async function l(r){if(e.now()-r.lastValidatedAt<n.revalidateIntervalMs)return r;let i=await s(r);if(!i)return null;let a=await e.onRevalidate({session:i,accessToken:i.tokens.accessToken});return a===null?(await t.delete(i.id),null):t.update(i.id,{data:a,lastValidatedAt:e.now()})}async function u(e){await t.delete(e.id)}return{load:a,start:o,ensureFreshTokens:s,revalidate:l,end:u}}function A(){let e=new Map;return{rows:e,async create(t){e.set(t.id,structuredClone(t))},async read(t){let n=e.get(t);return n?structuredClone(n):null},async update(t,n){let r=e.get(t);if(!r)return null;let i={...r,...n};return e.set(t,i),structuredClone(i)},async claimRefreshLease(t,n,r){let i=e.get(t);if(!i||i.refreshLeaseUntil!==null&&i.refreshLeaseUntil>r)return null;let a={...i,refreshLeaseUntil:n};return e.set(t,a),structuredClone(a)},async delete(t){e.delete(t)}}}function j(e){let t=f(e),n=k(t),r=D(t,n),a=l(t,n);async function o(e){return n.load(e)}async function s(e){return o(i(e,t.cookieName))}async function c(e){let t=await n.load(e);return t?(await n.ensureFreshTokens(t))?.tokens.accessToken??null:null}async function u(e){return c(i(e,t.cookieName))}async function d(e,r){let i=await n.load(e);return i?t.store.update(i.id,{data:r(i.data)}):null}return{config:t,sessions:n,handlers:r,guard:a,sessionFromCookie:o,sessionFromRequest:s,accessTokenFromCookie:c,accessTokenFromRequest:u,updateSessionData:d}}export{p as AccountsRequestError,m as OAuthTokenError,v as accountsEndpoint,j as createAccountsAuth,A as createMemorySessionStore,t as requestOrigin,r as sanitizeRedirectPath};
|
package/dist/next.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { a as AccountsSession } from "./types-DdCYMKxG.js";
|
|
2
|
+
import { t as AccountsAuth } from "./index-BDndKDqC.js";
|
|
3
|
+
//#region src/next.d.ts
|
|
4
|
+
declare function withNextRequestScope<Data>(auth: AccountsAuth<Data>): {
|
|
5
|
+
getSession(): Promise<AccountsSession<Data> | null>;
|
|
6
|
+
getAccessToken(): Promise<string | null>;
|
|
7
|
+
updateSessionData(update: (data: Data) => Data): Promise<AccountsSession<Data> | null>;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { withNextRequestScope };
|
package/dist/next.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./cookies-D3lpM5ER.js";import{cookies as t}from"next/headers";async function n(n){let r=await t();return r.get(e(n,!0))?.value||r.get(e(n,!1))?.value||null}function r(e){let t=e.config.cookieName;return{async getSession(){return e.sessionFromCookie(await n(t))},async getAccessToken(){return e.accessTokenFromCookie(await n(t))},async updateSessionData(r){return e.updateSessionData(await n(t),r)}}}export{r as withNextRequestScope};
|
package/dist/pg.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { s as AccountsSessionStore } from "./types-DdCYMKxG.js";
|
|
2
|
+
//#region src/pg.d.ts
|
|
3
|
+
type PgQuery = (text: string, values?: unknown[]) => Promise<{
|
|
4
|
+
rows: Record<string, unknown>[];
|
|
5
|
+
}>;
|
|
6
|
+
type PgSessionStoreOptions = {
|
|
7
|
+
query: PgQuery;
|
|
8
|
+
table?: string;
|
|
9
|
+
codec?: {
|
|
10
|
+
encrypt(value: string): Promise<string>;
|
|
11
|
+
decrypt(value: string): Promise<string>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
declare const PG_SESSION_TABLE_SQL = "CREATE TABLE \"accounts_sessions\" (\n \"id\" text PRIMARY KEY,\n \"user_id\" text NOT NULL,\n \"user\" jsonb NOT NULL,\n \"access_token\" text NOT NULL,\n \"refresh_token\" text,\n \"id_token\" text,\n \"access_token_expires_at\" timestamp with time zone NOT NULL,\n \"data\" jsonb NOT NULL,\n \"created_at\" timestamp with time zone NOT NULL,\n \"last_validated_at\" timestamp with time zone NOT NULL,\n \"refresh_lease_until\" timestamp with time zone\n);\nCREATE INDEX \"accounts_sessions_user_id_idx\" ON \"accounts_sessions\" (\"user_id\");";
|
|
15
|
+
declare function createPgSessionStore<Data>(options: PgSessionStoreOptions): AccountsSessionStore<Data>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { PG_SESSION_TABLE_SQL, PgQuery, PgSessionStoreOptions, createPgSessionStore };
|
package/dist/pg.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const e=`CREATE TABLE "accounts_sessions" (
|
|
2
|
+
"id" text PRIMARY KEY,
|
|
3
|
+
"user_id" text NOT NULL,
|
|
4
|
+
"user" jsonb NOT NULL,
|
|
5
|
+
"access_token" text NOT NULL,
|
|
6
|
+
"refresh_token" text,
|
|
7
|
+
"id_token" text,
|
|
8
|
+
"access_token_expires_at" timestamp with time zone NOT NULL,
|
|
9
|
+
"data" jsonb NOT NULL,
|
|
10
|
+
"created_at" timestamp with time zone NOT NULL,
|
|
11
|
+
"last_validated_at" timestamp with time zone NOT NULL,
|
|
12
|
+
"refresh_lease_until" timestamp with time zone
|
|
13
|
+
);
|
|
14
|
+
CREATE INDEX "accounts_sessions_user_id_idx" ON "accounts_sessions" ("user_id");`,t={encrypt:async e=>e,decrypt:async e=>e};function n(e){if(e instanceof Date)return e.getTime();if(typeof e==`string`)return new Date(e).getTime();if(typeof e==`number`)return e;throw Error(`Unexpected timestamp value in accounts session row`)}function r(e){return new Date(e)}function i(e){let i=e.table??`accounts_sessions`;if(!/^[a-z_][a-z0-9_]*$/.test(i))throw Error(`Invalid accounts session table name: ${i}`);let a=`"${i}"`,o=e.codec??t;async function s(e){return{accessToken:await o.encrypt(e.accessToken),refreshToken:e.refreshToken?await o.encrypt(e.refreshToken):null,idToken:e.idToken?await o.encrypt(e.idToken):null}}async function c(e){return{id:e.id,user:e.user,tokens:{accessToken:await o.decrypt(e.access_token),refreshToken:e.refresh_token?await o.decrypt(e.refresh_token):null,idToken:e.id_token?await o.decrypt(e.id_token):null,accessTokenExpiresAt:n(e.access_token_expires_at)},data:e.data,createdAt:n(e.created_at),lastValidatedAt:n(e.last_validated_at),refreshLeaseUntil:e.refresh_lease_until===null||e.refresh_lease_until===void 0?null:n(e.refresh_lease_until)}}return{async create(t){let n=await s(t.tokens);await e.query(`INSERT INTO ${a} ("id", "user_id", "user", "access_token", "refresh_token", "id_token", "access_token_expires_at", "data", "created_at", "last_validated_at", "refresh_lease_until") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NULL)`,[t.id,t.user.id,JSON.stringify(t.user),n.accessToken,n.refreshToken,n.idToken,r(t.tokens.accessTokenExpiresAt),JSON.stringify(t.data),r(t.createdAt),r(t.lastValidatedAt)])},async read(t){let{rows:n}=await e.query(`SELECT * FROM ${a} WHERE "id" = $1`,[t]);return n[0]?c(n[0]):null},async update(t,n){let i=[],o=[t],l=(e,t)=>{o.push(t),i.push(`"${e}" = $${o.length}`)};if(n.user!==void 0&&(l(`user_id`,n.user.id),l(`user`,JSON.stringify(n.user))),n.tokens!==void 0){let e=await s(n.tokens);l(`access_token`,e.accessToken),l(`refresh_token`,e.refreshToken),l(`id_token`,e.idToken),l(`access_token_expires_at`,r(n.tokens.accessTokenExpiresAt))}if(n.data!==void 0&&l(`data`,JSON.stringify(n.data)),n.lastValidatedAt!==void 0&&l(`last_validated_at`,r(n.lastValidatedAt)),n.refreshLeaseUntil!==void 0&&l(`refresh_lease_until`,n.refreshLeaseUntil===null?null:r(n.refreshLeaseUntil)),i.length===0)return this.read(t);let{rows:u}=await e.query(`UPDATE ${a} SET ${i.join(`, `)} WHERE "id" = $1 RETURNING *`,o);return u[0]?c(u[0]):null},async claimRefreshLease(t,n,i){let{rows:o}=await e.query(`UPDATE ${a} SET "refresh_lease_until" = $2 WHERE "id" = $1 AND ("refresh_lease_until" IS NULL OR "refresh_lease_until" <= $3) RETURNING *`,[t,r(n),r(i)]);return o[0]?c(o[0]):null},async delete(t){await e.query(`DELETE FROM ${a} WHERE "id" = $1`,[t])}}}export{e as PG_SESSION_TABLE_SQL,i as createPgSessionStore};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type AccountsUser = {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
image: string | null;
|
|
7
|
+
};
|
|
8
|
+
type AccountsTokens = {
|
|
9
|
+
accessToken: string;
|
|
10
|
+
refreshToken: string | null;
|
|
11
|
+
idToken: string | null;
|
|
12
|
+
accessTokenExpiresAt: number;
|
|
13
|
+
};
|
|
14
|
+
type AccountsSession<Data> = {
|
|
15
|
+
id: string;
|
|
16
|
+
user: AccountsUser;
|
|
17
|
+
tokens: AccountsTokens;
|
|
18
|
+
data: Data;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
lastValidatedAt: number;
|
|
21
|
+
refreshLeaseUntil: number | null;
|
|
22
|
+
};
|
|
23
|
+
type AccountsSessionPatch<Data> = Partial<Pick<AccountsSession<Data>, "user" | "tokens" | "data" | "lastValidatedAt" | "refreshLeaseUntil">>;
|
|
24
|
+
interface AccountsSessionStore<Data> {
|
|
25
|
+
create(session: AccountsSession<Data>): Promise<void>;
|
|
26
|
+
read(id: string): Promise<AccountsSession<Data> | null>;
|
|
27
|
+
update(id: string, patch: AccountsSessionPatch<Data>): Promise<AccountsSession<Data> | null>;
|
|
28
|
+
claimRefreshLease(id: string, until: number, now: number): Promise<AccountsSession<Data> | null>;
|
|
29
|
+
delete(id: string): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
type AccountsPaths = {
|
|
32
|
+
login: string;
|
|
33
|
+
callback: string;
|
|
34
|
+
logout: string;
|
|
35
|
+
logoutComplete: string;
|
|
36
|
+
error: string;
|
|
37
|
+
};
|
|
38
|
+
type AccountsLifetimes = {
|
|
39
|
+
absoluteMaxAgeMs: number;
|
|
40
|
+
idleMaxAgeMs: number;
|
|
41
|
+
revalidateIntervalMs: number;
|
|
42
|
+
refreshBeforeExpiryMs: number;
|
|
43
|
+
flowMaxAgeMs: number;
|
|
44
|
+
refreshLeaseMs: number;
|
|
45
|
+
leaseWaitMs: number;
|
|
46
|
+
};
|
|
47
|
+
type SignInInput<Data> = {
|
|
48
|
+
user: AccountsUser;
|
|
49
|
+
accessToken: string;
|
|
50
|
+
redirectPath: string;
|
|
51
|
+
previous: Data | null;
|
|
52
|
+
};
|
|
53
|
+
type RevalidateInput<Data> = {
|
|
54
|
+
session: AccountsSession<Data>;
|
|
55
|
+
accessToken: string;
|
|
56
|
+
};
|
|
57
|
+
type AccountsAuthConfig<Data> = {
|
|
58
|
+
issuer: string;
|
|
59
|
+
clientId: string;
|
|
60
|
+
clientSecret?: string;
|
|
61
|
+
scope?: string;
|
|
62
|
+
cookieName: string;
|
|
63
|
+
store: AccountsSessionStore<Data>;
|
|
64
|
+
paths?: Partial<AccountsPaths>;
|
|
65
|
+
lifetimes?: Partial<AccountsLifetimes>;
|
|
66
|
+
onSignIn(input: SignInInput<Data>): Promise<Data | null>;
|
|
67
|
+
onRevalidate(input: RevalidateInput<Data>): Promise<Data | null>;
|
|
68
|
+
fetch?: typeof fetch;
|
|
69
|
+
now?: () => number;
|
|
70
|
+
};
|
|
71
|
+
type ResolvedAccountsAuthConfig<Data> = Omit<AccountsAuthConfig<Data>, "paths" | "lifetimes" | "scope" | "fetch" | "now"> & {
|
|
72
|
+
scope: string;
|
|
73
|
+
paths: AccountsPaths;
|
|
74
|
+
lifetimes: AccountsLifetimes;
|
|
75
|
+
fetch: typeof fetch;
|
|
76
|
+
now: () => number;
|
|
77
|
+
};
|
|
78
|
+
declare class AccountsRequestError extends Error {
|
|
79
|
+
readonly status: number;
|
|
80
|
+
constructor(message: string, status: number);
|
|
81
|
+
}
|
|
82
|
+
declare class OAuthTokenError extends AccountsRequestError {
|
|
83
|
+
readonly code: string;
|
|
84
|
+
constructor(status: number, code: string, description: string);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { AccountsSession as a, AccountsTokens as c, ResolvedAccountsAuthConfig as d, RevalidateInput as f, AccountsRequestError as i, AccountsUser as l, AccountsLifetimes as n, AccountsSessionPatch as o, SignInInput as p, AccountsPaths as r, AccountsSessionStore as s, AccountsAuthConfig as t, OAuthTokenError as u };
|
package/package.json
CHANGED
|
@@ -1,12 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aui-auth",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Server side sessions for web apps that sign in through assistant-ui accounts: OIDC code flow with PKCE, tokens kept in the app's database, single flight refresh",
|
|
5
|
+
"license": "MIT",
|
|
4
6
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"types": "index.d.ts",
|
|
7
|
+
"sideEffects": false,
|
|
7
8
|
"files": [
|
|
8
|
-
"
|
|
9
|
-
"index.d.ts"
|
|
9
|
+
"dist"
|
|
10
10
|
],
|
|
11
|
-
"
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./next": {
|
|
17
|
+
"types": "./dist/next.d.ts",
|
|
18
|
+
"default": "./dist/next.js"
|
|
19
|
+
},
|
|
20
|
+
"./pg": {
|
|
21
|
+
"types": "./dist/pg.d.ts",
|
|
22
|
+
"default": "./dist/pg.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"next": ">=15"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"next": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^26.1.2",
|
|
35
|
+
"next": "^16.3.0",
|
|
36
|
+
"tsdown": "^0.22.14",
|
|
37
|
+
"typescript": "^7.0.2"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsdown",
|
|
44
|
+
"test": "node --experimental-strip-types --test test/*.test.ts",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
}
|
|
12
47
|
}
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {}
|