@starci/auth 0.4.0 → 0.5.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/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/dist/server/errors.d.ts +2 -2
- package/dist/server/errors.js +3 -5
- package/dist/server/flows.js +4 -3
- package/dist/server/primitives.d.ts +0 -2
- package/dist/server/primitives.js +2 -3
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-11-08
|
|
4
|
+
|
|
5
|
+
- Changed: `AuthError` extends `DomainError<AuthErrorCode>` of `@starci/common` (new dependency); its name, codes, constructor, `kind`, `context` and `cause` are unchanged, and it gains `params` (always empty).
|
|
6
|
+
- Changed: the package's own non-array object guard is `isPlainRecord` of `@starci/common`.
|
|
7
|
+
- Changed: requires `@starci/common` 1.0.1 (exact), the one copy an app installs, so `DomainError` stays one class (this version had not reached npm).
|
|
8
|
+
|
|
3
9
|
## 0.4.0 - 2026-11-04
|
|
4
10
|
|
|
5
11
|
- Added: `RefreshCookieOptions.domains`: the refresh cookie's `Domain` follows the request host (its `Host` header without the port). A host equal to a listed domain, or ending in `.` and one, takes the longest that fits; any other host takes `domain`, else stays host-only, so one api signs in on localhost (host-only) and on a tunnel origin (its parent domain).
|
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ The sign-in of a lite StarCi app: one package, four entries.
|
|
|
12
12
|
|
|
13
13
|
An app reaches the package only through `integrations/passport` (back end) and `modules/auth/client.ts` (front end); the lint refuses `passport*`, `jose`, `bcryptjs` and `@nestjs/jwt` in an app.
|
|
14
14
|
|
|
15
|
+
The server throws one error class, `AuthError`: a `DomainError` of `@starci/common` whose `code` is a wire code, with the `kind` it travels as and a non-secret `context`.
|
|
16
|
+
|
|
15
17
|
## Back end
|
|
16
18
|
|
|
17
19
|
```ts
|
package/dist/server/errors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { DomainError } from "@starci/common";
|
|
1
2
|
import type { AuthErrorCode, AuthErrorKind } from "../contracts/index.js";
|
|
2
3
|
/** The one error class of sign-in: a wire code, the kind it travels as and optional context for the log. */
|
|
3
|
-
export declare class AuthError extends
|
|
4
|
-
readonly code: AuthErrorCode;
|
|
4
|
+
export declare class AuthError extends DomainError<AuthErrorCode> {
|
|
5
5
|
readonly kind: AuthErrorKind;
|
|
6
6
|
readonly context: Readonly<Record<string, unknown>>;
|
|
7
7
|
constructor(code: AuthErrorCode, options?: {
|
package/dist/server/errors.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
import { DomainError } from "@starci/common";
|
|
1
2
|
import { AUTH_ERROR_KINDS } from "../contracts/index.js";
|
|
2
3
|
/** The one error class of sign-in: a wire code, the kind it travels as and optional context for the log. */
|
|
3
|
-
export class AuthError extends
|
|
4
|
-
code;
|
|
4
|
+
export class AuthError extends DomainError {
|
|
5
5
|
kind;
|
|
6
6
|
context;
|
|
7
7
|
constructor(code, options = {}) {
|
|
8
|
-
super(
|
|
9
|
-
this.name = "AuthError";
|
|
10
|
-
this.code = code;
|
|
8
|
+
super(options.cause === undefined ? { code } : { code, cause: options.cause });
|
|
11
9
|
this.kind = AUTH_ERROR_KINDS[code];
|
|
12
10
|
this.context = options.context ?? {};
|
|
13
11
|
}
|
package/dist/server/flows.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import passport from "passport";
|
|
2
2
|
import { AUTH_ERROR_CODES } from "../contracts/index.js";
|
|
3
3
|
import { AuthError } from "./errors.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { isPlainRecord } from "@starci/common";
|
|
5
|
+
import { sameSitePath } from "./primitives.js";
|
|
6
|
+
const isSubject = (value) => isPlainRecord(value) && typeof value.userId === "string" && typeof value.email === "string";
|
|
6
7
|
const isHandler = (value) => typeof value === "function";
|
|
7
8
|
/** The middleware passport built; passport types it as any, so it is checked once here. */
|
|
8
9
|
const handlerOf = (built) => {
|
|
@@ -57,7 +58,7 @@ export class AuthFlows {
|
|
|
57
58
|
reject(new AuthError(AUTH_ERROR_CODES.GoogleStateInvalid));
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
|
-
const state =
|
|
61
|
+
const state = isPlainRecord(info) && isPlainRecord(info.state) ? info.state.next : undefined;
|
|
61
62
|
resolve({ subject: user, next: sameSitePath(state, "/") });
|
|
62
63
|
};
|
|
63
64
|
handlerOf(passport.authenticate("google", { session: false }, settled))(exchange.req, exchange.res, reject);
|
|
@@ -14,8 +14,6 @@ export interface SignedState {
|
|
|
14
14
|
readonly e: number;
|
|
15
15
|
readonly p?: string;
|
|
16
16
|
}
|
|
17
|
-
/** A record: a non-null object that is not an array. */
|
|
18
|
-
export declare const isRecord: (value: unknown) => value is Readonly<Record<string, unknown>>;
|
|
19
17
|
/** A same-site path (one leading slash, no line break), else the fallback path. */
|
|
20
18
|
export declare const sameSitePath: (value: unknown, fallback: string) => string;
|
|
21
19
|
/** Signs a state: base64url(JSON).base64url(HMAC-SHA256). */
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { isPlainRecord } from "@starci/common";
|
|
2
3
|
import { PASSWORD_BOUNDS } from "../contracts/index.js";
|
|
3
4
|
/** The text of a secret input. */
|
|
4
5
|
export const revealed = (secret) => (typeof secret === "string" ? secret : secret.reveal());
|
|
5
|
-
/** A record: a non-null object that is not an array. */
|
|
6
|
-
export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
6
|
const SAME_SITE_PATH = /^\/(?![/\\])/u;
|
|
8
7
|
const LINE_BREAK = /[\r\n]/u;
|
|
9
8
|
/** A same-site path (one leading slash, no line break), else the fallback path. */
|
|
10
9
|
export const sameSitePath = (value, fallback) => typeof value === "string" && SAME_SITE_PATH.test(value) && !LINE_BREAK.test(value) ? value : fallback;
|
|
11
10
|
const encode = (buffer) => buffer.toString("base64url");
|
|
12
|
-
const isSignedState = (value) =>
|
|
11
|
+
const isSignedState = (value) => isPlainRecord(value) &&
|
|
13
12
|
typeof value.w === "string" &&
|
|
14
13
|
typeof value.u === "string" &&
|
|
15
14
|
typeof value.r === "string" &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starci/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The sign-in of a StarCi lite app in one package: a NestJS module (EdDSA access tokens with a multi-key JWKS, bcrypt passwords, Google OAuth with state and PKCE, rotating refresh tokens with a reuse grace window and reuse detection, a failures-only sign-in limit, the refresh cookie), the door contracts, a front-end auth client (access token in memory, one shared refresh, signed out only on 401 or 403) with React bindings, and test doubles.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"prepublishOnly": "npm test && npm run build"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
+
"@starci/common": "1.0.1",
|
|
77
78
|
"@nestjs/passport": "11.0.5",
|
|
78
79
|
"bcryptjs": "3.0.3",
|
|
79
80
|
"jose": "6.2.12",
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
"@nestjs/common": "11.2.5",
|
|
107
108
|
"@nestjs/core": "11.2.5",
|
|
108
109
|
"@nestjs/testing": "11.2.5",
|
|
110
|
+
"@starci/common": "file:../common",
|
|
109
111
|
"@types/express": "4.17.25",
|
|
110
112
|
"@types/node": "22.20.4",
|
|
111
113
|
"@types/passport-google-oauth20": "2.0.17",
|
|
@@ -124,5 +126,7 @@
|
|
|
124
126
|
"publishConfig": {
|
|
125
127
|
"access": "public",
|
|
126
128
|
"provenance": true
|
|
127
|
-
}
|
|
129
|
+
},
|
|
130
|
+
"gitHead": "f464f759079854716389ba92feb7e4a1512a0ee3",
|
|
131
|
+
"starciSource": "d302420a970828a385f4e2325e7ed2b3e34b10e48f9d4d552326ff9fa292d945"
|
|
128
132
|
}
|