@aooth/auth 0.1.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.
@@ -0,0 +1,124 @@
1
+ //#region src/credential/types.d.ts
2
+ /**
3
+ * What a successful auth check produces.
4
+ * Generic over TClaims for typed custom claims.
5
+ */
6
+ interface AuthContext<TClaims extends object = object> {
7
+ userId: string;
8
+ method: "session" | "token";
9
+ credentialId: string;
10
+ expiresAt: number;
11
+ claims?: TClaims;
12
+ }
13
+ /**
14
+ * Display metadata for stateful credentials.
15
+ * Extensible via TypeScript declaration merging:
16
+ * declare module '@aooth/auth' {
17
+ * interface CredentialMetadata { geoCountry?: string }
18
+ * }
19
+ */
20
+ interface CredentialMetadata {
21
+ ip?: string;
22
+ userAgent?: string;
23
+ fingerprint?: string;
24
+ label?: string;
25
+ }
26
+ /**
27
+ * Persisted state of a credential.
28
+ */
29
+ interface CredentialState<TClaims extends object = object> {
30
+ userId: string;
31
+ issuedAt: number;
32
+ expiresAt: number;
33
+ claims?: TClaims;
34
+ metadata?: CredentialMetadata;
35
+ /**
36
+ * Discriminant between access and refresh credentials persisted in the same store.
37
+ * Defaults to "access" when omitted.
38
+ */
39
+ kind?: "access" | "refresh";
40
+ /** For rotated refresh tokens — id of the parent credential this one replaced */
41
+ parentCredentialId?: string;
42
+ /** Timestamp of rotation; used by sliding rotation grace period */
43
+ rotatedAt?: number;
44
+ }
45
+ /**
46
+ * Result of issuing a credential or refreshing it.
47
+ */
48
+ interface IssueResult {
49
+ accessToken: string;
50
+ refreshToken?: string;
51
+ accessExpiresAt: number;
52
+ refreshExpiresAt?: number;
53
+ }
54
+ /**
55
+ * Refresh token configuration.
56
+ */
57
+ interface RefreshConfig {
58
+ /** Refresh token lifetime in milliseconds. */
59
+ ttl: number;
60
+ /**
61
+ * Rotation strategy. Defaults to 'sliding'.
62
+ *
63
+ * Note: 'sliding' grace-period replay tolerance only works against stateful
64
+ * stores (e.g. {@link CredentialStoreMemory}). Stateless stores (JWT,
65
+ * Encapsulated) cannot mutate an issued token in place; after the first
66
+ * rotation the old refresh becomes unusable, so 'sliding' degrades to
67
+ * 'always' semantics. Use 'always' explicitly for stateless deployments.
68
+ */
69
+ rotation?: "none" | "always" | "sliding";
70
+ /** Grace period for sliding rotation, in milliseconds. Defaults to 30_000. */
71
+ rotationGraceMs?: number;
72
+ /** Theft detection hook — invoked when a previously-rotated refresh is reused. */
73
+ onRotationReuse?: (state: CredentialState) => void;
74
+ }
75
+ //#endregion
76
+ //#region src/stores/store.d.ts
77
+ /**
78
+ * Pluggable credential storage. Implementations: Memory, JWT, Encapsulated.
79
+ * Same shape works for stateful and stateless stores. Stateless stores treat
80
+ * the token as the state itself (sign/encrypt on persist, verify/decrypt on retrieve).
81
+ *
82
+ * `TClaims` is constrained to `extends object`, which TypeScript widens to
83
+ * include arrays, class instances, and `Function`. Pass a plain object type
84
+ * (e.g. `{ role: string; tenant: string }`); using arrays/classes will type-
85
+ * check but won't survive JSON-serialised stateless storage round-trips.
86
+ */
87
+ interface CredentialStore<TClaims extends object = object> {
88
+ /** Persist state, return token. For stateless stores, token IS the state. */
89
+ persist(state: CredentialState<TClaims>, ttl?: number): Promise<string>;
90
+ /** Retrieve state from token. Returns null if expired/invalid/revoked. */
91
+ retrieve(token: string): Promise<CredentialState<TClaims> | null>;
92
+ /** Retrieve + invalidate (single-use). For stateless: needs denylist. */
93
+ consume(token: string): Promise<CredentialState<TClaims> | null>;
94
+ /**
95
+ * Update state. The returned token MAY differ from the input — stateful
96
+ * stores typically return the same token, while stateless stores re-issue
97
+ * a new token (denylisting the old). Callers must use the returned value.
98
+ */
99
+ update(token: string, state: CredentialState<TClaims>): Promise<string>;
100
+ /** Revoke a single token. For stateless: requires denylist; otherwise throws. */
101
+ revoke(token: string): Promise<void>;
102
+ /**
103
+ * Revoke all credentials for a user. Returns count revoked.
104
+ * Stateless stores cannot enumerate individual tokens; they MAY implement
105
+ * the cascade via a per-user revocation epoch (see `CredentialStoreJwt`)
106
+ * and return a sentinel `1` to indicate "revocation took effect".
107
+ */
108
+ revokeAllForUser(userId: string): Promise<number>;
109
+ /** List active credentials for a user (stateful only). */
110
+ listForUser?(userId: string): Promise<Array<CredentialState<TClaims> & {
111
+ token: string;
112
+ }>>;
113
+ }
114
+ /**
115
+ * Denylist for stateless revocation. Stores revoked-but-not-yet-expired token IDs.
116
+ * Auto-bounded by token TTLs.
117
+ */
118
+ interface DenylistStore {
119
+ add(jti: string, expiresAt: number): Promise<void>;
120
+ has(jti: string): Promise<boolean>;
121
+ cleanup(): Promise<number>;
122
+ }
123
+ //#endregion
124
+ export { CredentialState as a, CredentialMetadata as i, DenylistStore as n, IssueResult as o, AuthContext as r, RefreshConfig as s, CredentialStore as t };
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@aooth/auth",
3
+ "version": "0.1.1",
4
+ "description": "Auth method layer for aoothjs (sessions, tokens, password reset, MFA primitives)",
5
+ "keywords": [
6
+ "aoothjs",
7
+ "auth",
8
+ "authentication",
9
+ "jwt",
10
+ "mfa",
11
+ "password-reset",
12
+ "session",
13
+ "token"
14
+ ],
15
+ "homepage": "https://github.com/moostjs/aoothjs/tree/main/packages/auth#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/moostjs/aoothjs/issues"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Artem Maltsev",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/moostjs/aoothjs.git",
24
+ "directory": "packages/auth"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src/atscript-db/auth-credential.as"
29
+ ],
30
+ "type": "module",
31
+ "sideEffects": false,
32
+ "main": "dist/index.mjs",
33
+ "module": "./dist/index.mjs",
34
+ "types": "dist/index.d.mts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.mts",
38
+ "import": "./dist/index.mjs",
39
+ "require": "./dist/index.cjs"
40
+ },
41
+ "./redis": {
42
+ "types": "./dist/redis.d.mts",
43
+ "import": "./dist/redis.mjs",
44
+ "require": "./dist/redis.cjs"
45
+ },
46
+ "./atscript-db": {
47
+ "types": "./dist/atscript-db.d.mts",
48
+ "import": "./dist/atscript-db.mjs",
49
+ "require": "./dist/atscript-db.cjs"
50
+ },
51
+ "./atscript-db/model.as": "./src/atscript-db/auth-credential.as",
52
+ "./package.json": "./package.json"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "dependencies": {
58
+ "jose": "^6.2.3",
59
+ "@aooth/user": "0.1.1"
60
+ },
61
+ "peerDependencies": {
62
+ "@atscript/db": ">=0.1.79"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@atscript/db": {
66
+ "optional": true
67
+ }
68
+ },
69
+ "scripts": {
70
+ "build": "vp pack",
71
+ "dev": "vp pack --watch",
72
+ "test": "vp test",
73
+ "check": "vp check"
74
+ }
75
+ }
@@ -0,0 +1,44 @@
1
+ @db.table 'aooth_credentials'
2
+ @db.depth.limit 0
3
+ export interface AoothAuthCredential {
4
+ /**
5
+ * Opaque token id. Server-generated UUID in `CredentialStoreAtscriptDb.persist`.
6
+ * Also serves as the row PK so `findOne({ filter: { token } })` and
7
+ * `deleteOne(token)` are O(1).
8
+ */
9
+ @meta.id
10
+ token: string
11
+
12
+ /** Owner. Indexed for `revokeAllForUser` / `listForUser` scans. */
13
+ @db.index.plain
14
+ userId: string
15
+
16
+ issuedAt: number.timestamp
17
+ expiresAt: number.timestamp
18
+
19
+ /**
20
+ * "access" | "refresh" discriminant. Stored as a plain string to keep
21
+ * the model adapter-portable — engines that lack an enum type collapse
22
+ * literal unions to strings anyway.
23
+ */
24
+ kind?: string
25
+
26
+ /** Custom claims, opaque JSON. */
27
+ @db.json
28
+ claims?: {
29
+ [key: string]: any
30
+ }
31
+
32
+ /** Display metadata: ip, userAgent, fingerprint, label. */
33
+ @db.json
34
+ metadata?: {
35
+ ip?: string
36
+ userAgent?: string
37
+ fingerprint?: string
38
+ label?: string
39
+ }
40
+
41
+ /** Set by refresh-token rotation. */
42
+ parentCredentialId?: string
43
+ rotatedAt?: number.timestamp
44
+ }