@micacms/auth-oidc 0.2.1 → 0.3.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 CHANGED
@@ -3,7 +3,7 @@
3
3
  > **Experimental.** Pre-1.0: the API can still change without notice.
4
4
 
5
5
  The server-only OIDC gate for Mica: the authorization code flow with PKCE,
6
- sessions in SQLite, admission rules over provider claims, and the middleware
6
+ sessions in an encrypted cookie, admission rules over provider claims, and the middleware
7
7
  gate the runtime mounts in front of the admin API.
8
8
 
9
9
  It is a dependency of `@micacms/self-hosted` and of nothing else. In
@@ -11,13 +11,14 @@
11
11
  *
12
12
  * MICA_ADMIT_GROUP=mica-editors a claim the provider sends
13
13
  * MICA_ADMIT_DOMAIN=example.com every verified address there
14
+ * MICA_ADMIT_EMAILS=alice@gmail.com these verified addresses
14
15
  * MICA_ADMIT_SUBJECTS=https://id.example.com|a1b2c3
15
16
  *
16
17
  * The group rule is the one to grow into. Adding an editor is then a group
17
18
  * change in the identity provider — where the operator already manages
18
19
  * joiners and leavers — and Mica is not touched, not restarted, and holds no
19
- * copy of the list to go stale. The subject list exists for a provider with no
20
- * groups, and for the first sign-in before a group exists.
20
+ * copy of the list to go stale. The email list exists for a provider with no
21
+ * groups, such as Google; the subject list for one that verifies no addresses.
21
22
  *
22
23
  * What this costs, and it is a real cost: removing somebody from a group at
23
24
  * the provider does not take effect until their next sign-in, because the
@@ -27,7 +28,7 @@
27
28
  * decision in docs/decisions.md.
28
29
  */
29
30
  import type { AuthConfig } from "./config.js";
30
- import type { SessionIdentity } from "./sessions-repository.js";
31
+ import type { SessionIdentity } from "./session.js";
31
32
  export type AdmissionResult = {
32
33
  admitted: true;
33
34
  } | {
@@ -39,5 +40,5 @@ export interface AdmissionPolicy {
39
40
  readonly summary: string;
40
41
  admits(identity: SessionIdentity): AdmissionResult;
41
42
  }
42
- export declare function createAdmissionPolicy(config: Pick<AuthConfig, "admitGroups" | "admitDomains" | "admitSubjects">): AdmissionPolicy;
43
+ export declare function createAdmissionPolicy(config: Pick<AuthConfig, "admitGroups" | "admitDomains" | "admitEmails" | "admitSubjects">): AdmissionPolicy;
43
44
  //# sourceMappingURL=admission.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"admission.d.ts","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CAAC;CACpD;AAsCD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,cAAc,GAAG,eAAe,CAAC,GACzE,eAAe,CA+CjB"}
1
+ {"version":3,"file":"admission.d.ts","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CAAC;CACpD;AAsCD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,eAAe,CAAC,GACzF,eAAe,CAsDjB"}
package/dist/admission.js CHANGED
@@ -11,13 +11,14 @@
11
11
  *
12
12
  * MICA_ADMIT_GROUP=mica-editors a claim the provider sends
13
13
  * MICA_ADMIT_DOMAIN=example.com every verified address there
14
+ * MICA_ADMIT_EMAILS=alice@gmail.com these verified addresses
14
15
  * MICA_ADMIT_SUBJECTS=https://id.example.com|a1b2c3
15
16
  *
16
17
  * The group rule is the one to grow into. Adding an editor is then a group
17
18
  * change in the identity provider — where the operator already manages
18
19
  * joiners and leavers — and Mica is not touched, not restarted, and holds no
19
- * copy of the list to go stale. The subject list exists for a provider with no
20
- * groups, and for the first sign-in before a group exists.
20
+ * copy of the list to go stale. The email list exists for a provider with no
21
+ * groups, such as Google; the subject list for one that verifies no addresses.
21
22
  *
22
23
  * What this costs, and it is a real cost: removing somebody from a group at
23
24
  * the provider does not take effect until their next sign-in, because the
@@ -27,17 +28,16 @@
27
28
  * decision in docs/decisions.md.
28
29
  */
29
30
  /**
30
- * The subject is the account key, and an email address is never one.
31
- *
32
- * Most providers let a person change their address, and some let two people
33
- * hold one in turn. Keying admission on an address is therefore a standing
34
- * offer to whoever holds it next, which is a well-trodden route to account
35
- * takeover. The issuer is part of the key because a subject is only unique
36
- * within the provider that issued it.
31
+ * The strictest rule: the subject never changes and is never reassigned. The
32
+ * issuer is part of the key because a subject is only unique within the
33
+ * provider that issued it.
37
34
  */
38
35
  function matchesSubject(identity, entries) {
39
36
  return entries.some((entry) => entry.issuer === identity.issuer && entry.subject === identity.subject);
40
37
  }
38
+ function matchesEmail(identity, emails) {
39
+ return emails.includes(identity.email.toLowerCase());
40
+ }
41
41
  function matchesDomain(identity, domains) {
42
42
  const at = identity.email.lastIndexOf("@");
43
43
  if (at < 0)
@@ -56,10 +56,11 @@ function matchesGroup(identity, groups) {
56
56
  return groups.some((group) => held.has(group.replace(/^\//, "")));
57
57
  }
58
58
  export function createAdmissionPolicy(config) {
59
- const { admitGroups, admitDomains, admitSubjects } = config;
59
+ const { admitGroups, admitDomains, admitEmails, admitSubjects } = config;
60
60
  const summary = [
61
61
  admitGroups.length > 0 ? `group=${admitGroups.join("|")}` : null,
62
62
  admitDomains.length > 0 ? `domain=${admitDomains.join("|")}` : null,
63
+ admitEmails.length > 0 ? `emails=${admitEmails.length}` : null,
63
64
  admitSubjects.length > 0 ? `subjects=${admitSubjects.length}` : null,
64
65
  ]
65
66
  .filter(Boolean)
@@ -73,22 +74,28 @@ export function createAdmissionPolicy(config) {
73
74
  return { admitted: true };
74
75
  if (matchesGroup(identity, admitGroups))
75
76
  return { admitted: true };
76
- if (admitDomains.length > 0 && matchesDomain(identity, admitDomains)) {
77
+ const byEmail = matchesEmail(identity, admitEmails);
78
+ if (byEmail || matchesDomain(identity, admitDomains)) {
77
79
  /**
78
- * A domain rule trusts the provider to have checked the address.
80
+ * An email or domain rule trusts the provider to have checked the address.
79
81
  *
80
82
  * Where a provider allows self-registration, an unverified address is
81
83
  * a claim the person typed. Admitting on that would let anybody enter
82
- * a domain they do not hold, so the rule refuses and says why — the
84
+ * an address they do not hold, so the rule refuses and says why — the
83
85
  * operator can turn verification on, or use a group or subject rule
84
86
  * instead.
87
+ *
88
+ * What remains is reassignment: an organisation that gives a former
89
+ * editor's address to somebody new gives them this site too. Public
90
+ * providers such as Gmail never reuse an address; the trade is
91
+ * recorded in docs/decisions.md.
85
92
  */
86
93
  if (identity.emailVerified)
87
94
  return { admitted: true };
88
95
  return {
89
96
  admitted: false,
90
- reason: `${identity.email} is in an admitted domain, but the identity provider ` +
91
- "does not report the address as verified.",
97
+ reason: `${identity.email} is ${byEmail ? "an admitted address" : "in an admitted domain"}, ` +
98
+ "but the identity provider does not report the address as verified.",
92
99
  };
93
100
  }
94
101
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"admission.js","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAaH;;;;;;;;GAQG;AACH,SAAS,cAAc,CACrB,QAAyB,EACzB,OAA2D;IAE3D,OAAO,OAAO,CAAC,IAAI,CACjB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,CAClF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,QAAyB,EAAE,OAA0B;IAC1E,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,QAAyB,EAAE,MAAyB;IACxE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAA0E;IAE1E,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAE5D,MAAM,OAAO,GAAG;QACd,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QAChE,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QACnE,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;KACrE;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,OAAO;QAEP,MAAM,CAAC,QAAQ;YACb,sEAAsE;YACtE,gEAAgE;YAChE,IAAI,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YAEvE,IAAI,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YAEnE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,aAAa,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;gBACrE;;;;;;;;mBAQG;gBACH,IAAI,QAAQ,CAAC,aAAa;oBAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAEtD,OAAO;oBACL,QAAQ,EAAE,KAAK;oBACf,MAAM,EACJ,GAAG,QAAQ,CAAC,KAAK,uDAAuD;wBACxE,0CAA0C;iBAC7C,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,OAAO,gCAAgC;aAC9E,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"admission.js","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -28,11 +28,13 @@ export declare const authEnv: {
28
28
  /**
29
29
  * Who may edit this site. Authentication says who somebody is; these say
30
30
  * whether that person gets in. At least one is required — the composition
31
- * refuses to start without one. Comma-separated, all three.
31
+ * refuses to start without one. Comma-separated, all four.
32
32
  */
33
33
  MICA_ADMIT_GROUP: z.ZodOptional<z.ZodString>;
34
34
  MICA_ADMIT_DOMAIN: z.ZodOptional<z.ZodString>;
35
- /** `issuer|subject` pairs. Never an email — see admission.ts. */
35
+ /** Verified addresses — the list for a provider with no groups, such as Google. */
36
+ MICA_ADMIT_EMAILS: z.ZodOptional<z.ZodString>;
37
+ /** `issuer|subject` pairs — see admission.ts. */
36
38
  MICA_ADMIT_SUBJECTS: z.ZodOptional<z.ZodString>;
37
39
  /** Signs session cookies. Rotating it invalidates every session. */
38
40
  SESSION_SECRET: z.ZodOptional<z.ZodString>;
@@ -55,6 +57,7 @@ declare const authSchema: z.ZodObject<{
55
57
  MICA_OIDC_GROUPS_CLAIM: z.ZodDefault<z.ZodString>;
56
58
  MICA_ADMIT_GROUP: z.ZodOptional<z.ZodString>;
57
59
  MICA_ADMIT_DOMAIN: z.ZodOptional<z.ZodString>;
60
+ MICA_ADMIT_EMAILS: z.ZodOptional<z.ZodString>;
58
61
  MICA_ADMIT_SUBJECTS: z.ZodOptional<z.ZodString>;
59
62
  SESSION_SECRET: z.ZodOptional<z.ZodString>;
60
63
  SESSION_TTL_HOURS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -75,6 +78,7 @@ export declare function deriveAuth<T extends AuthEnv & {
75
78
  oidcAllowInsecure: boolean;
76
79
  admitGroups: string[];
77
80
  admitDomains: string[];
81
+ admitEmails: string[];
78
82
  admitSubjects: {
79
83
  issuer: string;
80
84
  subject: string;
@@ -93,6 +97,7 @@ export interface AuthConfig {
93
97
  oidcAllowInsecure: boolean;
94
98
  admitGroups: string[];
95
99
  admitDomains: string[];
100
+ admitEmails: string[];
96
101
  admitSubjects: Array<{
97
102
  issuer: string;
98
103
  subject: string;
@@ -117,6 +122,7 @@ export declare function requireAuthConfig(config: {
117
122
  MICA_OIDC_CLIENT_ID?: string | undefined;
118
123
  admitGroups: string[];
119
124
  admitDomains: string[];
125
+ admitEmails: string[];
120
126
  admitSubjects: Array<{
121
127
  issuer: string;
122
128
  subject: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,OAAO;IAClB;;;;OAIG;;;IAGH,iDAAiD;;;IAGjD;;;;;OAKG;;IAGH;;;;OAIG;;;IAGH,iEAAiE;;IAGjE,oEAAoE;;IAEpE;;;;;OAKG;;;;;CAUJ,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;;;iBAAoB,CAAC;AACrC,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEjD,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,EAC1F,GAAG,EAAE,CAAC;IAIJ;;;OAGG;;;;;;;;;EAqBN;AAED,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D,GAAG,IAAI,CA2CP"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,OAAO;IAClB;;;;OAIG;;;IAGH,iDAAiD;;;IAGjD;;;;;OAKG;;IAGH;;;;OAIG;;;IAGH,mFAAmF;;IAEnF,iDAAiD;;IAGjD,oEAAoE;;IAEpE;;;;;OAKG;;;;;CAUJ,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;iBAAoB,CAAC;AACrC,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEjD,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,EAC1F,GAAG,EAAE,CAAC;IAIJ;;;OAGG;;;;;;;;;;EAsBN;AAED,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AA0BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D,GAAG,IAAI,CA4DP"}
package/dist/config.js CHANGED
@@ -29,11 +29,13 @@ export const authEnv = {
29
29
  /**
30
30
  * Who may edit this site. Authentication says who somebody is; these say
31
31
  * whether that person gets in. At least one is required — the composition
32
- * refuses to start without one. Comma-separated, all three.
32
+ * refuses to start without one. Comma-separated, all four.
33
33
  */
34
34
  MICA_ADMIT_GROUP: z.string().optional(),
35
35
  MICA_ADMIT_DOMAIN: z.string().optional(),
36
- /** `issuer|subject` pairs. Never an email — see admission.ts. */
36
+ /** Verified addresses — the list for a provider with no groups, such as Google. */
37
+ MICA_ADMIT_EMAILS: z.string().optional(),
38
+ /** `issuer|subject` pairs — see admission.ts. */
37
39
  MICA_ADMIT_SUBJECTS: z.string().optional(),
38
40
  /** Signs session cookies. Rotating it invalidates every session. */
39
41
  SESSION_SECRET: z.string().min(32).optional(),
@@ -67,6 +69,7 @@ export function deriveAuth(env) {
67
69
  // Lowercased: a domain is case-insensitive, and an operator who writes
68
70
  // `Example.com` means the same thing the provider spells `example.com`.
69
71
  admitDomains: list(env.MICA_ADMIT_DOMAIN).map((domain) => domain.toLowerCase().replace(/^@/, "")),
72
+ admitEmails: list(env.MICA_ADMIT_EMAILS).map((email) => email.toLowerCase()),
70
73
  admitSubjects: list(env.MICA_ADMIT_SUBJECTS).map((entry) => {
71
74
  const separator = entry.indexOf("|");
72
75
  return {
@@ -79,6 +82,29 @@ export function deriveAuth(env) {
79
82
  cookieSecure: env.MICA_PUBLIC_URL.startsWith("https://"),
80
83
  };
81
84
  }
85
+ /**
86
+ * Domains where anybody can register an address. A domain rule naming one
87
+ * admits the public, so it is refused; name the addresses instead.
88
+ */
89
+ const PUBLIC_EMAIL_DOMAINS = new Set([
90
+ "gmail.com",
91
+ "googlemail.com",
92
+ "outlook.com",
93
+ "hotmail.com",
94
+ "live.com",
95
+ "msn.com",
96
+ "yahoo.com",
97
+ "icloud.com",
98
+ "me.com",
99
+ "aol.com",
100
+ "proton.me",
101
+ "protonmail.com",
102
+ "gmx.com",
103
+ "gmx.net",
104
+ "gmx.de",
105
+ "gmx.ch",
106
+ "web.de",
107
+ ]);
82
108
  /**
83
109
  * The refusals that make a deployment honest before it serves a request.
84
110
  *
@@ -96,23 +122,34 @@ export function requireAuthConfig(config) {
96
122
  }
97
123
  if (config.admitGroups.length === 0 &&
98
124
  config.admitDomains.length === 0 &&
125
+ config.admitEmails.length === 0 &&
99
126
  config.admitSubjects.length === 0) {
100
127
  throw new ConfigError("Nothing says who may edit this site.\n" +
101
128
  " Authentication is not admission: your provider will happily vouch for\n" +
102
129
  " everybody it knows. Set at least one of these:\n\n" +
103
130
  " MICA_ADMIT_GROUP=mica-editors a claim your provider sends\n" +
104
131
  " MICA_ADMIT_DOMAIN=example.com every verified address there\n" +
132
+ " MICA_ADMIT_EMAILS=alice@gmail.com these verified addresses\n" +
105
133
  " MICA_ADMIT_SUBJECTS=https://id.example.com|a1b2c3\n\n" +
106
134
  " The group rule is the one to grow into: adding an editor is then a\n" +
107
135
  " change in the identity provider, and Mica is not touched.");
108
136
  }
137
+ const open = config.admitDomains.filter((domain) => PUBLIC_EMAIL_DOMAINS.has(domain));
138
+ if (open.length > 0) {
139
+ throw new ConfigError(`MICA_ADMIT_DOMAIN names ${open.join(", ")}, where anybody can register an address.\n` +
140
+ " That rule would let the public edit this site. Name the people instead:\n" +
141
+ " MICA_ADMIT_EMAILS=alice@gmail.com,bob@gmail.com");
142
+ }
143
+ const malformed = config.admitEmails.filter((email) => !/^[^@\s]+@[^@\s]+$/.test(email));
144
+ if (malformed.length > 0) {
145
+ throw new ConfigError(`MICA_ADMIT_EMAILS entries are email addresses, and these are not: ${malformed.join(", ")}`);
146
+ }
109
147
  const wrong = config.admitSubjects.filter((entry) => !entry.issuer || !entry.subject);
110
148
  if (wrong.length > 0) {
111
149
  throw new ConfigError("MICA_ADMIT_SUBJECTS entries are `issuer|subject` pairs, and one is not:\n" +
112
150
  ` ${wrong.map((entry) => entry.subject || "(empty)").join(", ")}\n\n` +
113
151
  " The issuer is part of the key because a subject is only unique within\n" +
114
- " the provider that issued it. It is never an email address: providers\n" +
115
- " let people change those, and two people can hold one in turn.\n" +
152
+ " the provider that issued it. To admit an address, use MICA_ADMIT_EMAILS.\n" +
116
153
  ` MICA_ADMIT_SUBJECTS=${config.MICA_OIDC_ISSUER ?? "https://id.example.com"}|a1b2c3`);
117
154
  }
118
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/gate.d.ts CHANGED
@@ -3,19 +3,18 @@
3
3
  *
4
4
  * Two layers, in order. `requireAccess` turns a session cookie into an
5
5
  * `AccessContext` — or a 401 — re-checking admission on every request so a
6
- * configuration change is immediate. `csrfProtection` is the double-submit
6
+ * configuration change is immediate, and re-issuing the cookie while the
7
+ * session is in use. `csrfProtection` is the double-submit
7
8
  * token over the Origin check the runtime already applies: cross-origin
8
9
  * JavaScript can neither read the cookie nor set the header.
9
10
  */
10
11
  import type { Context, MiddlewareHandler } from "hono";
11
12
  import { type Env } from "@micacms/runtime";
12
13
  import type { AdmissionPolicy } from "./admission.js";
13
- import { type SessionService } from "./session.js";
14
- import type { SessionRecord, SessionRepository } from "./sessions-repository.js";
14
+ import { type SessionRecord, type SessionService } from "./session.js";
15
15
  export declare function sessionOf(c: Context<Env>): SessionRecord;
16
16
  export interface GateDeps {
17
17
  sessions: SessionService;
18
- sessionsRepo: SessionRepository;
19
18
  admission: AdmissionPolicy;
20
19
  }
21
20
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAuD,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAQjF,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,aAAa,CAGxD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAgCpE;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAiBvD"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAQtB,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,aAAa,CAGxD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAiCpE;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAiBvD"}
package/dist/gate.js CHANGED
@@ -3,13 +3,14 @@
3
3
  *
4
4
  * Two layers, in order. `requireAccess` turns a session cookie into an
5
5
  * `AccessContext` — or a 401 — re-checking admission on every request so a
6
- * configuration change is immediate. `csrfProtection` is the double-submit
6
+ * configuration change is immediate, and re-issuing the cookie while the
7
+ * session is in use. `csrfProtection` is the double-submit
7
8
  * token over the Origin check the runtime already applies: cross-origin
8
9
  * JavaScript can neither read the cookie nor set the header.
9
10
  */
10
- import { getCookie } from "hono/cookie";
11
+ import { getCookie, setCookie } from "hono/cookie";
11
12
  import { ApiError } from "@micacms/runtime";
12
- import { CSRF_COOKIE, CSRF_HEADER, safeEqual, SESSION_COOKIE } from "./session.js";
13
+ import { CSRF_COOKIE, CSRF_HEADER, safeEqual, SESSION_COOKIE, } from "./session.js";
13
14
  /**
14
15
  * The session behind the current request. Auth's own context variable —
15
16
  * the runtime knows only the `access` it derives from it.
@@ -31,18 +32,11 @@ export function sessionOf(c) {
31
32
  */
32
33
  export function requireAccess(deps) {
33
34
  return async (c, next) => {
34
- const session = deps.sessions.resolve(getCookie(c, SESSION_COOKIE));
35
+ let session = deps.sessions.resolve(getCookie(c, SESSION_COOKIE));
35
36
  if (!session) {
36
37
  throw new ApiError("unauthenticated", "Your session has expired. Please sign in again.");
37
38
  }
38
39
  const { identity } = session;
39
- if (!identity) {
40
- // A row from a pre-split local-mode run of the same database. It
41
- // authenticates nobody, so it is not a session for an authenticated
42
- // deployment.
43
- deps.sessionsRepo.delete(session.id);
44
- throw new ApiError("unauthenticated", "Please sign in again.");
45
- }
46
40
  const verdict = deps.admission.admits(identity);
47
41
  if (!verdict.admitted) {
48
42
  c.get("logger").warn("Session no longer admitted", {
@@ -51,11 +45,18 @@ export function requireAccess(deps) {
51
45
  });
52
46
  throw new ApiError("forbidden", "You no longer have access to this site.");
53
47
  }
48
+ const refreshed = deps.sessions.refresh(session);
49
+ if (refreshed)
50
+ session = refreshed.record;
54
51
  c.set(SESSION_VARIABLE, session);
55
52
  c.set("access", {
56
53
  identity: { subject: identity.subject, email: identity.email, name: identity.name },
57
54
  });
58
55
  await next();
56
+ // After the handler, so the cookie lands on whatever response it built.
57
+ if (refreshed) {
58
+ setCookie(c, SESSION_COOKIE, refreshed.cookie, deps.sessions.sessionCookieOptions());
59
+ }
59
60
  };
60
61
  }
61
62
  const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
package/dist/gate.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @micacms/auth-oidc — the server-only OIDC gate around @micacms/runtime.
3
3
  *
4
- * One factory: hand it the finished configuration, an open database and a
5
- * logger, and it returns the `AuthSurface` the runtime mounts — sign-in
4
+ * One factory: hand it the finished configuration and a logger, and it
5
+ * returns the `AuthSurface` the runtime mounts — sign-in
6
6
  * routes at `/api/auth`, and the session-plus-CSRF gate in front of the admin
7
7
  * API — together with the services the composing app reports on.
8
8
  *
@@ -10,24 +10,20 @@
10
10
  * The local editor must never gain a path to it: that absence is the product
11
11
  * boundary that keeps `npx @micacms/editor` free of authentication code.
12
12
  */
13
- import type { AuthSurface, Logger, SqlDatabase } from "@micacms/runtime";
13
+ import type { AuthSurface, Logger } from "@micacms/runtime";
14
14
  import { type RateLimiter } from "@micacms/runtime";
15
15
  import { type AdmissionPolicy } from "./admission.js";
16
16
  import type { AuthConfig } from "./config.js";
17
17
  import { type OidcService } from "./oidc.js";
18
18
  import { type SessionService } from "./session.js";
19
- import { type SessionRepository } from "./sessions-repository.js";
20
19
  export interface OidcAuthDeps {
21
20
  config: AuthConfig;
22
- /** The database sessions live in. Owned and opened by the composing app. */
23
- db: SqlDatabase;
24
21
  logger: Logger;
25
22
  }
26
23
  export interface OidcAuth {
27
24
  /** What `createApp` mounts: routes at /api/auth, the gate before /api/admin. */
28
25
  surface: AuthSurface;
29
26
  sessions: SessionService;
30
- sessionsRepo: SessionRepository;
31
27
  admission: AdmissionPolicy;
32
28
  oidc: OidcService;
33
29
  loginLimiter: RateLimiter;
@@ -37,6 +33,5 @@ export { createAdmissionPolicy, type AdmissionPolicy, type AdmissionResult } fro
37
33
  export { authEnv, deriveAuth, requireAuthConfig, type AuthConfig, type AuthEnv, } from "./config.js";
38
34
  export { csrfProtection, requireAccess, sessionOf } from "./gate.js";
39
35
  export { createOidcService, type OidcService } from "./oidc.js";
40
- export { createSessionService, CSRF_COOKIE, CSRF_HEADER, SESSION_COOKIE, type SessionService, } from "./session.js";
41
- export { createSessionRepository, type SessionIdentity, type SessionRecord, type SessionRepository, } from "./sessions-repository.js";
36
+ export { createSessionService, CSRF_COOKIE, CSRF_HEADER, SESSION_COOKIE, type SessionIdentity, type SessionRecord, type SessionService, } from "./session.js";
42
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,4EAA4E;IAC5E,EAAE,EAAE,WAAW,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,gFAAgF;IAChF,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAyB3D;AAED,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACnG,OAAO,EACL,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,EACd,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,gFAAgF;IAChF,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAuB3D;AAED,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACnG,OAAO,EACL,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @micacms/auth-oidc — the server-only OIDC gate around @micacms/runtime.
3
3
  *
4
- * One factory: hand it the finished configuration, an open database and a
5
- * logger, and it returns the `AuthSurface` the runtime mounts — sign-in
4
+ * One factory: hand it the finished configuration and a logger, and it
5
+ * returns the `AuthSurface` the runtime mounts — sign-in
6
6
  * routes at `/api/auth`, and the session-plus-CSRF gate in front of the admin
7
7
  * API — together with the services the composing app reports on.
8
8
  *
@@ -16,25 +16,22 @@ import { csrfProtection, requireAccess } from "./gate.js";
16
16
  import { createOidcService } from "./oidc.js";
17
17
  import { authRoutes } from "./routes.js";
18
18
  import { createSessionService } from "./session.js";
19
- import { createSessionRepository } from "./sessions-repository.js";
20
19
  export function createOidcAuth(deps) {
21
20
  const { config, logger } = deps;
22
- const sessionsRepo = createSessionRepository(deps.db);
23
- const sessions = createSessionService(sessionsRepo, config);
21
+ const sessions = createSessionService(config);
24
22
  const admission = createAdmissionPolicy(config);
25
23
  const oidc = createOidcService({ config, logger });
26
24
  const loginLimiter = createRateLimiter({
27
25
  limit: config.LOGIN_MAX_ATTEMPTS,
28
26
  windowMs: config.LOGIN_WINDOW_MS,
29
27
  });
30
- const gate = { sessions, sessionsRepo, admission };
28
+ const gate = { sessions, admission };
31
29
  return {
32
30
  surface: {
33
- routes: authRoutes({ config, sessions, sessionsRepo, oidc, admission, loginLimiter }),
31
+ routes: authRoutes({ config, sessions, oidc, admission, loginLimiter }),
34
32
  gate: [requireAccess(gate), csrfProtection()],
35
33
  },
36
34
  sessions,
37
- sessionsRepo,
38
35
  admission,
39
36
  oidc,
40
37
  loginLimiter,
@@ -45,5 +42,4 @@ export { authEnv, deriveAuth, requireAuthConfig, } from "./config.js";
45
42
  export { csrfProtection, requireAccess, sessionOf } from "./gate.js";
46
43
  export { createOidcService } from "./oidc.js";
47
44
  export { createSessionService, CSRF_COOKIE, CSRF_HEADER, SESSION_COOKIE, } from "./session.js";
48
- export { createSessionRepository, } from "./sessions-repository.js";
49
45
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,iBAAiB,EAAoB,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAwB,MAAM,gBAAgB,CAAC;AAE7E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAA0B,MAAM,0BAA0B,CAAC;AAmB3F,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAEhC,MAAM,YAAY,GAAG,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,iBAAiB,CAAC;QACrC,KAAK,EAAE,MAAM,CAAC,kBAAkB;QAChC,QAAQ,EAAE,MAAM,CAAC,eAAe;KACjC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IAEnD,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;YACrF,IAAI,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;SAC9C;QACD,QAAQ;QACR,YAAY;QACZ,SAAS;QACT,IAAI;QACJ,YAAY;KACb,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,qBAAqB,EAA8C,MAAM,gBAAgB,CAAC;AACnG,OAAO,EACL,OAAO,EACP,UAAU,EACV,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,GAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,GAIxB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,iBAAiB,EAAoB,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAwB,MAAM,gBAAgB,CAAC;AAE7E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AAgBzE,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAEhC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,iBAAiB,CAAC;QACrC,KAAK,EAAE,MAAM,CAAC,kBAAkB;QAChC,QAAQ,EAAE,MAAM,CAAC,eAAe;KACjC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAErC,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;YACvE,IAAI,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;SAC9C;QACD,QAAQ;QACR,SAAS;QACT,IAAI;QACJ,YAAY;KACb,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,qBAAqB,EAA8C,MAAM,gBAAgB,CAAC;AACnG,OAAO,EACL,OAAO,EACP,UAAU,EACV,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,GAIf,MAAM,cAAc,CAAC"}
package/dist/routes.d.ts CHANGED
@@ -15,12 +15,10 @@ import { type Env, type RateLimiter } from "@micacms/runtime";
15
15
  import type { AdmissionPolicy } from "./admission.js";
16
16
  import type { AuthConfig } from "./config.js";
17
17
  import { type SessionService } from "./session.js";
18
- import type { SessionRepository } from "./sessions-repository.js";
19
18
  import { type OidcService } from "./oidc.js";
20
19
  export interface AuthRoutesDeps {
21
20
  config: AuthConfig;
22
21
  sessions: SessionService;
23
- sessionsRepo: SessionRepository;
24
22
  oidc: OidcService;
25
23
  admission: AdmissionPolicy;
26
24
  loginLimiter: RateLimiter;
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAsB,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAmB,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,WAAW,CAAC;AAiBnB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,iBAAiB,CAAC;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAkI1D"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAsB,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,WAAW,CAAC;AAiBnB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAsI1D"}
package/dist/routes.js CHANGED
@@ -108,12 +108,18 @@ export function authRoutes(deps) {
108
108
  return refuse(`You are signed in as ${claims.email}, but that account has no access to ` +
109
109
  "this site. Ask whoever runs it to add you.");
110
110
  }
111
- establishSession(c, sessions, identity);
111
+ try {
112
+ establishSession(c, sessions, identity);
113
+ }
114
+ catch (error) {
115
+ logger.error("Could not start a session", { subject: identity.subject, error });
116
+ return refuse("Sign-in could not be completed: your account details are too large.");
117
+ }
112
118
  logger.info("Signed in", { subject: identity.subject, issuer: identity.issuer });
113
119
  return c.redirect(transaction.returnTo);
114
120
  });
121
+ // There is no server-side session to end; clearing the cookies is sign-out.
115
122
  app.post("/logout", async (c) => {
116
- sessions.destroy(getCookie(c, SESSION_COOKIE));
117
123
  deleteCookie(c, SESSION_COOKIE, sessions.clearedCookieOptions());
118
124
  deleteCookie(c, CSRF_COOKIE, sessions.clearedCookieOptions());
119
125
  // Offered, not followed: ending the provider's session is the provider's
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
package/dist/session.d.ts CHANGED
@@ -1,48 +1,64 @@
1
1
  /**
2
2
  * Session management.
3
3
  *
4
- * Server-side sessions in SQLite, addressed by an opaque random token held in a
5
- * Secure/HttpOnly/SameSite cookie. No JWTs: revocation has to be immediate, and
6
- * a row delete is the simplest way to get it.
4
+ * The session is the cookie: who the provider said this is, a CSRF token and
5
+ * an expiry, encrypted with a key derived from SESSION_SECRET. There is no
6
+ * session table, so Mica stores nothing about people at all.
7
7
  *
8
- * Three layers of defence on the cookie value:
9
- * 1. the token is 256 bits from `crypto.randomBytes`;
10
- * 2. it is HMAC-signed with SESSION_SECRET, so forged or truncated cookies
11
- * are rejected without touching the database;
12
- * 3. only its SHA-256 is stored, so a database dump yields no usable session.
8
+ * - AES-256-GCM, so the cookie can be neither read nor altered. Encrypted and
9
+ * not only signed, because it carries an email address and groups.
10
+ * - A fresh random IV per cookie, and a new cookie at every sign-in, so a
11
+ * client-supplied value is never adopted (session fixation).
12
+ * - The idle window slides: an active session is re-issued every few
13
+ * minutes with a later expiry, capped by the absolute lifetime.
13
14
  *
14
- * Session fixation is handled by never adopting a client-supplied identifier —
15
- * a token is minted at login and the previous one deleted.
15
+ * What this gives up is server-side revocation of one cookie. Sign-out clears
16
+ * it in the browser; a copy taken before then stays valid until it expires.
17
+ * Rotating SESSION_SECRET ends every session at once. The admission rules are
18
+ * still re-read on every request — see gate.ts and docs/decisions.md.
16
19
  */
17
20
  import type { Context } from "hono";
18
21
  import { type Env } from "@micacms/runtime";
19
22
  import type { AuthConfig } from "./config.js";
20
- import type { SessionIdentity, SessionRecord, SessionRepository } from "./sessions-repository.js";
21
23
  export declare const SESSION_COOKIE = "mica_session";
22
24
  export declare const CSRF_COOKIE = "mica_csrf";
23
25
  export declare const CSRF_HEADER = "x-csrf-token";
26
+ /** What the identity provider stated about the person holding this session. */
27
+ export interface SessionIdentity {
28
+ issuer: string;
29
+ /** Opaque and stable at that issuer. */
30
+ subject: string;
31
+ email: string;
32
+ /** The provider's own `email_verified`. Absent counts as false. */
33
+ emailVerified: boolean;
34
+ name: string;
35
+ /** The groups claim, reduced at sign-in to the groups the rules name. */
36
+ groups: string[];
37
+ }
38
+ export interface SessionRecord {
39
+ identity: SessionIdentity;
40
+ csrfToken: string;
41
+ createdAt: string;
42
+ /** When the session was last issued; drives the sliding idle window. */
43
+ refreshedAt: string;
44
+ expiresAt: string;
45
+ }
24
46
  export interface SessionService {
25
- /**
26
- * `identity` is null in local mode, where there is no provider and no user:
27
- * the row exists to carry a CSRF token and an expiry, and nothing else.
28
- */
29
- create(input: {
30
- identity: SessionIdentity | null;
31
- ip?: string | null;
32
- userAgent?: string | null;
33
- }): {
47
+ /** Seals a new session. Throws when the identity cannot fit in a cookie. */
48
+ create(identity: SessionIdentity): {
34
49
  record: SessionRecord;
35
50
  cookie: string;
36
- csrfToken: string;
37
51
  };
52
+ /** The live session in a cookie, or null for a missing, forged or expired one. */
38
53
  resolve(cookieValue: string | undefined): SessionRecord | null;
39
- destroy(cookieValue: string | undefined): void;
40
- /** Signs one person out on every device. */
41
- destroyAllForIdentity(issuer: string, subject: string): void;
54
+ /** A later-expiring copy of an active session, or null while it is fresh. */
55
+ refresh(record: SessionRecord): {
56
+ record: SessionRecord;
57
+ cookie: string;
58
+ } | null;
42
59
  sessionCookieOptions(): CookieOptions;
43
60
  csrfCookieOptions(): CookieOptions;
44
61
  clearedCookieOptions(): CookieOptions;
45
- purgeExpired(): number;
46
62
  }
47
63
  export interface CookieOptions {
48
64
  path: string;
@@ -53,7 +69,7 @@ export interface CookieOptions {
53
69
  }
54
70
  /** Constant-time string comparison that tolerates differing lengths. */
55
71
  export declare function safeEqual(a: string, b: string): boolean;
56
- export declare function createSessionService(sessions: SessionRepository, config: AuthConfig): SessionService;
72
+ export declare function createSessionService(config: AuthConfig): SessionService;
57
73
  /** Issues the session cookies for a browser that has just been identified. */
58
74
  export declare function establishSession(c: Context<Env>, sessions: SessionService, identity: SessionIdentity): void;
59
75
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,EAAyB,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAC7C,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAK1C,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE;QACZ,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;QACjC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,GAAG;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,IAAI,CAAC;IAC/D,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/C,4CAA4C;IAC5C,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7D,oBAAoB,IAAI,aAAa,CAAC;IACtC,iBAAiB,IAAI,aAAa,CAAC;IACnC,oBAAoB,IAAI,aAAa,CAAC;IACtC,YAAY,IAAI,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CASvD;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,UAAU,GACjB,cAAc,CA2GhB;AAMD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EACf,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,eAAe,GACxB,IAAI,CAYN"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,EAAe,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAC7C,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAW1C,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,kFAAkF;IAClF,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,IAAI,CAAC;IAC/D,6EAA6E;IAC7E,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjF,oBAAoB,IAAI,aAAa,CAAC;IACtC,iBAAiB,IAAI,aAAa,CAAC;IACnC,oBAAoB,IAAI,aAAa,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CASvD;AA6CD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAoHvE;AAMD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EACf,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,eAAe,GACxB,IAAI,CAKN"}
package/dist/session.js CHANGED
@@ -1,31 +1,34 @@
1
1
  /**
2
2
  * Session management.
3
3
  *
4
- * Server-side sessions in SQLite, addressed by an opaque random token held in a
5
- * Secure/HttpOnly/SameSite cookie. No JWTs: revocation has to be immediate, and
6
- * a row delete is the simplest way to get it.
4
+ * The session is the cookie: who the provider said this is, a CSRF token and
5
+ * an expiry, encrypted with a key derived from SESSION_SECRET. There is no
6
+ * session table, so Mica stores nothing about people at all.
7
7
  *
8
- * Three layers of defence on the cookie value:
9
- * 1. the token is 256 bits from `crypto.randomBytes`;
10
- * 2. it is HMAC-signed with SESSION_SECRET, so forged or truncated cookies
11
- * are rejected without touching the database;
12
- * 3. only its SHA-256 is stored, so a database dump yields no usable session.
8
+ * - AES-256-GCM, so the cookie can be neither read nor altered. Encrypted and
9
+ * not only signed, because it carries an email address and groups.
10
+ * - A fresh random IV per cookie, and a new cookie at every sign-in, so a
11
+ * client-supplied value is never adopted (session fixation).
12
+ * - The idle window slides: an active session is re-issued every few
13
+ * minutes with a later expiry, capped by the absolute lifetime.
13
14
  *
14
- * Session fixation is handled by never adopting a client-supplied identifier —
15
- * a token is minted at login and the previous one deleted.
15
+ * What this gives up is server-side revocation of one cookie. Sign-out clears
16
+ * it in the browser; a copy taken before then stays valid until it expires.
17
+ * Rotating SESSION_SECRET ends every session at once. The admission rules are
18
+ * still re-read on every request — see gate.ts and docs/decisions.md.
16
19
  */
17
- import { createHash, createHmac, timingSafeEqual } from "node:crypto";
18
- import { getCookie, setCookie } from "hono/cookie";
19
- import { clientIp, randomToken } from "@micacms/runtime";
20
+ import { createCipheriv, createDecipheriv, hkdfSync, randomBytes, timingSafeEqual } from "node:crypto";
21
+ import { setCookie } from "hono/cookie";
22
+ import { randomToken } from "@micacms/runtime";
20
23
  export const SESSION_COOKIE = "mica_session";
21
24
  export const CSRF_COOKIE = "mica_csrf";
22
25
  export const CSRF_HEADER = "x-csrf-token";
23
- /** Writing `last_seen_at` on every request would be a write per page view. */
24
- const TOUCH_INTERVAL_MS = 5 * 60 * 1000;
25
- const sha256 = (value) => createHash("sha256").update(value).digest("hex");
26
- function sign(token, secret) {
27
- return createHmac("sha256", secret).update(token).digest("base64url");
28
- }
26
+ /** How often an active session is re-issued with a later idle expiry. */
27
+ const REFRESH_INTERVAL_MS = 5 * 60 * 1000;
28
+ /** Browsers drop cookies over 4096 bytes; refuse rather than sign in to nothing. */
29
+ const MAX_COOKIE_BYTES = 4000;
30
+ const IV_BYTES = 12;
31
+ const TAG_BYTES = 16;
29
32
  /** Constant-time string comparison that tolerates differing lengths. */
30
33
  export function safeEqual(a, b) {
31
34
  const left = Buffer.from(a);
@@ -37,64 +40,117 @@ export function safeEqual(a, b) {
37
40
  }
38
41
  return timingSafeEqual(left, right);
39
42
  }
40
- export function createSessionService(sessions, config) {
43
+ /**
44
+ * Keycloak sends `/mica-editors` where the rule says `mica-editors`; both
45
+ * spellings are the same group — see admission.ts.
46
+ */
47
+ const groupName = (group) => group.replace(/^\//, "");
48
+ /** Defensive read of a decrypted payload: it was written by us, but checked anyway. */
49
+ function parseRecord(json) {
50
+ try {
51
+ const value = JSON.parse(json);
52
+ const identity = value.identity;
53
+ if (!identity ||
54
+ typeof identity.issuer !== "string" ||
55
+ typeof identity.subject !== "string" ||
56
+ typeof value.csrfToken !== "string" ||
57
+ typeof value.createdAt !== "string" ||
58
+ typeof value.refreshedAt !== "string" ||
59
+ typeof value.expiresAt !== "string") {
60
+ return null;
61
+ }
62
+ return {
63
+ identity: {
64
+ issuer: identity.issuer,
65
+ subject: identity.subject,
66
+ email: typeof identity.email === "string" ? identity.email : "",
67
+ emailVerified: identity.emailVerified === true,
68
+ name: typeof identity.name === "string" ? identity.name : "",
69
+ groups: Array.isArray(identity.groups)
70
+ ? identity.groups.filter((group) => typeof group === "string")
71
+ : [],
72
+ },
73
+ csrfToken: value.csrfToken,
74
+ createdAt: value.createdAt,
75
+ refreshedAt: value.refreshedAt,
76
+ expiresAt: value.expiresAt,
77
+ };
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ export function createSessionService(config) {
41
84
  const ttlMs = config.SESSION_TTL_HOURS * 3600_000;
42
85
  const idleMs = config.SESSION_IDLE_HOURS * 3600_000;
86
+ const key = Buffer.from(hkdfSync("sha256", config.sessionSecret, "", "mica session cookie v1", 32));
87
+ // Only the groups a rule names are kept: an Entra token can carry hundreds,
88
+ // and a cookie holds 4 KB. A group added to the rules later counts from the
89
+ // next sign-in.
90
+ const admitted = new Set(config.admitGroups.map(groupName));
43
91
  /** Expiry is the earlier of the idle window and the absolute lifetime. */
44
92
  const nextExpiry = (createdAt, now) => new Date(Math.min(now + idleMs, createdAt + ttlMs)).toISOString();
45
- const unwrap = (cookieValue) => {
46
- if (!cookieValue)
47
- return null;
48
- const separator = cookieValue.lastIndexOf(".");
49
- if (separator <= 0)
93
+ const seal = (record) => {
94
+ const iv = randomBytes(IV_BYTES);
95
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
96
+ const body = Buffer.concat([cipher.update(JSON.stringify(record), "utf8"), cipher.final()]);
97
+ const cookie = Buffer.concat([iv, body, cipher.getAuthTag()]).toString("base64url");
98
+ if (cookie.length > MAX_COOKIE_BYTES) {
99
+ throw new Error("The session does not fit in a cookie; the identity is too large.");
100
+ }
101
+ return cookie;
102
+ };
103
+ const open = (cookieValue) => {
104
+ const raw = Buffer.from(cookieValue, "base64url");
105
+ if (raw.length <= IV_BYTES + TAG_BYTES)
50
106
  return null;
51
- const token = cookieValue.slice(0, separator);
52
- const signature = cookieValue.slice(separator + 1);
53
- if (!safeEqual(signature, sign(token, config.sessionSecret)))
107
+ try {
108
+ const decipher = createDecipheriv("aes-256-gcm", key, raw.subarray(0, IV_BYTES));
109
+ decipher.setAuthTag(raw.subarray(raw.length - TAG_BYTES));
110
+ const json = Buffer.concat([
111
+ decipher.update(raw.subarray(IV_BYTES, raw.length - TAG_BYTES)),
112
+ decipher.final(),
113
+ ]).toString("utf8");
114
+ return parseRecord(json);
115
+ }
116
+ catch {
117
+ // Forged, truncated, or sealed with a secret that has since rotated.
54
118
  return null;
55
- return token;
119
+ }
56
120
  };
57
121
  return {
58
- create({ identity, ip, userAgent }) {
59
- const token = randomToken(32);
60
- const csrfToken = randomToken(32);
122
+ create(identity) {
61
123
  const now = Date.now();
62
- const record = sessions.create({
63
- id: sha256(token),
64
- identity,
65
- csrfToken,
124
+ const record = {
125
+ identity: {
126
+ ...identity,
127
+ groups: identity.groups.filter((group) => admitted.has(groupName(group))),
128
+ },
129
+ csrfToken: randomToken(32),
130
+ createdAt: new Date(now).toISOString(),
131
+ refreshedAt: new Date(now).toISOString(),
66
132
  expiresAt: nextExpiry(now, now),
67
- ip: ip ?? null,
68
- userAgent: userAgent ?? null,
69
- });
70
- return { record, cookie: `${token}.${sign(token, config.sessionSecret)}`, csrfToken };
133
+ };
134
+ return { record, cookie: seal(record) };
71
135
  },
72
136
  resolve(cookieValue) {
73
- const token = unwrap(cookieValue);
74
- if (!token)
75
- return null;
76
- const record = sessions.find(sha256(token));
77
- if (!record)
137
+ if (!cookieValue)
78
138
  return null;
79
- const now = Date.now();
80
- if (Date.parse(record.expiresAt) <= now) {
81
- sessions.delete(record.id);
139
+ const record = open(cookieValue);
140
+ if (!record || Date.parse(record.expiresAt) <= Date.now())
82
141
  return null;
83
- }
84
- if (now - Date.parse(record.lastSeenAt) > TOUCH_INTERVAL_MS) {
85
- const expiresAt = nextExpiry(Date.parse(record.createdAt), now);
86
- sessions.touch(record.id, new Date(now).toISOString(), expiresAt);
87
- return { ...record, lastSeenAt: new Date(now).toISOString(), expiresAt };
88
- }
89
142
  return record;
90
143
  },
91
- destroy(cookieValue) {
92
- const token = unwrap(cookieValue);
93
- if (token)
94
- sessions.delete(sha256(token));
95
- },
96
- destroyAllForIdentity(issuer, subject) {
97
- sessions.deleteForIdentity(issuer, subject);
144
+ refresh(record) {
145
+ const now = Date.now();
146
+ if (now - Date.parse(record.refreshedAt) < REFRESH_INTERVAL_MS)
147
+ return null;
148
+ const next = {
149
+ ...record,
150
+ refreshedAt: new Date(now).toISOString(),
151
+ expiresAt: nextExpiry(Date.parse(record.createdAt), now),
152
+ };
153
+ return { record: next, cookie: seal(next) };
98
154
  },
99
155
  sessionCookieOptions() {
100
156
  return {
@@ -127,9 +183,6 @@ export function createSessionService(sessions, config) {
127
183
  maxAge: 0,
128
184
  };
129
185
  },
130
- purgeExpired() {
131
- return sessions.deleteExpired();
132
- },
133
186
  };
134
187
  }
135
188
  /* ------------------------------------------------------------------ *
@@ -137,14 +190,8 @@ export function createSessionService(sessions, config) {
137
190
  * ------------------------------------------------------------------ */
138
191
  /** Issues the session cookies for a browser that has just been identified. */
139
192
  export function establishSession(c, sessions, identity) {
140
- // Session fixation: discard whatever identifier the client already holds.
141
- sessions.destroy(getCookie(c, SESSION_COOKIE));
142
- const { cookie, csrfToken } = sessions.create({
143
- identity,
144
- ip: clientIp(c),
145
- userAgent: c.req.header("user-agent") ?? null,
146
- });
193
+ const { record, cookie } = sessions.create(identity);
147
194
  setCookie(c, SESSION_COOKIE, cookie, sessions.sessionCookieOptions());
148
- setCookie(c, CSRF_COOKIE, csrfToken, sessions.csrfCookieOptions());
195
+ setCookie(c, CSRF_COOKIE, record.csrfToken, sessions.csrfCookieOptions());
149
196
  }
150
197
  //# sourceMappingURL=session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micacms/auth-oidc",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "The server-only OIDC gate for Mica: sign-in, sessions and admission around @micacms/runtime. Never enters a browser bundle, and never a dependency of the local editor.",
6
6
  "license": "MIT",
@@ -28,8 +28,8 @@
28
28
  "hono": "^4.6.20",
29
29
  "openid-client": "^6.8.5",
30
30
  "zod": "^4.3.6",
31
- "@micacms/core": "0.2.1",
32
- "@micacms/runtime": "0.2.1"
31
+ "@micacms/runtime": "0.3.0",
32
+ "@micacms/core": "0.3.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^22.20.1",
@@ -1,56 +0,0 @@
1
- /**
2
- * Session repository.
3
- *
4
- * The primary key is the SHA-256 of the cookie token, never the token itself:
5
- * a leaked database backup therefore does not hand an attacker usable sessions.
6
- *
7
- * A row carries who the provider said this is, because there is no user table
8
- * to look it up in. That is the whole of Mica's identity storage.
9
- */
10
- import type { SqlDatabase } from "@micacms/runtime";
11
- /**
12
- * What the identity provider stated about the person holding this session.
13
- *
14
- * `null` where there is no provider: local mode has no user, and the row is
15
- * then a CSRF token with an expiry.
16
- */
17
- export interface SessionIdentity {
18
- issuer: string;
19
- /** Opaque and stable at that issuer. The account key, and never the email. */
20
- subject: string;
21
- email: string;
22
- /** The provider's own `email_verified`. Absent counts as false. */
23
- emailVerified: boolean;
24
- name: string;
25
- /** The groups claim as it arrived, or empty when the provider sends none. */
26
- groups: string[];
27
- }
28
- export interface SessionRecord {
29
- id: string;
30
- identity: SessionIdentity | null;
31
- csrfToken: string;
32
- createdAt: string;
33
- lastSeenAt: string;
34
- expiresAt: string;
35
- ip: string | null;
36
- userAgent: string | null;
37
- }
38
- export declare function createSessionRepository(db: SqlDatabase): {
39
- create(input: {
40
- id: string;
41
- identity: SessionIdentity | null;
42
- csrfToken: string;
43
- expiresAt: string;
44
- ip?: string | null;
45
- userAgent?: string | null;
46
- }): SessionRecord;
47
- find(id: string): SessionRecord | undefined;
48
- touch(id: string, lastSeenAt: string, expiresAt: string): void;
49
- delete(id: string): boolean;
50
- /** Every session this person holds, on every device. */
51
- deleteForIdentity(issuer: string, subject: string): number;
52
- deleteExpired(now?: string): number;
53
- count(): number;
54
- };
55
- export type SessionRepository = ReturnType<typeof createSessionRepository>;
56
- //# sourceMappingURL=sessions-repository.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sessions-repository.d.ts","sourceRoot":"","sources":["../src/sessions-repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA2DD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,WAAW;kBAErC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,GAAG,aAAa;aAwBR,MAAM,GAAG,aAAa,GAAG,SAAS;cAKjC,MAAM,cAAc,MAAM,aAAa,MAAM,GAAG,IAAI;eAQnD,MAAM,GAAG,OAAO;IAI3B,wDAAwD;8BAC9B,MAAM,WAAW,MAAM,GAAG,MAAM;iCAKX,MAAM;aAI5C,MAAM;EAIlB;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
@@ -1,96 +0,0 @@
1
- /**
2
- * Session repository.
3
- *
4
- * The primary key is the SHA-256 of the cookie token, never the token itself:
5
- * a leaked database backup therefore does not hand an attacker usable sessions.
6
- *
7
- * A row carries who the provider said this is, because there is no user table
8
- * to look it up in. That is the whole of Mica's identity storage.
9
- */
10
- /**
11
- * The groups column holds whatever the provider sent, serialised. It is read
12
- * back defensively — a claim that was a string, or an object, or absent, must
13
- * not throw four weeks later on a request that has nothing to do with it.
14
- */
15
- function parseGroups(value) {
16
- try {
17
- const parsed = JSON.parse(value);
18
- return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
19
- }
20
- catch {
21
- return [];
22
- }
23
- }
24
- function toRecord(row) {
25
- return {
26
- id: row.id,
27
- identity: row.issuer && row.subject
28
- ? {
29
- issuer: row.issuer,
30
- subject: row.subject,
31
- email: row.email ?? "",
32
- emailVerified: row.email_verified === 1,
33
- name: row.name ?? "",
34
- groups: parseGroups(row.groups),
35
- }
36
- : null,
37
- csrfToken: row.csrf_token,
38
- createdAt: row.created_at,
39
- lastSeenAt: row.last_seen_at,
40
- expiresAt: row.expires_at,
41
- ip: row.ip,
42
- userAgent: row.user_agent,
43
- };
44
- }
45
- const COLUMNS = "id, issuer, subject, email, email_verified, name, groups, csrf_token, " +
46
- "created_at, last_seen_at, expires_at, ip, user_agent";
47
- export function createSessionRepository(db) {
48
- return {
49
- create(input) {
50
- const now = new Date().toISOString();
51
- db.run(`INSERT INTO sessions
52
- (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
53
- input.id,
54
- input.identity?.issuer ?? null,
55
- input.identity?.subject ?? null,
56
- input.identity?.email ?? null,
57
- input.identity?.emailVerified ? 1 : 0,
58
- input.identity?.name ?? null,
59
- JSON.stringify(input.identity?.groups ?? []),
60
- input.csrfToken,
61
- now,
62
- now,
63
- input.expiresAt,
64
- input.ip ?? null,
65
- input.userAgent ?? null,
66
- ]);
67
- return this.find(input.id);
68
- },
69
- find(id) {
70
- const row = db.get(`SELECT ${COLUMNS} FROM sessions WHERE id = ?`, [id]);
71
- return row && toRecord(row);
72
- },
73
- touch(id, lastSeenAt, expiresAt) {
74
- db.run("UPDATE sessions SET last_seen_at = ?, expires_at = ? WHERE id = ?", [
75
- lastSeenAt,
76
- expiresAt,
77
- id,
78
- ]);
79
- },
80
- delete(id) {
81
- return db.run("DELETE FROM sessions WHERE id = ?", [id]).changes > 0;
82
- },
83
- /** Every session this person holds, on every device. */
84
- deleteForIdentity(issuer, subject) {
85
- return db.run("DELETE FROM sessions WHERE issuer = ? AND subject = ?", [issuer, subject])
86
- .changes;
87
- },
88
- deleteExpired(now = new Date().toISOString()) {
89
- return db.run("DELETE FROM sessions WHERE expires_at <= ?", [now]).changes;
90
- },
91
- count() {
92
- return db.get("SELECT COUNT(*) AS count FROM sessions")?.count ?? 0;
93
- },
94
- };
95
- }
96
- //# sourceMappingURL=sessions-repository.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sessions-repository.js","sourceRoot":"","sources":["../src/sessions-repository.ts"],"names":[],"mappings":"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"}