@antelopejs/interface-auth 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -14
- package/dist/index.d.ts +18 -7
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/tests/class-decorator.test.d.ts +1 -0
- package/dist/tests/class-decorator.test.js +20 -0
- package/dist/tests/class-decorator.test.js.map +1 -0
- package/docs/1.introduction.md +23 -19
- package/docs/2.authentication.md +33 -23
- package/docs/3.token-handling.md +43 -31
- package/docs/4.parameter-decoration.md +24 -20
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@antelopejs/interface-auth`
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/@antelopejs/interface-auth"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-auth?style=for-the-badge&labelColor=000000&color=000000"></a>
|
|
5
|
-
<a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-auth?style=for-the-badge&labelColor=000000&color=000000"></a>
|
|
6
|
-
<a href="https://discord.gg/
|
|
7
|
-
<a href="https://interfaces.antelopejs.com/docs/auth"><img alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
|
|
4
|
+
<a href="https://www.npmjs.com/package/@antelopejs/interface-auth"><img data-illustration alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-auth?style=for-the-badge&labelColor=000000&color=000000"></a>
|
|
5
|
+
<a href="./LICENSE"><img data-illustration alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-auth?style=for-the-badge&labelColor=000000&color=000000"></a>
|
|
6
|
+
<a href="https://discord.gg/g4pFt6eXqu"><img data-illustration alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
|
|
7
|
+
<a href="https://interfaces.antelopejs.com/docs/auth"><img data-illustration alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
The AntelopeJS contract for token authentication: sign and verify tokens, and inject the verified payload into API handlers with a decorator.
|
|
11
11
|
|
|
12
|
+
Part of [AntelopeJS](https://github.com/AntelopeJS/antelopejs), the TypeScript framework where modules depend on contracts and the application chooses the provider. Built by the team behind [AntelopeJS DMS](https://github.com/AntelopeJS/dms).
|
|
13
|
+
|
|
12
14
|
## What it offers
|
|
13
15
|
|
|
14
16
|
- `SignRaw` and `ValidateRaw` sign a payload into a token and verify a token back into its payload.
|
|
15
17
|
- `SignServerResponse` signs a payload and attaches the token to a response as a cookie.
|
|
16
|
-
- `@Authentication()` reads the token from the `x-antelopejs-auth` header,
|
|
17
|
-
- `CreateAuthDecorator` builds your own decorator with a custom source, verifier and validator.
|
|
18
|
+
- `@Authentication()` reads the token from the `x-antelopejs-auth` header, or from the `ANTELOPEJS_AUTH` cookie when the header is absent. It verifies the token and injects the payload into a handler parameter.
|
|
19
|
+
- `CreateAuthDecorator` builds your own decorator with a custom source, verifier, and validator.
|
|
18
20
|
|
|
19
21
|
## Implementations
|
|
20
22
|
|
|
@@ -59,7 +61,9 @@ export class SessionController extends Controller("/session") {
|
|
|
59
61
|
}
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
A request to `GET /session/me` without a valid token fails before the handler runs. Send the token unchanged in `x-antelopejs-auth`, without a `Bearer ` prefix. The login method only
|
|
64
|
+
A request to `GET /session/me` without a valid token fails before the handler runs. Send the token unchanged in `x-antelopejs-auth`, without a `Bearer ` prefix. The `login` method only shows signing. In a real application, check the credentials before you issue a token.
|
|
65
|
+
|
|
66
|
+
The `Session` annotation describes the payload the application expects. It does not check it, so `session.id` is whatever the verified token carries. Add a [shape-checking verifier](./docs/2.authentication.md#create-a-custom-verifier) before you rely on claims from tokens whose format nothing enforces.
|
|
63
67
|
|
|
64
68
|
## Documentation
|
|
65
69
|
|
|
@@ -70,14 +74,16 @@ A request to `GET /session/me` without a valid token fails before the handler ru
|
|
|
70
74
|
|
|
71
75
|
## Compatibility
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
The module declares these peer dependencies:
|
|
78
|
+
|
|
79
|
+
<!-- antelopejs-docs-compat:start -->
|
|
74
80
|
|
|
75
|
-
|
|
81
|
+
| Package | Supported | Tested with |
|
|
82
|
+
| ---------------------------- | ----------------- | ----------- |
|
|
83
|
+
| `@antelopejs/interface-api` | `>=0.0.15 <1.0.0` | `0.2.0` |
|
|
84
|
+
| `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` | `0.4.0` |
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
| ---------------------------- | ----------------- |
|
|
79
|
-
| `@antelopejs/interface-api` | `>=0.0.15 <1.0.0` |
|
|
80
|
-
| `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` |
|
|
86
|
+
<!-- antelopejs-docs-compat:end -->
|
|
81
87
|
|
|
82
88
|
## Contributing
|
|
83
89
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type Class } from "@antelopejs/interface-core/decorators";
|
|
2
3
|
/**
|
|
3
4
|
* A value JSON can represent: a string, number, boolean, `null`, an array of
|
|
4
5
|
* JSON values, or a {@link JsonObject}. `Date`, `Buffer`, functions and class
|
|
@@ -97,6 +98,13 @@ export interface CookieOptions {
|
|
|
97
98
|
domain?: string;
|
|
98
99
|
secure?: boolean;
|
|
99
100
|
}
|
|
101
|
+
type TimeSpanUnit = "Years" | "Year" | "Yrs" | "Yr" | "Y" | "Weeks" | "Week" | "W" | "Days" | "Day" | "D" | "Hours" | "Hour" | "Hrs" | "Hr" | "H" | "Minutes" | "Minute" | "Mins" | "Min" | "M" | "Seconds" | "Second" | "Secs" | "Sec" | "s" | "Milliseconds" | "Millisecond" | "Msecs" | "Msec" | "Ms";
|
|
102
|
+
/**
|
|
103
|
+
* A timespan string in the grammar of the `ms` package: a number, optionally
|
|
104
|
+
* followed by a unit in any case, with or without a space (`"90"`, `"2h"`,
|
|
105
|
+
* `"7 days"`). A bare number string counts milliseconds.
|
|
106
|
+
*/
|
|
107
|
+
export type TimeSpan = `${number}` | `${number}${TimeSpanUnit | Uppercase<TimeSpanUnit> | Lowercase<TimeSpanUnit>}` | `${number} ${TimeSpanUnit | Uppercase<TimeSpanUnit> | Lowercase<TimeSpanUnit>}`;
|
|
100
108
|
/**
|
|
101
109
|
* Options for signing authentication tokens.
|
|
102
110
|
*
|
|
@@ -113,13 +121,13 @@ export interface CookieOptions {
|
|
|
113
121
|
*/
|
|
114
122
|
export interface SignOptions {
|
|
115
123
|
/**
|
|
116
|
-
* Expiration as a number of seconds or a
|
|
124
|
+
* Expiration as a number of seconds or a {@link TimeSpan}.
|
|
117
125
|
*/
|
|
118
|
-
expiresIn?:
|
|
126
|
+
expiresIn?: number | TimeSpan;
|
|
119
127
|
/**
|
|
120
|
-
* Not-before field as a number of seconds or a
|
|
128
|
+
* Not-before field as a number of seconds or a {@link TimeSpan}.
|
|
121
129
|
*/
|
|
122
|
-
notBefore?:
|
|
130
|
+
notBefore?: number | TimeSpan;
|
|
123
131
|
}
|
|
124
132
|
/**
|
|
125
133
|
* Options for verifying authentication tokens.
|
|
@@ -150,7 +158,10 @@ export interface VerifyOptions {
|
|
|
150
158
|
*/
|
|
151
159
|
maxAge?: string | number;
|
|
152
160
|
}
|
|
153
|
-
|
|
161
|
+
/**
|
|
162
|
+
* What a token signs: a string, a `Buffer`, or a {@link JsonObject}.
|
|
163
|
+
*/
|
|
164
|
+
export type AuthPayload = string | Buffer | JsonObject;
|
|
154
165
|
interface AuthDecoratorOptions<T, R> {
|
|
155
166
|
source?: AuthSource;
|
|
156
167
|
authenticatorOptions?: VerifyOptions;
|
|
@@ -168,7 +179,7 @@ interface ResolvedAuthDecoratorCallbacks<T, R> {
|
|
|
168
179
|
authenticatorOptions: VerifyOptions;
|
|
169
180
|
validator: AuthValidator<T, R> | undefined;
|
|
170
181
|
}
|
|
171
|
-
declare function buildAuthDecorator<T, R>(callbacks: ResolvedAuthDecoratorCallbacks<T, R>): (validator?: AuthValidator<T, R> | undefined) => import("@antelopejs/interface-core/decorators").
|
|
182
|
+
declare function buildAuthDecorator<T, R>(callbacks: ResolvedAuthDecoratorCallbacks<T, R>): (validator?: AuthValidator<T, R> | undefined) => import("@antelopejs/interface-core/decorators").PropertyDecorator & import("@antelopejs/interface-core/decorators").ClassDecorator<Class<object, any[]>> & import("@antelopejs/interface-core/decorators").ParameterDecorator;
|
|
172
183
|
type AuthDecoratorFactory<T, R> = ReturnType<typeof buildAuthDecorator<T, R>>;
|
|
173
184
|
/**
|
|
174
185
|
* @internal
|
|
@@ -317,5 +328,5 @@ export declare function CreateAuthDecorator<T, R = unknown>(callbacks: CustomAut
|
|
|
317
328
|
*
|
|
318
329
|
* @see {@link CreateAuthDecorator}
|
|
319
330
|
*/
|
|
320
|
-
export declare const Authentication: (validator?: AuthValidator<AuthClaims, unknown> | undefined) => import("@antelopejs/interface-core/decorators").
|
|
331
|
+
export declare const Authentication: (validator?: AuthValidator<AuthClaims, unknown> | undefined) => import("@antelopejs/interface-core/decorators").PropertyDecorator & import("@antelopejs/interface-core/decorators").ClassDecorator<Class<object, any[]>> & import("@antelopejs/interface-core/decorators").ParameterDecorator;
|
|
321
332
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -61,15 +61,26 @@ function createAuthParameterProvider(callbacks, validator) {
|
|
|
61
61
|
validator,
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
64
|
+
function isDecoratorTarget(value) {
|
|
65
|
+
return (typeof value === "object" &&
|
|
66
|
+
value !== null &&
|
|
67
|
+
"constructor" in value &&
|
|
68
|
+
typeof value.constructor === "function");
|
|
69
|
+
}
|
|
70
|
+
/** The prototype of the class a class decorator receives, where its handlers' parameters live. */
|
|
71
|
+
function classPrototype(target) {
|
|
72
|
+
const prototype = "prototype" in target ? target.prototype : undefined;
|
|
73
|
+
if (!isDecoratorTarget(prototype)) {
|
|
74
|
+
throw new TypeError("A class decorator applies to a class.");
|
|
75
|
+
}
|
|
76
|
+
return prototype;
|
|
66
77
|
}
|
|
67
78
|
function buildAuthDecorator(callbacks) {
|
|
68
79
|
return (0, decorators_1.MakeParameterAndPropertyAndClassDecorator)((target, key, index, validator) => {
|
|
69
80
|
const authValidator = validator ?? callbacks.validator;
|
|
70
81
|
const provider = createAuthParameterProvider(callbacks, authValidator);
|
|
71
82
|
if (key === undefined) {
|
|
72
|
-
|
|
83
|
+
(0, interface_api_1.SetParameterProvider)(classPrototype(target), Symbol(ClassParameterProviderSymbolDescription), undefined, provider);
|
|
73
84
|
return;
|
|
74
85
|
}
|
|
75
86
|
(0, interface_api_1.SetParameterProvider)(target, key, index, provider);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const __1 = require("..");
|
|
5
|
+
class Guarded {
|
|
6
|
+
}
|
|
7
|
+
const WITHOUT_PROTOTYPE = Guarded.bind(null);
|
|
8
|
+
describe("class decorator target", () => {
|
|
9
|
+
it("refuses a class that has no prototype object", () => {
|
|
10
|
+
(0, chai_1.expect)(() => {
|
|
11
|
+
(0, __1.Authentication)()(WITHOUT_PROTOTYPE);
|
|
12
|
+
}).to.throw(TypeError, "A class decorator applies to a class.");
|
|
13
|
+
});
|
|
14
|
+
it("accepts a class with a prototype object", () => {
|
|
15
|
+
(0, chai_1.expect)(() => {
|
|
16
|
+
(0, __1.Authentication)()(Guarded);
|
|
17
|
+
}).not.to.throw();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=class-decorator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-decorator.test.js","sourceRoot":"","sources":["../../src/tests/class-decorator.test.ts"],"names":[],"mappings":";;AAAA,+BAA8B;AAC9B,0BAAoC;AAEpC,MAAM,OAAO;CAAG;AAEhB,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAE7C,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,IAAA,aAAM,EAAC,GAAG,EAAE;YACV,IAAA,kBAAc,GAAE,CAAC,iBAAiB,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,uCAAuC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,IAAA,aAAM,EAAC,GAAG,EAAE;YACV,IAAA,kBAAc,GAAE,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/docs/1.introduction.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Authenticate API requests
|
|
3
|
+
description: Install the auth interface, protect a first endpoint with a verified token, and pick where to plug in your own source, verifier, or validator.
|
|
4
|
+
mode: how-to
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
## Overview
|
|
4
8
|
|
|
5
|
-
Use `@antelopejs/interface-auth` to sign tokens and inject verified payloads into API handlers. Your application still owns credential checks, payload validation, and permission rules
|
|
9
|
+
Use `@antelopejs/interface-auth` to sign tokens and inject verified payloads into API handlers. Your application still owns credential checks, payload validation, and permission rules. A valid signature alone does not grant access to a resource.
|
|
6
10
|
|
|
7
|
-
This interface depends on the [
|
|
11
|
+
This interface depends on the [interface-api](https://github.com/AntelopeJS/interface-api) package, which provides the decorators that inject parameters.
|
|
8
12
|
|
|
9
13
|
## Core concepts
|
|
10
14
|
|
|
11
|
-
The
|
|
15
|
+
The interface has three layers:
|
|
12
16
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
17
|
+
- Token handling. `SignRaw`, `ValidateRaw`, and `SignServerResponse` sign and verify tokens.
|
|
18
|
+
- Authentication decorators. The `@Authentication` decorator and the `CreateAuthDecorator` function inject verified payloads into handler parameters.
|
|
19
|
+
- A pipeline you can change. Custom sources, verifiers, and validators replace each step of the flow.
|
|
16
20
|
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
@@ -22,9 +26,9 @@ pnpm add @antelopejs/interface-auth
|
|
|
22
26
|
|
|
23
27
|
## Protect an endpoint
|
|
24
28
|
|
|
25
|
-
Start with a running AntelopeJS API module and an authentication
|
|
29
|
+
Start with a running AntelopeJS API module and an authentication module that implements this interface. Installing the package does not configure signing keys or attach a token implementation.
|
|
26
30
|
|
|
27
|
-
This controller requires a verified token for `GET /account`. It returns no claims, so you can
|
|
31
|
+
This controller requires a verified token for `GET /account`. It returns no claims, so you can check that your provider and transport work before you add payload validation.
|
|
28
32
|
|
|
29
33
|
```ts docs-check
|
|
30
34
|
import { Controller, Get } from "@antelopejs/interface-api";
|
|
@@ -39,25 +43,25 @@ class AccountController extends Controller("/account") {
|
|
|
39
43
|
}
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
Send an issued token in `x-antelopejs-auth` to receive `{ "authenticated": true }`. A missing or invalid token must fail verification before the handler runs. This checks authentication only, not permission to read another user's account.
|
|
46
|
+
Send an issued token in `x-antelopejs-auth` to receive `{ "authenticated": true }`. A missing or invalid token must fail verification before the handler runs. This route checks authentication only, not permission to read another user's account.
|
|
43
47
|
|
|
44
|
-
1. **Issue a token after
|
|
48
|
+
1. **Issue a token after you check the credentials.** Use [token handling](./3.token-handling.md) to choose `SignRaw` or `SignServerResponse` and set an expiry.
|
|
45
49
|
2. **Choose one transport.** The default source reads a raw token from `x-antelopejs-auth`, then tries `ANTELOPEJS_AUTH` only when the header is absent. For `Authorization: Bearer`, use the [custom source example](./2.authentication.md#create-a-custom-source).
|
|
46
50
|
3. **Require authentication.** Add [a parameter, property, or class decorator](./4.parameter-decoration.md) to the controller.
|
|
47
|
-
4. **Enforce your policy.** Validate the payload shape and throw when the caller lacks permission. A validator
|
|
51
|
+
4. **Enforce your policy.** Validate the payload shape, and throw when the caller lacks permission. A validator that returns `false` injects `false`. It does not deny access.
|
|
48
52
|
|
|
49
53
|
## Choose where to customize
|
|
50
54
|
|
|
51
|
-
| You need to change
|
|
55
|
+
| You need to change | Customize | Keep this boundary |
|
|
52
56
|
| -------------------------- | --------------- | -------------------------------------------- |
|
|
53
57
|
| Where the token comes from | `source` | Extraction does not establish trust. |
|
|
54
|
-
| How the token is verified | `authenticator` | Verify before
|
|
55
|
-
| Who may use the endpoint | `validator` | Throw to reject
|
|
58
|
+
| How the token is verified | `authenticator` | Verify before you return a payload. |
|
|
59
|
+
| Who may use the endpoint | `validator` | Throw to reject. Return the value to inject. |
|
|
56
60
|
|
|
57
|
-
Before
|
|
61
|
+
Before you expose the route, test a missing token, an invalid token, and a valid token without the required permission, as well as the successful request. See [authentication and authorization](./2.authentication.md) for the complete pipeline.
|
|
58
62
|
|
|
59
63
|
## Documentation sections
|
|
60
64
|
|
|
61
|
-
- [Authentication
|
|
62
|
-
- [Token
|
|
63
|
-
- [
|
|
65
|
+
- [Authentication and authorization](./2.authentication.md) covers the flow, sources, verifiers, and validators.
|
|
66
|
+
- [Token handling](./3.token-handling.md) covers the functions that sign and verify tokens.
|
|
67
|
+
- [Require authentication in controllers](./4.parameter-decoration.md) covers the decorators and custom decorators.
|
package/docs/2.authentication.md
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Authentication and authorization
|
|
3
|
+
description: How the auth interface turns a request into an injected payload, through a source, an authenticator, and an optional validator, and which part of the policy stays in your application.
|
|
4
|
+
mode: explanation
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
Authentication establishes trust in a token. Authorization decides what its holder may do. This interface connects token verification to API parameter injection
|
|
7
|
+
Authentication establishes trust in a token. Authorization decides what its holder may do. This interface connects token verification to API parameter injection. Your application supplies credential checks, payload validation, and access rules.
|
|
4
8
|
|
|
5
9
|
## Authentication flow
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
An authentication flow has four steps:
|
|
8
12
|
|
|
9
|
-
1.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
+
1. Token generation. `SignRaw` or `SignServerResponse` signs user data into a token.
|
|
14
|
+
2. Token transport. The client sends the token in the chosen header or cookie. You choose the client's storage and CSRF protection. This interface configures neither.
|
|
15
|
+
3. Token validation. The server verifies the token on each request.
|
|
16
|
+
4. Authorization. Your code checks whether the authenticated user may access the requested resource.
|
|
13
17
|
|
|
14
18
|
### Example authentication flow
|
|
15
19
|
|
|
16
|
-
The persistence functions in this example
|
|
20
|
+
The persistence functions in this example, `validateUserCredentials` and `getUserProfile`, belong to your application. The profile route verifies a token. A validator would add the payload shape and resource permissions. For production cookies, you also decide the Secure, SameSite, expiry, and CSRF policy of your deployment.
|
|
17
21
|
|
|
18
|
-
```typescript
|
|
22
|
+
```typescript fragment
|
|
19
23
|
import { SignRaw, Authentication } from "@antelopejs/interface-auth";
|
|
20
24
|
import {
|
|
21
25
|
Controller,
|
|
@@ -55,23 +59,23 @@ class UserController extends Controller("/users") {
|
|
|
55
59
|
async getProfile(
|
|
56
60
|
@Authentication() userData: { userId: number; role: string },
|
|
57
61
|
) {
|
|
58
|
-
// Step 3:
|
|
59
|
-
//
|
|
62
|
+
// Step 3: the token is verified. Resource authorization is application policy.
|
|
63
|
+
// An invalid token rejects the request before this code runs.
|
|
60
64
|
return { user: await getUserProfile(userData.userId) };
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
```
|
|
64
68
|
|
|
65
|
-
This cookie example requires HTTPS. `SameSite=Lax` is a starting policy for same-site browser use
|
|
69
|
+
This cookie example requires HTTPS. `SameSite=Lax` is a starting policy for same-site browser use. It does not replace a decision on CSRF protection or cross-site requirements. The helpers named above are application code, not a complete login implementation.
|
|
66
70
|
|
|
67
71
|
## Authentication sources
|
|
68
72
|
|
|
69
73
|
An `AuthSource` function extracts the authentication token from the incoming HTTP request. The default source checks two locations in order:
|
|
70
74
|
|
|
71
|
-
1.
|
|
72
|
-
2.
|
|
75
|
+
1. The `x-antelopejs-auth` header.
|
|
76
|
+
2. The `ANTELOPEJS_AUTH` cookie.
|
|
73
77
|
|
|
74
|
-
The header contains the raw token, not `Bearer <token>`. A present header takes precedence even when it is empty or invalid
|
|
78
|
+
The header contains the raw token, not `Bearer <token>`. A present header takes precedence even when it is empty or invalid. Authentication does not retry with the cookie after verification fails. The default source does not read `Authorization`.
|
|
75
79
|
|
|
76
80
|
### Create a custom source
|
|
77
81
|
|
|
@@ -97,6 +101,8 @@ const BearerAuth = CreateAuthDecorator({
|
|
|
97
101
|
The `AuthSource` type signature is:
|
|
98
102
|
|
|
99
103
|
```typescript
|
|
104
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
105
|
+
|
|
100
106
|
type AuthSource = (
|
|
101
107
|
req: IncomingMessage,
|
|
102
108
|
res: ServerResponse,
|
|
@@ -105,7 +111,7 @@ type AuthSource = (
|
|
|
105
111
|
|
|
106
112
|
## Authentication verifiers
|
|
107
113
|
|
|
108
|
-
An `AuthVerifier` function validates the token and extracts its payload. The default verifier
|
|
114
|
+
An `AuthVerifier` function validates the token and extracts its payload. The default verifier is `ValidateRaw`, which calls the module that implements this interface, such as `@antelopejs/auth-jwt` for JSON Web Tokens.
|
|
109
115
|
|
|
110
116
|
### Create a custom verifier
|
|
111
117
|
|
|
@@ -132,11 +138,13 @@ const UserAuth = CreateAuthDecorator({
|
|
|
132
138
|
});
|
|
133
139
|
```
|
|
134
140
|
|
|
135
|
-
A verifier must establish trust in the token before
|
|
141
|
+
A verifier must establish trust in the token before it returns the payload. Decoding Base64 or parsing JSON alone does not authenticate a request. `ValidateRaw` returns an `AuthClaims`, which is `string | JsonObject`. The interface does not know the shape of what you signed, so reading a field needs a check like the one above. A configured validator receives the verifier's return type as its input.
|
|
136
142
|
|
|
137
143
|
The `AuthVerifier` type signature is:
|
|
138
144
|
|
|
139
145
|
```typescript
|
|
146
|
+
import type { AuthClaims, VerifyOptions } from "@antelopejs/interface-auth";
|
|
147
|
+
|
|
140
148
|
type AuthVerifier<T = AuthClaims> = (
|
|
141
149
|
data?: string,
|
|
142
150
|
options?: VerifyOptions,
|
|
@@ -145,7 +153,7 @@ type AuthVerifier<T = AuthClaims> = (
|
|
|
145
153
|
|
|
146
154
|
## Authentication validators
|
|
147
155
|
|
|
148
|
-
An `AuthValidator` function
|
|
156
|
+
An `AuthValidator` function runs extra checks on the verified data. Validators run after the verifier. A validator can transform the result, or throw an error to reject the request.
|
|
149
157
|
|
|
150
158
|
```typescript
|
|
151
159
|
import { CreateAuthDecorator, AuthValidator } from "@antelopejs/interface-auth";
|
|
@@ -165,6 +173,8 @@ const AdminOnly = CreateAuthDecorator({
|
|
|
165
173
|
The `AuthValidator` type signature is:
|
|
166
174
|
|
|
167
175
|
```typescript
|
|
176
|
+
import type { AuthClaims } from "@antelopejs/interface-auth";
|
|
177
|
+
|
|
168
178
|
type AuthValidator<T = AuthClaims, R = unknown> = (data: T) => Promise<R> | R;
|
|
169
179
|
```
|
|
170
180
|
|
|
@@ -172,16 +182,16 @@ Without an `authenticator`, a validator receives the `AuthClaims` the default ve
|
|
|
172
182
|
|
|
173
183
|
## Authentication pipeline
|
|
174
184
|
|
|
175
|
-
The
|
|
185
|
+
The pipeline takes the request through three stages:
|
|
176
186
|
|
|
177
|
-
{width="480"}
|
|
187
|
+
{width="480"}
|
|
178
188
|
|
|
179
|
-
|
|
189
|
+
An omitted `source` or `authenticator` callback uses the default. There is no default policy validator. Without one, the decorator injects the verified payload unchanged.
|
|
180
190
|
|
|
181
191
|
| Stage | Input | Output | Failure boundary |
|
|
182
192
|
| --------------- | ------------------------------ | -------------------- | -------------------------------------------------------------- |
|
|
183
193
|
| `source` | Request and response | Token or `undefined` | Missing input still reaches the authenticator. |
|
|
184
194
|
| `authenticator` | Token and verification options | Verified payload | A thrown error or rejected promise stops processing. |
|
|
185
|
-
| `validator` | Verified payload | Value to inject | Throw to deny access
|
|
195
|
+
| `validator` | Verified payload | Value to inject | Throw to deny access. A falsy return value is not a rejection. |
|
|
186
196
|
|
|
187
|
-
A validator passed to a decorator
|
|
197
|
+
A validator passed to a decorator call replaces the configured validator. It does not run after it as an extra check. To compose validators, keep the shared policy in a function that each validator calls.
|
package/docs/3.token-handling.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Token handling
|
|
3
|
+
description: Reference for SignRaw, SignServerResponse, and ValidateRaw, with their options, argument types, return values, and cookie serialization limits.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
`@antelopejs/interface-auth` exports three functions that sign and verify tokens. The decorators call `ValidateRaw` by default.
|
|
4
8
|
|
|
5
9
|
## Token generation
|
|
6
10
|
|
|
@@ -26,7 +30,7 @@ The function accepts two arguments:
|
|
|
26
30
|
| `data` | `string \| Buffer \| JsonObject` | The data to sign |
|
|
27
31
|
| `options` | `SignOptions` (optional) | Signing configuration options |
|
|
28
32
|
|
|
29
|
-
`JsonObject` maps string keys to JSON values
|
|
33
|
+
`JsonObject` maps string keys to JSON values: `string`, `number`, `boolean`, `null`, arrays, and nested objects. A `Date`, a class instance, or a function is a compile error. Convert it first, for example with `date.toISOString()`. A key whose value is `undefined` is accepted, and serialization drops it. An object typed with an `interface` has no index signature and is not assignable. Declare it with a `type` alias, or spread it into a literal.
|
|
30
34
|
|
|
31
35
|
`SignRaw` returns a `Promise<string>` that resolves to the signed token.
|
|
32
36
|
|
|
@@ -34,10 +38,12 @@ The function accepts two arguments:
|
|
|
34
38
|
|
|
35
39
|
The `SignOptions` interface configures token generation.
|
|
36
40
|
|
|
37
|
-
| Property | Type
|
|
38
|
-
| ----------- |
|
|
39
|
-
| `expiresIn` | `
|
|
40
|
-
| `notBefore` | `
|
|
41
|
+
| Property | Type | Description |
|
|
42
|
+
| ----------- | -------------------- | ------------------------------------------------------------------------------- |
|
|
43
|
+
| `expiresIn` | `number \| TimeSpan` | Token expiration time as seconds or a timespan string, such as `"1h"` or `"2d"` |
|
|
44
|
+
| `notBefore` | `number \| TimeSpan` | Duration before which the token is not valid |
|
|
45
|
+
|
|
46
|
+
`TimeSpan` is the timespan grammar of the `ms` package. It is a number followed by an optional unit in any case, with or without a space, such as `"90"`, `"2h"`, or `"7 days"`. A bare number string counts milliseconds. A string outside this grammar, such as `"soon"`, does not compile.
|
|
41
47
|
|
|
42
48
|
### `SignServerResponse`
|
|
43
49
|
|
|
@@ -72,19 +78,19 @@ The cookie is set with the name `ANTELOPEJS_AUTH` and the signed token as its va
|
|
|
72
78
|
|
|
73
79
|
### `CookieOptions`
|
|
74
80
|
|
|
75
|
-
The `CookieOptions` interface supplies attributes to the authentication cookie. The
|
|
81
|
+
The `CookieOptions` interface supplies attributes to the authentication cookie. The serializer writes keys literally and omits `false` and `undefined` values. It writes `true` as a standalone directive and converts other values with `String(value)`.
|
|
76
82
|
|
|
77
|
-
| Property | Type | Description
|
|
78
|
-
| ---------- | --------- |
|
|
79
|
-
| `maxAge` | `number` | Written as `maxAge=<number
|
|
80
|
-
| `signed` | `boolean` | Written as a directive when true
|
|
81
|
-
| `expires` | `Date` | Written using `String(date)`, not an explicit HTTP-date formatter
|
|
82
|
-
| `httpOnly` | `boolean` | Prevents client-side JavaScript from accessing the cookie
|
|
83
|
-
| `path` | `string` | URL path for which the cookie is valid
|
|
84
|
-
| `domain` | `string` | Domain for which the cookie is valid
|
|
85
|
-
| `secure` | `boolean` | Only sends the cookie over HTTPS
|
|
83
|
+
| Property | Type | Description |
|
|
84
|
+
| ---------- | --------- | --------------------------------------------------------------------- |
|
|
85
|
+
| `maxAge` | `number` | Written as `maxAge=<number>`, not as the standard `Max-Age` attribute |
|
|
86
|
+
| `signed` | `boolean` | Written as a directive when true. It does not add a cookie signature. |
|
|
87
|
+
| `expires` | `Date` | Written using `String(date)`, not an explicit HTTP-date formatter |
|
|
88
|
+
| `httpOnly` | `boolean` | Prevents client-side JavaScript from accessing the cookie |
|
|
89
|
+
| `path` | `string` | URL path for which the cookie is valid |
|
|
90
|
+
| `domain` | `string` | Domain for which the cookie is valid |
|
|
91
|
+
| `secure` | `boolean` | Only sends the cookie over HTTPS |
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
`maxAge`, `signed`, and `expires` do not set the browser lifetime or add a signature. When you need those features, use a standards-compliant cookie serializer, and keep token expiration in `SignOptions`. The function adds no cookie attributes by default.
|
|
88
94
|
|
|
89
95
|
`CookieOptions` has no `sameSite` field. If your deployment requires SameSite, serialize the cookie yourself and set the response header, as in the [authentication flow](./2.authentication.md#example-authentication-flow). Do not cast unsupported options into this type.
|
|
90
96
|
|
|
@@ -97,14 +103,16 @@ The `ValidateRaw` function verifies a token and returns the data contained withi
|
|
|
97
103
|
```typescript
|
|
98
104
|
import { ValidateRaw } from "@antelopejs/interface-auth";
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
async function inspect(token: string) {
|
|
107
|
+
try {
|
|
108
|
+
const claims = await ValidateRaw(token);
|
|
109
|
+
console.log(claims); // { userId: 123, role: "admin", iat: ..., exp: ... } with auth-jwt
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(
|
|
112
|
+
"Invalid token:",
|
|
113
|
+
error instanceof Error ? error.message : String(error),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
108
116
|
}
|
|
109
117
|
```
|
|
110
118
|
|
|
@@ -115,7 +123,9 @@ The function accepts two arguments:
|
|
|
115
123
|
| `token` | `string` (optional) | The signed token to verify |
|
|
116
124
|
| `options` | `VerifyOptions` (optional) | Verification configuration |
|
|
117
125
|
|
|
118
|
-
`ValidateRaw` returns a `Promise<AuthClaims>`, where `AuthClaims` is `string | JsonObject
|
|
126
|
+
`ValidateRaw` returns a `Promise<AuthClaims>`, where `AuthClaims` is `string | JsonObject`. The value is the signed string, or the JSON object that was signed. `ValidateRaw` has no type parameter. Read a field only after you check it, or pass the result to a parser of your own. A [custom verifier](./2.authentication.md#create-a-custom-verifier) does that once for every route.
|
|
127
|
+
|
|
128
|
+
The promise rejects when the token is missing or fails verification. The rejection value comes from the implementation and is not always an `Error`. `@antelopejs/auth-jwt` rejects with an `HTTPResult`: status 401 for a missing token and 403 for an invalid or expired one. The example above handles both kinds of value.
|
|
119
129
|
|
|
120
130
|
### `VerifyOptions`
|
|
121
131
|
|
|
@@ -133,9 +143,11 @@ The `VerifyOptions` interface configures token validation.
|
|
|
133
143
|
import { ValidateRaw } from "@antelopejs/interface-auth";
|
|
134
144
|
|
|
135
145
|
// Limit token age without bypassing expiration or not-before checks.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
async function verifyRecent(token: string) {
|
|
147
|
+
return ValidateRaw(token, {
|
|
148
|
+
maxAge: "30m",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
139
151
|
```
|
|
140
152
|
|
|
141
|
-
Leave `ignoreExpiration` and `ignoreNotBefore` unset for normal authentication. They
|
|
153
|
+
Leave `ignoreExpiration` and `ignoreNotBefore` unset for normal authentication. They bypass the time checks. Set them only when a separate, explicit policy accepts such tokens. A successful `ValidateRaw` call verifies the token through your provider. It does not check the shape of the payload.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Require authentication in controllers
|
|
3
|
+
description: Protect a handler, a property, or a whole controller with @Authentication, and build reusable decorators with CreateAuthDecorator.
|
|
4
|
+
mode: how-to
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
Use a parameter decorator when one handler needs the payload
|
|
7
|
+
Use a parameter decorator when one handler needs the payload. Use a property decorator when several methods need it. Use a class decorator when every route must authenticate without receiving the payload. Pick one scope per need. You do not have to combine them.
|
|
4
8
|
|
|
5
9
|
## The `@Authentication` decorator
|
|
6
10
|
|
|
7
|
-
The `@Authentication` decorator
|
|
11
|
+
The `@Authentication` decorator reads a token from `x-antelopejs-auth`, or from the `ANTELOPEJS_AUTH` cookie when the header is absent. It verifies the token and injects the verified payload into the parameter.
|
|
8
12
|
|
|
9
13
|
```typescript
|
|
10
14
|
import { Controller, Get } from "@antelopejs/interface-api";
|
|
@@ -21,9 +25,9 @@ class UserController extends Controller("/users") {
|
|
|
21
25
|
}
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
If the token is missing or invalid, the
|
|
28
|
+
If the token is missing or invalid, the verifier rejects and the handler does not run. With `@antelopejs/auth-jwt`, the response is a 401 for a missing token and a 403 for an invalid one.
|
|
25
29
|
|
|
26
|
-
The payload annotations in these examples describe the
|
|
30
|
+
The payload annotations in these examples describe the token format the application expects. They do not check it. Add a [shape-checking verifier](./2.authentication.md#create-a-custom-verifier) before you read claims from tokens whose format nothing enforces yet. The API provider decides how an ordinary thrown error becomes an HTTP response. Throwing an `Error` does not set a 401 or 403 status.
|
|
27
31
|
|
|
28
32
|
### Inline validators
|
|
29
33
|
|
|
@@ -54,9 +58,9 @@ class PaymentController extends Controller("/payments") {
|
|
|
54
58
|
|
|
55
59
|
## Create custom decorators with `CreateAuthDecorator`
|
|
56
60
|
|
|
57
|
-
The `CreateAuthDecorator` function creates a reusable authentication decorator
|
|
61
|
+
The `CreateAuthDecorator` function creates a reusable authentication decorator from custom source, authenticator, and validator functions. `validateApiKey` in the example below belongs to your application.
|
|
58
62
|
|
|
59
|
-
```typescript
|
|
63
|
+
```typescript fragment
|
|
60
64
|
import { CreateAuthDecorator } from "@antelopejs/interface-auth";
|
|
61
65
|
import type { IncomingMessage } from "node:http";
|
|
62
66
|
|
|
@@ -85,28 +89,28 @@ export const ApiKeyAuth = CreateAuthDecorator({
|
|
|
85
89
|
});
|
|
86
90
|
```
|
|
87
91
|
|
|
88
|
-
`validateApiKey` is your application's lookup and verification function.
|
|
92
|
+
`validateApiKey` is your application's lookup and verification function. If the authenticator returned `null` for a missing or invalid key, the handler would run with `null`. This example throws instead.
|
|
89
93
|
|
|
90
94
|
### `CreateAuthDecorator` options
|
|
91
95
|
|
|
92
96
|
The function accepts an object with the following properties:
|
|
93
97
|
|
|
94
|
-
| Property | Type | Description
|
|
95
|
-
| ---------------------- | --------------------- |
|
|
96
|
-
| `source` | `AuthSource` | Extracts the token from the request
|
|
97
|
-
| `authenticator` | `AuthVerifier<T>` | Verifies the token and returns the payload
|
|
98
|
-
| `authenticatorOptions` | `VerifyOptions` | Options passed to the authenticator
|
|
99
|
-
| `validator` | `AuthValidator<T, R>` | Validates and transforms the authenticated data
|
|
98
|
+
| Property | Type | Description |
|
|
99
|
+
| ---------------------- | --------------------- | --------------------------------------------------------------------- |
|
|
100
|
+
| `source` | `AuthSource` | Extracts the token from the request. Defaults to header, then cookie. |
|
|
101
|
+
| `authenticator` | `AuthVerifier<T>` | Verifies the token and returns the payload |
|
|
102
|
+
| `authenticatorOptions` | `VerifyOptions` | Options passed to the authenticator |
|
|
103
|
+
| `validator` | `AuthValidator<T, R>` | Validates and transforms the authenticated data |
|
|
100
104
|
|
|
101
|
-
All properties are optional. The default source reads the header
|
|
105
|
+
All properties are optional. The default source reads the header, then the cookie. The default authenticator calls `ValidateRaw`, and the default verification options are `{}`. Without a validator, the decorator injects the verified payload unchanged.
|
|
102
106
|
|
|
103
|
-
`T` and `R`
|
|
107
|
+
TypeScript infers `T` and `R` from the callbacks. Without an `authenticator`, `T` is `AuthClaims`, which is `string | JsonObject`. A validator then checks the claims before it reads them, as in `AdminAuth` above. With an `authenticator`, `T` is what the authenticator returns, and the validator receives that type. To name both type arguments, write `CreateAuthDecorator<T, R>` with an `authenticator`. A single type argument names `R` when there is no `authenticator`, and `T` when there is one.
|
|
104
108
|
|
|
105
|
-
A validator passed to a decorator call replaces the configured validator
|
|
109
|
+
A validator passed to a decorator call replaces the configured validator. The two do not compose. Return a transformed value to inject it, or throw to deny access. Returning `false` or `null` does not reject the request. A custom authenticator that accepts a missing token lets the request through unless a validator throws.
|
|
106
110
|
|
|
107
111
|
## Class-level authentication
|
|
108
112
|
|
|
109
|
-
|
|
113
|
+
Apply the `@Authentication` decorator to a controller class to require authentication on every route of that controller. You do not need to decorate each handler.
|
|
110
114
|
|
|
111
115
|
```typescript
|
|
112
116
|
import { Controller, Get, Post } from "@antelopejs/interface-api";
|
|
@@ -149,7 +153,7 @@ class AdminController extends Controller("/admin") {
|
|
|
149
153
|
|
|
150
154
|
## Property-level authentication
|
|
151
155
|
|
|
152
|
-
Apply `@Authentication` to a class property to
|
|
156
|
+
Apply `@Authentication` to a class property to read the authenticated user data in every handler of the controller. The API module sets the property on each request.
|
|
153
157
|
|
|
154
158
|
```typescript
|
|
155
159
|
import { Controller, Get } from "@antelopejs/interface-api";
|
|
@@ -177,7 +181,7 @@ class UserController extends Controller("/users") {
|
|
|
177
181
|
|
|
178
182
|
## Decorator scope summary
|
|
179
183
|
|
|
180
|
-
The `@Authentication` decorator
|
|
184
|
+
The `@Authentication` decorator, and any custom decorator that `CreateAuthDecorator` creates, applies at three levels:
|
|
181
185
|
|
|
182
186
|
| Scope | Effect |
|
|
183
187
|
| --------- | --------------------------------------------------------------------- |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "AntelopeJS interface for token authentication: sign and verify tokens and inject the verified payload into API handlers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@antelopejs/auth-jwt": "1.0.3",
|
|
61
61
|
"@antelopejs/core": "^1.16.0",
|
|
62
|
-
"@antelopejs/interface-api": "^0.0
|
|
63
|
-
"@antelopejs/interface-core": "^0.
|
|
64
|
-
"@antelopejs/tooling-configs": "^0.2.
|
|
62
|
+
"@antelopejs/interface-api": "^0.2.0",
|
|
63
|
+
"@antelopejs/interface-core": "^0.4.0",
|
|
64
|
+
"@antelopejs/tooling-configs": "^0.2.17",
|
|
65
65
|
"@types/chai": "^4.3.20",
|
|
66
66
|
"@types/mocha": "^10.0.10",
|
|
67
67
|
"@types/node": "^22.19.15",
|
|
@@ -84,10 +84,19 @@
|
|
|
84
84
|
"@antelopejs/interface-core": ">=0.2.4 <1.0.0"
|
|
85
85
|
},
|
|
86
86
|
"packageManager": "pnpm@10.6.5",
|
|
87
|
+
"pnpm": {
|
|
88
|
+
"overrides": {
|
|
89
|
+
"@antelopejs/auth-jwt>@antelopejs/interface-auth": "link:.",
|
|
90
|
+
"@antelopejs/interface-core": "0.4.0"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
87
93
|
"antelopeJs": {
|
|
88
94
|
"test": "src/antelope.test.ts",
|
|
89
95
|
"skills": [
|
|
90
96
|
"./skills"
|
|
91
|
-
]
|
|
97
|
+
],
|
|
98
|
+
"docs": {
|
|
99
|
+
"standard": true
|
|
100
|
+
}
|
|
92
101
|
}
|
|
93
102
|
}
|