@ttoss/http-server-auth 0.4.0 → 0.6.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 +45 -0
- package/dist/index.cjs +94 -5
- package/dist/index.d.cts +45 -2
- package/dist/index.d.mts +45 -2
- package/dist/index.mjs +77 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -125,6 +125,51 @@ type AuthenticatedUser = {
|
|
|
125
125
|
|
|
126
126
|
Both `apiToken.lookup` and `jwt.mapPayload` receive the Koa `ctx` as a second argument, enabling request-scoped work (e.g. reading a per-request connection off `ctx.db` or updating `lastUsedAt`). The single-argument signatures keep working — `ctx` is purely additive.
|
|
127
127
|
|
|
128
|
+
## Email and password flows
|
|
129
|
+
|
|
130
|
+
`emailAuth()` mounts the credential flows — password sign-up and sign-in, magic
|
|
131
|
+
links, mailed numeric codes, address confirmation and password reset — as a Koa
|
|
132
|
+
`Router`. It is a thin adapter over `createEmailAuthHandlers` from
|
|
133
|
+
[`@ttoss/auth-core`](https://ttoss.dev/docs/modules/packages/auth-core), which
|
|
134
|
+
owns the mechanics; this package only maps `ctx` to and from it.
|
|
135
|
+
|
|
136
|
+
`modes` decides which routes exist, so an app that signs users in with a mailed
|
|
137
|
+
code alone never exposes a password endpoint.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { App, bodyParser } from '@ttoss/http-server';
|
|
141
|
+
import { authMiddleware, emailAuth } from '@ttoss/http-server-auth';
|
|
142
|
+
|
|
143
|
+
const app = new App();
|
|
144
|
+
app.use(bodyParser());
|
|
145
|
+
|
|
146
|
+
app.use(
|
|
147
|
+
emailAuth({
|
|
148
|
+
modes: ['emailCode'],
|
|
149
|
+
prefix: '/v1',
|
|
150
|
+
userStore,
|
|
151
|
+
oneTimeTokenStore,
|
|
152
|
+
issueSession: (user) => issueSession(user),
|
|
153
|
+
sendEmail: async ({ to, token }) => {
|
|
154
|
+
await ses.send(buildCodeEmail({ to, code: token }));
|
|
155
|
+
},
|
|
156
|
+
}).routes()
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Mount after, so the sign-in routes stay reachable without a token.
|
|
160
|
+
app.use(authMiddleware({ strategies: ['jwt'], jwt: { secret } }));
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every route is a `POST`, link redemptions included: the token arrives in the body
|
|
164
|
+
from the page the user landed on, which keeps it out of server logs and out of
|
|
165
|
+
the `Referer` header. The engine also reads a token from the query string, so a
|
|
166
|
+
`GET` redemption is available to an app that wants one.
|
|
167
|
+
|
|
168
|
+
Mount it before `authMiddleware` (or exempt its paths) — a client calls these
|
|
169
|
+
routes precisely because it has no token yet. `prefix` applies to every mounted
|
|
170
|
+
path when the app versions its API; individual paths are overridable through
|
|
171
|
+
`paths`.
|
|
172
|
+
|
|
128
173
|
## OAuth authorization server
|
|
129
174
|
|
|
130
175
|
The package also ships `oauthServer()` — a Koa `Router` that issues tokens (`/authorize`, `/token`, `/register`, discovery), a thin adapter over `createOAuthHandlers` from [`@ttoss/auth-core`](https://ttoss.dev/docs/modules/packages/auth-core). Pair it with the `oauth` verification strategy above when one deployment both issues and verifies. See the [OAuth Authorization Server](https://ttoss.dev/docs/engineering/guidelines/oauth-authorization-server) guideline.
|
package/dist/index.cjs
CHANGED
|
@@ -175,12 +175,17 @@ var authMiddleware = options => {
|
|
|
175
175
|
};
|
|
176
176
|
|
|
177
177
|
//#endregion
|
|
178
|
-
//#region src/
|
|
178
|
+
//#region src/koaAdapter.ts
|
|
179
|
+
/**
|
|
180
|
+
* Translation between Koa's `ctx` and the framework-agnostic request/response
|
|
181
|
+
* shapes the `@ttoss/auth-core` engines speak. Shared by every adapter in this
|
|
182
|
+
* package so the mapping exists once.
|
|
183
|
+
*/
|
|
179
184
|
/** Normalizes a Koa query/header value (which may be an array) to a string. */
|
|
180
185
|
var firstValue = value => {
|
|
181
186
|
return Array.isArray(value) ? value[0] : value;
|
|
182
187
|
};
|
|
183
|
-
var
|
|
188
|
+
var toAuthRequest = ctx => {
|
|
184
189
|
const query = {};
|
|
185
190
|
for (const [key, value] of Object.entries(ctx.query)) query[key] = firstValue(value);
|
|
186
191
|
const headers = {};
|
|
@@ -191,6 +196,10 @@ var toOAuthRequest = ctx => {
|
|
|
191
196
|
headers
|
|
192
197
|
};
|
|
193
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* Applies an engine response to `ctx`. A response carries either a JSON body
|
|
201
|
+
* with a status or a redirect, never both.
|
|
202
|
+
*/
|
|
194
203
|
var applyResponse = (ctx, res) => {
|
|
195
204
|
if (res.redirect !== void 0) {
|
|
196
205
|
ctx.redirect(res.redirect);
|
|
@@ -199,6 +208,67 @@ var applyResponse = (ctx, res) => {
|
|
|
199
208
|
ctx.status = res.status;
|
|
200
209
|
ctx.body = res.body;
|
|
201
210
|
};
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/emailAuth.ts
|
|
214
|
+
/**
|
|
215
|
+
* Mounts the email and password credential flows as a Koa `Router`, adapting
|
|
216
|
+
* the runner-agnostic engine from `@ttoss/auth-core`. Which routes appear is
|
|
217
|
+
* decided by `modes`, so an app that only signs users in with a mailed code
|
|
218
|
+
* never exposes a password endpoint.
|
|
219
|
+
*
|
|
220
|
+
* Every route is a `POST`, including the link redemptions: the token arrives in
|
|
221
|
+
* the body from the page the user landed on, which keeps it out of server logs
|
|
222
|
+
* and out of the `Referer` header. The engine also reads a token from the query
|
|
223
|
+
* string, so a `GET` redemption can be added by an application that wants one.
|
|
224
|
+
*
|
|
225
|
+
* Mount it **before** `authMiddleware`, or exempt its paths — the sign-in
|
|
226
|
+
* routes are what a client calls precisely because it has no token yet.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* import { App, bodyParser } from '@ttoss/http-server';
|
|
231
|
+
* import { emailAuth } from '@ttoss/http-server-auth';
|
|
232
|
+
*
|
|
233
|
+
* const app = new App();
|
|
234
|
+
* app.use(bodyParser());
|
|
235
|
+
* app.use(
|
|
236
|
+
* emailAuth({
|
|
237
|
+
* modes: ['emailCode'],
|
|
238
|
+
* userStore,
|
|
239
|
+
* oneTimeTokenStore,
|
|
240
|
+
* issueSession: (user) => issueSession(user),
|
|
241
|
+
* sendEmail: async ({ to, token }) => {
|
|
242
|
+
* await ses.send(buildCodeEmail({ to, code: token }));
|
|
243
|
+
* },
|
|
244
|
+
* }).routes()
|
|
245
|
+
* );
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
var emailAuth = options => {
|
|
249
|
+
const handlers = (0, _ttoss_auth_core.createEmailAuthHandlers)(options);
|
|
250
|
+
const router = new _ttoss_http_server.Router();
|
|
251
|
+
const prefix = options.prefix ?? "";
|
|
252
|
+
const mount = (path, handler) => {
|
|
253
|
+
if (!handler) return;
|
|
254
|
+
router.post(`${prefix}${path}`, async ctx => {
|
|
255
|
+
applyResponse(ctx, await handler(toAuthRequest(ctx)));
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
mount(handlers.paths.signUp, handlers.signUp);
|
|
259
|
+
mount(handlers.paths.signIn, handlers.signIn);
|
|
260
|
+
mount(handlers.paths.sendMagicLink, handlers.sendMagicLink);
|
|
261
|
+
mount(handlers.paths.verifyMagicLink, handlers.verifyMagicLink);
|
|
262
|
+
mount(handlers.paths.sendEmailCode, handlers.sendEmailCode);
|
|
263
|
+
mount(handlers.paths.verifyEmailCode, handlers.verifyEmailCode);
|
|
264
|
+
mount(handlers.paths.verifyEmail, handlers.verifyEmail);
|
|
265
|
+
mount(handlers.paths.requestPasswordReset, handlers.requestPasswordReset);
|
|
266
|
+
mount(handlers.paths.resetPassword, handlers.resetPassword);
|
|
267
|
+
return router;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/oauthServer.ts
|
|
202
272
|
/**
|
|
203
273
|
* Mounts an OAuth 2.1 Authorization Server (issuing tokens) as a Koa `Router`,
|
|
204
274
|
* adapting the runner-agnostic engine from `@ttoss/auth-core`. Exposes the
|
|
@@ -229,13 +299,13 @@ var oauthServer = options => {
|
|
|
229
299
|
applyResponse(ctx, prm);
|
|
230
300
|
});
|
|
231
301
|
router.get(engine.paths.authorize, async ctx => {
|
|
232
|
-
applyResponse(ctx, await engine.authorize(
|
|
302
|
+
applyResponse(ctx, await engine.authorize(toAuthRequest(ctx)));
|
|
233
303
|
});
|
|
234
304
|
router.post(engine.paths.token, async ctx => {
|
|
235
|
-
applyResponse(ctx, await engine.token(
|
|
305
|
+
applyResponse(ctx, await engine.token(toAuthRequest(ctx)));
|
|
236
306
|
});
|
|
237
307
|
router.post(engine.paths.register, async ctx => {
|
|
238
|
-
applyResponse(ctx, await engine.register(
|
|
308
|
+
applyResponse(ctx, await engine.register(toAuthRequest(ctx)));
|
|
239
309
|
});
|
|
240
310
|
return router;
|
|
241
311
|
};
|
|
@@ -275,6 +345,12 @@ var requireAuth = options => {
|
|
|
275
345
|
|
|
276
346
|
//#endregion
|
|
277
347
|
exports.authMiddleware = authMiddleware;
|
|
348
|
+
Object.defineProperty(exports, 'createEmailAuthHandlers', {
|
|
349
|
+
enumerable: true,
|
|
350
|
+
get: function () {
|
|
351
|
+
return _ttoss_auth_core.createEmailAuthHandlers;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
278
354
|
Object.defineProperty(exports, 'createOAuthHandlers', {
|
|
279
355
|
enumerable: true,
|
|
280
356
|
get: function () {
|
|
@@ -282,6 +358,13 @@ Object.defineProperty(exports, 'createOAuthHandlers', {
|
|
|
282
358
|
}
|
|
283
359
|
});
|
|
284
360
|
exports.createProtectedResourceMetadataMiddleware = createProtectedResourceMetadataMiddleware;
|
|
361
|
+
exports.emailAuth = emailAuth;
|
|
362
|
+
Object.defineProperty(exports, 'emailAuthErrorCodes', {
|
|
363
|
+
enumerable: true,
|
|
364
|
+
get: function () {
|
|
365
|
+
return _ttoss_auth_core.emailAuthErrorCodes;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
285
368
|
Object.defineProperty(exports, 'getWwwAuthenticateHeader', {
|
|
286
369
|
enumerable: true,
|
|
287
370
|
get: function () {
|
|
@@ -289,5 +372,11 @@ Object.defineProperty(exports, 'getWwwAuthenticateHeader', {
|
|
|
289
372
|
}
|
|
290
373
|
});
|
|
291
374
|
exports.isOriginAllowed = isOriginAllowed;
|
|
375
|
+
Object.defineProperty(exports, 'normalizeEmail', {
|
|
376
|
+
enumerable: true,
|
|
377
|
+
get: function () {
|
|
378
|
+
return _ttoss_auth_core.normalizeEmail;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
292
381
|
exports.oauthServer = oauthServer;
|
|
293
382
|
exports.requireAuth = requireAuth;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { Context, Middleware, Next, Router } from "@ttoss/http-server";
|
|
3
|
-
import { AuthCodeStore, AuthorizeRequest, ClientStore, IssueTokensArgs, IssuedTokens, OAuthClient, OAuthClientMetadata, OAuthHandlers, OAuthServerOptions, OAuthServerOptions as OAuthServerOptions$1, OnAuthorizeArgs, OnAuthorizeResult, OnRefreshTokenArgs, OnRefreshTokenResult, StoredAuthorizationCode, createOAuthHandlers, getWwwAuthenticateHeader } from "@ttoss/auth-core";
|
|
3
|
+
import { AuthCodeStore, AuthorizeRequest, ClientStore, EmailAuthDelivery, EmailAuthErrorCode, EmailAuthHandler, EmailAuthHandlers, EmailAuthHooks, EmailAuthMode, EmailAuthOptions, EmailAuthOptions as EmailAuthOptions$1, EmailAuthPaths, EmailAuthSession, EmailAuthTtl, EmailAuthUser, EmailAuthUserStore, EmailCodeOptions, IssueTokensArgs, IssuedTokens, OAuthClient, OAuthClientMetadata, OAuthHandlers, OAuthServerOptions, OAuthServerOptions as OAuthServerOptions$1, OnAuthorizeArgs, OnAuthorizeResult, OnRefreshTokenArgs, OnRefreshTokenResult, OneTimeTokenPurpose, OneTimeTokenStore, PasswordOptions, RequestRateLimit, RequestRateLimitStore, StoredAuthorizationCode, StoredOneTimeToken, createEmailAuthHandlers, createOAuthHandlers, emailAuthErrorCodes, getWwwAuthenticateHeader, normalizeEmail } from "@ttoss/auth-core";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
type AuthenticatedUser = {
|
|
@@ -93,6 +93,49 @@ type AuthMiddlewareOptions = {
|
|
|
93
93
|
*/
|
|
94
94
|
declare const authMiddleware: (options: AuthMiddlewareOptions) => (ctx: Context, next: Next) => Promise<void>;
|
|
95
95
|
//#endregion
|
|
96
|
+
//#region src/emailAuth.d.ts
|
|
97
|
+
/**
|
|
98
|
+
* Mounts the email and password credential flows as a Koa `Router`, adapting
|
|
99
|
+
* the runner-agnostic engine from `@ttoss/auth-core`. Which routes appear is
|
|
100
|
+
* decided by `modes`, so an app that only signs users in with a mailed code
|
|
101
|
+
* never exposes a password endpoint.
|
|
102
|
+
*
|
|
103
|
+
* Every route is a `POST`, including the link redemptions: the token arrives in
|
|
104
|
+
* the body from the page the user landed on, which keeps it out of server logs
|
|
105
|
+
* and out of the `Referer` header. The engine also reads a token from the query
|
|
106
|
+
* string, so a `GET` redemption can be added by an application that wants one.
|
|
107
|
+
*
|
|
108
|
+
* Mount it **before** `authMiddleware`, or exempt its paths — the sign-in
|
|
109
|
+
* routes are what a client calls precisely because it has no token yet.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { App, bodyParser } from '@ttoss/http-server';
|
|
114
|
+
* import { emailAuth } from '@ttoss/http-server-auth';
|
|
115
|
+
*
|
|
116
|
+
* const app = new App();
|
|
117
|
+
* app.use(bodyParser());
|
|
118
|
+
* app.use(
|
|
119
|
+
* emailAuth({
|
|
120
|
+
* modes: ['emailCode'],
|
|
121
|
+
* userStore,
|
|
122
|
+
* oneTimeTokenStore,
|
|
123
|
+
* issueSession: (user) => issueSession(user),
|
|
124
|
+
* sendEmail: async ({ to, token }) => {
|
|
125
|
+
* await ses.send(buildCodeEmail({ to, code: token }));
|
|
126
|
+
* },
|
|
127
|
+
* }).routes()
|
|
128
|
+
* );
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
declare const emailAuth: (options: EmailAuthOptions$1 & {
|
|
132
|
+
/**
|
|
133
|
+
* Prefix applied to every mounted path, e.g. `/v1`. Useful when the app
|
|
134
|
+
* versions its API and the engine's default paths are otherwise fine.
|
|
135
|
+
*/
|
|
136
|
+
prefix?: string;
|
|
137
|
+
}) => Router;
|
|
138
|
+
//#endregion
|
|
96
139
|
//#region src/oauthServer.d.ts
|
|
97
140
|
/**
|
|
98
141
|
* Mounts an OAuth 2.1 Authorization Server (issuing tokens) as a Koa `Router`,
|
|
@@ -141,4 +184,4 @@ declare const isOriginAllowed: (origin: string, allowedOrigins: Array<string | R
|
|
|
141
184
|
*/
|
|
142
185
|
declare const requireAuth: (options: AuthMiddlewareOptions) => ((ctx: Context, next: Next) => Promise<void>);
|
|
143
186
|
//#endregion
|
|
144
|
-
export { type ApiTokenOptions, type AuthCodeStore, type AuthMiddlewareOptions, type AuthStrategy, type AuthenticatedUser, type AuthorizeRequest, type ClientStore, type IssueTokensArgs, type IssuedTokens, type JwtOptions, type OAuthClient, type OAuthClientMetadata, type OAuthHandlers, type OAuthOptions, type OAuthServerOptions, type OnAuthorizeArgs, type OnAuthorizeResult, type OnRefreshTokenArgs, type OnRefreshTokenResult, type StoredAuthorizationCode, type SystemOptions, authMiddleware, createOAuthHandlers, createProtectedResourceMetadataMiddleware, getWwwAuthenticateHeader, isOriginAllowed, oauthServer, requireAuth };
|
|
187
|
+
export { type ApiTokenOptions, type AuthCodeStore, type AuthMiddlewareOptions, type AuthStrategy, type AuthenticatedUser, type AuthorizeRequest, type ClientStore, type EmailAuthDelivery, type EmailAuthErrorCode, type EmailAuthHandler, type EmailAuthHandlers, type EmailAuthHooks, type EmailAuthMode, type EmailAuthOptions, type EmailAuthPaths, type EmailAuthSession, type EmailAuthTtl, type EmailAuthUser, type EmailAuthUserStore, type EmailCodeOptions, type IssueTokensArgs, type IssuedTokens, type JwtOptions, type OAuthClient, type OAuthClientMetadata, type OAuthHandlers, type OAuthOptions, type OAuthServerOptions, type OnAuthorizeArgs, type OnAuthorizeResult, type OnRefreshTokenArgs, type OnRefreshTokenResult, type OneTimeTokenPurpose, type OneTimeTokenStore, type PasswordOptions, type RequestRateLimit, type RequestRateLimitStore, type StoredAuthorizationCode, type StoredOneTimeToken, type SystemOptions, authMiddleware, createEmailAuthHandlers, createOAuthHandlers, createProtectedResourceMetadataMiddleware, emailAuth, emailAuthErrorCodes, getWwwAuthenticateHeader, isOriginAllowed, normalizeEmail, oauthServer, requireAuth };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { AuthCodeStore, AuthorizeRequest, ClientStore, IssueTokensArgs, IssuedTokens, OAuthClient, OAuthClientMetadata, OAuthHandlers, OAuthServerOptions, OAuthServerOptions as OAuthServerOptions$1, OnAuthorizeArgs, OnAuthorizeResult, OnRefreshTokenArgs, OnRefreshTokenResult, StoredAuthorizationCode, createOAuthHandlers, getWwwAuthenticateHeader } from "@ttoss/auth-core";
|
|
2
|
+
import { AuthCodeStore, AuthorizeRequest, ClientStore, EmailAuthDelivery, EmailAuthErrorCode, EmailAuthHandler, EmailAuthHandlers, EmailAuthHooks, EmailAuthMode, EmailAuthOptions, EmailAuthOptions as EmailAuthOptions$1, EmailAuthPaths, EmailAuthSession, EmailAuthTtl, EmailAuthUser, EmailAuthUserStore, EmailCodeOptions, IssueTokensArgs, IssuedTokens, OAuthClient, OAuthClientMetadata, OAuthHandlers, OAuthServerOptions, OAuthServerOptions as OAuthServerOptions$1, OnAuthorizeArgs, OnAuthorizeResult, OnRefreshTokenArgs, OnRefreshTokenResult, OneTimeTokenPurpose, OneTimeTokenStore, PasswordOptions, RequestRateLimit, RequestRateLimitStore, StoredAuthorizationCode, StoredOneTimeToken, createEmailAuthHandlers, createOAuthHandlers, emailAuthErrorCodes, getWwwAuthenticateHeader, normalizeEmail } from "@ttoss/auth-core";
|
|
3
3
|
import { Context, Middleware, Next, Router } from "@ttoss/http-server";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
@@ -93,6 +93,49 @@ type AuthMiddlewareOptions = {
|
|
|
93
93
|
*/
|
|
94
94
|
declare const authMiddleware: (options: AuthMiddlewareOptions) => (ctx: Context, next: Next) => Promise<void>;
|
|
95
95
|
//#endregion
|
|
96
|
+
//#region src/emailAuth.d.ts
|
|
97
|
+
/**
|
|
98
|
+
* Mounts the email and password credential flows as a Koa `Router`, adapting
|
|
99
|
+
* the runner-agnostic engine from `@ttoss/auth-core`. Which routes appear is
|
|
100
|
+
* decided by `modes`, so an app that only signs users in with a mailed code
|
|
101
|
+
* never exposes a password endpoint.
|
|
102
|
+
*
|
|
103
|
+
* Every route is a `POST`, including the link redemptions: the token arrives in
|
|
104
|
+
* the body from the page the user landed on, which keeps it out of server logs
|
|
105
|
+
* and out of the `Referer` header. The engine also reads a token from the query
|
|
106
|
+
* string, so a `GET` redemption can be added by an application that wants one.
|
|
107
|
+
*
|
|
108
|
+
* Mount it **before** `authMiddleware`, or exempt its paths — the sign-in
|
|
109
|
+
* routes are what a client calls precisely because it has no token yet.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { App, bodyParser } from '@ttoss/http-server';
|
|
114
|
+
* import { emailAuth } from '@ttoss/http-server-auth';
|
|
115
|
+
*
|
|
116
|
+
* const app = new App();
|
|
117
|
+
* app.use(bodyParser());
|
|
118
|
+
* app.use(
|
|
119
|
+
* emailAuth({
|
|
120
|
+
* modes: ['emailCode'],
|
|
121
|
+
* userStore,
|
|
122
|
+
* oneTimeTokenStore,
|
|
123
|
+
* issueSession: (user) => issueSession(user),
|
|
124
|
+
* sendEmail: async ({ to, token }) => {
|
|
125
|
+
* await ses.send(buildCodeEmail({ to, code: token }));
|
|
126
|
+
* },
|
|
127
|
+
* }).routes()
|
|
128
|
+
* );
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
declare const emailAuth: (options: EmailAuthOptions$1 & {
|
|
132
|
+
/**
|
|
133
|
+
* Prefix applied to every mounted path, e.g. `/v1`. Useful when the app
|
|
134
|
+
* versions its API and the engine's default paths are otherwise fine.
|
|
135
|
+
*/
|
|
136
|
+
prefix?: string;
|
|
137
|
+
}) => Router;
|
|
138
|
+
//#endregion
|
|
96
139
|
//#region src/oauthServer.d.ts
|
|
97
140
|
/**
|
|
98
141
|
* Mounts an OAuth 2.1 Authorization Server (issuing tokens) as a Koa `Router`,
|
|
@@ -141,4 +184,4 @@ declare const isOriginAllowed: (origin: string, allowedOrigins: Array<string | R
|
|
|
141
184
|
*/
|
|
142
185
|
declare const requireAuth: (options: AuthMiddlewareOptions) => ((ctx: Context, next: Next) => Promise<void>);
|
|
143
186
|
//#endregion
|
|
144
|
-
export { type ApiTokenOptions, type AuthCodeStore, type AuthMiddlewareOptions, type AuthStrategy, type AuthenticatedUser, type AuthorizeRequest, type ClientStore, type IssueTokensArgs, type IssuedTokens, type JwtOptions, type OAuthClient, type OAuthClientMetadata, type OAuthHandlers, type OAuthOptions, type OAuthServerOptions, type OnAuthorizeArgs, type OnAuthorizeResult, type OnRefreshTokenArgs, type OnRefreshTokenResult, type StoredAuthorizationCode, type SystemOptions, authMiddleware, createOAuthHandlers, createProtectedResourceMetadataMiddleware, getWwwAuthenticateHeader, isOriginAllowed, oauthServer, requireAuth };
|
|
187
|
+
export { type ApiTokenOptions, type AuthCodeStore, type AuthMiddlewareOptions, type AuthStrategy, type AuthenticatedUser, type AuthorizeRequest, type ClientStore, type EmailAuthDelivery, type EmailAuthErrorCode, type EmailAuthHandler, type EmailAuthHandlers, type EmailAuthHooks, type EmailAuthMode, type EmailAuthOptions, type EmailAuthPaths, type EmailAuthSession, type EmailAuthTtl, type EmailAuthUser, type EmailAuthUserStore, type EmailCodeOptions, type IssueTokensArgs, type IssuedTokens, type JwtOptions, type OAuthClient, type OAuthClientMetadata, type OAuthHandlers, type OAuthOptions, type OAuthServerOptions, type OnAuthorizeArgs, type OnAuthorizeResult, type OnRefreshTokenArgs, type OnRefreshTokenResult, type OneTimeTokenPurpose, type OneTimeTokenStore, type PasswordOptions, type RequestRateLimit, type RequestRateLimitStore, type StoredAuthorizationCode, type StoredOneTimeToken, type SystemOptions, authMiddleware, createEmailAuthHandlers, createOAuthHandlers, createProtectedResourceMetadataMiddleware, emailAuth, emailAuthErrorCodes, getWwwAuthenticateHeader, isOriginAllowed, normalizeEmail, oauthServer, requireAuth };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import { createOAuthHandlers, createOAuthHandlers as createOAuthHandlers$1, getWwwAuthenticateHeader, hashApiToken, verifyJwt } from "@ttoss/auth-core";
|
|
3
|
+
import { createEmailAuthHandlers, createEmailAuthHandlers as createEmailAuthHandlers$1, createOAuthHandlers, createOAuthHandlers as createOAuthHandlers$1, emailAuthErrorCodes, getWwwAuthenticateHeader, hashApiToken, normalizeEmail, verifyJwt } from "@ttoss/auth-core";
|
|
4
4
|
import { Router } from "@ttoss/http-server";
|
|
5
5
|
|
|
6
6
|
//#region src/origin.ts
|
|
@@ -144,12 +144,17 @@ var authMiddleware = options => {
|
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
//#endregion
|
|
147
|
-
//#region src/
|
|
147
|
+
//#region src/koaAdapter.ts
|
|
148
|
+
/**
|
|
149
|
+
* Translation between Koa's `ctx` and the framework-agnostic request/response
|
|
150
|
+
* shapes the `@ttoss/auth-core` engines speak. Shared by every adapter in this
|
|
151
|
+
* package so the mapping exists once.
|
|
152
|
+
*/
|
|
148
153
|
/** Normalizes a Koa query/header value (which may be an array) to a string. */
|
|
149
154
|
var firstValue = value => {
|
|
150
155
|
return Array.isArray(value) ? value[0] : value;
|
|
151
156
|
};
|
|
152
|
-
var
|
|
157
|
+
var toAuthRequest = ctx => {
|
|
153
158
|
const query = {};
|
|
154
159
|
for (const [key, value] of Object.entries(ctx.query)) query[key] = firstValue(value);
|
|
155
160
|
const headers = {};
|
|
@@ -160,6 +165,10 @@ var toOAuthRequest = ctx => {
|
|
|
160
165
|
headers
|
|
161
166
|
};
|
|
162
167
|
};
|
|
168
|
+
/**
|
|
169
|
+
* Applies an engine response to `ctx`. A response carries either a JSON body
|
|
170
|
+
* with a status or a redirect, never both.
|
|
171
|
+
*/
|
|
163
172
|
var applyResponse = (ctx, res) => {
|
|
164
173
|
if (res.redirect !== void 0) {
|
|
165
174
|
ctx.redirect(res.redirect);
|
|
@@ -168,6 +177,67 @@ var applyResponse = (ctx, res) => {
|
|
|
168
177
|
ctx.status = res.status;
|
|
169
178
|
ctx.body = res.body;
|
|
170
179
|
};
|
|
180
|
+
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/emailAuth.ts
|
|
183
|
+
/**
|
|
184
|
+
* Mounts the email and password credential flows as a Koa `Router`, adapting
|
|
185
|
+
* the runner-agnostic engine from `@ttoss/auth-core`. Which routes appear is
|
|
186
|
+
* decided by `modes`, so an app that only signs users in with a mailed code
|
|
187
|
+
* never exposes a password endpoint.
|
|
188
|
+
*
|
|
189
|
+
* Every route is a `POST`, including the link redemptions: the token arrives in
|
|
190
|
+
* the body from the page the user landed on, which keeps it out of server logs
|
|
191
|
+
* and out of the `Referer` header. The engine also reads a token from the query
|
|
192
|
+
* string, so a `GET` redemption can be added by an application that wants one.
|
|
193
|
+
*
|
|
194
|
+
* Mount it **before** `authMiddleware`, or exempt its paths — the sign-in
|
|
195
|
+
* routes are what a client calls precisely because it has no token yet.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* import { App, bodyParser } from '@ttoss/http-server';
|
|
200
|
+
* import { emailAuth } from '@ttoss/http-server-auth';
|
|
201
|
+
*
|
|
202
|
+
* const app = new App();
|
|
203
|
+
* app.use(bodyParser());
|
|
204
|
+
* app.use(
|
|
205
|
+
* emailAuth({
|
|
206
|
+
* modes: ['emailCode'],
|
|
207
|
+
* userStore,
|
|
208
|
+
* oneTimeTokenStore,
|
|
209
|
+
* issueSession: (user) => issueSession(user),
|
|
210
|
+
* sendEmail: async ({ to, token }) => {
|
|
211
|
+
* await ses.send(buildCodeEmail({ to, code: token }));
|
|
212
|
+
* },
|
|
213
|
+
* }).routes()
|
|
214
|
+
* );
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
var emailAuth = options => {
|
|
218
|
+
const handlers = createEmailAuthHandlers$1(options);
|
|
219
|
+
const router = new Router();
|
|
220
|
+
const prefix = options.prefix ?? "";
|
|
221
|
+
const mount = (path, handler) => {
|
|
222
|
+
if (!handler) return;
|
|
223
|
+
router.post(`${prefix}${path}`, async ctx => {
|
|
224
|
+
applyResponse(ctx, await handler(toAuthRequest(ctx)));
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
mount(handlers.paths.signUp, handlers.signUp);
|
|
228
|
+
mount(handlers.paths.signIn, handlers.signIn);
|
|
229
|
+
mount(handlers.paths.sendMagicLink, handlers.sendMagicLink);
|
|
230
|
+
mount(handlers.paths.verifyMagicLink, handlers.verifyMagicLink);
|
|
231
|
+
mount(handlers.paths.sendEmailCode, handlers.sendEmailCode);
|
|
232
|
+
mount(handlers.paths.verifyEmailCode, handlers.verifyEmailCode);
|
|
233
|
+
mount(handlers.paths.verifyEmail, handlers.verifyEmail);
|
|
234
|
+
mount(handlers.paths.requestPasswordReset, handlers.requestPasswordReset);
|
|
235
|
+
mount(handlers.paths.resetPassword, handlers.resetPassword);
|
|
236
|
+
return router;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/oauthServer.ts
|
|
171
241
|
/**
|
|
172
242
|
* Mounts an OAuth 2.1 Authorization Server (issuing tokens) as a Koa `Router`,
|
|
173
243
|
* adapting the runner-agnostic engine from `@ttoss/auth-core`. Exposes the
|
|
@@ -198,13 +268,13 @@ var oauthServer = options => {
|
|
|
198
268
|
applyResponse(ctx, prm);
|
|
199
269
|
});
|
|
200
270
|
router.get(engine.paths.authorize, async ctx => {
|
|
201
|
-
applyResponse(ctx, await engine.authorize(
|
|
271
|
+
applyResponse(ctx, await engine.authorize(toAuthRequest(ctx)));
|
|
202
272
|
});
|
|
203
273
|
router.post(engine.paths.token, async ctx => {
|
|
204
|
-
applyResponse(ctx, await engine.token(
|
|
274
|
+
applyResponse(ctx, await engine.token(toAuthRequest(ctx)));
|
|
205
275
|
});
|
|
206
276
|
router.post(engine.paths.register, async ctx => {
|
|
207
|
-
applyResponse(ctx, await engine.register(
|
|
277
|
+
applyResponse(ctx, await engine.register(toAuthRequest(ctx)));
|
|
208
278
|
});
|
|
209
279
|
return router;
|
|
210
280
|
};
|
|
@@ -243,4 +313,4 @@ var requireAuth = options => {
|
|
|
243
313
|
};
|
|
244
314
|
|
|
245
315
|
//#endregion
|
|
246
|
-
export { authMiddleware, createOAuthHandlers, createProtectedResourceMetadataMiddleware, getWwwAuthenticateHeader, isOriginAllowed, oauthServer, requireAuth };
|
|
316
|
+
export { authMiddleware, createEmailAuthHandlers, createOAuthHandlers, createProtectedResourceMetadataMiddleware, emailAuth, emailAuthErrorCodes, getWwwAuthenticateHeader, isOriginAllowed, normalizeEmail, oauthServer, requireAuth };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/http-server-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Authentication middleware for @ttoss/http-server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ttoss/auth-core": "^0.
|
|
35
|
+
"@ttoss/auth-core": "^0.13.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/koa": "^3.0.3",
|