@spfn/auth 0.2.1 → 0.3.0-beta.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/LICENSE +1 -1
- package/README.md +1032 -2397
- package/dist/{authenticate-eucncHxN.d.ts → authenticate-DlTGaBT8.d.ts} +545 -82
- package/dist/client-proof.d.ts +606 -0
- package/dist/client-proof.js +1842 -0
- package/dist/client-proof.js.map +1 -0
- package/dist/config.d.ts +319 -3
- package/dist/config.js +155 -5
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +180 -3
- package/dist/errors.js +116 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +122 -18
- package/dist/index.js +129 -8
- package/dist/index.js.map +1 -1
- package/dist/nextjs/api.js +404 -96
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.d.ts +5 -4
- package/dist/nextjs/server.js +165 -26
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +2130 -1016
- package/dist/server.js +4916 -739
- package/dist/server.js.map +1 -1
- package/dist/session-DTHahDQ9.d.ts +53 -0
- package/dist/types-DYyhze28.d.ts +98 -0
- package/dist/wire-version-CtzMKvBB.d.ts +134 -0
- package/migrations/20251125021229_premium_famine/snapshot.json +2641 -0
- package/migrations/20260225130050_smooth_the_fury/snapshot.json +2686 -0
- package/migrations/20260308141417_deep_iceman/snapshot.json +2686 -0
- package/migrations/20260308151309_perfect_deathbird/snapshot.json +2731 -0
- package/migrations/20260308201135_concerned_rawhide_kid/snapshot.json +2786 -0
- package/migrations/20260629103209_lethal_lifeguard/migration.sql +32 -0
- package/migrations/20260629103209_lethal_lifeguard/snapshot.json +2786 -0
- package/migrations/20260709073531_easy_hardball/migration.sql +24 -0
- package/migrations/20260709073531_easy_hardball/snapshot.json +3119 -0
- package/migrations/20260714081434_glossy_major_mapleleaf/migration.sql +1 -0
- package/migrations/20260714081434_glossy_major_mapleleaf/snapshot.json +3112 -0
- package/migrations/20260804105939_amazing_bushwacker/migration.sql +3 -0
- package/migrations/20260804105939_amazing_bushwacker/snapshot.json +3112 -0
- package/migrations/20260804110033_fat_piledriver/migration.sql +2 -0
- package/migrations/20260804110033_fat_piledriver/snapshot.json +3138 -0
- package/migrations/20260805143152_vengeful_ravenous/migration.sql +4 -0
- package/migrations/20260805143152_vengeful_ravenous/snapshot.json +3190 -0
- package/package.json +54 -40
- package/migrations/meta/0000_snapshot.json +0 -1632
- package/migrations/meta/0001_snapshot.json +0 -1660
- package/migrations/meta/0002_snapshot.json +0 -1660
- package/migrations/meta/0003_snapshot.json +0 -1689
- package/migrations/meta/0004_snapshot.json +0 -1721
- package/migrations/meta/_journal.json +0 -41
- /package/migrations/{0000_premium_famine.sql → 20251125021229_premium_famine/migration.sql} +0 -0
- /package/migrations/{0001_smooth_the_fury.sql → 20260225130050_smooth_the_fury/migration.sql} +0 -0
- /package/migrations/{0002_deep_iceman.sql → 20260308141417_deep_iceman/migration.sql} +0 -0
- /package/migrations/{0003_perfect_deathbird.sql → 20260308151309_perfect_deathbird/migration.sql} +0 -0
- /package/migrations/{0004_concerned_rawhide_kid.sql → 20260308201135_concerned_rawhide_kid/migration.sql} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { K as KeyAlgorithmType } from './types-DYyhze28.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @spfn/auth - Client Session Management
|
|
5
|
+
*
|
|
6
|
+
* Uses Jose JWE (JSON Web Encryption) to securely store session data in cookies
|
|
7
|
+
* More efficient than Iron Session with better Edge Runtime support
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface SessionData {
|
|
11
|
+
userId: string;
|
|
12
|
+
privateKey: string;
|
|
13
|
+
keyId: string;
|
|
14
|
+
algorithm: KeyAlgorithmType;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Seal session data into encrypted JWT (JWE)
|
|
18
|
+
*
|
|
19
|
+
* @param data - Session data to encrypt
|
|
20
|
+
* @param ttl - Time to live in seconds (default: 7 days)
|
|
21
|
+
* @returns Encrypted JWT string
|
|
22
|
+
*/
|
|
23
|
+
declare function sealSession(data: SessionData, ttl?: number): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Unseal encrypted JWT (JWE) to session data
|
|
26
|
+
*
|
|
27
|
+
* @param jwt - Encrypted JWT string
|
|
28
|
+
* @returns Session data
|
|
29
|
+
* @throws Error if session is invalid or expired
|
|
30
|
+
*/
|
|
31
|
+
declare function unsealSession(jwt: string): Promise<SessionData>;
|
|
32
|
+
/**
|
|
33
|
+
* Get session metadata without decrypting
|
|
34
|
+
*
|
|
35
|
+
* @param jwt - Encrypted JWT string
|
|
36
|
+
* @returns Session metadata or null if invalid
|
|
37
|
+
*/
|
|
38
|
+
declare function getSessionInfo(jwt: string): Promise<{
|
|
39
|
+
issuedAt: Date;
|
|
40
|
+
expiresAt: Date;
|
|
41
|
+
issuer: string;
|
|
42
|
+
audience: string;
|
|
43
|
+
} | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if session is about to expire (within threshold)
|
|
46
|
+
*
|
|
47
|
+
* @param jwt - Encrypted JWT string
|
|
48
|
+
* @param thresholdHours - Hours before expiry to trigger refresh (default: 24)
|
|
49
|
+
* @returns True if session should be refreshed
|
|
50
|
+
*/
|
|
51
|
+
declare function shouldRefreshSession(jwt: string, thresholdHours?: number): Promise<boolean>;
|
|
52
|
+
|
|
53
|
+
export { type SessionData as S, shouldRefreshSession as a, getSessionInfo as g, sealSession as s, unsealSession as u };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @spfn/auth - Shared Types
|
|
3
|
+
*
|
|
4
|
+
* Common types and constants used across the auth package
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Supported JWT signature algorithms
|
|
8
|
+
*
|
|
9
|
+
* - ES256: ECDSA with P-256 and SHA-256 (recommended, smaller keys)
|
|
10
|
+
* - RS256: RSA with SHA-256 (fallback, larger keys)
|
|
11
|
+
*/
|
|
12
|
+
declare const KEY_ALGORITHM: readonly ["ES256", "RS256"];
|
|
13
|
+
/**
|
|
14
|
+
* Key algorithm type derived from the const array
|
|
15
|
+
*/
|
|
16
|
+
type KeyAlgorithmType = typeof KEY_ALGORITHM[number];
|
|
17
|
+
/**
|
|
18
|
+
* Where a registered key lives, as the client declares it.
|
|
19
|
+
*
|
|
20
|
+
* Only for telling one entry apart from another in the key list — nothing is
|
|
21
|
+
* authorized or refused by it, so a client that lies gains nothing. Stored via
|
|
22
|
+
* `enumText`, so adding a value here needs no migration.
|
|
23
|
+
*/
|
|
24
|
+
declare const KEY_PLATFORM: readonly ["ios", "android", "web", "desktop"];
|
|
25
|
+
/**
|
|
26
|
+
* Key platform type derived from the const array
|
|
27
|
+
*/
|
|
28
|
+
type KeyPlatformType = typeof KEY_PLATFORM[number];
|
|
29
|
+
/** Longest device label accepted at registration, and what the list returns. */
|
|
30
|
+
declare const KEY_DEVICE_NAME_MAX_LENGTH = 64;
|
|
31
|
+
/**
|
|
32
|
+
* Invitation status enum values
|
|
33
|
+
* Single source of truth for all invitation statuses
|
|
34
|
+
*/
|
|
35
|
+
declare const INVITATION_STATUSES: readonly ["pending", "accepted", "expired", "cancelled"];
|
|
36
|
+
/**
|
|
37
|
+
* Invitation status type derived from the const array
|
|
38
|
+
*/
|
|
39
|
+
type InvitationStatus = typeof INVITATION_STATUSES[number];
|
|
40
|
+
/**
|
|
41
|
+
* User status enum values
|
|
42
|
+
* Single source of truth for all user statuses
|
|
43
|
+
*
|
|
44
|
+
* - active: Normal operation (default)
|
|
45
|
+
* - inactive: Deactivated (user request, dormant)
|
|
46
|
+
* - suspended: Locked (security incident, ToS violation)
|
|
47
|
+
* - pending_deletion: Deletion requested, within the grace period (recoverable)
|
|
48
|
+
* - deleted: Grace period elapsed and the account was purged (anonymize mode only —
|
|
49
|
+
* hard-delete removes the row instead, so this status never appears for it)
|
|
50
|
+
*/
|
|
51
|
+
declare const USER_STATUSES: readonly ["active", "inactive", "suspended", "pending_deletion", "deleted"];
|
|
52
|
+
/**
|
|
53
|
+
* User status type derived from the const array
|
|
54
|
+
*/
|
|
55
|
+
type UserStatus = typeof USER_STATUSES[number];
|
|
56
|
+
/**
|
|
57
|
+
* Social provider enum values
|
|
58
|
+
* Single source of truth for supported OAuth providers
|
|
59
|
+
*/
|
|
60
|
+
declare const SOCIAL_PROVIDERS: readonly ["google", "apple", "github", "kakao", "naver", "superself"];
|
|
61
|
+
/**
|
|
62
|
+
* Social provider type derived from the const array
|
|
63
|
+
*/
|
|
64
|
+
type SocialProvider = typeof SOCIAL_PROVIDERS[number];
|
|
65
|
+
/**
|
|
66
|
+
* Account deletion request status enum values
|
|
67
|
+
* Single source of truth for `account_deletion_requests.status`
|
|
68
|
+
*
|
|
69
|
+
* - pending: Awaiting the grace period (or immediate purge)
|
|
70
|
+
* - cancelled: User (or admin) recovered the account before purge
|
|
71
|
+
* - completed: The purge ran (row is kept as an audit record, never deleted)
|
|
72
|
+
*/
|
|
73
|
+
declare const ACCOUNT_DELETION_REQUEST_STATUSES: readonly ["pending", "cancelled", "completed"];
|
|
74
|
+
/**
|
|
75
|
+
* Account deletion request status type derived from the const array
|
|
76
|
+
*/
|
|
77
|
+
type AccountDeletionRequestStatus = typeof ACCOUNT_DELETION_REQUEST_STATUSES[number];
|
|
78
|
+
/**
|
|
79
|
+
* Who initiated an account deletion request
|
|
80
|
+
*/
|
|
81
|
+
declare const ACCOUNT_DELETION_REQUESTED_BY: readonly ["self", "admin"];
|
|
82
|
+
/**
|
|
83
|
+
* Account deletion requester type derived from the const array
|
|
84
|
+
*/
|
|
85
|
+
type AccountDeletionRequestedBy = typeof ACCOUNT_DELETION_REQUESTED_BY[number];
|
|
86
|
+
/**
|
|
87
|
+
* Purge strategy enum values
|
|
88
|
+
*
|
|
89
|
+
* - anonymize: Scrub PII, keep the row (status becomes 'deleted') — default
|
|
90
|
+
* - hard-delete: Physically remove the `users` row (cascades to child rows)
|
|
91
|
+
*/
|
|
92
|
+
declare const PURGE_STRATEGIES: readonly ["anonymize", "hard-delete"];
|
|
93
|
+
/**
|
|
94
|
+
* Purge strategy type derived from the const array
|
|
95
|
+
*/
|
|
96
|
+
type PurgeStrategy = typeof PURGE_STRATEGIES[number];
|
|
97
|
+
|
|
98
|
+
export { ACCOUNT_DELETION_REQUESTED_BY as A, INVITATION_STATUSES as I, type KeyAlgorithmType as K, PURGE_STRATEGIES as P, SOCIAL_PROVIDERS as S, USER_STATUSES as U, ACCOUNT_DELETION_REQUEST_STATUSES as a, type AccountDeletionRequestStatus as b, type AccountDeletionRequestedBy as c, type InvitationStatus as d, KEY_ALGORITHM as e, KEY_DEVICE_NAME_MAX_LENGTH as f, KEY_PLATFORM as g, type KeyPlatformType as h, type PurgeStrategy as i, type SocialProvider as j, type UserStatus as k };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/** The six wire codes. The SDKs classify by code, never HTTP status. */
|
|
2
|
+
type ClientProofErrorCode = 'PROOF_INVALID' | 'PROOF_REPLAYED' | 'PROOF_EXPIRED' | 'SESSION_REVOKED' | 'PROFILE_REJECTED' | 'CONTRACT_UNSUPPORTED';
|
|
3
|
+
/** 128 random bits as lowercase base16 — request ids and control tokens. */
|
|
4
|
+
declare function newHexId(): string;
|
|
5
|
+
declare class ClientProofRefusal {
|
|
6
|
+
readonly code: ClientProofErrorCode;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
constructor(code: ClientProofErrorCode, message: string);
|
|
9
|
+
get httpStatus(): number;
|
|
10
|
+
/** The canonical bytes of `{"error":{"code":…,"message":…,"requestId":…}}`. */
|
|
11
|
+
envelopeBytes(requestId: string): Uint8Array;
|
|
12
|
+
/** Nothing request-derived reaches a log through this. */
|
|
13
|
+
toString(): string;
|
|
14
|
+
static unroutable(): ClientProofRefusal;
|
|
15
|
+
static malformedHeaders(): ClientProofRefusal;
|
|
16
|
+
static missingContentType(): ClientProofRefusal;
|
|
17
|
+
static bodyTooLarge(): ClientProofRefusal;
|
|
18
|
+
/**
|
|
19
|
+
* The body parsed but its bytes are not the canonical form of what it
|
|
20
|
+
* parsed to. Not PROOF_INVALID even though it is discovered next to the
|
|
21
|
+
* proof: the proof over these bytes verifies perfectly well, and an
|
|
22
|
+
* auth-family answer would tell the client to re-handshake and send the
|
|
23
|
+
* same non-canonical bytes again.
|
|
24
|
+
*/
|
|
25
|
+
static bodyNotCanonical(): ClientProofRefusal;
|
|
26
|
+
static bodyNotTheDeclaredType(): ClientProofRefusal;
|
|
27
|
+
static sessionHeaderMisplaced(): ClientProofRefusal;
|
|
28
|
+
static unprocessable(): ClientProofRefusal;
|
|
29
|
+
/**
|
|
30
|
+
* A client that ships separately from the server said nothing about which
|
|
31
|
+
* contract it was built against. Without it the server cannot tell whether
|
|
32
|
+
* the two ends agree, and answering as though they do is what produces the
|
|
33
|
+
* undecodable body this check exists to replace.
|
|
34
|
+
*/
|
|
35
|
+
static contractVersionMissing(): ClientProofRefusal;
|
|
36
|
+
static contractVersionUnsupported(): ClientProofRefusal;
|
|
37
|
+
static profileRejected(): ClientProofRefusal;
|
|
38
|
+
/**
|
|
39
|
+
* A request that names a profile and presents Bearer credentials as well.
|
|
40
|
+
* The profile named is a real one, so this is not a shape the two ends
|
|
41
|
+
* disagree about: the request asked to be authenticated two ways at once
|
|
42
|
+
* and the profile it named is the one refused.
|
|
43
|
+
*/
|
|
44
|
+
static credentialsMixed(): ClientProofRefusal;
|
|
45
|
+
static sessionRevoked(): ClientProofRefusal;
|
|
46
|
+
static proofExpired(): ClientProofRefusal;
|
|
47
|
+
static proofReplayed(): ClientProofRefusal;
|
|
48
|
+
static proofInvalid(): ClientProofRefusal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The header names each end announces itself under.
|
|
53
|
+
*
|
|
54
|
+
* Separated from the logic that reads them so the contract bundle can name them
|
|
55
|
+
* without importing the version comparison, which reads the bundle back. These
|
|
56
|
+
* are declarations and depend on nothing.
|
|
57
|
+
*
|
|
58
|
+
* @module server/client-proof/wire-headers
|
|
59
|
+
*/
|
|
60
|
+
/** What a client says about itself, one header each. */
|
|
61
|
+
declare const CLIENT_IDENTITY_HEADERS: {
|
|
62
|
+
readonly kind: "x-spfn-client-kind";
|
|
63
|
+
readonly version: "x-spfn-client-version";
|
|
64
|
+
readonly contractVersion: "x-spfn-client-contract-version";
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* What the server says about itself, on every response.
|
|
68
|
+
*
|
|
69
|
+
* Distinct names from the request headers on purpose: a proxy that echoes a
|
|
70
|
+
* request header into the response would otherwise make the client's own
|
|
71
|
+
* version look like the server's.
|
|
72
|
+
*/
|
|
73
|
+
declare const SERVER_CONTRACT_HEADERS: {
|
|
74
|
+
readonly version: "x-spfn-server-contract-version";
|
|
75
|
+
readonly supportedRange: "x-spfn-supported-contract-range";
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The client kinds the server distinguishes.
|
|
79
|
+
*
|
|
80
|
+
* `web` is separated from the two app kinds because it carries no contract
|
|
81
|
+
* version: a browser bundle is deployed with the server that serves it, so
|
|
82
|
+
* there is no second version to reconcile.
|
|
83
|
+
*/
|
|
84
|
+
declare const CLIENT_KINDS: readonly ["web", "ios", "android"];
|
|
85
|
+
type ClientKind = typeof CLIENT_KINDS[number];
|
|
86
|
+
/** A kind that ships independently of the server, so its contract version matters. */
|
|
87
|
+
declare function isAppKind(kind: ClientKind): boolean;
|
|
88
|
+
|
|
89
|
+
/** What one request announced about the client that sent it. */
|
|
90
|
+
interface ClientIdentity {
|
|
91
|
+
kind: ClientKind;
|
|
92
|
+
/** The client's own release — a store version, or a bundle build. */
|
|
93
|
+
version: string | null;
|
|
94
|
+
/** The contract version the client was generated from. Never set for `web`. */
|
|
95
|
+
contractVersion: string | null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Reads the identity headers, or null when the kind is absent or unrecognised.
|
|
99
|
+
*
|
|
100
|
+
* Null is not by itself a refusal — a request from something that predates
|
|
101
|
+
* these headers reaches here too. `judgeClientIdentity` decides.
|
|
102
|
+
*/
|
|
103
|
+
declare function readClientIdentity(headers: Headers): ClientIdentity | null;
|
|
104
|
+
/**
|
|
105
|
+
* Whether the server serves what the client was generated against.
|
|
106
|
+
*
|
|
107
|
+
* Under 0.x the minor carries breaking changes, so a supported client agrees on
|
|
108
|
+
* major and minor. From 1.0.0 the major alone decides. This is the rule
|
|
109
|
+
* `CONTRACT_SUPPORTED_RANGE` spells out; keeping it as a comparison rather than
|
|
110
|
+
* parsing that string leaves one place to change when the line reaches 1.0.0.
|
|
111
|
+
*/
|
|
112
|
+
declare function isContractVersionSupported(clientVersion: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* The refusal a request's announced identity earns, or null to let it through.
|
|
115
|
+
*
|
|
116
|
+
* An app kind must state a contract version this server serves. A version it
|
|
117
|
+
* does not serve, and the absence of one, are the same answer: the two ends do
|
|
118
|
+
* not agree on what the contract is, which is what CONTRACT_UNSUPPORTED means.
|
|
119
|
+
* The response carries the server's version and range, so the client can say
|
|
120
|
+
* which way the gap runs.
|
|
121
|
+
*
|
|
122
|
+
* `web` is exempt from the contract check by construction, not by leniency.
|
|
123
|
+
*
|
|
124
|
+
* A request with no recognised kind passes. The check is on what a client says
|
|
125
|
+
* about itself, and a caller that says nothing — a curl, a health probe, a
|
|
126
|
+
* server-to-server call — is not a deployed client this rule is about.
|
|
127
|
+
*/
|
|
128
|
+
declare function judgeClientIdentity(identity: ClientIdentity | null): ClientProofRefusal | null;
|
|
129
|
+
/** Writes the server's own announcement onto a response's headers. */
|
|
130
|
+
declare function applyServerContractHeaders(headers: Headers): void;
|
|
131
|
+
/** The same announcement as a plain object, for a response built from one. */
|
|
132
|
+
declare function serverContractHeaders(): Record<string, string>;
|
|
133
|
+
|
|
134
|
+
export { ClientProofRefusal as C, SERVER_CONTRACT_HEADERS as S, CLIENT_IDENTITY_HEADERS as a, CLIENT_KINDS as b, type ClientIdentity as c, type ClientKind as d, type ClientProofErrorCode as e, applyServerContractHeaders as f, isContractVersionSupported as g, isAppKind as i, judgeClientIdentity as j, newHexId as n, readClientIdentity as r, serverContractHeaders as s };
|