@seamless-auth/express 0.2.0 → 0.3.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 +18 -0
- package/dist/createServer.d.ts +78 -0
- package/dist/createServer.d.ts.map +1 -0
- package/dist/getSeamlessUser.d.ts +4 -0
- package/dist/getSeamlessUser.d.ts.map +1 -0
- package/dist/handlers/admin.d.ts +14 -0
- package/dist/handlers/admin.d.ts.map +1 -0
- package/dist/handlers/bootstrapAdmininvite.d.ts +4 -0
- package/dist/handlers/bootstrapAdmininvite.d.ts.map +1 -0
- package/dist/handlers/finishLogin.d.ts +6 -0
- package/dist/handlers/finishLogin.d.ts.map +1 -0
- package/dist/handlers/finishRegister.d.ts +6 -0
- package/dist/handlers/finishRegister.d.ts.map +1 -0
- package/dist/handlers/internalMetrics.d.ts +9 -0
- package/dist/handlers/internalMetrics.d.ts.map +1 -0
- package/dist/handlers/login.d.ts +4 -0
- package/dist/handlers/login.d.ts.map +1 -0
- package/dist/handlers/logout.d.ts +4 -0
- package/dist/handlers/logout.d.ts.map +1 -0
- package/dist/handlers/me.d.ts +6 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/pollMagicLinkConfirmation.d.ts +6 -0
- package/dist/handlers/pollMagicLinkConfirmation.d.ts.map +1 -0
- package/dist/handlers/register.d.ts +4 -0
- package/dist/handlers/register.d.ts.map +1 -0
- package/dist/handlers/requestMagicLink.d.ts +7 -0
- package/dist/handlers/requestMagicLink.d.ts.map +1 -0
- package/dist/handlers/requestOtp.d.ts +7 -0
- package/dist/handlers/requestOtp.d.ts.map +1 -0
- package/dist/handlers/sessions.d.ts +6 -0
- package/dist/handlers/sessions.d.ts.map +1 -0
- package/dist/handlers/systemConfig.d.ts +6 -0
- package/dist/handlers/systemConfig.d.ts.map +1 -0
- package/dist/index.d.ts +9 -197
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +248 -8
- package/dist/internal/buildAuthorization.d.ts +6 -0
- package/dist/internal/buildAuthorization.d.ts.map +1 -0
- package/dist/internal/cookie.d.ts +17 -0
- package/dist/internal/cookie.d.ts.map +1 -0
- package/dist/internal/deliverAuthMessage.d.ts +6 -0
- package/dist/internal/deliverAuthMessage.d.ts.map +1 -0
- package/dist/messaging.d.ts +98 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/middleware/ensureCookies.d.ts +16 -0
- package/dist/middleware/ensureCookies.d.ts.map +1 -0
- package/dist/middleware/requireAuth.d.ts +69 -0
- package/dist/middleware/requireAuth.d.ts.map +1 -0
- package/dist/middleware/requireRole.d.ts +36 -0
- package/dist/middleware/requireRole.d.ts.map +1 -0
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ This package:
|
|
|
14
14
|
- Manages signed, HttpOnly session cookies
|
|
15
15
|
- Enforces authentication and authorization in your API
|
|
16
16
|
- Handles all API ↔ Auth Server communication via short-lived service tokens
|
|
17
|
+
- Establishes the initializer surface for adopter-supplied auth messaging
|
|
17
18
|
|
|
18
19
|
> **npm:** https://www.npmjs.com/package/@seamless-auth/express
|
|
19
20
|
> **Docs:** https://docs.seamlessauth.com
|
|
@@ -149,9 +150,26 @@ Routes include:
|
|
|
149
150
|
registrationCookieName?: string;
|
|
150
151
|
refreshCookieName?: string;
|
|
151
152
|
preAuthCookieName?: string;
|
|
153
|
+
messaging?: {
|
|
154
|
+
email?: EmailTransport;
|
|
155
|
+
sms?: SmsTransport;
|
|
156
|
+
handlers?: Partial<AuthMessagingHandlers>;
|
|
157
|
+
overrides?: AuthMessageOverrides;
|
|
158
|
+
};
|
|
152
159
|
}
|
|
153
160
|
```
|
|
154
161
|
|
|
162
|
+
`messaging` is the initializer-facing contract for adopter-supplied auth messaging capabilities.
|
|
163
|
+
|
|
164
|
+
When `messaging` is provided, `@seamless-auth/express` requests external-delivery payloads from the upstream auth server for auth-message flows and completes delivery locally through the configured transports or handlers.
|
|
165
|
+
|
|
166
|
+
This currently applies to:
|
|
167
|
+
|
|
168
|
+
- OTP email
|
|
169
|
+
- OTP SMS
|
|
170
|
+
- magic-link email
|
|
171
|
+
- bootstrap invite email
|
|
172
|
+
|
|
155
173
|
---
|
|
156
174
|
|
|
157
175
|
### `requireAuth(options?)`
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import type { SeamlessAuthMessagingOptions } from "./messaging";
|
|
3
|
+
export type SeamlessAuthServerOptions = {
|
|
4
|
+
authServerUrl: string;
|
|
5
|
+
cookieSecret: string;
|
|
6
|
+
serviceSecret: string;
|
|
7
|
+
issuer: string;
|
|
8
|
+
audience: string;
|
|
9
|
+
jwksKid?: string;
|
|
10
|
+
cookieDomain?: string;
|
|
11
|
+
accessCookieName?: string;
|
|
12
|
+
registrationCookieName?: string;
|
|
13
|
+
refreshCookieName?: string;
|
|
14
|
+
preAuthCookieName?: string;
|
|
15
|
+
messaging?: SeamlessAuthMessagingOptions;
|
|
16
|
+
};
|
|
17
|
+
export interface SeamlessAuthUser {
|
|
18
|
+
id: string;
|
|
19
|
+
sub: string;
|
|
20
|
+
roles: string[];
|
|
21
|
+
email: string;
|
|
22
|
+
phone: string;
|
|
23
|
+
iat?: number;
|
|
24
|
+
exp?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
|
|
28
|
+
*
|
|
29
|
+
* This helper wires your API backend to a Seamless Auth instance running in
|
|
30
|
+
* "server mode." It automatically forwards login, registration, WebAuthn,
|
|
31
|
+
* logout, token refresh, and session validation routes to the auth server
|
|
32
|
+
* and handles all cookie management required for a seamless login flow.
|
|
33
|
+
*
|
|
34
|
+
* ### Responsibilities
|
|
35
|
+
* - Proxies all `/auth/*` routes to the upstream Seamless Auth server
|
|
36
|
+
* - Manages `access`, `registration`, `pre-auth`, and `refresh` cookies
|
|
37
|
+
* - Normalizes cookie settings for cross-domain or same-domain deployments
|
|
38
|
+
* - Ensures authentication routes behave consistently across environments
|
|
39
|
+
* - Provides shared middleware for auth flows
|
|
40
|
+
*
|
|
41
|
+
* ### Cookie Types
|
|
42
|
+
* - **accessCookie** – long-lived session cookie for authenticated API requests
|
|
43
|
+
* - **registrationCookie** – ephemeral cookie used during registration and OTP/WebAuthn flows
|
|
44
|
+
* - **preAuthCookie** – short-lived cookie used during login initiation
|
|
45
|
+
* - **refreshCookie** – opaque refresh token cookie used to rotate session tokens
|
|
46
|
+
*
|
|
47
|
+
* All cookie names and their domains may be customized via the `opts` parameter.
|
|
48
|
+
*
|
|
49
|
+
* ### Example
|
|
50
|
+
* ```ts
|
|
51
|
+
* app.use("/auth", createSeamlessAuthServer({
|
|
52
|
+
* authServerUrl: "https://identifier.seamlessauth.com",
|
|
53
|
+
* cookieDomain: "mycompany.com",
|
|
54
|
+
* cookieSecret: "someLongRandomValue"
|
|
55
|
+
* serviceSecret: "someLongRandomValueToo"
|
|
56
|
+
* jwksKid: "dev-main"
|
|
57
|
+
* accessCookieName: "sa_access",
|
|
58
|
+
* registrationCookieName: "sa_registration",
|
|
59
|
+
* refreshCookieName: "sa_refresh",
|
|
60
|
+
* }));
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @param opts - Configuration options for the Seamless Auth proxy:
|
|
64
|
+
* - `authServerUrl` — Base URL of your Seamless Auth instance (required)
|
|
65
|
+
* - `cookieSecret` — The value to encode your cookies secrets with (required)
|
|
66
|
+
* - `serviceSecret` - An machine to machine shared secret that matches your auth servers (required)
|
|
67
|
+
* - `jwksKid` - The active jwks KID
|
|
68
|
+
* - `cookieDomain` — Domain attribute applied to all auth cookies
|
|
69
|
+
* - `accessCookieName` — Name of the session access cookie
|
|
70
|
+
* - `registrationCookieName` — Name of the ephemeral registration cookie
|
|
71
|
+
* - `refreshCookieName` — Name of the refresh token cookie
|
|
72
|
+
* - `preAuthCookieName` — Name of the cookie used during login initiation
|
|
73
|
+
* - `messaging` — Optional auth-messaging transports, handlers, and overrides
|
|
74
|
+
*
|
|
75
|
+
* @returns An Express `Router` preconfigured with all Seamless Auth routes.
|
|
76
|
+
*/
|
|
77
|
+
export declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Router;
|
|
78
|
+
//# sourceMappingURL=createServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAqB,MAAM,EAAE,MAAM,SAAS,CAAC;AAI7D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAqDhE,MAAM,MAAM,yBAAyB,GAAG;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,4BAA4B,CAAC;CAC1C,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,yBAAyB,GAC9B,MAAM,CAkPR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSeamlessUser.d.ts","sourceRoot":"","sources":["../src/getSeamlessUser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAMvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,yBAAyB,gBAUhC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare const getUsers: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
4
|
+
export declare const createUser: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
5
|
+
export declare const deleteUser: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
6
|
+
export declare const updateUser: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
export declare const getUserDetail: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
8
|
+
export declare const getUserAnomalies: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
9
|
+
export declare const getAuthEvents: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
10
|
+
export declare const getCredentialCount: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
11
|
+
export declare const listAllSessions: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
12
|
+
export declare const listUserSessions: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
13
|
+
export declare const revokeAllUserSessions: (req: Request, res: Response, opts: SeamlessAuthServerOptions) => Promise<Response<any, Record<string, any>>>;
|
|
14
|
+
//# sourceMappingURL=admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/handlers/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAgB5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAS5D,eAAO,MAAM,QAAQ,GACnB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,UAAU,GACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAS9B,CAAC;AAEJ,eAAO,MAAM,UAAU,GACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,UAAU,GACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAS9B,CAAC;AAEJ,eAAO,MAAM,aAAa,GACxB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,gBAAgB,GAC3B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,aAAa,GACxB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAS9B,CAAC;AAEJ,eAAO,MAAM,kBAAkB,GAC7B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,eAAe,GAC1B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAS9B,CAAC;AAEJ,eAAO,MAAM,gBAAgB,GAC3B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC;AAEJ,eAAO,MAAM,qBAAqB,GAChC,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,yBAAyB,gDAQ9B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function bootstrapAdminInvite(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
4
|
+
//# sourceMappingURL=bootstrapAdmininvite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapAdmininvite.d.ts","sourceRoot":"","sources":["../../src/handlers/bootstrapAdmininvite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAsBhC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function finishLogin(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
}, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
+
//# sourceMappingURL=finishLogin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finishLogin.d.ts","sourceRoot":"","sources":["../../src/handlers/finishLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtC,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DA+ChC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function finishRegister(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
}, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
+
//# sourceMappingURL=finishRegister.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finishRegister.d.ts","sourceRoot":"","sources":["../../src/handlers/finishRegister.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAG5D,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtC,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAkEhC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function getAuthEventSummary(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
4
|
+
export declare function getAuthEventTimeseries(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
5
|
+
export declare function getLoginStats(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
6
|
+
export declare function getSecurityAnomalies(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
export declare function getDashboardMetrics(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
8
|
+
export declare function getGroupedEventSummary(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
9
|
+
//# sourceMappingURL=internalMetrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internalMetrics.d.ts","sourceRoot":"","sources":["../../src/handlers/internalMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAW5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAS5D,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAWhC;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAWhC;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC;AAED,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function login(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
4
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/handlers/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,KAAK,CACzB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DA4ChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/handlers/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,MAAM,CAC1B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,iBAYhC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function me(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
}, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
+
//# sourceMappingURL=me.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../src/handlers/me.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,EAAE,CACtB,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtC,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAoBhC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function pollMagicLinkConfirmation(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
}, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
+
//# sourceMappingURL=pollMagicLinkConfirmation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pollMagicLinkConfirmation.d.ts","sourceRoot":"","sources":["../../src/handlers/pollMagicLinkConfirmation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtC,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAgDhC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function register(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
4
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/handlers/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAqDhC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function requestMagicLink(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
user?: any;
|
|
6
|
+
}, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
//# sourceMappingURL=requestMagicLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestMagicLink.d.ts","sourceRoot":"","sources":["../../src/handlers/requestMagicLink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,EAClD,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAyBhC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function requestOtp(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
user?: any;
|
|
6
|
+
}, res: Response, opts: SeamlessAuthServerOptions, kind: "email" | "phone"): Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
//# sourceMappingURL=requestOtp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestOtp.d.ts","sourceRoot":"","sources":["../../src/handlers/requestOtp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,EAClD,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,EAC/B,IAAI,EAAE,OAAO,GAAG,OAAO,+CA0BxB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function listSessions(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
4
|
+
export declare function revokeSession(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
5
|
+
export declare function revokeAllSessions(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>>>;
|
|
6
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/handlers/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAS5D,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,+CAUhC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function getAvailableRoles(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
4
|
+
export declare function getSystemConfigAdmin(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
5
|
+
export declare function updateSystemConfig(req: Request, res: Response, opts: SeamlessAuthServerOptions): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
+
//# sourceMappingURL=systemConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemConfig.d.ts","sourceRoot":"","sources":["../../src/handlers/systemConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAchC;AAED,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAchC;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,yBAAyB,2DAehC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,197 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
cookieDomain?: string;
|
|
11
|
-
accessCookieName?: string;
|
|
12
|
-
registrationCookieName?: string;
|
|
13
|
-
refreshCookieName?: string;
|
|
14
|
-
preAuthCookieName?: string;
|
|
15
|
-
};
|
|
16
|
-
interface SeamlessAuthUser {
|
|
17
|
-
id: string;
|
|
18
|
-
sub: string;
|
|
19
|
-
roles: string[];
|
|
20
|
-
email: string;
|
|
21
|
-
phone: string;
|
|
22
|
-
iat?: number;
|
|
23
|
-
exp?: number;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
|
|
27
|
-
*
|
|
28
|
-
* This helper wires your API backend to a Seamless Auth instance running in
|
|
29
|
-
* "server mode." It automatically forwards login, registration, WebAuthn,
|
|
30
|
-
* logout, token refresh, and session validation routes to the auth server
|
|
31
|
-
* and handles all cookie management required for a seamless login flow.
|
|
32
|
-
*
|
|
33
|
-
* ### Responsibilities
|
|
34
|
-
* - Proxies all `/auth/*` routes to the upstream Seamless Auth server
|
|
35
|
-
* - Manages `access`, `registration`, `pre-auth`, and `refresh` cookies
|
|
36
|
-
* - Normalizes cookie settings for cross-domain or same-domain deployments
|
|
37
|
-
* - Ensures authentication routes behave consistently across environments
|
|
38
|
-
* - Provides shared middleware for auth flows
|
|
39
|
-
*
|
|
40
|
-
* ### Cookie Types
|
|
41
|
-
* - **accessCookie** – long-lived session cookie for authenticated API requests
|
|
42
|
-
* - **registrationCookie** – ephemeral cookie used during registration and OTP/WebAuthn flows
|
|
43
|
-
* - **preAuthCookie** – short-lived cookie used during login initiation
|
|
44
|
-
* - **refreshCookie** – opaque refresh token cookie used to rotate session tokens
|
|
45
|
-
*
|
|
46
|
-
* All cookie names and their domains may be customized via the `opts` parameter.
|
|
47
|
-
*
|
|
48
|
-
* ### Example
|
|
49
|
-
* ```ts
|
|
50
|
-
* app.use("/auth", createSeamlessAuthServer({
|
|
51
|
-
* authServerUrl: "https://identifier.seamlessauth.com",
|
|
52
|
-
* cookieDomain: "mycompany.com",
|
|
53
|
-
* cookieSecret: "someLongRandomValue"
|
|
54
|
-
* serviceSecret: "someLongRandomValueToo"
|
|
55
|
-
* jwksKid: "dev-main"
|
|
56
|
-
* accessCookieName: "sa_access",
|
|
57
|
-
* registrationCookieName: "sa_registration",
|
|
58
|
-
* refreshCookieName: "sa_refresh",
|
|
59
|
-
* }));
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* @param opts - Configuration options for the Seamless Auth proxy:
|
|
63
|
-
* - `authServerUrl` — Base URL of your Seamless Auth instance (required)
|
|
64
|
-
* - `cookieSecret` — The value to encode your cookies secrets with (required)
|
|
65
|
-
* - `serviceSecret` - An machine to machine shared secret that matches your auth servers (required)
|
|
66
|
-
* - `jwksKid` - The active jwks KID
|
|
67
|
-
* - `cookieDomain` — Domain attribute applied to all auth cookies
|
|
68
|
-
* - `accessCookieName` — Name of the session access cookie
|
|
69
|
-
* - `registrationCookieName` — Name of the ephemeral registration cookie
|
|
70
|
-
* - `refreshCookieName` — Name of the refresh token cookie
|
|
71
|
-
* - `preAuthCookieName` — Name of the cookie used during login initiation
|
|
72
|
-
*
|
|
73
|
-
* @returns An Express `Router` preconfigured with all Seamless Auth routes.
|
|
74
|
-
*/
|
|
75
|
-
declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Router;
|
|
76
|
-
|
|
77
|
-
interface RequireAuthOptions {
|
|
78
|
-
cookieName?: string;
|
|
79
|
-
cookieSecret: string;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Express middleware that enforces authentication using Seamless Auth cookies.
|
|
83
|
-
*
|
|
84
|
-
* This guard verifies the signed access cookie generated by the Seamless Auth
|
|
85
|
-
* server. If the access cookie is valid and unexpired, the decoded session
|
|
86
|
-
* payload is attached to `req.user` and the request proceeds.
|
|
87
|
-
*
|
|
88
|
-
* If the access cookie is expired or missing *but* a valid refresh cookie is
|
|
89
|
-
* present, the middleware automatically attempts a silent token refresh using
|
|
90
|
-
* the Seamless Auth server. When successful, new session cookies are issued and
|
|
91
|
-
* the request continues with an updated `req.user`.
|
|
92
|
-
*
|
|
93
|
-
* If neither the access token nor refresh token can validate the session,
|
|
94
|
-
* the middleware returns a 401 Unauthorized error and prevents further
|
|
95
|
-
* route execution.
|
|
96
|
-
*
|
|
97
|
-
* ### Responsibilities
|
|
98
|
-
* - Validates the Seamless Auth session access cookie
|
|
99
|
-
* - Attempts refresh-token–based session renewal when necessary
|
|
100
|
-
* - Populates `req.user` with the verified session payload
|
|
101
|
-
* - Handles all cookie rewriting during refresh flows
|
|
102
|
-
* - Acts as a request-level authentication guard for API routes
|
|
103
|
-
*
|
|
104
|
-
* ### Cookie Parameters
|
|
105
|
-
* - **cookieName** — Name of the access cookie that holds the signed session JWT
|
|
106
|
-
* - **refreshCookieName** — Name of the refresh cookie used for silent token refresh
|
|
107
|
-
* - **cookieDomain** — Domain or path value applied to issued cookies
|
|
108
|
-
*
|
|
109
|
-
* ### Example
|
|
110
|
-
* ```ts
|
|
111
|
-
* // Protect a route
|
|
112
|
-
* app.get("/api/me", requireAuth(), (req, res) => {
|
|
113
|
-
* res.json({ user: req.user });
|
|
114
|
-
* });
|
|
115
|
-
*
|
|
116
|
-
* // Custom cookie names (if your Seamless Auth server uses overrides)
|
|
117
|
-
* app.use(
|
|
118
|
-
* "/internal",
|
|
119
|
-
* requireAuth("sa_access", "sa_refresh", "mycompany.com"),
|
|
120
|
-
* internalRouter
|
|
121
|
-
* );
|
|
122
|
-
* ```
|
|
123
|
-
*
|
|
124
|
-
* @param cookieName - The access cookie name. Defaults to `"seamless-access"`.
|
|
125
|
-
* @param refreshCookieName - The refresh cookie name used for session rotation. Defaults to `"seamless-refresh"`.
|
|
126
|
-
* @param cookieDomain - Domain or path used when rewriting cookies. Defaults to `"/"`.
|
|
127
|
-
*
|
|
128
|
-
* @returns An Express middleware function that enforces Seamless Auth
|
|
129
|
-
* authentication on incoming requests.
|
|
130
|
-
*/
|
|
131
|
-
interface RequireAuthOptions {
|
|
132
|
-
cookieName?: string;
|
|
133
|
-
cookieSecret: string;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Express middleware that enforces authentication
|
|
137
|
-
* using an already-issued Seamless Auth access cookie.
|
|
138
|
-
*
|
|
139
|
-
* NOTE:
|
|
140
|
-
* - This middleware does NOT attempt token refresh.
|
|
141
|
-
* - Refresh is handled upstream by ensureCookies().
|
|
142
|
-
*/
|
|
143
|
-
declare function requireAuth(opts: RequireAuthOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Express middleware that enforces role-based authorization for Seamless Auth.
|
|
147
|
-
*
|
|
148
|
-
* This middleware assumes `requireAuth()` has already:
|
|
149
|
-
* - authenticated the request
|
|
150
|
-
* - populated `req.user` with the authenticated session payload
|
|
151
|
-
*
|
|
152
|
-
* `requireRole` performs **authorization only**. It does not inspect cookies,
|
|
153
|
-
* verify tokens, or read environment variables.
|
|
154
|
-
*
|
|
155
|
-
* If any of the required roles are present on the user, access is granted.
|
|
156
|
-
* Otherwise, a 403 Forbidden response is returned.
|
|
157
|
-
*
|
|
158
|
-
* * ### Example
|
|
159
|
-
* ```ts
|
|
160
|
-
* // Require a single role
|
|
161
|
-
* app.get("/admin/users",
|
|
162
|
-
* requireAuth(),
|
|
163
|
-
* requireRole("admin"),
|
|
164
|
-
* (req, res) => {
|
|
165
|
-
* res.send("Welcome admin!");
|
|
166
|
-
* }
|
|
167
|
-
* );
|
|
168
|
-
*
|
|
169
|
-
* // Allow any of multiple roles
|
|
170
|
-
* app.post("/settings",
|
|
171
|
-
* requireAuth(),
|
|
172
|
-
* requireRole(["admin", "supervisor"]),
|
|
173
|
-
* updateSettingsHandler
|
|
174
|
-
* );
|
|
175
|
-
*
|
|
176
|
-
* @param requiredRoles - A role or list of roles required to access the route
|
|
177
|
-
*/
|
|
178
|
-
declare function requireRole(requiredRoles: string | string[]): RequestHandler;
|
|
179
|
-
|
|
180
|
-
interface EnsureCookiesMiddlewareOptions {
|
|
181
|
-
authServerUrl: string;
|
|
182
|
-
cookieDomain?: string;
|
|
183
|
-
accessCookieName: string;
|
|
184
|
-
registrationCookieName: string;
|
|
185
|
-
refreshCookieName: string;
|
|
186
|
-
preAuthCookieName: string;
|
|
187
|
-
cookieSecret: string;
|
|
188
|
-
serviceSecret: string;
|
|
189
|
-
issuer: string;
|
|
190
|
-
audience: string;
|
|
191
|
-
keyId: string;
|
|
192
|
-
}
|
|
193
|
-
declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
194
|
-
|
|
195
|
-
declare function getSeamlessUser(req: Request, opts: SeamlessAuthServerOptions): Promise<any>;
|
|
196
|
-
|
|
197
|
-
export { type SeamlessAuthServerOptions, type SeamlessAuthUser, createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
|
|
1
|
+
import { createSeamlessAuthServer, SeamlessAuthServerOptions, SeamlessAuthUser } from "./createServer";
|
|
2
|
+
export { SeamlessAuthServerOptions, SeamlessAuthUser };
|
|
3
|
+
export type { AuthMessageOverrides, AuthMessagingHandlers, DeliveryResult, EmailMessage, EmailTransport, SeamlessAuthMessagingOptions, SmsMessage, SmsTransport, } from "./messaging";
|
|
4
|
+
export { requireAuth } from "./middleware/requireAuth";
|
|
5
|
+
export { requireRole } from "./middleware/requireRole";
|
|
6
|
+
export { createEnsureCookiesMiddleware } from "./middleware/ensureCookies";
|
|
7
|
+
export { getSeamlessUser } from "./getSeamlessUser";
|
|
8
|
+
export default createSeamlessAuthServer;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,CAAC;AACvD,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,4BAA4B,EAC5B,UAAU,EACV,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,eAAe,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -139,11 +139,12 @@ import { finishLoginHandler } from "@seamless-auth/core/handlers/finishLogin";
|
|
|
139
139
|
// src/internal/buildAuthorization.ts
|
|
140
140
|
import { createServiceToken } from "@seamless-auth/core";
|
|
141
141
|
function buildServiceAuthorization(req, opts) {
|
|
142
|
-
|
|
142
|
+
const subject = req.cookiePayload?.sub || req.user?.sub;
|
|
143
|
+
if (!subject) {
|
|
143
144
|
return void 0;
|
|
144
145
|
}
|
|
145
146
|
const token = createServiceToken({
|
|
146
|
-
subject
|
|
147
|
+
subject,
|
|
147
148
|
issuer: opts.issuer,
|
|
148
149
|
audience: opts.audience,
|
|
149
150
|
serviceSecret: opts.serviceSecret,
|
|
@@ -194,6 +195,174 @@ async function finishLogin(req, res, opts) {
|
|
|
194
195
|
|
|
195
196
|
// src/handlers/register.ts
|
|
196
197
|
import { registerHandler } from "@seamless-auth/core/handlers/register";
|
|
198
|
+
|
|
199
|
+
// src/internal/deliverAuthMessage.ts
|
|
200
|
+
function applyEmailOverride(override, input, defaults, appName) {
|
|
201
|
+
return override ? override(input, defaults, { appName }) : defaults;
|
|
202
|
+
}
|
|
203
|
+
function applySmsOverride(override, input, defaults, appName) {
|
|
204
|
+
return override ? override(input, defaults, { appName }) : defaults;
|
|
205
|
+
}
|
|
206
|
+
function buildOtpEmailMessage(input, messaging) {
|
|
207
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
208
|
+
return applyEmailOverride(
|
|
209
|
+
messaging.overrides?.otpEmail,
|
|
210
|
+
{
|
|
211
|
+
to: input.to,
|
|
212
|
+
token: input.token,
|
|
213
|
+
from: messaging.defaults?.emailFrom,
|
|
214
|
+
subject: `${appName} - Verify your email`
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
to: input.to,
|
|
218
|
+
from: messaging.defaults?.emailFrom,
|
|
219
|
+
subject: `${appName} - Verify your email`,
|
|
220
|
+
text: `Verify your account with ${appName}.
|
|
221
|
+
|
|
222
|
+
Your verification code is: ${input.token}
|
|
223
|
+
|
|
224
|
+
If you did not request this code, you can safely ignore this message.`,
|
|
225
|
+
html: `<div><h1>Verify your account with ${appName}</h1><p>Please use the verification code below:</p><p><strong>${input.token}</strong></p><p>If you did not request this code, you can safely ignore this message.</p></div>`
|
|
226
|
+
},
|
|
227
|
+
appName
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
function buildOtpSmsMessage(input, messaging) {
|
|
231
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
232
|
+
return applySmsOverride(
|
|
233
|
+
messaging.overrides?.otpSms,
|
|
234
|
+
{
|
|
235
|
+
to: input.to,
|
|
236
|
+
token: input.token,
|
|
237
|
+
from: messaging.defaults?.smsFrom
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
to: input.to,
|
|
241
|
+
from: messaging.defaults?.smsFrom,
|
|
242
|
+
body: `Your ${appName} verification code is: ${input.token}. No one will ever ask you for this code. Do not share it.`
|
|
243
|
+
},
|
|
244
|
+
appName
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
function buildMagicLinkMessage(input, messaging) {
|
|
248
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
249
|
+
return applyEmailOverride(
|
|
250
|
+
messaging.overrides?.magicLinkEmail,
|
|
251
|
+
{
|
|
252
|
+
to: input.to,
|
|
253
|
+
magicLinkUrl: input.magicLinkUrl,
|
|
254
|
+
token: input.token,
|
|
255
|
+
from: messaging.defaults?.emailFrom,
|
|
256
|
+
subject: `${appName} - Your sign-in link`
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
to: input.to,
|
|
260
|
+
from: messaging.defaults?.emailFrom,
|
|
261
|
+
subject: `${appName} - Your sign-in link`,
|
|
262
|
+
text: `Use the link below to sign in to ${appName}:
|
|
263
|
+
|
|
264
|
+
${input.magicLinkUrl}
|
|
265
|
+
|
|
266
|
+
If you did not request this email, you can safely ignore it.`,
|
|
267
|
+
html: `<div><h1>Sign in to ${appName}</h1><p>Use the link below to complete sign-in:</p><p><a href="${input.magicLinkUrl}">${input.magicLinkUrl}</a></p><p>If you did not request this email, you can safely ignore it.</p></div>`
|
|
268
|
+
},
|
|
269
|
+
appName
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
function buildBootstrapInviteMessage(input, messaging) {
|
|
273
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
274
|
+
return applyEmailOverride(
|
|
275
|
+
messaging.overrides?.bootstrapInviteEmail,
|
|
276
|
+
{
|
|
277
|
+
to: input.to,
|
|
278
|
+
inviteUrl: input.inviteUrl,
|
|
279
|
+
token: input.token,
|
|
280
|
+
from: messaging.defaults?.emailFrom,
|
|
281
|
+
subject: `${appName} - Bootstrap invite`
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
to: input.to,
|
|
285
|
+
from: messaging.defaults?.emailFrom,
|
|
286
|
+
subject: `${appName} - Bootstrap invite`,
|
|
287
|
+
text: `You have been invited to bootstrap ${appName}.
|
|
288
|
+
|
|
289
|
+
Use the link below to continue:
|
|
290
|
+
${input.inviteUrl}`,
|
|
291
|
+
html: `<div><h1>Bootstrap invite for ${appName}</h1><p>Use the link below to continue:</p><p><a href="${input.inviteUrl}">${input.inviteUrl}</a></p></div>`
|
|
292
|
+
},
|
|
293
|
+
appName
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
async function deliverAuthMessage(messaging, delivery) {
|
|
297
|
+
if (!messaging || !delivery) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
switch (delivery.kind) {
|
|
301
|
+
case "otp_email":
|
|
302
|
+
if (messaging.handlers?.sendOtpEmail) {
|
|
303
|
+
await messaging.handlers.sendOtpEmail({
|
|
304
|
+
to: delivery.to,
|
|
305
|
+
token: delivery.token,
|
|
306
|
+
from: messaging.defaults?.emailFrom
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (!messaging.email) {
|
|
311
|
+
throw new Error("Missing email transport for OTP email delivery.");
|
|
312
|
+
}
|
|
313
|
+
await messaging.email.send(buildOtpEmailMessage(delivery, messaging));
|
|
314
|
+
return;
|
|
315
|
+
case "otp_sms":
|
|
316
|
+
if (messaging.handlers?.sendOtpSms) {
|
|
317
|
+
await messaging.handlers.sendOtpSms({
|
|
318
|
+
to: delivery.to,
|
|
319
|
+
token: delivery.token,
|
|
320
|
+
from: messaging.defaults?.smsFrom
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (!messaging.sms) {
|
|
325
|
+
throw new Error("Missing SMS transport for OTP SMS delivery.");
|
|
326
|
+
}
|
|
327
|
+
await messaging.sms.send(buildOtpSmsMessage(delivery, messaging));
|
|
328
|
+
return;
|
|
329
|
+
case "magic_link_email":
|
|
330
|
+
if (messaging.handlers?.sendMagicLinkEmail) {
|
|
331
|
+
await messaging.handlers.sendMagicLinkEmail({
|
|
332
|
+
to: delivery.to,
|
|
333
|
+
token: delivery.token,
|
|
334
|
+
magicLinkUrl: delivery.magicLinkUrl,
|
|
335
|
+
from: messaging.defaults?.emailFrom
|
|
336
|
+
});
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!messaging.email) {
|
|
340
|
+
throw new Error("Missing email transport for magic link delivery.");
|
|
341
|
+
}
|
|
342
|
+
await messaging.email.send(buildMagicLinkMessage(delivery, messaging));
|
|
343
|
+
return;
|
|
344
|
+
case "bootstrap_invite_email":
|
|
345
|
+
if (messaging.handlers?.sendBootstrapInviteEmail) {
|
|
346
|
+
await messaging.handlers.sendBootstrapInviteEmail({
|
|
347
|
+
to: delivery.to,
|
|
348
|
+
inviteUrl: delivery.inviteUrl,
|
|
349
|
+
from: messaging.defaults?.emailFrom
|
|
350
|
+
});
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!messaging.email) {
|
|
354
|
+
throw new Error("Missing email transport for bootstrap invite delivery.");
|
|
355
|
+
}
|
|
356
|
+
await messaging.email.send(buildBootstrapInviteMessage(delivery, messaging));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function stripDelivery(body) {
|
|
361
|
+
const { delivery: _delivery, ...rest } = body;
|
|
362
|
+
return rest;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/handlers/register.ts
|
|
197
366
|
async function register(req, res, opts) {
|
|
198
367
|
const cookieSigner = {
|
|
199
368
|
secret: opts.cookieSecret,
|
|
@@ -205,7 +374,8 @@ async function register(req, res, opts) {
|
|
|
205
374
|
{
|
|
206
375
|
authServerUrl: opts.authServerUrl,
|
|
207
376
|
cookieDomain: opts.cookieDomain,
|
|
208
|
-
registrationCookieName: opts.registrationCookieName
|
|
377
|
+
registrationCookieName: opts.registrationCookieName,
|
|
378
|
+
externalDelivery: Boolean(opts.messaging)
|
|
209
379
|
}
|
|
210
380
|
);
|
|
211
381
|
if (!cookieSigner.secret) {
|
|
@@ -228,9 +398,42 @@ async function register(req, res, opts) {
|
|
|
228
398
|
if (result.error) {
|
|
229
399
|
return res.status(result.status).json(result.error);
|
|
230
400
|
}
|
|
401
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
402
|
+
await deliverAuthMessage(
|
|
403
|
+
opts.messaging,
|
|
404
|
+
result.body.delivery
|
|
405
|
+
);
|
|
406
|
+
return res.status(result.status).json(stripDelivery(result.body)).end();
|
|
407
|
+
}
|
|
231
408
|
res.status(result.status).json(result.body).end();
|
|
232
409
|
}
|
|
233
410
|
|
|
411
|
+
// src/handlers/requestOtp.ts
|
|
412
|
+
import { requestOtpHandler } from "@seamless-auth/core/handlers/requestOtpHandler";
|
|
413
|
+
async function requestOtp(req, res, opts, kind) {
|
|
414
|
+
const result = await requestOtpHandler(
|
|
415
|
+
{
|
|
416
|
+
kind,
|
|
417
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
authServerUrl: opts.authServerUrl,
|
|
421
|
+
externalDelivery: Boolean(opts.messaging)
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
if (result.error) {
|
|
425
|
+
return res.status(result.status).json(result.error);
|
|
426
|
+
}
|
|
427
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
428
|
+
await deliverAuthMessage(
|
|
429
|
+
opts.messaging,
|
|
430
|
+
result.body.delivery
|
|
431
|
+
);
|
|
432
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
433
|
+
}
|
|
434
|
+
return res.status(result.status).json(result.body);
|
|
435
|
+
}
|
|
436
|
+
|
|
234
437
|
// src/handlers/finishRegister.ts
|
|
235
438
|
import { finishRegisterHandler } from "@seamless-auth/core/handlers/finishRegister";
|
|
236
439
|
import { verifyCookieJwt } from "@seamless-auth/core";
|
|
@@ -363,6 +566,31 @@ async function pollMagicLinkConfirmation(req, res, opts) {
|
|
|
363
566
|
res.status(result.status).json(result.body).end();
|
|
364
567
|
}
|
|
365
568
|
|
|
569
|
+
// src/handlers/requestMagicLink.ts
|
|
570
|
+
import { requestMagicLinkHandler } from "@seamless-auth/core/handlers/requestMagicLinkHandler";
|
|
571
|
+
async function requestMagicLink(req, res, opts) {
|
|
572
|
+
const result = await requestMagicLinkHandler(
|
|
573
|
+
{
|
|
574
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
authServerUrl: opts.authServerUrl,
|
|
578
|
+
externalDelivery: Boolean(opts.messaging)
|
|
579
|
+
}
|
|
580
|
+
);
|
|
581
|
+
if (result.error) {
|
|
582
|
+
return res.status(result.status).json(result.error);
|
|
583
|
+
}
|
|
584
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
585
|
+
await deliverAuthMessage(
|
|
586
|
+
opts.messaging,
|
|
587
|
+
result.body.delivery
|
|
588
|
+
);
|
|
589
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
590
|
+
}
|
|
591
|
+
return res.status(result.status).json(result.body);
|
|
592
|
+
}
|
|
593
|
+
|
|
366
594
|
// src/handlers/admin.ts
|
|
367
595
|
import {
|
|
368
596
|
getUsersHandler,
|
|
@@ -476,11 +704,19 @@ async function bootstrapAdminInvite(req, res, opts) {
|
|
|
476
704
|
const result = await bootstrapAdminInviteHandler({
|
|
477
705
|
authServerUrl: opts.authServerUrl,
|
|
478
706
|
email: req.body.email,
|
|
479
|
-
authorization: req.headers["authorization"]
|
|
707
|
+
authorization: req.headers["authorization"],
|
|
708
|
+
externalDelivery: Boolean(opts.messaging)
|
|
480
709
|
});
|
|
481
710
|
if (result.error) {
|
|
482
711
|
return res.status(result.status).json({ error: result.error });
|
|
483
712
|
}
|
|
713
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
714
|
+
await deliverAuthMessage(
|
|
715
|
+
opts.messaging,
|
|
716
|
+
result.body.delivery
|
|
717
|
+
);
|
|
718
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
719
|
+
}
|
|
484
720
|
res.status(result.status).json(result.body);
|
|
485
721
|
}
|
|
486
722
|
|
|
@@ -644,7 +880,8 @@ function createSeamlessAuthServer(opts) {
|
|
|
644
880
|
accessCookieName: opts.accessCookieName ?? "seamless-access",
|
|
645
881
|
registrationCookieName: opts.registrationCookieName ?? "seamless-ephemeral",
|
|
646
882
|
refreshCookieName: opts.refreshCookieName ?? "seamless-refresh",
|
|
647
|
-
preAuthCookieName: opts.preAuthCookieName ?? "seamless-ephemeral"
|
|
883
|
+
preAuthCookieName: opts.preAuthCookieName ?? "seamless-ephemeral",
|
|
884
|
+
messaging: opts.messaging
|
|
648
885
|
};
|
|
649
886
|
const proxyWithIdentity = (path, identity, method = "POST") => async (req, res) => {
|
|
650
887
|
if (!req.cookiePayload?.sub) {
|
|
@@ -717,11 +954,11 @@ function createSeamlessAuthServer(opts) {
|
|
|
717
954
|
);
|
|
718
955
|
r.get(
|
|
719
956
|
"/otp/generate-phone-otp",
|
|
720
|
-
|
|
957
|
+
(req, res) => requestOtp(req, res, resolvedOpts, "phone")
|
|
721
958
|
);
|
|
722
959
|
r.get(
|
|
723
960
|
"/otp/generate-email-otp",
|
|
724
|
-
|
|
961
|
+
(req, res) => requestOtp(req, res, resolvedOpts, "email")
|
|
725
962
|
);
|
|
726
963
|
r.post("/login", (req, res) => login(req, res, resolvedOpts));
|
|
727
964
|
r.post(
|
|
@@ -739,7 +976,10 @@ function createSeamlessAuthServer(opts) {
|
|
|
739
976
|
"/users/credentials",
|
|
740
977
|
proxyWithIdentity("users/credentials", "access")
|
|
741
978
|
);
|
|
742
|
-
r.get(
|
|
979
|
+
r.get(
|
|
980
|
+
"/magic-link",
|
|
981
|
+
(req, res) => requestMagicLink(req, res, resolvedOpts)
|
|
982
|
+
);
|
|
743
983
|
r.get("/magic-link/verify/:token", async (req, res) => {
|
|
744
984
|
const upstream = await authFetch(
|
|
745
985
|
`${resolvedOpts.authServerUrl}/magic-link/verify/${req.params.token}`,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request } from "express";
|
|
2
|
+
import { SeamlessAuthServerOptions } from "../createServer";
|
|
3
|
+
export declare function buildServiceAuthorization(req: Request & {
|
|
4
|
+
cookiePayload?: any;
|
|
5
|
+
}, opts: SeamlessAuthServerOptions): string | undefined;
|
|
6
|
+
//# sourceMappingURL=buildAuthorization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildAuthorization.d.ts","sourceRoot":"","sources":["../../src/internal/buildAuthorization.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,GAAG;IAAE,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE,EACtC,IAAI,EAAE,yBAAyB,sBAiBhC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
2
|
+
import { Response } from "express";
|
|
3
|
+
export interface CookieSignerOptions {
|
|
4
|
+
secret: string;
|
|
5
|
+
secure: boolean;
|
|
6
|
+
sameSite: "lax" | "none";
|
|
7
|
+
}
|
|
8
|
+
export interface SetCookieOptions {
|
|
9
|
+
name: string;
|
|
10
|
+
payload: JwtPayload;
|
|
11
|
+
domain?: string;
|
|
12
|
+
ttlSeconds: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function setSessionCookie(res: Response, opts: SetCookieOptions, signer: CookieSignerOptions): void;
|
|
15
|
+
export declare function clearSessionCookie(res: Response, domain: string | undefined, name: string): void;
|
|
16
|
+
export declare function clearAllCookies(res: Response, domain: string | undefined, ...cookieNames: string[]): void;
|
|
17
|
+
//# sourceMappingURL=cookie.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/internal/cookie.ts"],"names":[],"mappings":"AAAA,OAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,mBAAmB,QAe5B;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,QAGb;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,GAAG,WAAW,EAAE,MAAM,EAAE,QAKzB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AuthDeliveryInstruction, SeamlessAuthMessagingOptions } from "../messaging";
|
|
2
|
+
export declare function deliverAuthMessage(messaging: SeamlessAuthMessagingOptions | undefined, delivery: AuthDeliveryInstruction | undefined): Promise<void>;
|
|
3
|
+
export declare function stripDelivery<T extends {
|
|
4
|
+
delivery?: unknown;
|
|
5
|
+
}>(body: T): Omit<T, "delivery">;
|
|
6
|
+
//# sourceMappingURL=deliverAuthMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliverAuthMessage.d.ts","sourceRoot":"","sources":["../../src/internal/deliverAuthMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAEvB,4BAA4B,EAE7B,MAAM,cAAc,CAAC;AA2HtB,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,4BAA4B,GAAG,SAAS,EACnD,QAAQ,EAAE,uBAAuB,GAAG,SAAS,GAC5C,OAAO,CAAC,IAAI,CAAC,CA2Ef;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAG5F"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export type MessagingChannel = "email" | "sms";
|
|
2
|
+
export interface DeliveryResult {
|
|
3
|
+
accepted: boolean;
|
|
4
|
+
provider: string;
|
|
5
|
+
channel: MessagingChannel;
|
|
6
|
+
messageId?: string;
|
|
7
|
+
raw?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface EmailMessage {
|
|
10
|
+
to: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
subject: string;
|
|
13
|
+
text: string;
|
|
14
|
+
html?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SmsMessage {
|
|
17
|
+
to: string;
|
|
18
|
+
from?: string;
|
|
19
|
+
body: string;
|
|
20
|
+
}
|
|
21
|
+
export interface EmailTransport {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
send(message: EmailMessage): Promise<DeliveryResult>;
|
|
24
|
+
}
|
|
25
|
+
export interface SmsTransport {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
send(message: SmsMessage): Promise<DeliveryResult>;
|
|
28
|
+
}
|
|
29
|
+
export interface SendOtpEmailInput {
|
|
30
|
+
to: string;
|
|
31
|
+
token: string;
|
|
32
|
+
from?: string;
|
|
33
|
+
subject?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SendOtpSmsInput {
|
|
36
|
+
to: string;
|
|
37
|
+
token: string | number;
|
|
38
|
+
from?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface SendMagicLinkEmailInput {
|
|
41
|
+
to: string;
|
|
42
|
+
magicLinkUrl: string;
|
|
43
|
+
token?: string;
|
|
44
|
+
from?: string;
|
|
45
|
+
subject?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SendBootstrapInviteEmailInput {
|
|
48
|
+
to: string;
|
|
49
|
+
inviteUrl: string;
|
|
50
|
+
from?: string;
|
|
51
|
+
subject?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AuthMessageOverrideContext {
|
|
54
|
+
appName?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface AuthMessageOverrides {
|
|
57
|
+
otpEmail?: (input: SendOtpEmailInput, defaults: EmailMessage, context: AuthMessageOverrideContext) => EmailMessage;
|
|
58
|
+
otpSms?: (input: SendOtpSmsInput, defaults: SmsMessage, context: AuthMessageOverrideContext) => SmsMessage;
|
|
59
|
+
magicLinkEmail?: (input: SendMagicLinkEmailInput, defaults: EmailMessage, context: AuthMessageOverrideContext) => EmailMessage;
|
|
60
|
+
bootstrapInviteEmail?: (input: SendBootstrapInviteEmailInput, defaults: EmailMessage, context: AuthMessageOverrideContext) => EmailMessage;
|
|
61
|
+
}
|
|
62
|
+
export interface AuthMessagingHandlers {
|
|
63
|
+
sendOtpEmail(input: SendOtpEmailInput): Promise<DeliveryResult>;
|
|
64
|
+
sendOtpSms(input: SendOtpSmsInput): Promise<DeliveryResult>;
|
|
65
|
+
sendMagicLinkEmail(input: SendMagicLinkEmailInput): Promise<DeliveryResult>;
|
|
66
|
+
sendBootstrapInviteEmail(input: SendBootstrapInviteEmailInput): Promise<DeliveryResult>;
|
|
67
|
+
}
|
|
68
|
+
export interface SeamlessAuthMessagingOptions {
|
|
69
|
+
email?: EmailTransport;
|
|
70
|
+
sms?: SmsTransport;
|
|
71
|
+
handlers?: Partial<AuthMessagingHandlers>;
|
|
72
|
+
overrides?: AuthMessageOverrides;
|
|
73
|
+
defaults?: {
|
|
74
|
+
appName?: string;
|
|
75
|
+
emailFrom?: string;
|
|
76
|
+
smsFrom?: string;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export type AuthDeliveryInstruction = {
|
|
80
|
+
kind: "otp_email";
|
|
81
|
+
to: string;
|
|
82
|
+
token: string;
|
|
83
|
+
} | {
|
|
84
|
+
kind: "otp_sms";
|
|
85
|
+
to: string;
|
|
86
|
+
token: string | number;
|
|
87
|
+
} | {
|
|
88
|
+
kind: "magic_link_email";
|
|
89
|
+
to: string;
|
|
90
|
+
token?: string;
|
|
91
|
+
magicLinkUrl: string;
|
|
92
|
+
} | {
|
|
93
|
+
kind: "bootstrap_invite_email";
|
|
94
|
+
to: string;
|
|
95
|
+
token?: string;
|
|
96
|
+
inviteUrl: string;
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=messaging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../src/messaging.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
export interface EnsureCookiesMiddlewareOptions {
|
|
3
|
+
authServerUrl: string;
|
|
4
|
+
cookieDomain?: string;
|
|
5
|
+
accessCookieName: string;
|
|
6
|
+
registrationCookieName: string;
|
|
7
|
+
refreshCookieName: string;
|
|
8
|
+
preAuthCookieName: string;
|
|
9
|
+
cookieSecret: string;
|
|
10
|
+
serviceSecret: string;
|
|
11
|
+
issuer: string;
|
|
12
|
+
audience: string;
|
|
13
|
+
keyId: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
16
|
+
//# sourceMappingURL=ensureCookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureCookies.d.ts","sourceRoot":"","sources":["../../src/middleware/ensureCookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK1D,MAAM,WAAW,8BAA8B;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,8BAA8B,IAmBlC,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,mBA0BrB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
export interface RequireAuthOptions {
|
|
3
|
+
cookieName?: string;
|
|
4
|
+
cookieSecret: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Express middleware that enforces authentication using Seamless Auth cookies.
|
|
8
|
+
*
|
|
9
|
+
* This guard verifies the signed access cookie generated by the Seamless Auth
|
|
10
|
+
* server. If the access cookie is valid and unexpired, the decoded session
|
|
11
|
+
* payload is attached to `req.user` and the request proceeds.
|
|
12
|
+
*
|
|
13
|
+
* If the access cookie is expired or missing *but* a valid refresh cookie is
|
|
14
|
+
* present, the middleware automatically attempts a silent token refresh using
|
|
15
|
+
* the Seamless Auth server. When successful, new session cookies are issued and
|
|
16
|
+
* the request continues with an updated `req.user`.
|
|
17
|
+
*
|
|
18
|
+
* If neither the access token nor refresh token can validate the session,
|
|
19
|
+
* the middleware returns a 401 Unauthorized error and prevents further
|
|
20
|
+
* route execution.
|
|
21
|
+
*
|
|
22
|
+
* ### Responsibilities
|
|
23
|
+
* - Validates the Seamless Auth session access cookie
|
|
24
|
+
* - Attempts refresh-token–based session renewal when necessary
|
|
25
|
+
* - Populates `req.user` with the verified session payload
|
|
26
|
+
* - Handles all cookie rewriting during refresh flows
|
|
27
|
+
* - Acts as a request-level authentication guard for API routes
|
|
28
|
+
*
|
|
29
|
+
* ### Cookie Parameters
|
|
30
|
+
* - **cookieName** — Name of the access cookie that holds the signed session JWT
|
|
31
|
+
* - **refreshCookieName** — Name of the refresh cookie used for silent token refresh
|
|
32
|
+
* - **cookieDomain** — Domain or path value applied to issued cookies
|
|
33
|
+
*
|
|
34
|
+
* ### Example
|
|
35
|
+
* ```ts
|
|
36
|
+
* // Protect a route
|
|
37
|
+
* app.get("/api/me", requireAuth(), (req, res) => {
|
|
38
|
+
* res.json({ user: req.user });
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Custom cookie names (if your Seamless Auth server uses overrides)
|
|
42
|
+
* app.use(
|
|
43
|
+
* "/internal",
|
|
44
|
+
* requireAuth("sa_access", "sa_refresh", "mycompany.com"),
|
|
45
|
+
* internalRouter
|
|
46
|
+
* );
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param cookieName - The access cookie name. Defaults to `"seamless-access"`.
|
|
50
|
+
* @param refreshCookieName - The refresh cookie name used for session rotation. Defaults to `"seamless-refresh"`.
|
|
51
|
+
* @param cookieDomain - Domain or path used when rewriting cookies. Defaults to `"/"`.
|
|
52
|
+
*
|
|
53
|
+
* @returns An Express middleware function that enforces Seamless Auth
|
|
54
|
+
* authentication on incoming requests.
|
|
55
|
+
*/
|
|
56
|
+
export interface RequireAuthOptions {
|
|
57
|
+
cookieName?: string;
|
|
58
|
+
cookieSecret: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Express middleware that enforces authentication
|
|
62
|
+
* using an already-issued Seamless Auth access cookie.
|
|
63
|
+
*
|
|
64
|
+
* NOTE:
|
|
65
|
+
* - This middleware does NOT attempt token refresh.
|
|
66
|
+
* - Refresh is handled upstream by ensureCookies().
|
|
67
|
+
*/
|
|
68
|
+
export declare function requireAuth(opts: RequireAuthOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
69
|
+
//# sourceMappingURL=requireAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI1D,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,IAOjC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAoCjE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { RequestHandler } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Express middleware that enforces role-based authorization for Seamless Auth.
|
|
4
|
+
*
|
|
5
|
+
* This middleware assumes `requireAuth()` has already:
|
|
6
|
+
* - authenticated the request
|
|
7
|
+
* - populated `req.user` with the authenticated session payload
|
|
8
|
+
*
|
|
9
|
+
* `requireRole` performs **authorization only**. It does not inspect cookies,
|
|
10
|
+
* verify tokens, or read environment variables.
|
|
11
|
+
*
|
|
12
|
+
* If any of the required roles are present on the user, access is granted.
|
|
13
|
+
* Otherwise, a 403 Forbidden response is returned.
|
|
14
|
+
*
|
|
15
|
+
* * ### Example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Require a single role
|
|
18
|
+
* app.get("/admin/users",
|
|
19
|
+
* requireAuth(),
|
|
20
|
+
* requireRole("admin"),
|
|
21
|
+
* (req, res) => {
|
|
22
|
+
* res.send("Welcome admin!");
|
|
23
|
+
* }
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* // Allow any of multiple roles
|
|
27
|
+
* app.post("/settings",
|
|
28
|
+
* requireAuth(),
|
|
29
|
+
* requireRole(["admin", "supervisor"]),
|
|
30
|
+
* updateSettingsHandler
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* @param requiredRoles - A role or list of roles required to access the route
|
|
34
|
+
*/
|
|
35
|
+
export declare function requireRole(requiredRoles: string | string[]): RequestHandler;
|
|
36
|
+
//# sourceMappingURL=requireRole.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requireRole.d.ts","sourceRoot":"","sources":["../../src/middleware/requireRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,CAiC5E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamless-auth/express",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Express adapter for Seamless Auth passwordless authentication",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -23,10 +23,12 @@
|
|
|
23
23
|
"node": ">=18"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "
|
|
26
|
+
"build": "npm run build:js && npm run build:types",
|
|
27
|
+
"build:js": "tsup src/index.ts --format esm --out-dir dist --splitting",
|
|
28
|
+
"build:types": "tsc -p tsconfig.build.json",
|
|
27
29
|
"dev": "tsc --watch",
|
|
28
|
-
"test": "npm run build && NODE_OPTIONS=--experimental-vm-modules jest",
|
|
29
|
-
"test:watch": "npm run build && NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
30
|
+
"test": "npm --prefix ../core run build && npm run build && NODE_OPTIONS=--experimental-vm-modules jest",
|
|
31
|
+
"test:watch": "npm --prefix ../core run build && npm run build && NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
30
32
|
},
|
|
31
33
|
"repository": {
|
|
32
34
|
"type": "git",
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
"express": ">=4.18.0"
|
|
38
40
|
},
|
|
39
41
|
"dependencies": {
|
|
40
|
-
"@seamless-auth/core": "^0.
|
|
42
|
+
"@seamless-auth/core": "^0.4.1",
|
|
41
43
|
"cookie-parser": "^1.4.6",
|
|
42
44
|
"jsonwebtoken": "^9.0.3"
|
|
43
45
|
},
|