@wtfalch/auth 0.9.1 → 0.11.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/dist/sessions.js CHANGED
@@ -24,7 +24,12 @@ export function createMemorySessionStore() {
24
24
  const record = records.get(key);
25
25
  if (!record || record.revokedAt !== null || record.generation !== generation)
26
26
  return false;
27
- records.set(key, { ...record, ...next, generation: generation + 1 });
27
+ records.set(key, {
28
+ ...record,
29
+ ...next,
30
+ generation: generation + 1,
31
+ lastSeenAt: Math.floor(Date.now() / 1000),
32
+ });
28
33
  return true;
29
34
  },
30
35
  async revoke(key, at) {
@@ -41,6 +46,16 @@ export function createMemorySessionStore() {
41
46
  }
42
47
  return count;
43
48
  },
49
+ async listSubject(context, subject) {
50
+ const nowSeconds = Math.floor(Date.now() / 1000);
51
+ return [...records.values()]
52
+ .filter((r) => r.context === context &&
53
+ r.subject === subject &&
54
+ r.revokedAt === null &&
55
+ r.expiresAt > nowSeconds)
56
+ .sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))
57
+ .map((r) => ({ ...r }));
58
+ },
44
59
  };
45
60
  }
46
61
  /** The table `createPostgresSessionStore` expects. Run it in the app's own migrations. */
@@ -52,11 +67,25 @@ export const POSTGRES_SESSION_TABLE = `create table if not exists wtfalch_auth_s
52
67
  payload text not null,
53
68
  expires_at bigint not null,
54
69
  lease_until bigint,
55
- revoked_at bigint
70
+ revoked_at bigint,
71
+ created_at bigint,
72
+ last_seen_at bigint,
73
+ user_agent text,
74
+ ip text
56
75
  );
57
76
  create index if not exists wtfalch_auth_sessions_subject on wtfalch_auth_sessions (context, subject);
58
77
  create index if not exists wtfalch_auth_sessions_expiry on wtfalch_auth_sessions (expires_at);`;
78
+ /**
79
+ * `POSTGRES_SESSION_TABLE` already has these columns for a fresh table. A
80
+ * consumer whose table predates them runs this once, in its own migrations,
81
+ * to catch up; `add column if not exists` makes it safe to run again.
82
+ */
83
+ export const POSTGRES_SESSION_MIGRATION_1 = `alter table wtfalch_auth_sessions add column if not exists created_at bigint;
84
+ alter table wtfalch_auth_sessions add column if not exists last_seen_at bigint;
85
+ alter table wtfalch_auth_sessions add column if not exists user_agent text;
86
+ alter table wtfalch_auth_sessions add column if not exists ip text;`;
59
87
  const nullable = (value) => value === null || value === undefined ? null : Number(value);
88
+ const nullableString = (value) => value === null || value === undefined ? null : String(value);
60
89
  /**
61
90
  * Postgres through whatever driver the app already has. `query` runs one
62
91
  * parameterised statement and resolves its rows:
@@ -77,14 +106,19 @@ export function createPostgresSessionStore(input) {
77
106
  expiresAt: Number(row.expires_at),
78
107
  leaseUntil: nullable(row.lease_until),
79
108
  revokedAt: nullable(row.revoked_at),
109
+ createdAt: nullable(row.created_at),
110
+ lastSeenAt: nullable(row.last_seen_at),
111
+ userAgent: nullableString(row.user_agent),
112
+ ip: nullableString(row.ip),
80
113
  });
81
114
  return {
82
115
  async create(r) {
83
116
  await query(`delete from wtfalch_auth_sessions where key in
84
117
  (select key from wtfalch_auth_sessions where expires_at <= $1 limit 100)`, [Math.floor(Date.now() / 1000)]);
85
118
  await query(`insert into wtfalch_auth_sessions
86
- (key, context, subject, generation, payload, expires_at, lease_until, revoked_at)
87
- values ($1, $2, $3, $4, $5, $6, $7, $8)`, [
119
+ (key, context, subject, generation, payload, expires_at, lease_until, revoked_at,
120
+ created_at, last_seen_at, user_agent, ip)
121
+ values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
88
122
  r.key,
89
123
  r.context,
90
124
  r.subject,
@@ -93,6 +127,10 @@ export function createPostgresSessionStore(input) {
93
127
  r.expiresAt,
94
128
  r.leaseUntil,
95
129
  r.revokedAt,
130
+ r.createdAt,
131
+ r.lastSeenAt,
132
+ r.userAgent,
133
+ r.ip,
96
134
  ]);
97
135
  },
98
136
  async get(key) {
@@ -101,9 +139,17 @@ export function createPostgresSessionStore(input) {
101
139
  },
102
140
  async advance(key, generation, next) {
103
141
  const rows = await query(`update wtfalch_auth_sessions
104
- set payload = $3, expires_at = $4, lease_until = $5, generation = generation + 1
142
+ set payload = $3, expires_at = $4, lease_until = $5, generation = generation + 1,
143
+ last_seen_at = $6
105
144
  where key = $1 and generation = $2 and revoked_at is null
106
- returning key`, [key, generation, next.payload, next.expiresAt, next.leaseUntil]);
145
+ returning key`, [
146
+ key,
147
+ generation,
148
+ next.payload,
149
+ next.expiresAt,
150
+ next.leaseUntil,
151
+ Math.floor(Date.now() / 1000),
152
+ ]);
107
153
  return rows.length === 1;
108
154
  },
109
155
  async revoke(key, at) {
@@ -115,5 +161,11 @@ export function createPostgresSessionStore(input) {
115
161
  returning key`, [context, subject, at]);
116
162
  return rows.length;
117
163
  },
164
+ async listSubject(context, subject) {
165
+ const rows = await query(`select * from wtfalch_auth_sessions
166
+ where context = $1 and subject = $2 and revoked_at is null and expires_at > $3
167
+ order by created_at desc nulls last`, [context, subject, Math.floor(Date.now() / 1000)]);
168
+ return rows.map(record);
169
+ },
118
170
  };
119
171
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/auth",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Sign in against auth.wtfalch.dev: a server session for a Next.js app, and a browser client with silent single sign-on across subdomains.",
5
5
  "repository": {
6
6
  "type": "git",