@authdog/express 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @authdog/express
2
+
3
+ Authdog SDK for [Express](https://expressjs.com) — session middleware, an
4
+ authentication gate, and a logout handler for Node.js backends. Built on
5
+ [`@authdog/node-commons`](../node-commons), so public-key parsing, cookie
6
+ handling, and the trusted identity-host allowlist are shared with the rest of
7
+ the Authdog Web SDK.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ bun add @authdog/express express
13
+ ```
14
+
15
+ `express` is a peer dependency (Express 4 or 5).
16
+
17
+ ## Quick start
18
+
19
+ ```ts
20
+ import express from "express";
21
+ import { createAuthdog } from "@authdog/express";
22
+
23
+ const app = express();
24
+
25
+ const authdog = createAuthdog({
26
+ publicKey: process.env.PK_AUTHDOG!, // pk_… (safe to expose to the browser)
27
+ });
28
+
29
+ // Resolve the session for every request and attach `req.authdog`.
30
+ app.use(authdog.attachSession());
31
+
32
+ // Public route — `req.authdog` is always present after `attachSession`.
33
+ app.get("/", (req, res) => {
34
+ res.json({ authenticated: req.authdog?.isAuthenticated ?? false });
35
+ });
36
+
37
+ // Protected route — `requireAuth` is the real server-side enforcement point.
38
+ app.get("/me", authdog.requireAuth, (req, res) => {
39
+ res.json(req.authdog!.user);
40
+ });
41
+
42
+ // Clears the session cookie and redirects to a sanitized `redirect_uri`.
43
+ app.get("/logout", authdog.logout);
44
+
45
+ app.listen(3000);
46
+ ```
47
+
48
+ ## How it works
49
+
50
+ - **`attachSession(options?)`** — reads the session token from the
51
+ `authdog-session` cookie or an `Authorization: Bearer <token>` header. When a
52
+ token is present it calls the identity provider's `userinfo` endpoint and
53
+ attaches the result to `req.authdog`:
54
+
55
+ ```ts
56
+ interface AuthdogRequestContext {
57
+ token: string | null;
58
+ user: unknown | null;
59
+ isAuthenticated: boolean;
60
+ userInfo?: UserInfoResponse | null;
61
+ }
62
+ ```
63
+
64
+ It **never throws and never blocks** the request — a missing, invalid, or
65
+ unverifiable token simply yields `isAuthenticated: false`. Mount it once,
66
+ early, so every downstream handler can read `req.authdog`.
67
+
68
+ - **`requireAuth`** — responds `401 { "error": "Unauthorized" }` when
69
+ `req.authdog?.isAuthenticated` is falsy, otherwise calls `next()`. **This is
70
+ the security boundary.** Client-side checks are presentational only; every
71
+ protected route must sit behind `requireAuth`.
72
+
73
+ - **`logout`** — expires the `authdog-session` cookie (`HttpOnly`,
74
+ `SameSite=Lax`, `Secure` in production) and redirects to the `redirect_uri`
75
+ query parameter after running it through `sanitizeRedirectPath` to prevent
76
+ open redirects.
77
+
78
+ ## Options
79
+
80
+ ### `attachSession({ fetchUser })`
81
+
82
+ By default `attachSession` performs **one outbound HTTPS request per incoming
83
+ request that carries a token** to resolve the full user object. For
84
+ high-throughput services that only need to know whether a token is present (and
85
+ validate it elsewhere), opt out:
86
+
87
+ ```ts
88
+ app.use(authdog.attachSession({ fetchUser: false }));
89
+ ```
90
+
91
+ With `fetchUser: false`, `req.authdog.token` is populated but
92
+ `isAuthenticated` stays `false` and `user` stays `null` — you are responsible
93
+ for validating the token where it matters.
94
+
95
+ ## Security
96
+
97
+ - The public key is validated and parsed **once at startup**; a malformed or
98
+ untrusted key (one whose identity host is not on the allowlist) throws
99
+ immediately rather than per-request.
100
+ - The bearer token is only ever sent to a trusted, `https:` identity host —
101
+ enforced by `@authdog/node-commons`.
102
+ - A request is treated as authenticated **only** when the `userinfo` envelope
103
+ reports success (`meta.code === 200` with a `user`).
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,158 @@
1
+ import { UserInfoResponse, PublicKeyPayload } from '@authdog/node-commons';
2
+ export { PublicKeyPayload } from '@authdog/node-commons';
3
+ import { RequestHandler, Request, Response } from 'express';
4
+
5
+ /**
6
+ * Per-request authentication context attached to `req.authdog` by the
7
+ * `attachSession` middleware. `isAuthenticated` is the single source of truth
8
+ * the rest of the app (and `requireAuth`) should branch on.
9
+ */
10
+ interface AuthdogRequestContext {
11
+ /** The raw session token (JWT) extracted from the cookie or bearer header. */
12
+ token: string | null;
13
+ /** The userinfo `user` object, present only when `isAuthenticated` is true. */
14
+ user: unknown | null;
15
+ /** Whether the request carries a valid, authenticated Authdog session. */
16
+ isAuthenticated: boolean;
17
+ /** The full userinfo envelope, when a userinfo fetch was performed. */
18
+ userInfo?: UserInfoResponse | null;
19
+ }
20
+ /**
21
+ * Configuration for {@link createAuthdog}. The `publicKey` is validated and
22
+ * parsed once at construction time (enforcing the trusted identity-host
23
+ * allowlist), so an invalid or untrusted key fails fast rather than per-request.
24
+ */
25
+ interface AuthdogConfig {
26
+ /** The Authdog public key (`pk_…`). Safe to expose to the browser. */
27
+ publicKey: string;
28
+ /**
29
+ * The Authdog secret key. Reserved for future server-side session
30
+ * revocation; currently optional and unused by the request lifecycle.
31
+ */
32
+ secretKey?: string;
33
+ }
34
+ /** Options for the {@link AuthdogServer.attachSession} middleware factory. */
35
+ interface AttachSessionOptions {
36
+ /**
37
+ * Whether to call the identity provider's `userinfo` endpoint to resolve the
38
+ * full user object for every authenticated request. Defaults to `true`.
39
+ *
40
+ * ⚠️ Network cost: when enabled this performs one outbound HTTPS request per
41
+ * incoming request that carries a token. For high-throughput services that
42
+ * only need to know *whether* a token is present (and validate it elsewhere),
43
+ * set this to `false` and perform userinfo resolution lazily where needed.
44
+ */
45
+ fetchUser?: boolean;
46
+ }
47
+ declare global {
48
+ namespace Express {
49
+ interface Request {
50
+ /**
51
+ * Authentication context populated by Authdog's `attachSession`
52
+ * middleware. Always present once the middleware has run; check
53
+ * `req.authdog?.isAuthenticated` to gate behaviour.
54
+ */
55
+ authdog?: AuthdogRequestContext;
56
+ }
57
+ }
58
+ }
59
+
60
+ /** The instance returned by {@link createAuthdog}. */
61
+ interface AuthdogServer {
62
+ /**
63
+ * Middleware that resolves the session and attaches `req.authdog`. Mount it
64
+ * early (typically app-wide) so downstream handlers and `requireAuth` can
65
+ * read the authentication context. Never throws or blocks the request.
66
+ */
67
+ attachSession: (options?: AttachSessionOptions) => RequestHandler;
68
+ /**
69
+ * Gate middleware that returns `401` for unauthenticated requests. This is
70
+ * the real server-side enforcement point — place it before any protected
71
+ * route. Requires `attachSession` to have run first.
72
+ */
73
+ requireAuth: RequestHandler;
74
+ /** Handler that clears the session cookie and performs a safe redirect. */
75
+ logout: (req: Request, res: Response) => void;
76
+ /** The validated, parsed public-key payload (`environmentId`, `identityHost`, …). */
77
+ getPublicKeyPayload: () => PublicKeyPayload;
78
+ /** The validated public-key payload as a JSON string, e.g. to inline into HTML. */
79
+ getPublicKey: () => string;
80
+ }
81
+ /**
82
+ * Creates an Authdog server instance for Express.
83
+ *
84
+ * The public key is validated and parsed once here — enforcing the trusted
85
+ * identity-host allowlist (SSRF / token-exfiltration protection) — so a
86
+ * malformed or untrusted key fails fast at startup rather than on the first
87
+ * request.
88
+ *
89
+ * ```ts
90
+ * const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });
91
+ *
92
+ * app.use(authdog.attachSession());
93
+ * app.get("/me", authdog.requireAuth, (req, res) => res.json(req.authdog!.user));
94
+ * app.get("/logout", authdog.logout);
95
+ * ```
96
+ */
97
+ declare const createAuthdog: (config: AuthdogConfig) => AuthdogServer;
98
+
99
+ /**
100
+ * Builds the `attachSession` middleware. It never throws and never short-
101
+ * circuits the request: it resolves the session (if any) and attaches the
102
+ * result to `req.authdog`, leaving the decision of what to do with an
103
+ * unauthenticated request to `requireAuth` or your route handlers.
104
+ *
105
+ * When `fetchUser` is enabled (the default) it calls the identity provider's
106
+ * `userinfo` endpoint and only marks the request authenticated when the
107
+ * envelope reports success (`isAuthenticatedUserInfo`). Any network or parse
108
+ * failure degrades to an unauthenticated context rather than a 500.
109
+ */
110
+ declare const createAttachSession: (payload: PublicKeyPayload, options?: AttachSessionOptions) => RequestHandler;
111
+ /**
112
+ * Gate middleware that enforces authentication. Responds with `401` JSON when
113
+ * the request has no valid Authdog session and calls `next()` otherwise.
114
+ *
115
+ * ⚠️ This is the real server-side enforcement point. Client-side checks are
116
+ * presentational only; every protected route MUST sit behind `requireAuth`
117
+ * (after `attachSession` has run) for the protection to be real.
118
+ */
119
+ declare const requireAuth: RequestHandler;
120
+
121
+ /**
122
+ * Express handler that clears the Authdog session cookie and redirects to a
123
+ * safe, same-origin location.
124
+ *
125
+ * The cookie is expired in place with the same security attributes it was set
126
+ * with (`HttpOnly`, `SameSite=Lax`, and `Secure` in production) so browsers
127
+ * actually drop it. `Secure` is gated on `NODE_ENV === "production"` so local
128
+ * HTTP development still clears the cookie.
129
+ *
130
+ * The redirect target is taken from the `redirect_uri` query parameter and run
131
+ * through `sanitizeRedirectPath` to prevent an open redirect via an
132
+ * attacker-controlled value (falls back to `/`).
133
+ */
134
+ declare const logoutHandler: (req: Request, res: Response) => void;
135
+
136
+ /** Name of the cookie that carries the Authdog session token. */
137
+ declare const SESSION_COOKIE_NAME = "authdog-session";
138
+ /**
139
+ * Extracts the session token from an incoming request. The token may arrive
140
+ * either as the `authdog-session` cookie (set server-side, HttpOnly) or as an
141
+ * `Authorization: Bearer <token>` header — the latter covers API clients and
142
+ * mobile callers that do not use cookies.
143
+ *
144
+ * Cookie parsing goes through `parseCookies`, which splits on the first `=` and
145
+ * URL-decodes values, correctly handling tokens that themselves contain `=`
146
+ * (e.g. base64 / JWT padding).
147
+ */
148
+ declare const getSessionToken: (req: Request) => string | null;
149
+
150
+ /**
151
+ * Decodes and validates an Authdog public key. Delegates to the hardened
152
+ * shared parser in @authdog/node-commons, which validates the payload and
153
+ * enforces a trusted identity-host allowlist (SSRF / token-exfiltration
154
+ * protection) rather than blindly decoding base64/JSON.
155
+ */
156
+ declare const getPublicKeyPayload: (publicKey: string) => PublicKeyPayload;
157
+
158
+ export { type AttachSessionOptions, type AuthdogConfig, type AuthdogRequestContext, type AuthdogServer, SESSION_COOKIE_NAME, createAttachSession, createAuthdog, getPublicKeyPayload, getSessionToken, logoutHandler, requireAuth };
@@ -0,0 +1,158 @@
1
+ import { UserInfoResponse, PublicKeyPayload } from '@authdog/node-commons';
2
+ export { PublicKeyPayload } from '@authdog/node-commons';
3
+ import { RequestHandler, Request, Response } from 'express';
4
+
5
+ /**
6
+ * Per-request authentication context attached to `req.authdog` by the
7
+ * `attachSession` middleware. `isAuthenticated` is the single source of truth
8
+ * the rest of the app (and `requireAuth`) should branch on.
9
+ */
10
+ interface AuthdogRequestContext {
11
+ /** The raw session token (JWT) extracted from the cookie or bearer header. */
12
+ token: string | null;
13
+ /** The userinfo `user` object, present only when `isAuthenticated` is true. */
14
+ user: unknown | null;
15
+ /** Whether the request carries a valid, authenticated Authdog session. */
16
+ isAuthenticated: boolean;
17
+ /** The full userinfo envelope, when a userinfo fetch was performed. */
18
+ userInfo?: UserInfoResponse | null;
19
+ }
20
+ /**
21
+ * Configuration for {@link createAuthdog}. The `publicKey` is validated and
22
+ * parsed once at construction time (enforcing the trusted identity-host
23
+ * allowlist), so an invalid or untrusted key fails fast rather than per-request.
24
+ */
25
+ interface AuthdogConfig {
26
+ /** The Authdog public key (`pk_…`). Safe to expose to the browser. */
27
+ publicKey: string;
28
+ /**
29
+ * The Authdog secret key. Reserved for future server-side session
30
+ * revocation; currently optional and unused by the request lifecycle.
31
+ */
32
+ secretKey?: string;
33
+ }
34
+ /** Options for the {@link AuthdogServer.attachSession} middleware factory. */
35
+ interface AttachSessionOptions {
36
+ /**
37
+ * Whether to call the identity provider's `userinfo` endpoint to resolve the
38
+ * full user object for every authenticated request. Defaults to `true`.
39
+ *
40
+ * ⚠️ Network cost: when enabled this performs one outbound HTTPS request per
41
+ * incoming request that carries a token. For high-throughput services that
42
+ * only need to know *whether* a token is present (and validate it elsewhere),
43
+ * set this to `false` and perform userinfo resolution lazily where needed.
44
+ */
45
+ fetchUser?: boolean;
46
+ }
47
+ declare global {
48
+ namespace Express {
49
+ interface Request {
50
+ /**
51
+ * Authentication context populated by Authdog's `attachSession`
52
+ * middleware. Always present once the middleware has run; check
53
+ * `req.authdog?.isAuthenticated` to gate behaviour.
54
+ */
55
+ authdog?: AuthdogRequestContext;
56
+ }
57
+ }
58
+ }
59
+
60
+ /** The instance returned by {@link createAuthdog}. */
61
+ interface AuthdogServer {
62
+ /**
63
+ * Middleware that resolves the session and attaches `req.authdog`. Mount it
64
+ * early (typically app-wide) so downstream handlers and `requireAuth` can
65
+ * read the authentication context. Never throws or blocks the request.
66
+ */
67
+ attachSession: (options?: AttachSessionOptions) => RequestHandler;
68
+ /**
69
+ * Gate middleware that returns `401` for unauthenticated requests. This is
70
+ * the real server-side enforcement point — place it before any protected
71
+ * route. Requires `attachSession` to have run first.
72
+ */
73
+ requireAuth: RequestHandler;
74
+ /** Handler that clears the session cookie and performs a safe redirect. */
75
+ logout: (req: Request, res: Response) => void;
76
+ /** The validated, parsed public-key payload (`environmentId`, `identityHost`, …). */
77
+ getPublicKeyPayload: () => PublicKeyPayload;
78
+ /** The validated public-key payload as a JSON string, e.g. to inline into HTML. */
79
+ getPublicKey: () => string;
80
+ }
81
+ /**
82
+ * Creates an Authdog server instance for Express.
83
+ *
84
+ * The public key is validated and parsed once here — enforcing the trusted
85
+ * identity-host allowlist (SSRF / token-exfiltration protection) — so a
86
+ * malformed or untrusted key fails fast at startup rather than on the first
87
+ * request.
88
+ *
89
+ * ```ts
90
+ * const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });
91
+ *
92
+ * app.use(authdog.attachSession());
93
+ * app.get("/me", authdog.requireAuth, (req, res) => res.json(req.authdog!.user));
94
+ * app.get("/logout", authdog.logout);
95
+ * ```
96
+ */
97
+ declare const createAuthdog: (config: AuthdogConfig) => AuthdogServer;
98
+
99
+ /**
100
+ * Builds the `attachSession` middleware. It never throws and never short-
101
+ * circuits the request: it resolves the session (if any) and attaches the
102
+ * result to `req.authdog`, leaving the decision of what to do with an
103
+ * unauthenticated request to `requireAuth` or your route handlers.
104
+ *
105
+ * When `fetchUser` is enabled (the default) it calls the identity provider's
106
+ * `userinfo` endpoint and only marks the request authenticated when the
107
+ * envelope reports success (`isAuthenticatedUserInfo`). Any network or parse
108
+ * failure degrades to an unauthenticated context rather than a 500.
109
+ */
110
+ declare const createAttachSession: (payload: PublicKeyPayload, options?: AttachSessionOptions) => RequestHandler;
111
+ /**
112
+ * Gate middleware that enforces authentication. Responds with `401` JSON when
113
+ * the request has no valid Authdog session and calls `next()` otherwise.
114
+ *
115
+ * ⚠️ This is the real server-side enforcement point. Client-side checks are
116
+ * presentational only; every protected route MUST sit behind `requireAuth`
117
+ * (after `attachSession` has run) for the protection to be real.
118
+ */
119
+ declare const requireAuth: RequestHandler;
120
+
121
+ /**
122
+ * Express handler that clears the Authdog session cookie and redirects to a
123
+ * safe, same-origin location.
124
+ *
125
+ * The cookie is expired in place with the same security attributes it was set
126
+ * with (`HttpOnly`, `SameSite=Lax`, and `Secure` in production) so browsers
127
+ * actually drop it. `Secure` is gated on `NODE_ENV === "production"` so local
128
+ * HTTP development still clears the cookie.
129
+ *
130
+ * The redirect target is taken from the `redirect_uri` query parameter and run
131
+ * through `sanitizeRedirectPath` to prevent an open redirect via an
132
+ * attacker-controlled value (falls back to `/`).
133
+ */
134
+ declare const logoutHandler: (req: Request, res: Response) => void;
135
+
136
+ /** Name of the cookie that carries the Authdog session token. */
137
+ declare const SESSION_COOKIE_NAME = "authdog-session";
138
+ /**
139
+ * Extracts the session token from an incoming request. The token may arrive
140
+ * either as the `authdog-session` cookie (set server-side, HttpOnly) or as an
141
+ * `Authorization: Bearer <token>` header — the latter covers API clients and
142
+ * mobile callers that do not use cookies.
143
+ *
144
+ * Cookie parsing goes through `parseCookies`, which splits on the first `=` and
145
+ * URL-decodes values, correctly handling tokens that themselves contain `=`
146
+ * (e.g. base64 / JWT padding).
147
+ */
148
+ declare const getSessionToken: (req: Request) => string | null;
149
+
150
+ /**
151
+ * Decodes and validates an Authdog public key. Delegates to the hardened
152
+ * shared parser in @authdog/node-commons, which validates the payload and
153
+ * enforces a trusted identity-host allowlist (SSRF / token-exfiltration
154
+ * protection) rather than blindly decoding base64/JSON.
155
+ */
156
+ declare const getPublicKeyPayload: (publicKey: string) => PublicKeyPayload;
157
+
158
+ export { type AttachSessionOptions, type AuthdogConfig, type AuthdogRequestContext, type AuthdogServer, SESSION_COOKIE_NAME, createAttachSession, createAuthdog, getPublicKeyPayload, getSessionToken, logoutHandler, requireAuth };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var f=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var O=(t,e)=>{for(var o in e)f(t,o,{get:e[o],enumerable:!0})},b=(t,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of R(e))!K.call(t,s)&&s!==o&&f(t,s,{get:()=>e[s],enumerable:!(r=x(e,s))||r.enumerable});return t};var q=t=>b(f({},"__esModule",{value:!0}),t);var N={};O(N,{SESSION_COOKIE_NAME:()=>i,createAttachSession:()=>p,createAuthdog:()=>S,getPublicKeyPayload:()=>u,getSessionToken:()=>a,logoutHandler:()=>c,requireAuth:()=>d});module.exports=q(N);var g=require("@authdog/node-commons"),u=t=>(0,g.validateAndParsePublicKey)(t);var P=require("@authdog/node-commons");var A=require("@authdog/node-commons"),i="authdog-session",a=t=>{let e=t.headers.authorization;if(e&&/^Bearer\s+/i.test(e)){let s=e.replace(/^Bearer\s+/i,"").trim();if(s)return s}let o=t.headers.cookie??null;return o?(0,A.parseCookies)(o).find(s=>s.name===i)?.value??null:null};var c=(t,e)=>{let o=process.env.NODE_ENV==="production"?" Secure;":"";e.setHeader("Set-Cookie",`${i}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly;${o} SameSite=Lax`);let r=(0,P.sanitizeRedirectPath)(t.query.redirect_uri,"/");e.redirect(302,r)};var l=require("@authdog/node-commons");var H={token:null,user:null,isAuthenticated:!1,userInfo:null},p=(t,e={})=>{let o=e.fetchUser??!0;return async(r,s,h)=>{let n=a(r);if(!n)return r.authdog={...H},h();if(!o)return r.authdog={token:n,user:null,isAuthenticated:!1,userInfo:null},h();try{let y=await(0,l.fetchUserData)(t.identityHost,t.environmentId,n),m=(0,l.isAuthenticatedUserInfo)(y);r.authdog={token:n,user:m?y.user??null:null,isAuthenticated:m,userInfo:y}}catch{r.authdog={token:n,user:null,isAuthenticated:!1,userInfo:null}}return h()}},d=(t,e,o)=>{if(!t.authdog?.isAuthenticated){e.status(401).json({error:"Unauthorized"});return}o()};var S=t=>{if(!t.publicKey)throw new Error("Public key is not defined");let e=u(t.publicKey);return{attachSession:o=>p(e,o),requireAuth:d,logout:c,getPublicKeyPayload:()=>e,getPublicKey:()=>JSON.stringify(e)}};0&&(module.exports={SESSION_COOKIE_NAME,createAttachSession,createAuthdog,getPublicKeyPayload,getSessionToken,logoutHandler,requireAuth});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commons.ts","../src/logout.ts","../src/cookies.ts","../src/middleware.ts","../src/authdog.ts"],"sourcesContent":["export { createAuthdog } from \"./authdog\";\nexport type { AuthdogServer } from \"./authdog\";\nexport { createAttachSession, requireAuth } from \"./middleware\";\nexport { logoutHandler } from \"./logout\";\nexport { getSessionToken, SESSION_COOKIE_NAME } from \"./cookies\";\nexport { getPublicKeyPayload } from \"./commons\";\nexport type { PublicKeyPayload } from \"./commons\";\nexport type {\n AuthdogConfig,\n AuthdogRequestContext,\n AttachSessionOptions,\n} from \"./types\";\n","import {\n validateAndParsePublicKey,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\n\nexport type { PublicKeyPayload };\n\n/**\n * Decodes and validates an Authdog public key. Delegates to the hardened\n * shared parser in @authdog/node-commons, which validates the payload and\n * enforces a trusted identity-host allowlist (SSRF / token-exfiltration\n * protection) rather than blindly decoding base64/JSON.\n */\nexport const getPublicKeyPayload = (publicKey: string): PublicKeyPayload => {\n return validateAndParsePublicKey(publicKey);\n};\n","import { sanitizeRedirectPath } from \"@authdog/node-commons\";\nimport type { Request, Response } from \"express\";\nimport { SESSION_COOKIE_NAME } from \"./cookies\";\n\n/**\n * Express handler that clears the Authdog session cookie and redirects to a\n * safe, same-origin location.\n *\n * The cookie is expired in place with the same security attributes it was set\n * with (`HttpOnly`, `SameSite=Lax`, and `Secure` in production) so browsers\n * actually drop it. `Secure` is gated on `NODE_ENV === \"production\"` so local\n * HTTP development still clears the cookie.\n *\n * The redirect target is taken from the `redirect_uri` query parameter and run\n * through `sanitizeRedirectPath` to prevent an open redirect via an\n * attacker-controlled value (falls back to `/`).\n */\nexport const logoutHandler = (req: Request, res: Response): void => {\n const secure = process.env.NODE_ENV === \"production\" ? \" Secure;\" : \"\";\n\n res.setHeader(\n \"Set-Cookie\",\n `${SESSION_COOKIE_NAME}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly;${secure} SameSite=Lax`,\n );\n\n const redirectUrl = sanitizeRedirectPath(req.query.redirect_uri, \"/\");\n\n res.redirect(302, redirectUrl);\n};\n","import { parseCookies } from \"@authdog/node-commons\";\nimport type { Request } from \"express\";\n\n/** Name of the cookie that carries the Authdog session token. */\nexport const SESSION_COOKIE_NAME = \"authdog-session\";\n\n/**\n * Extracts the session token from an incoming request. The token may arrive\n * either as the `authdog-session` cookie (set server-side, HttpOnly) or as an\n * `Authorization: Bearer <token>` header — the latter covers API clients and\n * mobile callers that do not use cookies.\n *\n * Cookie parsing goes through `parseCookies`, which splits on the first `=` and\n * URL-decodes values, correctly handling tokens that themselves contain `=`\n * (e.g. base64 / JWT padding).\n */\nexport const getSessionToken = (req: Request): string | null => {\n // Prefer an explicit bearer token when present.\n const authHeader = req.headers.authorization;\n if (authHeader && /^Bearer\\s+/i.test(authHeader)) {\n const token = authHeader.replace(/^Bearer\\s+/i, \"\").trim();\n if (token) {\n return token;\n }\n }\n\n const cookieHeader = req.headers.cookie ?? null;\n if (!cookieHeader) {\n return null;\n }\n\n const cookies = parseCookies(cookieHeader);\n return cookies.find((c) => c.name === SESSION_COOKIE_NAME)?.value ?? null;\n};\n","import {\n fetchUserData,\n isAuthenticatedUserInfo,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\nimport type { NextFunction, Request, RequestHandler, Response } from \"express\";\nimport { getSessionToken } from \"./cookies\";\nimport type { AttachSessionOptions, AuthdogRequestContext } from \"./types\";\n\n/** The context attached when no usable token is present on the request. */\nconst ANONYMOUS: AuthdogRequestContext = {\n token: null,\n user: null,\n isAuthenticated: false,\n userInfo: null,\n};\n\n/**\n * Builds the `attachSession` middleware. It never throws and never short-\n * circuits the request: it resolves the session (if any) and attaches the\n * result to `req.authdog`, leaving the decision of what to do with an\n * unauthenticated request to `requireAuth` or your route handlers.\n *\n * When `fetchUser` is enabled (the default) it calls the identity provider's\n * `userinfo` endpoint and only marks the request authenticated when the\n * envelope reports success (`isAuthenticatedUserInfo`). Any network or parse\n * failure degrades to an unauthenticated context rather than a 500.\n */\nexport const createAttachSession = (\n payload: PublicKeyPayload,\n options: AttachSessionOptions = {},\n): RequestHandler => {\n const fetchUser = options.fetchUser ?? true;\n\n return async (req: Request, _res: Response, next: NextFunction) => {\n const token = getSessionToken(req);\n\n if (!token) {\n req.authdog = { ...ANONYMOUS };\n return next();\n }\n\n // Without a userinfo round-trip we cannot vouch for the token's validity,\n // so we surface the token but leave `isAuthenticated` false. Callers that\n // opt out of fetching are expected to validate the token themselves.\n if (!fetchUser) {\n req.authdog = {\n token,\n user: null,\n isAuthenticated: false,\n userInfo: null,\n };\n return next();\n }\n\n try {\n const userInfo = await fetchUserData(\n payload.identityHost,\n payload.environmentId,\n token,\n );\n\n const authenticated = isAuthenticatedUserInfo(userInfo);\n\n req.authdog = {\n token,\n user: authenticated ? (userInfo.user ?? null) : null,\n isAuthenticated: authenticated,\n userInfo,\n };\n } catch {\n // A failed or untrusted userinfo lookup is treated as \"not authenticated\"\n // — never as a server error and never as an authenticated session.\n req.authdog = { token, user: null, isAuthenticated: false, userInfo: null };\n }\n\n return next();\n };\n};\n\n/**\n * Gate middleware that enforces authentication. Responds with `401` JSON when\n * the request has no valid Authdog session and calls `next()` otherwise.\n *\n * ⚠️ This is the real server-side enforcement point. Client-side checks are\n * presentational only; every protected route MUST sit behind `requireAuth`\n * (after `attachSession` has run) for the protection to be real.\n */\nexport const requireAuth: RequestHandler = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n if (!req.authdog?.isAuthenticated) {\n res.status(401).json({ error: \"Unauthorized\" });\n return;\n }\n next();\n};\n","import type { PublicKeyPayload } from \"@authdog/node-commons\";\nimport type { Request, RequestHandler, Response } from \"express\";\nimport { getPublicKeyPayload } from \"./commons\";\nimport { logoutHandler } from \"./logout\";\nimport { createAttachSession, requireAuth } from \"./middleware\";\nimport type { AttachSessionOptions, AuthdogConfig } from \"./types\";\n\n/** The instance returned by {@link createAuthdog}. */\nexport interface AuthdogServer {\n /**\n * Middleware that resolves the session and attaches `req.authdog`. Mount it\n * early (typically app-wide) so downstream handlers and `requireAuth` can\n * read the authentication context. Never throws or blocks the request.\n */\n attachSession: (options?: AttachSessionOptions) => RequestHandler;\n /**\n * Gate middleware that returns `401` for unauthenticated requests. This is\n * the real server-side enforcement point — place it before any protected\n * route. Requires `attachSession` to have run first.\n */\n requireAuth: RequestHandler;\n /** Handler that clears the session cookie and performs a safe redirect. */\n logout: (req: Request, res: Response) => void;\n /** The validated, parsed public-key payload (`environmentId`, `identityHost`, …). */\n getPublicKeyPayload: () => PublicKeyPayload;\n /** The validated public-key payload as a JSON string, e.g. to inline into HTML. */\n getPublicKey: () => string;\n}\n\n/**\n * Creates an Authdog server instance for Express.\n *\n * The public key is validated and parsed once here — enforcing the trusted\n * identity-host allowlist (SSRF / token-exfiltration protection) — so a\n * malformed or untrusted key fails fast at startup rather than on the first\n * request.\n *\n * ```ts\n * const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });\n *\n * app.use(authdog.attachSession());\n * app.get(\"/me\", authdog.requireAuth, (req, res) => res.json(req.authdog!.user));\n * app.get(\"/logout\", authdog.logout);\n * ```\n */\nexport const createAuthdog = (config: AuthdogConfig): AuthdogServer => {\n if (!config.publicKey) {\n throw new Error(\"Public key is not defined\");\n }\n\n // Validate + parse eagerly so an invalid/untrusted key throws at startup.\n const payload = getPublicKeyPayload(config.publicKey);\n\n return {\n attachSession: (options?: AttachSessionOptions) =>\n createAttachSession(payload, options),\n requireAuth,\n logout: logoutHandler,\n getPublicKeyPayload: () => payload,\n getPublicKey: () => JSON.stringify(payload),\n };\n};\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,yBAAAE,EAAA,wBAAAC,EAAA,kBAAAC,EAAA,wBAAAC,EAAA,oBAAAC,EAAA,kBAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAAT,GCAA,IAAAU,EAGO,iCAUMC,EAAuBC,MAC3B,6BAA0BA,CAAS,ECd5C,IAAAC,EAAqC,iCCArC,IAAAC,EAA6B,iCAIhBC,EAAsB,kBAYtBC,EAAmBC,GAAgC,CAE9D,IAAMC,EAAaD,EAAI,QAAQ,cAC/B,GAAIC,GAAc,cAAc,KAAKA,CAAU,EAAG,CAChD,IAAMC,EAAQD,EAAW,QAAQ,cAAe,EAAE,EAAE,KAAK,EACzD,GAAIC,EACF,OAAOA,CAEX,CAEA,IAAMC,EAAeH,EAAI,QAAQ,QAAU,KAC3C,OAAKG,KAIW,gBAAaA,CAAY,EAC1B,KAAMC,GAAMA,EAAE,OAASN,CAAmB,GAAG,OAAS,KAJ5D,IAKX,EDhBO,IAAMO,EAAgB,CAACC,EAAcC,IAAwB,CAClE,IAAMC,EAAS,QAAQ,IAAI,WAAa,aAAe,WAAa,GAEpED,EAAI,UACF,aACA,GAAGE,CAAmB,8DAA8DD,CAAM,eAC5F,EAEA,IAAME,KAAc,wBAAqBJ,EAAI,MAAM,aAAc,GAAG,EAEpEC,EAAI,SAAS,IAAKG,CAAW,CAC/B,EE5BA,IAAAC,EAIO,iCAMP,IAAMC,EAAmC,CACvC,MAAO,KACP,KAAM,KACN,gBAAiB,GACjB,SAAU,IACZ,EAaaC,EAAsB,CACjCC,EACAC,EAAgC,CAAC,IACd,CACnB,IAAMC,EAAYD,EAAQ,WAAa,GAEvC,MAAO,OAAOE,EAAcC,EAAgBC,IAAuB,CACjE,IAAMC,EAAQC,EAAgBJ,CAAG,EAEjC,GAAI,CAACG,EACH,OAAAH,EAAI,QAAU,CAAE,GAAGL,CAAU,EACtBO,EAAK,EAMd,GAAI,CAACH,EACH,OAAAC,EAAI,QAAU,CACZ,MAAAG,EACA,KAAM,KACN,gBAAiB,GACjB,SAAU,IACZ,EACOD,EAAK,EAGd,GAAI,CACF,IAAMG,EAAW,QAAM,iBACrBR,EAAQ,aACRA,EAAQ,cACRM,CACF,EAEMG,KAAgB,2BAAwBD,CAAQ,EAEtDL,EAAI,QAAU,CACZ,MAAAG,EACA,KAAMG,EAAiBD,EAAS,MAAQ,KAAQ,KAChD,gBAAiBC,EACjB,SAAAD,CACF,CACF,MAAQ,CAGNL,EAAI,QAAU,CAAE,MAAAG,EAAO,KAAM,KAAM,gBAAiB,GAAO,SAAU,IAAK,CAC5E,CAEA,OAAOD,EAAK,CACd,CACF,EAUaK,EAA8B,CACzCP,EACAQ,EACAN,IACG,CACH,GAAI,CAACF,EAAI,SAAS,gBAAiB,CACjCQ,EAAI,OAAO,GAAG,EAAE,KAAK,CAAE,MAAO,cAAe,CAAC,EAC9C,MACF,CACAN,EAAK,CACP,ECrDO,IAAMO,EAAiBC,GAAyC,CACrE,GAAI,CAACA,EAAO,UACV,MAAM,IAAI,MAAM,2BAA2B,EAI7C,IAAMC,EAAUC,EAAoBF,EAAO,SAAS,EAEpD,MAAO,CACL,cAAgBG,GACdC,EAAoBH,EAASE,CAAO,EACtC,YAAAE,EACA,OAAQC,EACR,oBAAqB,IAAML,EAC3B,aAAc,IAAM,KAAK,UAAUA,CAAO,CAC5C,CACF","names":["index_exports","__export","SESSION_COOKIE_NAME","createAttachSession","createAuthdog","getPublicKeyPayload","getSessionToken","logoutHandler","requireAuth","__toCommonJS","import_node_commons","getPublicKeyPayload","publicKey","import_node_commons","import_node_commons","SESSION_COOKIE_NAME","getSessionToken","req","authHeader","token","cookieHeader","c","logoutHandler","req","res","secure","SESSION_COOKIE_NAME","redirectUrl","import_node_commons","ANONYMOUS","createAttachSession","payload","options","fetchUser","req","_res","next","token","getSessionToken","userInfo","authenticated","requireAuth","res","createAuthdog","config","payload","getPublicKeyPayload","options","createAttachSession","requireAuth","logoutHandler"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{validateAndParsePublicKey as f}from"@authdog/node-commons";var c=e=>f(e);import{sanitizeRedirectPath as g}from"@authdog/node-commons";import{parseCookies as m}from"@authdog/node-commons";var i="authdog-session",l=e=>{let t=e.headers.authorization;if(t&&/^Bearer\s+/i.test(t)){let s=t.replace(/^Bearer\s+/i,"").trim();if(s)return s}let o=e.headers.cookie??null;return o?m(o).find(s=>s.name===i)?.value??null:null};var p=(e,t)=>{let o=process.env.NODE_ENV==="production"?" Secure;":"";t.setHeader("Set-Cookie",`${i}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly;${o} SameSite=Lax`);let r=g(e.query.redirect_uri,"/");t.redirect(302,r)};import{fetchUserData as A,isAuthenticatedUserInfo as P}from"@authdog/node-commons";var S={token:null,user:null,isAuthenticated:!1,userInfo:null},d=(e,t={})=>{let o=t.fetchUser??!0;return async(r,s,u)=>{let n=l(r);if(!n)return r.authdog={...S},u();if(!o)return r.authdog={token:n,user:null,isAuthenticated:!1,userInfo:null},u();try{let a=await A(e.identityHost,e.environmentId,n),y=P(a);r.authdog={token:n,user:y?a.user??null:null,isAuthenticated:y,userInfo:a}}catch{r.authdog={token:n,user:null,isAuthenticated:!1,userInfo:null}}return u()}},h=(e,t,o)=>{if(!e.authdog?.isAuthenticated){t.status(401).json({error:"Unauthorized"});return}o()};var x=e=>{if(!e.publicKey)throw new Error("Public key is not defined");let t=c(e.publicKey);return{attachSession:o=>d(t,o),requireAuth:h,logout:p,getPublicKeyPayload:()=>t,getPublicKey:()=>JSON.stringify(t)}};export{i as SESSION_COOKIE_NAME,d as createAttachSession,x as createAuthdog,c as getPublicKeyPayload,l as getSessionToken,p as logoutHandler,h as requireAuth};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commons.ts","../src/logout.ts","../src/cookies.ts","../src/middleware.ts","../src/authdog.ts"],"sourcesContent":["import {\n validateAndParsePublicKey,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\n\nexport type { PublicKeyPayload };\n\n/**\n * Decodes and validates an Authdog public key. Delegates to the hardened\n * shared parser in @authdog/node-commons, which validates the payload and\n * enforces a trusted identity-host allowlist (SSRF / token-exfiltration\n * protection) rather than blindly decoding base64/JSON.\n */\nexport const getPublicKeyPayload = (publicKey: string): PublicKeyPayload => {\n return validateAndParsePublicKey(publicKey);\n};\n","import { sanitizeRedirectPath } from \"@authdog/node-commons\";\nimport type { Request, Response } from \"express\";\nimport { SESSION_COOKIE_NAME } from \"./cookies\";\n\n/**\n * Express handler that clears the Authdog session cookie and redirects to a\n * safe, same-origin location.\n *\n * The cookie is expired in place with the same security attributes it was set\n * with (`HttpOnly`, `SameSite=Lax`, and `Secure` in production) so browsers\n * actually drop it. `Secure` is gated on `NODE_ENV === \"production\"` so local\n * HTTP development still clears the cookie.\n *\n * The redirect target is taken from the `redirect_uri` query parameter and run\n * through `sanitizeRedirectPath` to prevent an open redirect via an\n * attacker-controlled value (falls back to `/`).\n */\nexport const logoutHandler = (req: Request, res: Response): void => {\n const secure = process.env.NODE_ENV === \"production\" ? \" Secure;\" : \"\";\n\n res.setHeader(\n \"Set-Cookie\",\n `${SESSION_COOKIE_NAME}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly;${secure} SameSite=Lax`,\n );\n\n const redirectUrl = sanitizeRedirectPath(req.query.redirect_uri, \"/\");\n\n res.redirect(302, redirectUrl);\n};\n","import { parseCookies } from \"@authdog/node-commons\";\nimport type { Request } from \"express\";\n\n/** Name of the cookie that carries the Authdog session token. */\nexport const SESSION_COOKIE_NAME = \"authdog-session\";\n\n/**\n * Extracts the session token from an incoming request. The token may arrive\n * either as the `authdog-session` cookie (set server-side, HttpOnly) or as an\n * `Authorization: Bearer <token>` header — the latter covers API clients and\n * mobile callers that do not use cookies.\n *\n * Cookie parsing goes through `parseCookies`, which splits on the first `=` and\n * URL-decodes values, correctly handling tokens that themselves contain `=`\n * (e.g. base64 / JWT padding).\n */\nexport const getSessionToken = (req: Request): string | null => {\n // Prefer an explicit bearer token when present.\n const authHeader = req.headers.authorization;\n if (authHeader && /^Bearer\\s+/i.test(authHeader)) {\n const token = authHeader.replace(/^Bearer\\s+/i, \"\").trim();\n if (token) {\n return token;\n }\n }\n\n const cookieHeader = req.headers.cookie ?? null;\n if (!cookieHeader) {\n return null;\n }\n\n const cookies = parseCookies(cookieHeader);\n return cookies.find((c) => c.name === SESSION_COOKIE_NAME)?.value ?? null;\n};\n","import {\n fetchUserData,\n isAuthenticatedUserInfo,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\nimport type { NextFunction, Request, RequestHandler, Response } from \"express\";\nimport { getSessionToken } from \"./cookies\";\nimport type { AttachSessionOptions, AuthdogRequestContext } from \"./types\";\n\n/** The context attached when no usable token is present on the request. */\nconst ANONYMOUS: AuthdogRequestContext = {\n token: null,\n user: null,\n isAuthenticated: false,\n userInfo: null,\n};\n\n/**\n * Builds the `attachSession` middleware. It never throws and never short-\n * circuits the request: it resolves the session (if any) and attaches the\n * result to `req.authdog`, leaving the decision of what to do with an\n * unauthenticated request to `requireAuth` or your route handlers.\n *\n * When `fetchUser` is enabled (the default) it calls the identity provider's\n * `userinfo` endpoint and only marks the request authenticated when the\n * envelope reports success (`isAuthenticatedUserInfo`). Any network or parse\n * failure degrades to an unauthenticated context rather than a 500.\n */\nexport const createAttachSession = (\n payload: PublicKeyPayload,\n options: AttachSessionOptions = {},\n): RequestHandler => {\n const fetchUser = options.fetchUser ?? true;\n\n return async (req: Request, _res: Response, next: NextFunction) => {\n const token = getSessionToken(req);\n\n if (!token) {\n req.authdog = { ...ANONYMOUS };\n return next();\n }\n\n // Without a userinfo round-trip we cannot vouch for the token's validity,\n // so we surface the token but leave `isAuthenticated` false. Callers that\n // opt out of fetching are expected to validate the token themselves.\n if (!fetchUser) {\n req.authdog = {\n token,\n user: null,\n isAuthenticated: false,\n userInfo: null,\n };\n return next();\n }\n\n try {\n const userInfo = await fetchUserData(\n payload.identityHost,\n payload.environmentId,\n token,\n );\n\n const authenticated = isAuthenticatedUserInfo(userInfo);\n\n req.authdog = {\n token,\n user: authenticated ? (userInfo.user ?? null) : null,\n isAuthenticated: authenticated,\n userInfo,\n };\n } catch {\n // A failed or untrusted userinfo lookup is treated as \"not authenticated\"\n // — never as a server error and never as an authenticated session.\n req.authdog = { token, user: null, isAuthenticated: false, userInfo: null };\n }\n\n return next();\n };\n};\n\n/**\n * Gate middleware that enforces authentication. Responds with `401` JSON when\n * the request has no valid Authdog session and calls `next()` otherwise.\n *\n * ⚠️ This is the real server-side enforcement point. Client-side checks are\n * presentational only; every protected route MUST sit behind `requireAuth`\n * (after `attachSession` has run) for the protection to be real.\n */\nexport const requireAuth: RequestHandler = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n if (!req.authdog?.isAuthenticated) {\n res.status(401).json({ error: \"Unauthorized\" });\n return;\n }\n next();\n};\n","import type { PublicKeyPayload } from \"@authdog/node-commons\";\nimport type { Request, RequestHandler, Response } from \"express\";\nimport { getPublicKeyPayload } from \"./commons\";\nimport { logoutHandler } from \"./logout\";\nimport { createAttachSession, requireAuth } from \"./middleware\";\nimport type { AttachSessionOptions, AuthdogConfig } from \"./types\";\n\n/** The instance returned by {@link createAuthdog}. */\nexport interface AuthdogServer {\n /**\n * Middleware that resolves the session and attaches `req.authdog`. Mount it\n * early (typically app-wide) so downstream handlers and `requireAuth` can\n * read the authentication context. Never throws or blocks the request.\n */\n attachSession: (options?: AttachSessionOptions) => RequestHandler;\n /**\n * Gate middleware that returns `401` for unauthenticated requests. This is\n * the real server-side enforcement point — place it before any protected\n * route. Requires `attachSession` to have run first.\n */\n requireAuth: RequestHandler;\n /** Handler that clears the session cookie and performs a safe redirect. */\n logout: (req: Request, res: Response) => void;\n /** The validated, parsed public-key payload (`environmentId`, `identityHost`, …). */\n getPublicKeyPayload: () => PublicKeyPayload;\n /** The validated public-key payload as a JSON string, e.g. to inline into HTML. */\n getPublicKey: () => string;\n}\n\n/**\n * Creates an Authdog server instance for Express.\n *\n * The public key is validated and parsed once here — enforcing the trusted\n * identity-host allowlist (SSRF / token-exfiltration protection) — so a\n * malformed or untrusted key fails fast at startup rather than on the first\n * request.\n *\n * ```ts\n * const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });\n *\n * app.use(authdog.attachSession());\n * app.get(\"/me\", authdog.requireAuth, (req, res) => res.json(req.authdog!.user));\n * app.get(\"/logout\", authdog.logout);\n * ```\n */\nexport const createAuthdog = (config: AuthdogConfig): AuthdogServer => {\n if (!config.publicKey) {\n throw new Error(\"Public key is not defined\");\n }\n\n // Validate + parse eagerly so an invalid/untrusted key throws at startup.\n const payload = getPublicKeyPayload(config.publicKey);\n\n return {\n attachSession: (options?: AttachSessionOptions) =>\n createAttachSession(payload, options),\n requireAuth,\n logout: logoutHandler,\n getPublicKeyPayload: () => payload,\n getPublicKey: () => JSON.stringify(payload),\n };\n};\n"],"mappings":"AAAA,OACE,6BAAAA,MAEK,wBAUA,IAAMC,EAAuBC,GAC3BF,EAA0BE,CAAS,ECd5C,OAAS,wBAAAC,MAA4B,wBCArC,OAAS,gBAAAC,MAAoB,wBAItB,IAAMC,EAAsB,kBAYtBC,EAAmBC,GAAgC,CAE9D,IAAMC,EAAaD,EAAI,QAAQ,cAC/B,GAAIC,GAAc,cAAc,KAAKA,CAAU,EAAG,CAChD,IAAMC,EAAQD,EAAW,QAAQ,cAAe,EAAE,EAAE,KAAK,EACzD,GAAIC,EACF,OAAOA,CAEX,CAEA,IAAMC,EAAeH,EAAI,QAAQ,QAAU,KAC3C,OAAKG,EAIWN,EAAaM,CAAY,EAC1B,KAAMC,GAAMA,EAAE,OAASN,CAAmB,GAAG,OAAS,KAJ5D,IAKX,EDhBO,IAAMO,EAAgB,CAACC,EAAcC,IAAwB,CAClE,IAAMC,EAAS,QAAQ,IAAI,WAAa,aAAe,WAAa,GAEpED,EAAI,UACF,aACA,GAAGE,CAAmB,8DAA8DD,CAAM,eAC5F,EAEA,IAAME,EAAcC,EAAqBL,EAAI,MAAM,aAAc,GAAG,EAEpEC,EAAI,SAAS,IAAKG,CAAW,CAC/B,EE5BA,OACE,iBAAAE,EACA,2BAAAC,MAEK,wBAMP,IAAMC,EAAmC,CACvC,MAAO,KACP,KAAM,KACN,gBAAiB,GACjB,SAAU,IACZ,EAaaC,EAAsB,CACjCC,EACAC,EAAgC,CAAC,IACd,CACnB,IAAMC,EAAYD,EAAQ,WAAa,GAEvC,MAAO,OAAOE,EAAcC,EAAgBC,IAAuB,CACjE,IAAMC,EAAQC,EAAgBJ,CAAG,EAEjC,GAAI,CAACG,EACH,OAAAH,EAAI,QAAU,CAAE,GAAGL,CAAU,EACtBO,EAAK,EAMd,GAAI,CAACH,EACH,OAAAC,EAAI,QAAU,CACZ,MAAAG,EACA,KAAM,KACN,gBAAiB,GACjB,SAAU,IACZ,EACOD,EAAK,EAGd,GAAI,CACF,IAAMG,EAAW,MAAMC,EACrBT,EAAQ,aACRA,EAAQ,cACRM,CACF,EAEMI,EAAgBC,EAAwBH,CAAQ,EAEtDL,EAAI,QAAU,CACZ,MAAAG,EACA,KAAMI,EAAiBF,EAAS,MAAQ,KAAQ,KAChD,gBAAiBE,EACjB,SAAAF,CACF,CACF,MAAQ,CAGNL,EAAI,QAAU,CAAE,MAAAG,EAAO,KAAM,KAAM,gBAAiB,GAAO,SAAU,IAAK,CAC5E,CAEA,OAAOD,EAAK,CACd,CACF,EAUaO,EAA8B,CACzCT,EACAU,EACAR,IACG,CACH,GAAI,CAACF,EAAI,SAAS,gBAAiB,CACjCU,EAAI,OAAO,GAAG,EAAE,KAAK,CAAE,MAAO,cAAe,CAAC,EAC9C,MACF,CACAR,EAAK,CACP,ECrDO,IAAMS,EAAiBC,GAAyC,CACrE,GAAI,CAACA,EAAO,UACV,MAAM,IAAI,MAAM,2BAA2B,EAI7C,IAAMC,EAAUC,EAAoBF,EAAO,SAAS,EAEpD,MAAO,CACL,cAAgBG,GACdC,EAAoBH,EAASE,CAAO,EACtC,YAAAE,EACA,OAAQC,EACR,oBAAqB,IAAML,EAC3B,aAAc,IAAM,KAAK,UAAUA,CAAO,CAC5C,CACF","names":["validateAndParsePublicKey","getPublicKeyPayload","publicKey","sanitizeRedirectPath","parseCookies","SESSION_COOKIE_NAME","getSessionToken","req","authHeader","token","cookieHeader","c","logoutHandler","req","res","secure","SESSION_COOKIE_NAME","redirectUrl","sanitizeRedirectPath","fetchUserData","isAuthenticatedUserInfo","ANONYMOUS","createAttachSession","payload","options","fetchUser","req","_res","next","token","getSessionToken","userInfo","fetchUserData","authenticated","isAuthenticatedUserInfo","requireAuth","res","createAuthdog","config","payload","getPublicKeyPayload","options","createAttachSession","requireAuth","logoutHandler"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@authdog/express",
3
+ "version": "0.2.0",
4
+ "description": "Authdog Express SDK",
5
+ "source": "src/index.ts",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist/"
18
+ ],
19
+ "sideEffects": false,
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/authdog-labs/web-sdk.git",
23
+ "directory": "packages/express"
24
+ },
25
+ "homepage": "https://github.com/authdog-labs/web-sdk/tree/main/packages/express#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/authdog-labs/web-sdk/issues"
28
+ },
29
+ "scripts": {
30
+ "format": "prettier --config .prettierrc.json --write \"**/*.{ts,md}\"",
31
+ "type-check": "tsc",
32
+ "clean": "rm -rf dist",
33
+ "build": "bun run clean && tsup",
34
+ "ship": "bun run build && bun publish --access public"
35
+ },
36
+ "dependencies": {
37
+ "@authdog/node-commons": "workspace:*"
38
+ },
39
+ "peerDependencies": {
40
+ "express": "^4.18.0 || ^5.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/express": "^5.0.0",
44
+ "@types/node": "^22.14.1",
45
+ "express": "^5.0.0",
46
+ "prettier": "^3.4.2",
47
+ "tsup": "^8.3.5",
48
+ "typescript": "^5.7.2",
49
+ "vitest": "^2.1.8"
50
+ },
51
+ "publishConfig": {
52
+ "registry": "https://registry.npmjs.org/",
53
+ "access": "public"
54
+ }
55
+ }